diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..9e26507462f2d0ff6c2f6c89f38bb0fc5f8c0c39 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,52 @@ + +cmake_minimum_required(VERSION 2.8) + +project(bgs) + +set(CMAKE_CXX_FLAGS "${CMAKE_CSS_FLAGS} -std=gnu++0x") + +find_package(OpenCV REQUIRED) + +if(${OpenCV_VERSION} VERSION_LESS 2.3.1) + message (FATAL_ERROR "OpenCV version is not compatible: ${OpenCV_VERSION}") +endif() + +file(GLOB sources FrameProcessor.cpp PreProcessor.cpp VideoAnalysis.cpp VideoCapture.cpp) +file(GLOB main Main.cpp) +file(GLOB demo Demo.cpp) + +list(REMOVE_ITEM sources ${demo}) + +file(GLOB_RECURSE analysis package_analysis/*.cpp) +file(GLOB_RECURSE bgs package_bgs/*.cpp) +file(GLOB_RECURSE bgs_include package_bgs/*.h) + +# GMG is not available in older OpenCV versions +if(${OpenCV_VERSION} VERSION_LESS 2.4.3) + file(GLOB gmg package_bgs/GMG.cpp) + list(REMOVE_ITEM bgs ${gmg}) +endif() + +include_directories(${CMAKE_SOURCE_DIR}) + +add_library(bgs SHARED ${sources} ${bgs} ${analysis}) +target_link_libraries(bgs ${OpenCV_LIBS}) +set_property(TARGET bgs PROPERTY PUBLIC_HEADER ${bgs_include}) + +add_executable(bgs_bin ${main}) +target_link_libraries(bgs_bin ${OpenCV_LIBS} bgs) +set_target_properties(bgs_bin + PROPERTIES OUTPUT_NAME bgs) + +add_executable(bgs_demo ${demo}) +target_link_libraries(bgs_demo ${OpenCV_LIBS} bgs) + +INSTALL(TARGETS bgs + bgs_demo + bgs_bin + RUNTIME DESTINATION bin COMPONENT app + LIBRARY DESTINATION lib COMPONENT runtime + ARCHIVE DESTINATION lib COMPONENT runtime + PUBLIC_HEADER DESTINATION include/package_bgs COMPONENT dev + FRAMEWORK DESTINATION "/Library/Frameworks" +) diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 0000000000000000000000000000000000000000..94a9ed024d3859793618152ea559a168bbcbb5e2 --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/Config.h b/Config.h new file mode 100644 index 0000000000000000000000000000000000000000..01cf7210a47f3682e5e8010311a4f8fc11b0bedd --- /dev/null +++ b/Config.h @@ -0,0 +1,22 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +const int KEY_REPEAT = 'r'; +const int KEY_SPACE = 32; +const int KEY_ESC = 27; +const int KEY_ESC2 = 'q'; diff --git a/Demo.cpp b/Demo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3936976268b5f548f0b99a7e96fc1f74ba92c52c --- /dev/null +++ b/Demo.cpp @@ -0,0 +1,182 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "package_bgs/FrameDifferenceBGS.h" +#include "package_bgs/StaticFrameDifferenceBGS.h" +#include "package_bgs/WeightedMovingMeanBGS.h" +#include "package_bgs/WeightedMovingVarianceBGS.h" +#include "package_bgs/MixtureOfGaussianV1BGS.h" +#include "package_bgs/MixtureOfGaussianV2BGS.h" +#include "package_bgs/AdaptiveBackgroundLearning.h" +#include "package_bgs/AdaptiveSelectiveBackgroundLearning.h" + +#if CV_MAJOR_VERSION >= 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3 +#include "package_bgs/GMG.h" +#endif + +#include "package_bgs/dp/DPAdaptiveMedianBGS.h" +#include "package_bgs/dp/DPGrimsonGMMBGS.h" +#include "package_bgs/dp/DPZivkovicAGMMBGS.h" +#include "package_bgs/dp/DPMeanBGS.h" +#include "package_bgs/dp/DPWrenGABGS.h" +#include "package_bgs/dp/DPPratiMediodBGS.h" +#include "package_bgs/dp/DPEigenbackgroundBGS.h" +#include "package_bgs/dp/DPTextureBGS.h" + +#include "package_bgs/tb/T2FGMM_UM.h" +#include "package_bgs/tb/T2FGMM_UV.h" +#include "package_bgs/tb/T2FMRF_UM.h" +#include "package_bgs/tb/T2FMRF_UV.h" +#include "package_bgs/tb/FuzzySugenoIntegral.h" +#include "package_bgs/tb/FuzzyChoquetIntegral.h" + +#include "package_bgs/lb/LBSimpleGaussian.h" +#include "package_bgs/lb/LBFuzzyGaussian.h" +#include "package_bgs/lb/LBMixtureOfGaussians.h" +#include "package_bgs/lb/LBAdaptiveSOM.h" +#include "package_bgs/lb/LBFuzzyAdaptiveSOM.h" + +#if !defined(_WIN32) +// Currently this method works only on Linux platform. +#include "package_bgs/ck/LbpMrf.h" +#endif + +#include "package_bgs/jmo/MultiLayerBGS.h" +#include "package_bgs/pt/PixelBasedAdaptiveSegmenter.h" +#include "package_bgs/av/VuMeter.h" +#include "package_bgs/ae/KDE.h" +#include "package_bgs/db/IndependentMultimodalBGS.h" +#include "package_bgs/sjn/SJN_MultiCueBGS.h" + +void main(int argc, char **argv) +{ + std::cout << "Using OpenCV " << CV_MAJOR_VERSION << "." << CV_MINOR_VERSION << "." << CV_SUBMINOR_VERSION << std::endl; + + CvCapture *capture = 0; + int resize_factor = 100; + + if(argc > 1) + { + std::cout << "Openning: " << argv[1] << std::endl; + capture = cvCaptureFromAVI(argv[1]); + } + else + { + capture = cvCaptureFromCAM(0); + resize_factor = 50; // set size = 50% of original image + } + + if(!capture) + { + std::cerr << "Cannot initialize video!" << std::endl; + return; + } + + IplImage *frame_aux = cvQueryFrame(capture); + IplImage *frame = cvCreateImage(cvSize((int)((frame_aux->width*resize_factor)/100) , (int)((frame_aux->height*resize_factor)/100)), frame_aux->depth, frame_aux->nChannels); + cvResize(frame_aux, frame); + + /* Background Subtraction Methods */ + IBGS *bgs; + + /*** Default Package ***/ + bgs = new FrameDifferenceBGS; + //bgs = new StaticFrameDifferenceBGS; + //bgs = new WeightedMovingMeanBGS; + //bgs = new WeightedMovingVarianceBGS; + //bgs = new MixtureOfGaussianV1BGS; + //bgs = new MixtureOfGaussianV2BGS; + //bgs = new AdaptiveBackgroundLearning; + //bgs = new AdaptiveSelectiveBackgroundLearning; + //bgs = new GMG; + + /*** DP Package (thanks to Donovan Parks) ***/ + //bgs = new DPAdaptiveMedianBGS; + //bgs = new DPGrimsonGMMBGS; + //bgs = new DPZivkovicAGMMBGS; + //bgs = new DPMeanBGS; + //bgs = new DPWrenGABGS; + //bgs = new DPPratiMediodBGS; + //bgs = new DPEigenbackgroundBGS; + //bgs = new DPTextureBGS; + + /*** TB Package (thanks to Thierry Bouwmans, Fida EL BAF and Zhenjie Zhao) ***/ + //bgs = new T2FGMM_UM; + //bgs = new T2FGMM_UV; + //bgs = new T2FMRF_UM; + //bgs = new T2FMRF_UV; + //bgs = new FuzzySugenoIntegral; + //bgs = new FuzzyChoquetIntegral; + + /*** JMO Package (thanks to Jean-Marc Odobez) ***/ + //bgs = new MultiLayerBGS; + + /*** PT Package (thanks to Martin Hofmann, Philipp Tiefenbacher and Gerhard Rigoll) ***/ + //bgs = new PixelBasedAdaptiveSegmenter; + + /*** LB Package (thanks to Laurence Bender) ***/ + //bgs = new LBSimpleGaussian; + //bgs = new LBFuzzyGaussian; + //bgs = new LBMixtureOfGaussians; + //bgs = new LBAdaptiveSOM; + //bgs = new LBFuzzyAdaptiveSOM; + + /*** LBP-MRF Package (thanks to Csaba Kertész) ***/ + //bgs = new LbpMrf; + + /*** AV Package (thanks to Lionel Robinault and Antoine Vacavant) ***/ + //bgs = new VuMeter; + + /*** EG Package (thanks to Ahmed Elgammal) ***/ + //bgs = new KDE; + + /*** DB Package (thanks to Domenico Daniele Bloisi) ***/ + //bgs = new IndependentMultimodalBGS; + + /*** SJN Package (thanks to SeungJong Noh) ***/ + //bgs = new SJN_MultiCueBGS; + + int key = 0; + while(key != 'q') + { + frame_aux = cvQueryFrame(capture); + if(!frame_aux) break; + + cvResize(frame_aux, frame); + + cv::Mat img_input(frame); + cv::imshow("input", img_input); + + cv::Mat img_mask; + cv::Mat img_bkgmodel; + bgs->process(img_input, img_mask, img_bkgmodel); // by default, it shows automatically the foreground mask image + + //if(!img_mask.empty()) + // cv::imshow("Foreground", img_mask); + // do something + + key = cvWaitKey(33); + } + + delete bgs; + + cvDestroyAllWindows(); + cvReleaseCapture(&capture); +} diff --git a/FrameProcessor.cpp b/FrameProcessor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..585cb9b544d19679b51e7bc5314981118b7da542 --- /dev/null +++ b/FrameProcessor.cpp @@ -0,0 +1,557 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "FrameProcessor.h" + +FrameProcessor::FrameProcessor() : firstTime(true), frameNumber(0), duration(0), tictoc(""), frameToStop(0) +{ + std::cout << "FrameProcessor()" << std::endl; + + loadConfig(); + saveConfig(); +} + +FrameProcessor::~FrameProcessor() +{ + std::cout << "~FrameProcessor()" << std::endl; +} + +void FrameProcessor::init() +{ + if(enablePreProcessor) + preProcessor = new PreProcessor; + + if(enableFrameDifferenceBGS) + frameDifference = new FrameDifferenceBGS; + + if(enableStaticFrameDifferenceBGS) + staticFrameDifference = new StaticFrameDifferenceBGS; + + if(enableWeightedMovingMeanBGS) + weightedMovingMean = new WeightedMovingMeanBGS; + + if(enableWeightedMovingVarianceBGS) + weightedMovingVariance = new WeightedMovingVarianceBGS; + + if(enableMixtureOfGaussianV1BGS) + mixtureOfGaussianV1BGS = new MixtureOfGaussianV1BGS; + + if(enableMixtureOfGaussianV2BGS) + mixtureOfGaussianV2BGS = new MixtureOfGaussianV2BGS; + + if(enableAdaptiveBackgroundLearning) + adaptiveBackgroundLearning = new AdaptiveBackgroundLearning; + +#if CV_MAJOR_VERSION >= 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3 + if(enableGMG) + gmg = new GMG; +#endif + + if(enableDPAdaptiveMedianBGS) + adaptiveMedian = new DPAdaptiveMedianBGS; + + if(enableDPGrimsonGMMBGS) + grimsonGMM = new DPGrimsonGMMBGS; + + if(enableDPZivkovicAGMMBGS) + zivkovicAGMM = new DPZivkovicAGMMBGS; + + if(enableDPMeanBGS) + temporalMean = new DPMeanBGS; + + if(enableDPWrenGABGS) + wrenGA = new DPWrenGABGS; + + if(enableDPPratiMediodBGS) + pratiMediod = new DPPratiMediodBGS; + + if(enableDPEigenbackgroundBGS) + eigenBackground = new DPEigenbackgroundBGS; + + if(enableDPTextureBGS) + textureBGS = new DPTextureBGS; + + if(enableT2FGMM_UM) + type2FuzzyGMM_UM = new T2FGMM_UM; + + if(enableT2FGMM_UV) + type2FuzzyGMM_UV = new T2FGMM_UV; + + if(enableT2FMRF_UM) + type2FuzzyMRF_UM = new T2FMRF_UM; + + if(enableT2FMRF_UV) + type2FuzzyMRF_UV = new T2FMRF_UV; + + if(enableFuzzySugenoIntegral) + fuzzySugenoIntegral = new FuzzySugenoIntegral; + + if(enableFuzzyChoquetIntegral) + fuzzyChoquetIntegral = new FuzzyChoquetIntegral; + + if(enableLBSimpleGaussian) + lbSimpleGaussian = new LBSimpleGaussian; + + if(enableLBFuzzyGaussian) + lbFuzzyGaussian = new LBFuzzyGaussian; + + if(enableLBMixtureOfGaussians) + lbMixtureOfGaussians = new LBMixtureOfGaussians; + + if(enableLBAdaptiveSOM) + lbAdaptiveSOM = new LBAdaptiveSOM; + + if(enableLBFuzzyAdaptiveSOM) + lbFuzzyAdaptiveSOM = new LBFuzzyAdaptiveSOM; + + #if !defined(_WIN32) + if(enableLbpMrf) + lbpMrf = new LbpMrf; + #endif + + if(enableMultiLayerBGS) + multiLayerBGS = new MultiLayerBGS; + + if(enablePBAS) + pixelBasedAdaptiveSegmenter = new PixelBasedAdaptiveSegmenter; + + if(enableVuMeter) + vuMeter = new VuMeter; + + if(enableKDE) + kde = new KDE; + + if(enableForegroundMaskAnalysis) + foregroundMaskAnalysis = new ForegroundMaskAnalysis; +} + +void FrameProcessor::process(std::string name, IBGS *bgs, const cv::Mat &img_input, cv::Mat &img_bgs) +{ + if(tictoc == name) + tic(name); + + cv::Mat img_bkgmodel; + bgs->process(img_input, img_bgs, img_bkgmodel); + + if(tictoc == name) + toc(); +} + +void FrameProcessor::process(const cv::Mat &img_input) +{ + frameNumber++; + + if(enablePreProcessor) + preProcessor->process(img_input, img_prep); + + if(enableFrameDifferenceBGS) + process("FrameDifferenceBGS", frameDifference, img_prep, img_framediff); + + if(enableStaticFrameDifferenceBGS) + process("StaticFrameDifferenceBGS", staticFrameDifference, img_prep, img_staticfdiff); + + if(enableWeightedMovingMeanBGS) + process("WeightedMovingMeanBGS", weightedMovingMean, img_prep, img_wmovmean); + + if(enableWeightedMovingVarianceBGS) + process("WeightedMovingVarianceBGS", weightedMovingVariance, img_prep, img_movvar); + + if(enableMixtureOfGaussianV1BGS) + process("MixtureOfGaussianV1BGS", mixtureOfGaussianV1BGS, img_prep, img_mog1); + + if(enableMixtureOfGaussianV2BGS) + process("MixtureOfGaussianV2BGS", mixtureOfGaussianV2BGS, img_prep, img_mog2); + + if(enableAdaptiveBackgroundLearning) + process("AdaptiveBackgroundLearning", adaptiveBackgroundLearning, img_prep, img_bkgl_fgmask); + +#if CV_MAJOR_VERSION >= 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3 + if(enableGMG) + process("GMG", gmg, img_prep, img_gmg); +#endif + + if(enableDPAdaptiveMedianBGS) + process("DPAdaptiveMedianBGS", adaptiveMedian, img_prep, img_adpmed); + + if(enableDPGrimsonGMMBGS) + process("DPGrimsonGMMBGS", grimsonGMM, img_prep, img_grigmm); + + if(enableDPZivkovicAGMMBGS) + process("DPZivkovicAGMMBGS", zivkovicAGMM, img_prep, img_zivgmm); + + if(enableDPMeanBGS) + process("DPMeanBGS", temporalMean, img_prep, img_tmpmean); + + if(enableDPWrenGABGS) + process("DPWrenGABGS", wrenGA, img_prep, img_wrenga); + + if(enableDPPratiMediodBGS) + process("DPPratiMediodBGS", pratiMediod, img_prep, img_pramed); + + if(enableDPEigenbackgroundBGS) + process("DPEigenbackgroundBGS", eigenBackground, img_prep, img_eigbkg); + + if(enableDPTextureBGS) + process("DPTextureBGS", textureBGS, img_prep, img_texbgs); + + if(enableT2FGMM_UM) + process("T2FGMM_UM", type2FuzzyGMM_UM, img_prep, img_t2fgmm_um); + + if(enableT2FGMM_UV) + process("T2FGMM_UV", type2FuzzyGMM_UV, img_prep, img_t2fgmm_uv); + + if(enableT2FMRF_UM) + process("T2FMRF_UM", type2FuzzyMRF_UM, img_prep, img_t2fmrf_um); + + if(enableT2FMRF_UV) + process("T2FMRF_UV", type2FuzzyMRF_UV, img_prep, img_t2fmrf_uv); + + if(enableFuzzySugenoIntegral) + process("FuzzySugenoIntegral", fuzzySugenoIntegral, img_prep, img_fsi); + + if(enableFuzzyChoquetIntegral) + process("FuzzyChoquetIntegral", fuzzyChoquetIntegral, img_prep, img_fci); + + if(enableLBSimpleGaussian) + process("LBSimpleGaussian", lbSimpleGaussian, img_prep, img_lb_sg); + + if(enableLBFuzzyGaussian) + process("LBFuzzyGaussian", lbFuzzyGaussian, img_prep, img_lb_fg); + + if(enableLBMixtureOfGaussians) + process("LBMixtureOfGaussians", lbMixtureOfGaussians, img_prep, img_lb_mog); + + if(enableLBAdaptiveSOM) + process("LBAdaptiveSOM", lbAdaptiveSOM, img_prep, img_lb_som); + + if(enableLBFuzzyAdaptiveSOM) + process("LBFuzzyAdaptiveSOM", lbFuzzyAdaptiveSOM, img_prep, img_lb_fsom); + + #if !defined(_WIN32) + if(enableLbpMrf) + process("LbpMrf", lbpMrf, img_prep, img_lbp_mrf); + #endif + + if(enableMultiLayerBGS) + { + multiLayerBGS->setStatus(MultiLayerBGS::Status::MLBGS_LEARN); + //multiLayerBGS->setStatus(MultiLayerBGS::Status::MLBGS_DETECT); + process("MultiLayerBGS", multiLayerBGS, img_prep, img_mlbgs); + } + + if(enablePBAS) + process("PBAS", pixelBasedAdaptiveSegmenter, img_prep, img_pt_pbas); + + if(enableVuMeter) + process("VuMeter", vuMeter, img_prep, img_vumeter); + + if(enableKDE) + process("KDE", kde, img_prep, img_kde); + + if(enableForegroundMaskAnalysis) + { + foregroundMaskAnalysis->stopAt = frameToStop; + foregroundMaskAnalysis->img_ref_path = imgref; + + foregroundMaskAnalysis->process(frameNumber, "FrameDifferenceBGS", img_framediff); + foregroundMaskAnalysis->process(frameNumber, "StaticFrameDifferenceBGS", img_staticfdiff); + foregroundMaskAnalysis->process(frameNumber, "WeightedMovingMeanBGS", img_wmovmean); + foregroundMaskAnalysis->process(frameNumber, "WeightedMovingVarianceBGS", img_movvar); + foregroundMaskAnalysis->process(frameNumber, "MixtureOfGaussianV1BGS", img_mog1); + foregroundMaskAnalysis->process(frameNumber, "MixtureOfGaussianV2BGS", img_mog2); + foregroundMaskAnalysis->process(frameNumber, "AdaptiveBackgroundLearning", img_bkgl_fgmask); +#if CV_MAJOR_VERSION >= 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3 + foregroundMaskAnalysis->process(frameNumber, "GMG", img_gmg); +#endif + foregroundMaskAnalysis->process(frameNumber, "DPAdaptiveMedianBGS", img_adpmed); + foregroundMaskAnalysis->process(frameNumber, "DPGrimsonGMMBGS", img_grigmm); + foregroundMaskAnalysis->process(frameNumber, "DPZivkovicAGMMBGS", img_zivgmm); + foregroundMaskAnalysis->process(frameNumber, "DPMeanBGS", img_tmpmean); + foregroundMaskAnalysis->process(frameNumber, "DPWrenGABGS", img_wrenga); + foregroundMaskAnalysis->process(frameNumber, "DPPratiMediodBGS", img_pramed); + foregroundMaskAnalysis->process(frameNumber, "DPEigenbackgroundBGS", img_eigbkg); + foregroundMaskAnalysis->process(frameNumber, "DPTextureBGS", img_texbgs); + foregroundMaskAnalysis->process(frameNumber, "T2FGMM_UM", img_t2fgmm_um); + foregroundMaskAnalysis->process(frameNumber, "T2FGMM_UV", img_t2fgmm_uv); + foregroundMaskAnalysis->process(frameNumber, "T2FMRF_UM", img_t2fmrf_um); + foregroundMaskAnalysis->process(frameNumber, "T2FMRF_UV", img_t2fmrf_uv); + foregroundMaskAnalysis->process(frameNumber, "FuzzySugenoIntegral", img_fsi); + foregroundMaskAnalysis->process(frameNumber, "FuzzyChoquetIntegral", img_fci); + foregroundMaskAnalysis->process(frameNumber, "LBSimpleGaussian", img_lb_sg); + foregroundMaskAnalysis->process(frameNumber, "LBFuzzyGaussian", img_lb_fg); + foregroundMaskAnalysis->process(frameNumber, "LBMixtureOfGaussians", img_lb_mog); + foregroundMaskAnalysis->process(frameNumber, "LBAdaptiveSOM", img_lb_som); + foregroundMaskAnalysis->process(frameNumber, "LBFuzzyAdaptiveSOM", img_lb_fsom); + #if !defined(_WIN32) + foregroundMaskAnalysis->process(frameNumber, "LbpMrf", img_lbp_mrf); + #endif + foregroundMaskAnalysis->process(frameNumber, "MultiLayerBGS", img_mlbgs); + foregroundMaskAnalysis->process(frameNumber, "PBAS", img_pt_pbas); + foregroundMaskAnalysis->process(frameNumber, "VuMeter", img_vumeter); + foregroundMaskAnalysis->process(frameNumber, "KDE", img_kde); + } + + firstTime = false; +} + +void FrameProcessor::finish(void) +{ + /*if(enableMultiLayerBGS) + multiLayerBGS->finish(); + + if(enableLBSimpleGaussian) + lbSimpleGaussian->finish(); + + if(enableLBFuzzyGaussian) + lbFuzzyGaussian->finish(); + + if(enableLBMixtureOfGaussians) + lbMixtureOfGaussians->finish(); + + if(enableLBAdaptiveSOM) + lbAdaptiveSOM->finish(); + + if(enableLBFuzzyAdaptiveSOM) + lbFuzzyAdaptiveSOM->finish();*/ + + if(enableForegroundMaskAnalysis) + delete foregroundMaskAnalysis; + + if(enableKDE) + delete kde; + + if(enableVuMeter) + delete vuMeter; + + if(enablePBAS) + delete pixelBasedAdaptiveSegmenter; + + if(enableMultiLayerBGS) + delete multiLayerBGS; + + if(enableLBFuzzyAdaptiveSOM) + delete lbFuzzyAdaptiveSOM; + + if(enableLBAdaptiveSOM) + delete lbAdaptiveSOM; + + if(enableLBMixtureOfGaussians) + delete lbMixtureOfGaussians; + + if(enableLBFuzzyGaussian) + delete lbFuzzyGaussian; + + if(enableLBSimpleGaussian) + delete lbSimpleGaussian; + + #if !defined(_WIN32) + if(enableLbpMrf) + delete lbpMrf; + #endif + + if(enableFuzzyChoquetIntegral) + delete fuzzyChoquetIntegral; + + if(enableFuzzySugenoIntegral) + delete fuzzySugenoIntegral; + + if(enableT2FMRF_UV) + delete type2FuzzyMRF_UV; + + if(enableT2FMRF_UM) + delete type2FuzzyMRF_UM; + + if(enableT2FGMM_UV) + delete type2FuzzyGMM_UV; + + if(enableT2FGMM_UM) + delete type2FuzzyGMM_UM; + + if(enableDPTextureBGS) + delete textureBGS; + + if(enableDPEigenbackgroundBGS) + delete eigenBackground; + + if(enableDPPratiMediodBGS) + delete pratiMediod; + + if(enableDPWrenGABGS) + delete wrenGA; + + if(enableDPMeanBGS) + delete temporalMean; + + if(enableDPZivkovicAGMMBGS) + delete zivkovicAGMM; + + if(enableDPGrimsonGMMBGS) + delete grimsonGMM; + + if(enableDPAdaptiveMedianBGS) + delete adaptiveMedian; + +#if CV_MAJOR_VERSION >= 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3 + if(enableGMG) + delete gmg; +#endif + + if(enableAdaptiveBackgroundLearning) + delete adaptiveBackgroundLearning; + + if(enableMixtureOfGaussianV2BGS) + delete mixtureOfGaussianV2BGS; + + if(enableMixtureOfGaussianV1BGS) + delete mixtureOfGaussianV1BGS; + + if(enableWeightedMovingVarianceBGS) + delete weightedMovingVariance; + + if(enableWeightedMovingMeanBGS) + delete weightedMovingMean; + + if(enableStaticFrameDifferenceBGS) + delete staticFrameDifference; + + if(enableFrameDifferenceBGS) + delete frameDifference; + + if(enablePreProcessor) + delete preProcessor; +} + +void FrameProcessor::tic(std::string value) +{ + processname = value; + duration = static_cast<double>(cv::getTickCount()); +} + +void FrameProcessor::toc() +{ + duration = (static_cast<double>(cv::getTickCount()) - duration)/cv::getTickFrequency(); + std::cout << processname << "\ttime(sec):" << std::fixed << std::setprecision(6) << duration << std::endl; +} + +void FrameProcessor::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/FrameProcessor.xml", 0, CV_STORAGE_WRITE); + + cvWriteString(fs, "tictoc", tictoc.c_str()); + + cvWriteInt(fs, "enablePreProcessor", enablePreProcessor); + + cvWriteInt(fs, "enableForegroundMaskAnalysis", enableForegroundMaskAnalysis); + + cvWriteInt(fs, "enableFrameDifferenceBGS", enableFrameDifferenceBGS); + cvWriteInt(fs, "enableStaticFrameDifferenceBGS", enableStaticFrameDifferenceBGS); + cvWriteInt(fs, "enableWeightedMovingMeanBGS", enableWeightedMovingMeanBGS); + cvWriteInt(fs, "enableWeightedMovingVarianceBGS", enableWeightedMovingVarianceBGS); + cvWriteInt(fs, "enableMixtureOfGaussianV1BGS", enableMixtureOfGaussianV1BGS); + cvWriteInt(fs, "enableMixtureOfGaussianV2BGS", enableMixtureOfGaussianV2BGS); + cvWriteInt(fs, "enableAdaptiveBackgroundLearning", enableAdaptiveBackgroundLearning); +#if CV_MAJOR_VERSION >= 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3 + cvWriteInt(fs, "enableGMG", enableGMG); +#endif + + cvWriteInt(fs, "enableDPAdaptiveMedianBGS", enableDPAdaptiveMedianBGS); + cvWriteInt(fs, "enableDPGrimsonGMMBGS", enableDPGrimsonGMMBGS); + cvWriteInt(fs, "enableDPZivkovicAGMMBGS", enableDPZivkovicAGMMBGS); + cvWriteInt(fs, "enableDPMeanBGS", enableDPMeanBGS); + cvWriteInt(fs, "enableDPWrenGABGS", enableDPWrenGABGS); + cvWriteInt(fs, "enableDPPratiMediodBGS", enableDPPratiMediodBGS); + cvWriteInt(fs, "enableDPEigenbackgroundBGS", enableDPEigenbackgroundBGS); + cvWriteInt(fs, "enableDPTextureBGS", enableDPTextureBGS); + + cvWriteInt(fs, "enableT2FGMM_UM", enableT2FGMM_UM); + cvWriteInt(fs, "enableT2FGMM_UV", enableT2FGMM_UV); + cvWriteInt(fs, "enableT2FMRF_UM", enableT2FMRF_UM); + cvWriteInt(fs, "enableT2FMRF_UV", enableT2FMRF_UV); + cvWriteInt(fs, "enableFuzzySugenoIntegral", enableFuzzySugenoIntegral); + cvWriteInt(fs, "enableFuzzyChoquetIntegral", enableFuzzyChoquetIntegral); + + cvWriteInt(fs, "enableLBSimpleGaussian", enableLBSimpleGaussian); + cvWriteInt(fs, "enableLBFuzzyGaussian", enableLBFuzzyGaussian); + cvWriteInt(fs, "enableLBMixtureOfGaussians", enableLBMixtureOfGaussians); + cvWriteInt(fs, "enableLBAdaptiveSOM", enableLBAdaptiveSOM); + cvWriteInt(fs, "enableLBFuzzyAdaptiveSOM", enableLBFuzzyAdaptiveSOM); + + #if !defined(_WIN32) + cvWriteInt(fs, "enableLbpMrf", enableLbpMrf); + #endif + + cvWriteInt(fs, "enableMultiLayerBGS", enableMultiLayerBGS); + cvWriteInt(fs, "enablePBAS", enablePBAS); + cvWriteInt(fs, "enableVuMeter", enableVuMeter); + cvWriteInt(fs, "enableKDE", enableKDE); + + cvReleaseFileStorage(&fs); +} + +void FrameProcessor::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/FrameProcessor.xml", 0, CV_STORAGE_READ); + + tictoc = cvReadStringByName(fs, 0, "tictoc", ""); + + enablePreProcessor = cvReadIntByName(fs, 0, "enablePreProcessor", true); + + enableForegroundMaskAnalysis = cvReadIntByName(fs, 0, "enableForegroundMaskAnalysis", false); + + enableFrameDifferenceBGS = cvReadIntByName(fs, 0, "enableFrameDifferenceBGS", false); + enableStaticFrameDifferenceBGS = cvReadIntByName(fs, 0, "enableStaticFrameDifferenceBGS", false); + enableWeightedMovingMeanBGS = cvReadIntByName(fs, 0, "enableWeightedMovingMeanBGS", false); + enableWeightedMovingVarianceBGS = cvReadIntByName(fs, 0, "enableWeightedMovingVarianceBGS", false); + enableMixtureOfGaussianV1BGS = cvReadIntByName(fs, 0, "enableMixtureOfGaussianV1BGS", false); + enableMixtureOfGaussianV2BGS = cvReadIntByName(fs, 0, "enableMixtureOfGaussianV2BGS", false); + enableAdaptiveBackgroundLearning = cvReadIntByName(fs, 0, "enableAdaptiveBackgroundLearning", false); +#if CV_MAJOR_VERSION >= 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3 + enableGMG = cvReadIntByName(fs, 0, "enableGMG", false); +#endif + + enableDPAdaptiveMedianBGS = cvReadIntByName(fs, 0, "enableDPAdaptiveMedianBGS", false); + enableDPGrimsonGMMBGS = cvReadIntByName(fs, 0, "enableDPGrimsonGMMBGS", false); + enableDPZivkovicAGMMBGS = cvReadIntByName(fs, 0, "enableDPZivkovicAGMMBGS", false); + enableDPMeanBGS = cvReadIntByName(fs, 0, "enableDPMeanBGS", false); + enableDPWrenGABGS = cvReadIntByName(fs, 0, "enableDPWrenGABGS", false); + enableDPPratiMediodBGS = cvReadIntByName(fs, 0, "enableDPPratiMediodBGS", false); + enableDPEigenbackgroundBGS = cvReadIntByName(fs, 0, "enableDPEigenbackgroundBGS", false); + enableDPTextureBGS = cvReadIntByName(fs, 0, "enableDPTextureBGS", false); + + enableT2FGMM_UM = cvReadIntByName(fs, 0, "enableT2FGMM_UM", false); + enableT2FGMM_UV = cvReadIntByName(fs, 0, "enableT2FGMM_UV", false); + enableT2FMRF_UM = cvReadIntByName(fs, 0, "enableT2FMRF_UM", false); + enableT2FMRF_UV = cvReadIntByName(fs, 0, "enableT2FMRF_UV", false); + enableFuzzySugenoIntegral = cvReadIntByName(fs, 0, "enableFuzzySugenoIntegral", false); + enableFuzzyChoquetIntegral = cvReadIntByName(fs, 0, "enableFuzzyChoquetIntegral", false); + + enableLBSimpleGaussian = cvReadIntByName(fs, 0, "enableLBSimpleGaussian", false); + enableLBFuzzyGaussian = cvReadIntByName(fs, 0, "enableLBFuzzyGaussian", false); + enableLBMixtureOfGaussians = cvReadIntByName(fs, 0, "enableLBMixtureOfGaussians", false); + enableLBAdaptiveSOM = cvReadIntByName(fs, 0, "enableLBAdaptiveSOM", false); + enableLBFuzzyAdaptiveSOM = cvReadIntByName(fs, 0, "enableLBFuzzyAdaptiveSOM", false); + + #if !defined(_WIN32) + enableLbpMrf = cvReadIntByName(fs, 0, "enableLbpMrf", false); + #endif + + enableMultiLayerBGS = cvReadIntByName(fs, 0, "enableMultiLayerBGS", false); + enablePBAS = cvReadIntByName(fs, 0, "enablePBAS", false); + enableVuMeter = cvReadIntByName(fs, 0, "enableVuMeter", false); + enableKDE = cvReadIntByName(fs, 0, "enableKDE", false); + + cvReleaseFileStorage(&fs); +} diff --git a/FrameProcessor.h b/FrameProcessor.h new file mode 100644 index 0000000000000000000000000000000000000000..44ad2aa6c7939a58371516a03c01d7386e58757c --- /dev/null +++ b/FrameProcessor.h @@ -0,0 +1,235 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once +#pragma warning(disable : 4482) + +#include "IFrameProcessor.h" +#include "PreProcessor.h" + +#include "package_bgs/IBGS.h" + +#include "package_bgs/FrameDifferenceBGS.h" +#include "package_bgs/StaticFrameDifferenceBGS.h" +#include "package_bgs/WeightedMovingMeanBGS.h" +#include "package_bgs/WeightedMovingVarianceBGS.h" +#include "package_bgs/MixtureOfGaussianV1BGS.h" +#include "package_bgs/MixtureOfGaussianV2BGS.h" +#include "package_bgs/AdaptiveBackgroundLearning.h" +#if CV_MAJOR_VERSION >= 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3 +#include "package_bgs/GMG.h" +#endif + +#include "package_bgs/dp/DPAdaptiveMedianBGS.h" +#include "package_bgs/dp/DPGrimsonGMMBGS.h" +#include "package_bgs/dp/DPZivkovicAGMMBGS.h" +#include "package_bgs/dp/DPMeanBGS.h" +#include "package_bgs/dp/DPWrenGABGS.h" +#include "package_bgs/dp/DPPratiMediodBGS.h" +#include "package_bgs/dp/DPEigenbackgroundBGS.h" +#include "package_bgs/dp/DPTextureBGS.h" + +#include "package_bgs/tb/T2FGMM_UM.h" +#include "package_bgs/tb/T2FGMM_UV.h" +#include "package_bgs/tb/T2FMRF_UM.h" +#include "package_bgs/tb/T2FMRF_UV.h" +#include "package_bgs/tb/FuzzySugenoIntegral.h" +#include "package_bgs/tb/FuzzyChoquetIntegral.h" + +#include "package_bgs/lb/LBSimpleGaussian.h" +#include "package_bgs/lb/LBFuzzyGaussian.h" +#include "package_bgs/lb/LBMixtureOfGaussians.h" +#include "package_bgs/lb/LBAdaptiveSOM.h" +#include "package_bgs/lb/LBFuzzyAdaptiveSOM.h" + +#if !defined(_WIN32) +#include "package_bgs/ck/LbpMrf.h" +#endif + +#include "package_bgs/jmo/MultiLayerBGS.h" +#include "package_bgs/pt/PixelBasedAdaptiveSegmenter.h" +#include "package_bgs/av/VuMeter.h" +#include "package_bgs/ae/KDE.h" + +#include "package_analysis/ForegroundMaskAnalysis.h" + +class FrameProcessor : public IFrameProcessor +{ +private: + bool firstTime; + long frameNumber; + std::string processname; + double duration; + std::string tictoc; + + cv::Mat img_prep; + PreProcessor* preProcessor; + bool enablePreProcessor; + + cv::Mat img_framediff; + FrameDifferenceBGS* frameDifference; + bool enableFrameDifferenceBGS; + + cv::Mat img_staticfdiff; + StaticFrameDifferenceBGS* staticFrameDifference; + bool enableStaticFrameDifferenceBGS; + + cv::Mat img_wmovmean; + WeightedMovingMeanBGS* weightedMovingMean; + bool enableWeightedMovingMeanBGS; + + cv::Mat img_movvar; + WeightedMovingVarianceBGS* weightedMovingVariance; + bool enableWeightedMovingVarianceBGS; + + cv::Mat img_mog1; + MixtureOfGaussianV1BGS* mixtureOfGaussianV1BGS; + bool enableMixtureOfGaussianV1BGS; + + cv::Mat img_mog2; + MixtureOfGaussianV2BGS* mixtureOfGaussianV2BGS; + bool enableMixtureOfGaussianV2BGS; + + cv::Mat img_bkgl_fgmask; + AdaptiveBackgroundLearning* adaptiveBackgroundLearning; + bool enableAdaptiveBackgroundLearning; + +#if CV_MAJOR_VERSION >= 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3 + cv::Mat img_gmg; + GMG* gmg; + bool enableGMG; +#endif + + cv::Mat img_adpmed; + DPAdaptiveMedianBGS* adaptiveMedian; + bool enableDPAdaptiveMedianBGS; + + cv::Mat img_grigmm; + DPGrimsonGMMBGS* grimsonGMM; + bool enableDPGrimsonGMMBGS; + + cv::Mat img_zivgmm; + DPZivkovicAGMMBGS* zivkovicAGMM; + bool enableDPZivkovicAGMMBGS; + + cv::Mat img_tmpmean; + DPMeanBGS* temporalMean; + bool enableDPMeanBGS; + + cv::Mat img_wrenga; + DPWrenGABGS* wrenGA; + bool enableDPWrenGABGS; + + cv::Mat img_pramed; + DPPratiMediodBGS* pratiMediod; + bool enableDPPratiMediodBGS; + + cv::Mat img_eigbkg; + DPEigenbackgroundBGS* eigenBackground; + bool enableDPEigenbackgroundBGS; + + cv::Mat img_texbgs; + DPTextureBGS* textureBGS; + bool enableDPTextureBGS; + + cv::Mat img_t2fgmm_um; + T2FGMM_UM* type2FuzzyGMM_UM; + bool enableT2FGMM_UM; + + cv::Mat img_t2fgmm_uv; + T2FGMM_UV* type2FuzzyGMM_UV; + bool enableT2FGMM_UV; + + cv::Mat img_t2fmrf_um; + T2FMRF_UM* type2FuzzyMRF_UM; + bool enableT2FMRF_UM; + + cv::Mat img_t2fmrf_uv; + T2FMRF_UV* type2FuzzyMRF_UV; + bool enableT2FMRF_UV; + + cv::Mat img_fsi; + FuzzySugenoIntegral* fuzzySugenoIntegral; + bool enableFuzzySugenoIntegral; + + cv::Mat img_fci; + FuzzyChoquetIntegral* fuzzyChoquetIntegral; + bool enableFuzzyChoquetIntegral; + + cv::Mat img_lb_sg; + LBSimpleGaussian* lbSimpleGaussian; + bool enableLBSimpleGaussian; + + cv::Mat img_lb_fg; + LBFuzzyGaussian* lbFuzzyGaussian; + bool enableLBFuzzyGaussian; + + cv::Mat img_lb_mog; + LBMixtureOfGaussians* lbMixtureOfGaussians; + bool enableLBMixtureOfGaussians; + + cv::Mat img_lb_som; + LBAdaptiveSOM* lbAdaptiveSOM; + bool enableLBAdaptiveSOM; + + cv::Mat img_lb_fsom; + LBFuzzyAdaptiveSOM* lbFuzzyAdaptiveSOM; + bool enableLBFuzzyAdaptiveSOM; + + #if !defined(_WIN32) + cv::Mat img_lbp_mrf; + LbpMrf* lbpMrf; + bool enableLbpMrf; + #endif + + cv::Mat img_mlbgs; + MultiLayerBGS* multiLayerBGS; + bool enableMultiLayerBGS; + + cv::Mat img_pt_pbas; + PixelBasedAdaptiveSegmenter* pixelBasedAdaptiveSegmenter; + bool enablePBAS; + + cv::Mat img_vumeter; + VuMeter* vuMeter; + bool enableVuMeter; + + cv::Mat img_kde; + KDE* kde; + bool enableKDE; + + ForegroundMaskAnalysis* foregroundMaskAnalysis; + bool enableForegroundMaskAnalysis; + +public: + FrameProcessor(); + ~FrameProcessor(); + + long frameToStop; + std::string imgref; + + void init(); + void process(const cv::Mat &img_input); + void finish(void); + +private: + void process(std::string name, IBGS *bgs, const cv::Mat &img_input, cv::Mat &img_bgs); + void tic(std::string value); + void toc(); + + void saveConfig(); + void loadConfig(); +}; diff --git a/IFrameProcessor.h b/IFrameProcessor.h new file mode 100644 index 0000000000000000000000000000000000000000..ef50b1ff9d5b44effdc3d2f44c138417ac83205f --- /dev/null +++ b/IFrameProcessor.h @@ -0,0 +1,26 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <cv.h> + +class IFrameProcessor +{ + public: + virtual void process(const cv:: Mat &input) = 0; + virtual ~IFrameProcessor(){} +}; \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..e2ce0d8b1060343138fcd323c66fc027c4308051 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,12 @@ +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. \ No newline at end of file diff --git a/Main.cpp b/Main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4e59ee5b2d9d644b8f6989fbf030d9b0f0748e15 --- /dev/null +++ b/Main.cpp @@ -0,0 +1,82 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "Config.h" +#include "VideoAnalysis.h" +#include <iostream> + +class Main +{ +private: + Main(); + +public: + static void start(int argc, const char **argv) + { + std::cout << "-----------------------------------------" << std::endl; + std::cout << "Background Subtraction Library v1.7.0 " << std::endl; + std::cout << "http://code.google.com/p/bgslibrary " << std::endl; + std::cout << "by: " << std::endl; + std::cout << "Andrews Sobral (andrewssobral@gmail.com) " << std::endl; + std::cout << "-----------------------------------------" << std::endl; + std::cout << "Using OpenCV version " << CV_VERSION << std::endl; + + try + { + int key = KEY_ESC; + + do + { + VideoAnalysis* videoAnalysis = new VideoAnalysis; + + if(videoAnalysis->setup(argc, argv)) + { + videoAnalysis->start(); + + std::cout << "Processing finished, enter:" << std::endl; + std::cout << "R - Repeat" << std::endl; + std::cout << "Q - Quit" << std::endl; + + key = cv::waitKey(); + } + + cv::destroyAllWindows(); + delete videoAnalysis; + + }while(key == KEY_REPEAT); + } + catch(const std::exception& ex) + { + std::cout << "std::exception:" << ex.what() << std::endl; + return; + } + catch(...) + { + std::cout << "Unknow error" << std::endl; + return; + } + +#ifdef WIN32 + //system("pause"); +#endif + } +}; + +int main(int argc, const char **argv) +{ + Main::start(argc, argv); + return 0; +} diff --git a/PreProcessor.cpp b/PreProcessor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..834996bb82d44e9498edf4a4ec7b7c54afdcedd3 --- /dev/null +++ b/PreProcessor.cpp @@ -0,0 +1,146 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "PreProcessor.h" + +PreProcessor::PreProcessor() : firstTime(true), equalizeHist(false), gaussianBlur(false) +{ + std::cout << "PreProcessor()" << std::endl; +} + +PreProcessor::~PreProcessor() +{ + std::cout << "~PreProcessor()" << std::endl; +} + +void PreProcessor::setEqualizeHist(bool value) +{ + equalizeHist = value; +} + +void PreProcessor::setGaussianBlur(bool value) +{ + gaussianBlur = value; +} + +cv::Mat PreProcessor::getGrayScale() +{ + return img_gray.clone(); +} + +void PreProcessor::process(const cv::Mat &img_input, cv::Mat &img_output) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + img_input.copyTo(img_output); + + // Converts image from one color space to another + // http://opencv.willowgarage.com/documentation/cpp/miscellaneous_image_transformations.html#cv-cvtcolor + cv::cvtColor(img_input, img_gray, CV_BGR2GRAY); + //img_gray.copyTo(img_output); + + // Equalizes the histogram of a grayscale image + // http://opencv.willowgarage.com/documentation/cpp/histograms.html#cv-equalizehist + if(equalizeHist) + cv::equalizeHist(img_output, img_output); + + // Smoothes image using a Gaussian filter + // http://opencv.willowgarage.com/documentation/cpp/imgproc_image_filtering.html#GaussianBlur + if(gaussianBlur) + cv::GaussianBlur(img_output, img_output, cv::Size(7,7), 1.5); + + if(enableShow) + cv::imshow("Pre Processor", img_output); + + firstTime = false; +} + +void PreProcessor::rotate(const cv::Mat &img_input, cv::Mat &img_output, float angle) +{ + IplImage* image = new IplImage(img_input); + + //IplImage *rotatedImage = cvCreateImage(cvSize(480,320), IPL_DEPTH_8U, image->nChannels); + //IplImage *rotatedImage = cvCreateImage(cvSize(image->width,image->height), IPL_DEPTH_8U, image->nChannels); + IplImage* rotatedImage = cvCreateImage(cvSize(image->height,image->width), IPL_DEPTH_8U, image->nChannels); + + CvPoint2D32f center; + //center.x = 160; + //center.y = 160; + center.x = (image->height / 2); + center.y = (image->width / 2); + + CvMat* mapMatrix = cvCreateMat(2, 3, CV_32FC1); + + cv2DRotationMatrix(center, angle, 1.0, mapMatrix); + cvWarpAffine(image, rotatedImage, mapMatrix, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll(0)); + + cv::Mat img_rot(rotatedImage); + img_rot.copyTo(img_output); + + cvReleaseImage(&image); + cvReleaseImage(&rotatedImage); + cvReleaseMat(&mapMatrix); +} + +void PreProcessor::applyCanny(const cv::Mat &img_input, cv::Mat &img_output) +{ + if(img_input.empty()) + return; + + //------------------------------------------------------------------ + // Canny + // Finds edges in an image using Canny algorithm. + // http://opencv.willowgarage.com/documentation/cpp/imgproc_feature_detection.html#cv-canny + //------------------------------------------------------------------ + + cv::Mat img_canny; + cv::Canny( + img_input, // image � Single-channel 8-bit input image + img_canny, // edges � The output edge map. It will have the same size and the same type as image + 100, // threshold1 � The first threshold for the hysteresis procedure + 200); // threshold2 � The second threshold for the hysteresis procedure + cv::threshold(img_canny, img_canny, 128, 255, cv::THRESH_BINARY_INV); + + img_canny.copyTo(img_output); +} + +void PreProcessor::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/PreProcessor.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "equalizeHist", equalizeHist); + cvWriteInt(fs, "gaussianBlur", gaussianBlur); + cvWriteInt(fs, "enableShow", enableShow); + + cvReleaseFileStorage(&fs); +} + +void PreProcessor::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/PreProcessor.xml", 0, CV_STORAGE_READ); + + equalizeHist = cvReadIntByName(fs, 0, "equalizeHist", false); + gaussianBlur = cvReadIntByName(fs, 0, "gaussianBlur", false); + enableShow = cvReadIntByName(fs, 0, "enableShow", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/PreProcessor.h b/PreProcessor.h new file mode 100644 index 0000000000000000000000000000000000000000..9a0462366ec4821a1a803050619ccd3f0189b2ed --- /dev/null +++ b/PreProcessor.h @@ -0,0 +1,49 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +class PreProcessor +{ +private: + bool firstTime; + bool equalizeHist; + bool gaussianBlur; + cv::Mat img_gray; + bool enableShow; + +public: + PreProcessor(); + ~PreProcessor(); + + void setEqualizeHist(bool value); + void setGaussianBlur(bool value); + cv::Mat getGrayScale(); + + void process(const cv::Mat &img_input, cv::Mat &img_output); + + void rotate(const cv::Mat &img_input, cv::Mat &img_output, float angle); + void applyCanny(const cv::Mat &img_input, cv::Mat &img_output); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/README.txt b/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..b7fe6c5137dfa447f8700abf859b976211c71bfb --- /dev/null +++ b/README.txt @@ -0,0 +1,16 @@ +# +# HOW TO COMPILE ON LINUX +# +# Requirements: +# cmake >= 2.8 +# opencv >= 2.3.1 + +cd build +cmake .. +make +cd .. + +chmod +x run_video.sh run_camera.sh run_demo.sh +./run_video.sh +./run_camera.sh +./run_demo.sh diff --git a/VideoAnalysis.cpp b/VideoAnalysis.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0123933b9f29b21ce42f6142a7ebda87ff7f9de3 --- /dev/null +++ b/VideoAnalysis.cpp @@ -0,0 +1,130 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "VideoAnalysis.h" + +VideoAnalysis::VideoAnalysis() : use_file(false), use_camera(false), cameraIndex(0), use_comp(false), frameToStop(0) +{ + std::cout << "VideoAnalysis()" << std::endl; +} + +VideoAnalysis::~VideoAnalysis() +{ + std::cout << "~VideoAnalysis()" << std::endl; +} + +bool VideoAnalysis::setup(int argc, const char **argv) +{ + bool flag = false; + + const char* keys = + "{hp|help|false|Print help message}" + "{uf|use_file|false|Use video file}" + "{fn|filename||Specify video file}" + "{uc|use_cam|false|Use camera}" + "{ca|camera|0|Specify camera index}" + "{co|use_comp|false|Use mask comparator}" + "{st|stopAt|0|Frame number to stop}" + "{im|imgref||Specify image file}" + ; + cv::CommandLineParser cmd(argc, argv, keys); + + if(argc <= 1 || cmd.get<bool>("help") == true) + { + std::cout << "Usage: " << argv[0] << " [options]" << std::endl; + std::cout << "Avaible options:" << std::endl; + cmd.printParams(); + return false; + } + + use_file = cmd.get<bool>("use_file"); + if(use_file) + { + filename = cmd.get<std::string>("filename"); + + if(filename.empty()) + { + std::cout << "Specify filename"<< std::endl; + return false; + } + + flag = true; + } + + use_camera = cmd.get<bool>("use_cam"); + if(use_camera) + { + cameraIndex = cmd.get<int>("camera"); + flag = true; + } + + if(flag == true) + { + use_comp = cmd.get<bool>("use_comp"); + if(use_comp) + { + frameToStop = cmd.get<int>("stopAt"); + imgref = cmd.get<std::string>("imgref"); + + if(imgref.empty()) + { + std::cout << "Specify image reference"<< std::endl; + return false; + } + } + } + + return flag; +} + +void VideoAnalysis::start() +{ + do + { + videoCapture = new VideoCapture; + frameProcessor = new FrameProcessor; + + frameProcessor->init(); + frameProcessor->frameToStop = frameToStop; + frameProcessor->imgref = imgref; + + videoCapture->setFrameProcessor(frameProcessor); + + if(use_file) + videoCapture->setVideo(filename); + + if(use_camera) + videoCapture->setCamera(cameraIndex); + + videoCapture->start(); + + if(use_file || use_camera) + break; + + frameProcessor->finish(); + + int key = cvWaitKey(500); + if(key == KEY_ESC) + break; + + delete frameProcessor; + delete videoCapture; + + }while(1); + + delete frameProcessor; + delete videoCapture; +} \ No newline at end of file diff --git a/VideoAnalysis.h b/VideoAnalysis.h new file mode 100644 index 0000000000000000000000000000000000000000..74eacf764cc8a49655c23b897ca7b73f76eef4e9 --- /dev/null +++ b/VideoAnalysis.h @@ -0,0 +1,45 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <sstream> + +#include "VideoCapture.h" +#include "FrameProcessor.h" + +class VideoAnalysis +{ +private: + VideoCapture* videoCapture; + FrameProcessor* frameProcessor; + bool use_file; + std::string filename; + bool use_camera; + int cameraIndex; + bool use_comp; + long frameToStop; + std::string imgref; + +public: + VideoAnalysis(); + ~VideoAnalysis(); + + bool setup(int argc, const char **argv); + void start(); +}; + diff --git a/VideoCapture.cpp b/VideoCapture.cpp new file mode 100644 index 0000000000000000000000000000000000000000..895205376a3474c7b03a5e21de923b1352c62332 --- /dev/null +++ b/VideoCapture.cpp @@ -0,0 +1,275 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "VideoCapture.h" + +namespace VC_ROI +{ + IplImage* img_input1 = 0; + IplImage* img_input2 = 0; + int roi_x0 = 0; + int roi_y0 = 0; + int roi_x1 = 0; + int roi_y1 = 0; + int numOfRec = 0; + int startDraw = 0; + bool roi_defined = false; + bool use_roi = true; + bool disable_event = false; + + void reset(void) + { + disable_event = false; + startDraw = false; + } + + void VideoCapture_on_mouse(int evt, int x, int y, int flag, void* param) + { + if(use_roi == false || disable_event == true) + return; + + if(evt == CV_EVENT_LBUTTONDOWN) + { + if(!startDraw) + { + roi_x0 = x; + roi_y0 = y; + startDraw = 1; + } + else + { + roi_x1 = x; + roi_y1 = y; + startDraw = 0; + roi_defined = true; + disable_event = true; + } + } + + if(evt == CV_EVENT_MOUSEMOVE && startDraw) + { + //redraw ROI selection + img_input2 = cvCloneImage(img_input1); + cvRectangle(img_input2, cvPoint(roi_x0,roi_y0), cvPoint(x,y), CV_RGB(255,0,0), 1); + cvShowImage("Input", img_input2); + cvReleaseImage(&img_input2); + //startDraw = false; + //disable_event = true; + } + } +} + +VideoCapture::VideoCapture() : key(0), start_time(0), delta_time(0), freq(0), fps(0), frameNumber(0), stopAt(0), + useCamera(false), useVideo(false), input_resize_percent(100), showOutput(true), enableFlip(false) +{ + std::cout << "VideoCapture()" << std::endl; +} + +VideoCapture::~VideoCapture() +{ + std::cout << "~VideoCapture()" << std::endl; +} + +void VideoCapture::setFrameProcessor(IFrameProcessor* frameProcessorPtr) +{ + frameProcessor = frameProcessorPtr; +} + +void VideoCapture::setCamera(int index) +{ + useCamera = true; + cameraIndex = index; + + useVideo = false; +} + +void VideoCapture::setUpCamera() +{ + std::cout << "Camera index:" << cameraIndex << std::endl; + capture = cvCaptureFromCAM(cameraIndex); + + if(!capture) + std::cerr << "Cannot open initialize webcam!\n" << std::endl; +} + +void VideoCapture::setVideo(std::string filename) +{ + useVideo = true; + videoFileName = filename; + + useCamera = false; +} + +void VideoCapture::setUpVideo() +{ + capture = cvCaptureFromFile(videoFileName.c_str()); + + if(!capture) + std::cerr << "Cannot open video file "<< videoFileName << std::endl; +} + +void VideoCapture::start() +{ + loadConfig(); + + if(useCamera) setUpCamera(); + if(useVideo) setUpVideo(); + if(!capture) std::cerr << "Capture error..." << std::endl; + + int input_fps = cvGetCaptureProperty(capture,CV_CAP_PROP_FPS); + std::cout << "input->fps:" << input_fps << std::endl; + + IplImage* frame1 = cvQueryFrame(capture); + frame = cvCreateImage(cvSize((int)((frame1->width*input_resize_percent)/100) , (int)((frame1->height*input_resize_percent)/100)), frame1->depth, frame1->nChannels); + //cvCreateImage(cvSize(frame1->width/input_resize_factor, frame1->height/input_resize_factor), frame1->depth, frame1->nChannels); + std::cout << "input->resize_percent:" << input_resize_percent << std::endl; + std::cout << "input->width:" << frame->width << std::endl; + std::cout << "input->height:" << frame->height << std::endl; + + double loopDelay = 33.333; + if(input_fps > 0) + loopDelay = (1./input_fps)*1000.; + std::cout << "loopDelay:" << loopDelay << std::endl; + + bool firstTime = true; + do + { + frameNumber++; + + frame1 = cvQueryFrame(capture); + if(!frame1) break; + + cvResize(frame1, frame); + + if(enableFlip) + cvFlip(frame, frame, 0); + + if(VC_ROI::use_roi == true && VC_ROI::roi_defined == false && firstTime == true) + { + VC_ROI::reset(); + + do + { + cv::Mat img_input(frame); + + if(showOutput) + { + cv::imshow("Input", img_input); + + std::cout << "Set ROI (press ESC to skip)" << std::endl; + VC_ROI::img_input1 = new IplImage(img_input); + cvSetMouseCallback("Input", VC_ROI::VideoCapture_on_mouse, NULL); + key = cvWaitKey(0); + delete VC_ROI::img_input1; + } + else + key = KEY_ESC; + + if(key == KEY_ESC) + { + std::cout << "ROI disabled" << std::endl; + VC_ROI::reset(); + VC_ROI::use_roi = false; + break; + } + + if(VC_ROI::roi_defined) + { + std::cout << "ROI defined (" << VC_ROI::roi_x0 << "," << VC_ROI::roi_y0 << "," << VC_ROI::roi_x1 << "," << VC_ROI::roi_y1 << ")" << std::endl; + break; + } + else + std::cout << "ROI undefined" << std::endl; + + }while(1); + } + + if(VC_ROI::use_roi == true && VC_ROI::roi_defined == true) + { + CvRect rect = cvRect(VC_ROI::roi_x0, VC_ROI::roi_y0, VC_ROI::roi_x1 - VC_ROI::roi_x0, VC_ROI::roi_y1 - VC_ROI::roi_y0); + cvSetImageROI(frame, rect); + } + + cv::Mat img_input(frame); + + if(showOutput) + cv::imshow("Input", img_input); + + if(firstTime) + saveConfig(); + + start_time = cv::getTickCount(); + frameProcessor->process(img_input); + int64 delta_time = cv::getTickCount() - start_time; + freq = cv::getTickFrequency(); + fps = freq / delta_time; + //std::cout << "FPS: " << fps << std::endl; + + cvResetImageROI(frame); + + key = cvWaitKey(loopDelay); + //std::cout << "key: " << key << std::endl; + + if(key == KEY_SPACE) + key = cvWaitKey(0); + + if(key == KEY_ESC) + break; + + if(stopAt > 0 && stopAt == frameNumber) + key = cvWaitKey(0); + + firstTime = false; + }while(1); + + cvReleaseCapture(&capture); +} + +void VideoCapture::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/VideoCapture.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "stopAt", stopAt); + cvWriteInt(fs, "input_resize_percent", input_resize_percent); + cvWriteInt(fs, "enableFlip", enableFlip); + cvWriteInt(fs, "use_roi", VC_ROI::use_roi); + cvWriteInt(fs, "roi_defined", VC_ROI::roi_defined); + cvWriteInt(fs, "roi_x0", VC_ROI::roi_x0); + cvWriteInt(fs, "roi_y0", VC_ROI::roi_y0); + cvWriteInt(fs, "roi_x1", VC_ROI::roi_x1); + cvWriteInt(fs, "roi_y1", VC_ROI::roi_y1); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void VideoCapture::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/VideoCapture.xml", 0, CV_STORAGE_READ); + + stopAt = cvReadIntByName(fs, 0, "stopAt", 0); + input_resize_percent = cvReadIntByName(fs, 0, "input_resize_percent", 100); + enableFlip = cvReadIntByName(fs, 0, "enableFlip", false); + VC_ROI::use_roi = cvReadIntByName(fs, 0, "use_roi", true); + VC_ROI::roi_defined = cvReadIntByName(fs, 0, "roi_defined", false); + VC_ROI::roi_x0 = cvReadIntByName(fs, 0, "roi_x0", 0); + VC_ROI::roi_y0 = cvReadIntByName(fs, 0, "roi_y0", 0); + VC_ROI::roi_x1 = cvReadIntByName(fs, 0, "roi_x1", 0); + VC_ROI::roi_y1 = cvReadIntByName(fs, 0, "roi_y1", 0); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/VideoCapture.h b/VideoCapture.h new file mode 100644 index 0000000000000000000000000000000000000000..caf6f6cb91d241a13bd9cac491d45a4ddf3897d2 --- /dev/null +++ b/VideoCapture.h @@ -0,0 +1,63 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "Config.h" +#include "IFrameProcessor.h" + +class VideoCapture +{ +private: + IFrameProcessor* frameProcessor; + CvCapture* capture; + IplImage* frame; + int key; + int64 start_time; + int64 delta_time; + double freq; + double fps; + long frameNumber; + long stopAt; + bool useCamera; + int cameraIndex; + bool useVideo; + std::string videoFileName; + int input_resize_percent; + bool showOutput; + bool enableFlip; + +public: + VideoCapture(); + ~VideoCapture(); + + void setFrameProcessor(IFrameProcessor* frameProcessorPtr); + void setCamera(int cameraIndex); + void setVideo(std::string filename); + void start(); + +private: + void setUpCamera(); + void setUpVideo(); + + void saveConfig(); + void loadConfig(); +}; + diff --git a/bgslibrary_vs2010_opencv.txt b/bgslibrary_vs2010_opencv.txt new file mode 100644 index 0000000000000000000000000000000000000000..6ea8256e859b0a714e3dc1510ad285a7bfab12a2 --- /dev/null +++ b/bgslibrary_vs2010_opencv.txt @@ -0,0 +1,39 @@ +--------------------------------------------------- +BGSLibrary with Visual Studio 2010 and Opencv 2.4.5 +--------------------------------------------------- + +1) Check our example project at [vs2010] folder +http://code.google.com/p/bgslibrary/source/browse/trunk/vs2010 + + +Or configure manually by: + + +1) Install OpenCV +1.a) Download OpenCV 2.4.5 from http://opencv.org/ +2.b) Install in: C:\OpenCV2.4.5 +2.c) Add OpenCV binaries in your Path +C:\OpenCV2.4.5\build\x86\vc10\bin + +2) Download BGSLibrary +2.a) Checkout bgslibrary SVN at C:\bgslibrary + +3) Start Visual Studio 2010 +3.a) Create New Project +3.b) Select Visual C++ -> Win32 -> Win32 Console Application +3.c) Set project location: C:\bgslibrary +3.d) Set project name: bgslibrary +3.e) Set Empty project +3.f) Add Demo.cpp in [Source Files] +3.g) Add content of c:\bgslibrary\package_bgs\*.* in [Header Files] +3.h) Add content of c:\bgslibrary\package_analysis\*.* in [Header Files] +3.i) Change to [Release] [Win32] mode +3.j) Click on Project->Properties +3.k) Change [Output Directory] to ..\ +3.l) Add OpenCV include in [C/C++] -> [Additional Include Directories] +C:\OpenCV2.4.5\build\include;C:\OpenCV2.4.5\build\include\opencv; +3.m) Add OpenCV libraries in [Linker]->[Input] +C:\OpenCV2.4.5\build\x86\vc10\lib\*.lib +3.n) Click in Build and wait +3.o) Run C:\bgslibrary\bgslibrary.exe +Enjoy! \ No newline at end of file diff --git a/config/FrameProcessor.xml b/config/FrameProcessor.xml new file mode 100644 index 0000000000000000000000000000000000000000..c76f826ebb87dd91db37552312138bf0f161fce3 --- /dev/null +++ b/config/FrameProcessor.xml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<opencv_storage> +<tictoc>""</tictoc> +<enablePreProcessor>1</enablePreProcessor> +<enableForegroundMaskAnalysis>0</enableForegroundMaskAnalysis> +<enableFrameDifferenceBGS>1</enableFrameDifferenceBGS> +<enableStaticFrameDifferenceBGS>0</enableStaticFrameDifferenceBGS> +<enableWeightedMovingMeanBGS>0</enableWeightedMovingMeanBGS> +<enableWeightedMovingVarianceBGS>0</enableWeightedMovingVarianceBGS> +<enableMixtureOfGaussianV1BGS>0</enableMixtureOfGaussianV1BGS> +<enableMixtureOfGaussianV2BGS>0</enableMixtureOfGaussianV2BGS> +<enableAdaptiveBackgroundLearning>0</enableAdaptiveBackgroundLearning> +<enableGMG>0</enableGMG> +<enableDPAdaptiveMedianBGS>0</enableDPAdaptiveMedianBGS> +<enableDPGrimsonGMMBGS>0</enableDPGrimsonGMMBGS> +<enableDPZivkovicAGMMBGS>0</enableDPZivkovicAGMMBGS> +<enableDPMeanBGS>0</enableDPMeanBGS> +<enableDPWrenGABGS>0</enableDPWrenGABGS> +<enableDPPratiMediodBGS>0</enableDPPratiMediodBGS> +<enableDPEigenbackgroundBGS>0</enableDPEigenbackgroundBGS> +<enableDPTextureBGS>0</enableDPTextureBGS> +<enableT2FGMM_UM>0</enableT2FGMM_UM> +<enableT2FGMM_UV>0</enableT2FGMM_UV> +<enableT2FMRF_UM>0</enableT2FMRF_UM> +<enableT2FMRF_UV>0</enableT2FMRF_UV> +<enableFuzzySugenoIntegral>0</enableFuzzySugenoIntegral> +<enableFuzzyChoquetIntegral>0</enableFuzzyChoquetIntegral> +<enableLBSimpleGaussian>0</enableLBSimpleGaussian> +<enableLBFuzzyGaussian>0</enableLBFuzzyGaussian> +<enableLBMixtureOfGaussians>0</enableLBMixtureOfGaussians> +<enableLBAdaptiveSOM>0</enableLBAdaptiveSOM> +<enableLBFuzzyAdaptiveSOM>0</enableLBFuzzyAdaptiveSOM> +<enableLbpMrf>0</enableLbpMrf> +<enableMultiLayerBGS>0</enableMultiLayerBGS> +<enablePBAS>0</enablePBAS> +<enableVuMeter>0</enableVuMeter> +<enableKDE>0</enableKDE> +</opencv_storage> diff --git a/config/PreProcessor.xml b/config/PreProcessor.xml new file mode 100644 index 0000000000000000000000000000000000000000..e58b3187a702e2b1fc36c4576aff75e7b7f4a9cb --- /dev/null +++ b/config/PreProcessor.xml @@ -0,0 +1,6 @@ +<?xml version="1.0"?> +<opencv_storage> +<equalizeHist>0</equalizeHist> +<gaussianBlur>0</gaussianBlur> +<enableShow>1</enableShow> +</opencv_storage> diff --git a/config/VideoCapture.xml b/config/VideoCapture.xml new file mode 100644 index 0000000000000000000000000000000000000000..60dd9aa81010d78759f5986f0aa5a2876da25f63 --- /dev/null +++ b/config/VideoCapture.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<opencv_storage> +<stopAt>0</stopAt> +<input_resize_percent>100</input_resize_percent> +<enableFlip>0</enableFlip> +<use_roi>0</use_roi> +<roi_defined>0</roi_defined> +<roi_x0>0</roi_x0> +<roi_y0>0</roi_y0> +<roi_x1>0</roi_x1> +<roi_y1>0</roi_y1> +<showOutput>1</showOutput> +</opencv_storage> diff --git a/dataset/video.avi b/dataset/video.avi new file mode 100644 index 0000000000000000000000000000000000000000..a29f00658be7d05b10344859e6bee9a501a7a2b1 Binary files /dev/null and b/dataset/video.avi differ diff --git a/demos/DemoFrameDifferenceBGS.cpp b/demos/DemoFrameDifferenceBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5f81242be085a5b6b150a4e5dc6f4cdf2c7a475f --- /dev/null +++ b/demos/DemoFrameDifferenceBGS.cpp @@ -0,0 +1,49 @@ +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "package_bgs/FrameDifferenceBGS.h" + +int main(int argc, char **argv) +{ + CvCapture *capture = 0; + + capture = cvCaptureFromCAM(0); + //capture = cvCaptureFromAVI("video.avi"); + + if(!capture){ + std::cerr << "Cannot open initialize webcam!" << std::endl; + return 1; + } + + IplImage *frame = cvQueryFrame(capture); + + FrameDifferenceBGS* bgs = new FrameDifferenceBGS; + + int key = 0; + while(key != 'q') + { + frame = cvQueryFrame(capture); + + if(!frame) break; + + cv::Mat img_input(frame,true); + cv::resize(img_input,img_input,cv::Size(320,240)); + cv::imshow("input", img_input); + + cv::Mat img_mask; + bgs->process(img_input, img_mask); // automatically shows the foreground mask image + + //if(!img_mask.empty()) + // do something + + key = cvWaitKey(1); + } + + delete bgs; + + cvDestroyAllWindows(); + cvReleaseCapture(&capture); + + return 0; +} diff --git a/demos/DemoMultiLayerBGS.cpp b/demos/DemoMultiLayerBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bf2bbad06817f9aebc14db2244e4cc09b29e4e42 --- /dev/null +++ b/demos/DemoMultiLayerBGS.cpp @@ -0,0 +1,49 @@ +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "package_bgs/jmo/MultiLayerBGS.h" + +int main(int argc, char **argv) +{ + CvCapture *capture = 0; + + capture = cvCaptureFromCAM(0); + //capture = cvCaptureFromAVI("video.avi"); + + if(!capture){ + std::cerr << "Cannot open initialize webcam!" << std::endl; + return 1; + } + + IplImage *frame = cvQueryFrame(capture); + + MultiLayerBGS* bgs = new MultiLayerBGS; + + int key = 0; + while(key != 'q') + { + frame = cvQueryFrame(capture); + + if(!frame) break; + + cv::Mat img_input(frame,true); + cv::resize(img_input,img_input,cv::Size(320,240)); + cv::imshow("input", img_input); + + cv::Mat img_mask; + bgs->process(img_input, img_mask); // automatically shows the foreground mask image + + //if(!img_mask.empty()) + // do something + + key = cvWaitKey(1); + } + + delete bgs; + + cvDestroyAllWindows(); + cvReleaseCapture(&capture); + + return 0; +} diff --git a/package_analysis/ForegroundMaskAnalysis.cpp b/package_analysis/ForegroundMaskAnalysis.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45645a01e3feb98ce478cca89ef9754c29d99f52 --- /dev/null +++ b/package_analysis/ForegroundMaskAnalysis.cpp @@ -0,0 +1,101 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "ForegroundMaskAnalysis.h" + +ForegroundMaskAnalysis::ForegroundMaskAnalysis() : firstTime(true), stopAt(0), showOutput(true), img_ref_path("") +{ + std::cout << "ForegroundMaskAnalysis()" << std::endl; +} + +ForegroundMaskAnalysis::~ForegroundMaskAnalysis() +{ + std::cout << "~ForegroundMaskAnalysis()" << std::endl; +} + +void ForegroundMaskAnalysis::process(const long &frameNumber, const std::string &name, const cv::Mat &img_input) +{ + if(img_input.empty()) + return; + + if(stopAt == 0) + { + loadConfig(); + + if(firstTime) + saveConfig(); + } + + if(stopAt == frameNumber && img_ref_path.empty() == false) + { + cv::Mat img_ref = cv::imread(img_ref_path, 0); + + if(showOutput) + cv::imshow("ForegroundMaskAnalysis", img_ref); + + int rn = cv::countNonZero(img_ref); + cv::Mat i; + cv::Mat u; + + if(rn > 0) + { + i = img_input & img_ref; + u = img_input | img_ref; + } + else + { + i = (~img_input) & (~img_ref); + u = (~img_input) | (~img_ref); + } + + int in = cv::countNonZero(i); + int un = cv::countNonZero(u); + + double s = (((double)in) / ((double)un)); + + if(showOutput) + { + cv::imshow("A^B", i); + cv::imshow("AvB", u); + } + + std::cout << name << " - Similarity Measure: " << s << " press ENTER to continue" << std::endl; + + cv::waitKey(0); + } + + firstTime = false; +} + +void ForegroundMaskAnalysis::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/ForegroundMaskAnalysis.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "stopAt", stopAt); + cvWriteString(fs, "img_ref_path", img_ref_path.c_str()); + + cvReleaseFileStorage(&fs); +} + +void ForegroundMaskAnalysis::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/ForegroundMaskAnalysis.xml", 0, CV_STORAGE_READ); + + stopAt = cvReadIntByName(fs, 0, "stopAt", 0); + img_ref_path = cvReadStringByName(fs, 0, "img_ref_path", ""); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_analysis/ForegroundMaskAnalysis.h b/package_analysis/ForegroundMaskAnalysis.h new file mode 100644 index 0000000000000000000000000000000000000000..d6bfdaea123037b8b9b0d380910c84124a28204c --- /dev/null +++ b/package_analysis/ForegroundMaskAnalysis.h @@ -0,0 +1,42 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <string> +#include <cv.h> +#include <highgui.h> + +class ForegroundMaskAnalysis +{ +private: + bool firstTime; + bool showOutput; + +public: + ForegroundMaskAnalysis(); + ~ForegroundMaskAnalysis(); + + long stopAt; + std::string img_ref_path; + + void process(const long &frameNumber, const std::string &name, const cv::Mat &img_input); + +private: + void saveConfig(); + void loadConfig(); +}; \ No newline at end of file diff --git a/package_bgs/AdaptiveBackgroundLearning.cpp b/package_bgs/AdaptiveBackgroundLearning.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b111e65be5ca7b12274af2f91dcd1672f1bd3e36 --- /dev/null +++ b/package_bgs/AdaptiveBackgroundLearning.cpp @@ -0,0 +1,111 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "AdaptiveBackgroundLearning.h" + +AdaptiveBackgroundLearning::AdaptiveBackgroundLearning() : firstTime(true), alpha(0.05), limit(-1), counter(0), minVal(0.0), maxVal(1.0), + enableThreshold(true), threshold(15), showForeground(true), showBackground(true) +{ + std::cout << "AdaptiveBackgroundLearning()" << std::endl; +} + +AdaptiveBackgroundLearning::~AdaptiveBackgroundLearning() +{ + std::cout << "~AdaptiveBackgroundLearning()" << std::endl; +} + +void AdaptiveBackgroundLearning::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + if(img_background.empty()) + img_input.copyTo(img_background); + + cv::Mat img_input_f(img_input.size(), CV_32F); + img_input.convertTo(img_input_f, CV_32F, 1./255.); + + cv::Mat img_background_f(img_background.size(), CV_32F); + img_background.convertTo(img_background_f, CV_32F, 1./255.); + + cv::Mat img_diff_f(img_input.size(), CV_32F); + cv::absdiff(img_input_f, img_background_f, img_diff_f); + + if((limit > 0 && limit < counter) || limit == -1) + { + img_background_f = alpha*img_input_f + (1-alpha)*img_background_f; + + cv::Mat img_new_background(img_input.size(), CV_8U); + img_background_f.convertTo(img_new_background, CV_8U, 255.0/(maxVal - minVal), -minVal); + img_new_background.copyTo(img_background); + + if(limit > 0 && limit < counter) + counter++; + } + + cv::Mat img_foreground(img_input.size(), CV_8U); + img_diff_f.convertTo(img_foreground, CV_8U, 255.0/(maxVal - minVal), -minVal); + + if(img_foreground.channels() == 3) + cv::cvtColor(img_foreground, img_foreground, CV_BGR2GRAY); + + if(enableThreshold) + cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY); + + if(showForeground) + cv::imshow("A-Learning FG", img_foreground); + + if(showBackground) + cv::imshow("A-Learning BG", img_background); + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + firstTime = false; +} + +void AdaptiveBackgroundLearning::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/AdaptiveBackgroundLearning.xml", 0, CV_STORAGE_WRITE); + + cvWriteReal(fs, "alpha", alpha); + cvWriteInt(fs, "limit", limit); + cvWriteInt(fs, "enableThreshold", enableThreshold); + cvWriteInt(fs, "threshold", threshold); + cvWriteInt(fs, "showForeground", showForeground); + cvWriteInt(fs, "showBackground", showBackground); + + cvReleaseFileStorage(&fs); +} + +void AdaptiveBackgroundLearning::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/AdaptiveBackgroundLearning.xml", 0, CV_STORAGE_READ); + + alpha = cvReadRealByName(fs, 0, "alpha", 0.05); + limit = cvReadIntByName(fs, 0, "limit", -1); + enableThreshold = cvReadIntByName(fs, 0, "enableThreshold", true); + threshold = cvReadIntByName(fs, 0, "threshold", 15); + showForeground = cvReadIntByName(fs, 0, "showForeground", true); + showBackground = cvReadIntByName(fs, 0, "showBackground", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/AdaptiveBackgroundLearning.h b/package_bgs/AdaptiveBackgroundLearning.h new file mode 100644 index 0000000000000000000000000000000000000000..eb07a643ecc09a3acee8dcc24d0f2e2f81fc1395 --- /dev/null +++ b/package_bgs/AdaptiveBackgroundLearning.h @@ -0,0 +1,50 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "IBGS.h" + +class AdaptiveBackgroundLearning : public IBGS +{ +private: + bool firstTime; + cv::Mat img_background; + double alpha; + long limit; + long counter; + double minVal; + double maxVal; + bool enableThreshold; + int threshold; + bool showForeground; + bool showBackground; + +public: + AdaptiveBackgroundLearning(); + ~AdaptiveBackgroundLearning(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/AdaptiveSelectiveBackgroundLearning.cpp b/package_bgs/AdaptiveSelectiveBackgroundLearning.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd3e7d3d966ec6b2e2850d21305f15e1341889df --- /dev/null +++ b/package_bgs/AdaptiveSelectiveBackgroundLearning.cpp @@ -0,0 +1,131 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "AdaptiveSelectiveBackgroundLearning.h" + +AdaptiveSelectiveBackgroundLearning::AdaptiveSelectiveBackgroundLearning() : firstTime(true), +alphaLearn(0.05), alphaDetection(0.05), learningFrames(-1), counter(0), minVal(0.0), maxVal(1.0), +threshold(15), showOutput(true) +{ + std::cout << "AdaptiveSelectiveBackgroundLearning()" << std::endl; +} + +AdaptiveSelectiveBackgroundLearning::~AdaptiveSelectiveBackgroundLearning() +{ + std::cout << "~AdaptiveSelectiveBackgroundLearning()" << std::endl; +} + +void AdaptiveSelectiveBackgroundLearning::process(const cv::Mat &img_input_, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input_.empty()) + return; + + cv::Mat img_input; + if (img_input_.channels() == 3) + cv::cvtColor(img_input_, img_input, CV_BGR2GRAY); + else + img_input_.copyTo(img_input); + + loadConfig(); + + if(firstTime) + saveConfig(); + + if(img_background.empty()) + img_input.copyTo(img_background); + + cv::Mat img_input_f(img_input.size(), CV_32F); + img_input.convertTo(img_input_f, CV_32F, 1./255.); + + cv::Mat img_background_f(img_background.size(), CV_32F); + img_background.convertTo(img_background_f, CV_32F, 1./255.); + + cv::Mat img_diff_f(img_input.size(), CV_32F); + cv::absdiff(img_input_f, img_background_f, img_diff_f); + + cv::Mat img_foreground(img_input.size(), CV_8U); + img_diff_f.convertTo(img_foreground, CV_8U, 255.0 / (maxVal - minVal), -minVal); + + cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY); + cv::medianBlur(img_foreground, img_foreground, 3); + + if (learningFrames > 0 && counter <= learningFrames) + { + //std::cout << "Adaptive update..." << std::endl; + // Only Adaptive update of the background model + img_background_f = alphaLearn * img_input_f + (1 - alphaLearn) * img_background_f; + counter++; + } + else + { + //std::cout << "Adaptive and Selective update..." << std::endl; + int rows = img_input.rows; + int cols = img_input.cols; + + for (int i = 0; i < rows; i++) + { + for (int j = 0; j < cols; j++) + { + // Adaptive and Selective update of the background model + if (img_foreground.at<uchar>(i, j) == 0) + { + img_background_f.at<float>(i, j) = alphaDetection * img_input_f.at<float>(i, j) + (1 - alphaDetection) * img_background_f.at<float>(i, j); + } + } + } + } + + cv::Mat img_new_background(img_input.size(), CV_8U); + img_background_f.convertTo(img_new_background, CV_8U, 255.0 / (maxVal - minVal), -minVal); + img_new_background.copyTo(img_background); + + if(showOutput) + { + cv::imshow("AS-Learning FG", img_foreground); + cv::imshow("AS-Learning BG", img_background); + } + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + firstTime = false; +} + +void AdaptiveSelectiveBackgroundLearning::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/AdaptiveSelectiveBackgroundLearning.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "learningFrames", learningFrames); + cvWriteReal(fs, "alphaLearn", alphaLearn); + cvWriteReal(fs, "alphaDetection", alphaDetection); + cvWriteInt(fs, "threshold", threshold); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void AdaptiveSelectiveBackgroundLearning::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/AdaptiveSelectiveBackgroundLearning.xml", 0, CV_STORAGE_READ); + + learningFrames = cvReadIntByName(fs, 0, "learningFrames", 90); + alphaLearn = cvReadRealByName(fs, 0, "alphaLearn", 0.05); + alphaDetection = cvReadRealByName(fs, 0, "alphaDetection", 0.05); + threshold = cvReadIntByName(fs, 0, "threshold", 25); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/AdaptiveSelectiveBackgroundLearning.h b/package_bgs/AdaptiveSelectiveBackgroundLearning.h new file mode 100644 index 0000000000000000000000000000000000000000..cb9f4d57320be6c0e4df50808af576ff4f869772 --- /dev/null +++ b/package_bgs/AdaptiveSelectiveBackgroundLearning.h @@ -0,0 +1,49 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "IBGS.h" + +class AdaptiveSelectiveBackgroundLearning : public IBGS +{ +private: + bool firstTime; + cv::Mat img_background; + double alphaLearn; + double alphaDetection; + long learningFrames; + long counter; + double minVal; + double maxVal; + int threshold; + bool showOutput; + +public: + AdaptiveSelectiveBackgroundLearning(); + ~AdaptiveSelectiveBackgroundLearning(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/FrameDifferenceBGS.cpp b/package_bgs/FrameDifferenceBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e87165120919861a03b54fd8066b073122bfdf59 --- /dev/null +++ b/package_bgs/FrameDifferenceBGS.cpp @@ -0,0 +1,83 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "FrameDifferenceBGS.h" + +FrameDifferenceBGS::FrameDifferenceBGS() : firstTime(true), enableThreshold(true), threshold(15), showOutput(true) +{ + std::cout << "FrameDifferenceBGS()" << std::endl; +} + +FrameDifferenceBGS::~FrameDifferenceBGS() +{ + std::cout << "~FrameDifferenceBGS()" << std::endl; +} + +void FrameDifferenceBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + if(img_input_prev.empty()) + { + img_input.copyTo(img_input_prev); + return; + } + + cv::absdiff(img_input_prev, img_input, img_foreground); + + if(img_foreground.channels() == 3) + cv::cvtColor(img_foreground, img_foreground, CV_BGR2GRAY); + + if(enableThreshold) + cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY); + + if(showOutput) + cv::imshow("Frame Difference", img_foreground); + + img_foreground.copyTo(img_output); + + img_input.copyTo(img_input_prev); + + firstTime = false; +} + +void FrameDifferenceBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/FrameDifferenceBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "enableThreshold", enableThreshold); + cvWriteInt(fs, "threshold", threshold); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void FrameDifferenceBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/FrameDifferenceBGS.xml", 0, CV_STORAGE_READ); + + enableThreshold = cvReadIntByName(fs, 0, "enableThreshold", true); + threshold = cvReadIntByName(fs, 0, "threshold", 15); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/FrameDifferenceBGS.h b/package_bgs/FrameDifferenceBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..3fab44d1f8250be50d62b5dd7a3662b2567e7b54 --- /dev/null +++ b/package_bgs/FrameDifferenceBGS.h @@ -0,0 +1,44 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "IBGS.h" + +class FrameDifferenceBGS : public IBGS +{ +private: + bool firstTime; + cv::Mat img_input_prev; + cv::Mat img_foreground; + bool enableThreshold; + int threshold; + bool showOutput; + +public: + FrameDifferenceBGS(); + ~FrameDifferenceBGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; \ No newline at end of file diff --git a/package_bgs/GMG.cpp b/package_bgs/GMG.cpp new file mode 100644 index 0000000000000000000000000000000000000000..675b23cba8f3a9f71a35bc99fa66d95c7094b456 --- /dev/null +++ b/package_bgs/GMG.cpp @@ -0,0 +1,99 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "GMG.h" + +GMG::GMG() : firstTime(true), initializationFrames(20), decisionThreshold(0.7), showOutput(true) +{ + std::cout << "GMG()" << std::endl; + + cv::initModule_video(); + cv::setUseOptimized(true); + cv::setNumThreads(8); + + fgbg = cv::Algorithm::create<cv::BackgroundSubtractorGMG>("BackgroundSubtractor.GMG"); +} + +GMG::~GMG() +{ + std::cout << "~GMG()" << std::endl; +} + +void GMG::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + { + fgbg->set("initializationFrames", initializationFrames); + fgbg->set("decisionThreshold", decisionThreshold); + + saveConfig(); + } + + if(fgbg.empty()) + { + std::cerr << "Failed to create BackgroundSubtractor.GMG Algorithm." << std::endl; + return; + } + + (*fgbg)(img_input, img_foreground); + + cv::Mat img_background; + (*fgbg).getBackgroundImage(img_background); + + img_input.copyTo(img_segmentation); + cv::add(img_input, cv::Scalar(100, 100, 0), img_segmentation, img_foreground); + + if(showOutput) + { + if (!img_foreground.empty()) + cv::imshow("GMG FG (Godbehere-Matsukawa-Goldberg)", img_foreground); + + if (!img_background.empty()) + cv::imshow("GMG BG (Godbehere-Matsukawa-Goldberg)", img_background); + } + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + firstTime = false; +} + +void GMG::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/GMG.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "initializationFrames", initializationFrames); + cvWriteReal(fs, "decisionThreshold", decisionThreshold); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void GMG::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/GMG.xml", 0, CV_STORAGE_READ); + + initializationFrames = cvReadIntByName(fs, 0, "initializationFrames", 20); + decisionThreshold = cvReadRealByName(fs, 0, "decisionThreshold", 0.7); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/GMG.h b/package_bgs/GMG.h new file mode 100644 index 0000000000000000000000000000000000000000..9da28adb7a40d2cb443d34372a6e6220c09f3af3 --- /dev/null +++ b/package_bgs/GMG.h @@ -0,0 +1,45 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <opencv2/opencv.hpp> + +#include "IBGS.h" + +class GMG : public IBGS +{ +private: + bool firstTime; + cv::Ptr<cv::BackgroundSubtractorGMG> fgbg; + int initializationFrames; + double decisionThreshold; + cv::Mat img_foreground; + cv::Mat img_segmentation; + bool showOutput; + +public: + GMG(); + ~GMG(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/IBGS.h b/package_bgs/IBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..0b57658660950d70bc6902bcfbfeea4bee5cd695 --- /dev/null +++ b/package_bgs/IBGS.h @@ -0,0 +1,33 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <cv.h> + +class IBGS +{ +public: + virtual void process(const cv::Mat &img_input, cv::Mat &img_foreground, cv::Mat &img_background) = 0; + /*virtual void process(const cv::Mat &img_input, cv::Mat &img_foreground){ + process(img_input, img_foreground, cv::Mat()); + }*/ + virtual ~IBGS(){} + +private: + virtual void saveConfig() = 0; + virtual void loadConfig() = 0; +}; diff --git a/package_bgs/MixtureOfGaussianV1BGS.cpp b/package_bgs/MixtureOfGaussianV1BGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..51d41eb57348d76695a5c6c4392dc0e5e385a371 --- /dev/null +++ b/package_bgs/MixtureOfGaussianV1BGS.cpp @@ -0,0 +1,95 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "MixtureOfGaussianV1BGS.h" + +MixtureOfGaussianV1BGS::MixtureOfGaussianV1BGS() : firstTime(true), alpha(0.05), enableThreshold(true), threshold(15), showOutput(true) +{ + std::cout << "MixtureOfGaussianV1BGS()" << std::endl; +} + +MixtureOfGaussianV1BGS::~MixtureOfGaussianV1BGS() +{ + std::cout << "~MixtureOfGaussianV1BGS()" << std::endl; +} + +void MixtureOfGaussianV1BGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + //------------------------------------------------------------------ + // BackgroundSubtractorMOG + // http://opencv.itseez.com/modules/video/doc/motion_analysis_and_object_tracking.html#backgroundsubtractormog + // + // Gaussian Mixture-based Backbround/Foreground Segmentation Algorithm. + // + // The class implements the algorithm described in: + // P. KadewTraKuPong and R. Bowden, + // An improved adaptive background mixture model for real-time tracking with shadow detection, + // Proc. 2nd European Workshp on Advanced Video-Based Surveillance Systems, 2001 + //------------------------------------------------------------------ + + mog(img_input, img_foreground, alpha); + cv::Mat img_background; + mog.getBackgroundImage(img_background); + + if(enableThreshold) + cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY); + + if(showOutput) + { + if (!img_foreground.empty()) + cv::imshow("GMM FG (KadewTraKuPong&Bowden)", img_foreground); + + if (!img_background.empty()) + cv::imshow("GMM BG (KadewTraKuPong&Bowden)", img_background); + } + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + firstTime = false; +} + +void MixtureOfGaussianV1BGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/MixtureOfGaussianV1BGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteReal(fs, "alpha", alpha); + cvWriteInt(fs, "enableThreshold", enableThreshold); + cvWriteInt(fs, "threshold", threshold); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void MixtureOfGaussianV1BGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/MixtureOfGaussianV1BGS.xml", 0, CV_STORAGE_READ); + + alpha = cvReadRealByName(fs, 0, "alpha", 0.05); + enableThreshold = cvReadIntByName(fs, 0, "enableThreshold", true); + threshold = cvReadIntByName(fs, 0, "threshold", 15); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/MixtureOfGaussianV1BGS.h b/package_bgs/MixtureOfGaussianV1BGS.h new file mode 100644 index 0000000000000000000000000000000000000000..8a67acc27fbb9859d6fd2b60a5dd1ea15d0ffcb4 --- /dev/null +++ b/package_bgs/MixtureOfGaussianV1BGS.h @@ -0,0 +1,47 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> +#include <opencv2/video/background_segm.hpp> + +#include "IBGS.h" + +class MixtureOfGaussianV1BGS : public IBGS +{ +private: + bool firstTime; + cv::BackgroundSubtractorMOG mog; + cv::Mat img_foreground; + double alpha; + bool enableThreshold; + int threshold; + bool showOutput; + +public: + MixtureOfGaussianV1BGS(); + ~MixtureOfGaussianV1BGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/MixtureOfGaussianV2BGS.cpp b/package_bgs/MixtureOfGaussianV2BGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5ce33a3ef1b36a5948c20288530be22332106f0c --- /dev/null +++ b/package_bgs/MixtureOfGaussianV2BGS.cpp @@ -0,0 +1,98 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "MixtureOfGaussianV2BGS.h" + +MixtureOfGaussianV2BGS::MixtureOfGaussianV2BGS() : firstTime(true), alpha(0.05), enableThreshold(true), threshold(15), showOutput(true) +{ + std::cout << "MixtureOfGaussianV2BGS()" << std::endl; +} + +MixtureOfGaussianV2BGS::~MixtureOfGaussianV2BGS() +{ + std::cout << "~MixtureOfGaussianV2BGS()" << std::endl; +} + +void MixtureOfGaussianV2BGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + //------------------------------------------------------------------ + // BackgroundSubtractorMOG2 + // http://opencv.itseez.com/modules/video/doc/motion_analysis_and_object_tracking.html#backgroundsubtractormog2 + // + // Gaussian Mixture-based Backbround/Foreground Segmentation Algorithm. + // + // The class implements the Gaussian mixture model background subtraction described in: + // (1) Z.Zivkovic, Improved adaptive Gausian mixture model for background subtraction, International Conference Pattern Recognition, UK, August, 2004, + // The code is very fast and performs also shadow detection. Number of Gausssian components is adapted per pixel. + // + // (2) Z.Zivkovic, F. van der Heijden, Efficient Adaptive Density Estimation per Image Pixel for the Task of Background Subtraction, + // Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006. + // The algorithm similar to the standard Stauffer&Grimson algorithm with additional selection of the number of the Gaussian components based on: + // Z.Zivkovic, F.van der Heijden, Recursive unsupervised learning of finite mixture models, IEEE Trans. on Pattern Analysis and Machine Intelligence, + // vol.26, no.5, pages 651-656, 2004. + //------------------------------------------------------------------ + + mog(img_input, img_foreground, alpha); + + cv::Mat img_background; + mog.getBackgroundImage(img_background); + + if(enableThreshold) + cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY); + + if(showOutput) + { + cv::imshow("GMM (Zivkovic&Heijden)", img_foreground); + cv::imshow("GMM BKG (Zivkovic&Heijden)", img_background); + } + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + firstTime = false; +} + +void MixtureOfGaussianV2BGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/MixtureOfGaussianV2BGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteReal(fs, "alpha", alpha); + cvWriteInt(fs, "enableThreshold", enableThreshold); + cvWriteInt(fs, "threshold", threshold); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void MixtureOfGaussianV2BGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/MixtureOfGaussianV2BGS.xml", 0, CV_STORAGE_READ); + + alpha = cvReadRealByName(fs, 0, "alpha", 0.05); + enableThreshold = cvReadIntByName(fs, 0, "enableThreshold", true); + threshold = cvReadIntByName(fs, 0, "threshold", 15); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/MixtureOfGaussianV2BGS.h b/package_bgs/MixtureOfGaussianV2BGS.h new file mode 100644 index 0000000000000000000000000000000000000000..495d70128f183df709a60d030916a48543af96b3 --- /dev/null +++ b/package_bgs/MixtureOfGaussianV2BGS.h @@ -0,0 +1,47 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> +#include <opencv2/video/background_segm.hpp> + +#include "IBGS.h" + +class MixtureOfGaussianV2BGS : public IBGS +{ +private: + bool firstTime; + cv::BackgroundSubtractorMOG2 mog; + cv::Mat img_foreground; + double alpha; + bool enableThreshold; + int threshold; + bool showOutput; + +public: + MixtureOfGaussianV2BGS(); + ~MixtureOfGaussianV2BGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/StaticFrameDifferenceBGS.cpp b/package_bgs/StaticFrameDifferenceBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3463c457b2ae509560903d76d46ff8d2a61daf5d --- /dev/null +++ b/package_bgs/StaticFrameDifferenceBGS.cpp @@ -0,0 +1,79 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "StaticFrameDifferenceBGS.h" + +StaticFrameDifferenceBGS::StaticFrameDifferenceBGS() : firstTime(true), enableThreshold(true), threshold(15), showOutput(true) +{ + std::cout << "StaticFrameDifferenceBGS()" << std::endl; +} + +StaticFrameDifferenceBGS::~StaticFrameDifferenceBGS() +{ + std::cout << "~StaticFrameDifferenceBGS()" << std::endl; +} + +void StaticFrameDifferenceBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + if(img_background.empty()) + img_input.copyTo(img_background); + + loadConfig(); + + if(firstTime) + saveConfig(); + + cv::absdiff(img_input, img_background, img_foreground); + + if(img_foreground.channels() == 3) + cv::cvtColor(img_foreground, img_foreground, CV_BGR2GRAY); + + if(enableThreshold) + cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY); + + if(showOutput) + cv::imshow("Static Frame Difference", img_foreground); + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + firstTime = false; +} + +void StaticFrameDifferenceBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/StaticFrameDifferenceBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "enableThreshold", enableThreshold); + cvWriteInt(fs, "threshold", threshold); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void StaticFrameDifferenceBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/StaticFrameDifferenceBGS.xml", 0, CV_STORAGE_READ); + + enableThreshold = cvReadIntByName(fs, 0, "enableThreshold", true); + threshold = cvReadIntByName(fs, 0, "threshold", 15); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/StaticFrameDifferenceBGS.h b/package_bgs/StaticFrameDifferenceBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..416d13be3a4fc21fffe7bd86ab155b978499a3c2 --- /dev/null +++ b/package_bgs/StaticFrameDifferenceBGS.h @@ -0,0 +1,45 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "IBGS.h" + +class StaticFrameDifferenceBGS : public IBGS +{ +private: + bool firstTime; + cv::Mat img_background; + cv::Mat img_foreground; + bool enableThreshold; + int threshold; + bool showOutput; + +public: + StaticFrameDifferenceBGS(); + ~StaticFrameDifferenceBGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/WeightedMovingMeanBGS.cpp b/package_bgs/WeightedMovingMeanBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..841c467393c3012aff42eac1a36b44321b024b80 --- /dev/null +++ b/package_bgs/WeightedMovingMeanBGS.cpp @@ -0,0 +1,122 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "WeightedMovingMeanBGS.h" + +WeightedMovingMeanBGS::WeightedMovingMeanBGS() : firstTime(true), enableWeight(true), enableThreshold(true), threshold(15), showOutput(true), showBackground(false) +{ + std::cout << "WeightedMovingMeanBGS()" << std::endl; +} + +WeightedMovingMeanBGS::~WeightedMovingMeanBGS() +{ + std::cout << "~WeightedMovingMeanBGS()" << std::endl; +} + +void WeightedMovingMeanBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + if(img_input_prev_1.empty()) + { + img_input.copyTo(img_input_prev_1); + return; + } + + if(img_input_prev_2.empty()) + { + img_input_prev_1.copyTo(img_input_prev_2); + img_input.copyTo(img_input_prev_1); + return; + } + + cv::Mat img_input_f(img_input.size(), CV_32F); + img_input.convertTo(img_input_f, CV_32F, 1./255.); + + cv::Mat img_input_prev_1_f(img_input.size(), CV_32F); + img_input_prev_1.convertTo(img_input_prev_1_f, CV_32F, 1./255.); + + cv::Mat img_input_prev_2_f(img_input.size(), CV_32F); + img_input_prev_2.convertTo(img_input_prev_2_f, CV_32F, 1./255.); + + cv::Mat img_background_f(img_input.size(), CV_32F); + + if(enableWeight) + img_background_f = ((img_input_f * 0.5) + (img_input_prev_1_f * 0.3) + (img_input_prev_2_f * 0.2)); + else + img_background_f = ((img_input_f) + (img_input_prev_1_f) + (img_input_prev_2_f)) / 3.0; + + cv::Mat img_background(img_background_f.size(), CV_8U); + + double minVal, maxVal; + minVal = 0.; maxVal = 1.; + img_background_f.convertTo(img_background, CV_8U, 255.0/(maxVal - minVal), -minVal); + + if(showBackground) + cv::imshow("W Moving Mean BG Model", img_background); + + cv::Mat img_foreground; + cv::absdiff(img_input, img_background, img_foreground); + + if(img_foreground.channels() == 3) + cv::cvtColor(img_foreground, img_foreground, CV_BGR2GRAY); + + if(enableThreshold) + cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY); + + if(showOutput) + cv::imshow("W Moving Mean FG Mask", img_foreground); + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + img_input_prev_1.copyTo(img_input_prev_2); + img_input.copyTo(img_input_prev_1); + + firstTime = false; +} + +void WeightedMovingMeanBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/WeightedMovingMeanBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "enableWeight", enableWeight); + cvWriteInt(fs, "enableThreshold", enableThreshold); + cvWriteInt(fs, "threshold", threshold); + cvWriteInt(fs, "showOutput", showOutput); + cvWriteInt(fs, "showBackground", showBackground); + + cvReleaseFileStorage(&fs); +} + +void WeightedMovingMeanBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/WeightedMovingMeanBGS.xml", 0, CV_STORAGE_READ); + + enableWeight = cvReadIntByName(fs, 0, "enableWeight", true); + enableThreshold = cvReadIntByName(fs, 0, "enableThreshold", true); + threshold = cvReadIntByName(fs, 0, "threshold", 15); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + showBackground = cvReadIntByName(fs, 0, "showBackground", false); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/WeightedMovingMeanBGS.h b/package_bgs/WeightedMovingMeanBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..a6684e7ab8cb866ae4517fab27707e8004d2ae14 --- /dev/null +++ b/package_bgs/WeightedMovingMeanBGS.h @@ -0,0 +1,47 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "IBGS.h" + +class WeightedMovingMeanBGS : public IBGS +{ +private: + bool firstTime; + cv::Mat img_input_prev_1; + cv::Mat img_input_prev_2; + bool enableWeight; + bool enableThreshold; + int threshold; + bool showOutput; + bool showBackground; + +public: + WeightedMovingMeanBGS(); + ~WeightedMovingMeanBGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/WeightedMovingVarianceBGS.cpp b/package_bgs/WeightedMovingVarianceBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e691e9e470ffa42236c4a6408f3d9703c904798 --- /dev/null +++ b/package_bgs/WeightedMovingVarianceBGS.cpp @@ -0,0 +1,161 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "WeightedMovingVarianceBGS.h" + +WeightedMovingVarianceBGS::WeightedMovingVarianceBGS() : firstTime(true), enableWeight(true), + enableThreshold(true), threshold(15), showOutput(true) +{ + std::cout << "WeightedMovingVarianceBGS()" << std::endl; +} + +WeightedMovingVarianceBGS::~WeightedMovingVarianceBGS() +{ + std::cout << "~WeightedMovingVarianceBGS()" << std::endl; +} + +void WeightedMovingVarianceBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + if(img_input_prev_1.empty()) + { + img_input.copyTo(img_input_prev_1); + return; + } + + if(img_input_prev_2.empty()) + { + img_input_prev_1.copyTo(img_input_prev_2); + img_input.copyTo(img_input_prev_1); + return; + } + + cv::Mat img_input_f(img_input.size(), CV_32F); + img_input.convertTo(img_input_f, CV_32F, 1./255.); + + cv::Mat img_input_prev_1_f(img_input.size(), CV_32F); + img_input_prev_1.convertTo(img_input_prev_1_f, CV_32F, 1./255.); + + cv::Mat img_input_prev_2_f(img_input.size(), CV_32F); + img_input_prev_2.convertTo(img_input_prev_2_f, CV_32F, 1./255.); + + cv::Mat img_foreground; + + // Weighted mean + cv::Mat img_mean_f(img_input.size(), CV_32F); + + if(enableWeight) + img_mean_f = ((img_input_f * 0.5) + (img_input_prev_1_f * 0.3) + (img_input_prev_2_f * 0.2)); + else + img_mean_f = ((img_input_f * 0.3) + (img_input_prev_1_f * 0.3) + (img_input_prev_2_f * 0.3)); + + // Weighted variance + cv::Mat img_1_f(img_input.size(), CV_32F); + cv::Mat img_2_f(img_input.size(), CV_32F); + cv::Mat img_3_f(img_input.size(), CV_32F); + cv::Mat img_4_f(img_input.size(), CV_32F); + + if(enableWeight) + { + img_1_f = computeWeightedVariance(img_input_f, img_mean_f, 0.5); + img_2_f = computeWeightedVariance(img_input_prev_1_f, img_mean_f, 0.3); + img_3_f = computeWeightedVariance(img_input_prev_2_f, img_mean_f, 0.2); + img_4_f = (img_1_f + img_2_f + img_3_f); + } + else + { + img_1_f = computeWeightedVariance(img_input_f, img_mean_f, 0.3); + img_2_f = computeWeightedVariance(img_input_prev_1_f, img_mean_f, 0.3); + img_3_f = computeWeightedVariance(img_input_prev_2_f, img_mean_f, 0.3); + img_4_f = (img_1_f + img_2_f + img_3_f); + } + + // Standard deviation + cv::Mat img_sqrt_f(img_input.size(), CV_32F); + cv::sqrt(img_4_f, img_sqrt_f); + cv::Mat img_sqrt(img_input.size(), CV_8U); + double minVal, maxVal; + minVal = 0.; maxVal = 1.; + img_sqrt_f.convertTo(img_sqrt, CV_8U, 255.0/(maxVal - minVal), -minVal); + img_sqrt.copyTo(img_foreground); + + if(img_foreground.channels() == 3) + cv::cvtColor(img_foreground, img_foreground, CV_BGR2GRAY); + + if(enableThreshold) + cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY); + + if(showOutput) + cv::imshow("W Moving Variance", img_foreground); + + img_foreground.copyTo(img_output); + + img_input_prev_1.copyTo(img_input_prev_2); + img_input.copyTo(img_input_prev_1); + + firstTime = false; +} + +//unused +cv::Mat WeightedMovingVarianceBGS::computeWeightedMean(const std::vector<cv::Mat> &v_img_input_f, const std::vector<double> weights) +{ + cv::Mat img; + return img; +} + +cv::Mat WeightedMovingVarianceBGS::computeWeightedVariance(const cv::Mat &img_input_f, const cv::Mat &img_mean_f, const double weight) +{ + //ERROR in return (weight * ((cv::abs(img_input_f - img_mean_f))^2.)); + + cv::Mat img_f_absdiff(img_input_f.size(), CV_32F); + cv::absdiff(img_input_f, img_mean_f, img_f_absdiff); + cv::Mat img_f_pow(img_input_f.size(), CV_32F); + cv::pow(img_f_absdiff, 2.0, img_f_pow); + cv::Mat img_f = weight * img_f_pow; + + return img_f; +} + +void WeightedMovingVarianceBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/WeightedMovingVarianceBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "enableWeight", enableWeight); + cvWriteInt(fs, "enableThreshold", enableThreshold); + cvWriteInt(fs, "threshold", threshold); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void WeightedMovingVarianceBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/WeightedMovingVarianceBGS.xml", 0, CV_STORAGE_READ); + + enableWeight = cvReadIntByName(fs, 0, "enableWeight", true); + enableThreshold = cvReadIntByName(fs, 0, "enableThreshold", true); + threshold = cvReadIntByName(fs, 0, "threshold", 15); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/WeightedMovingVarianceBGS.h b/package_bgs/WeightedMovingVarianceBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..f07cf592fcecdc33c115e2e391b5ce55f5f70910 --- /dev/null +++ b/package_bgs/WeightedMovingVarianceBGS.h @@ -0,0 +1,48 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "IBGS.h" + +class WeightedMovingVarianceBGS : public IBGS +{ +private: + bool firstTime; + cv::Mat img_input_prev_1; + cv::Mat img_input_prev_2; + bool enableWeight; + bool enableThreshold; + int threshold; + bool showOutput; + +public: + WeightedMovingVarianceBGS(); + ~WeightedMovingVarianceBGS(); + + cv::Mat computeWeightedMean(const std::vector<cv::Mat> &v_img_input_f, const std::vector<double> weights); + cv::Mat computeWeightedVariance(const cv::Mat &img_input_f, const cv::Mat &img_mean_f, const double weight); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; diff --git a/package_bgs/ae/KDE.cpp b/package_bgs/ae/KDE.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2cbbd8c808066f8a6c30c7dc31f6ed90068caa84 --- /dev/null +++ b/package_bgs/ae/KDE.cpp @@ -0,0 +1,128 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "KDE.h" + +KDE::KDE() : SequenceLength(50), TimeWindowSize(100), SDEstimationFlag(1), lUseColorRatiosFlag(1), + th(10e-8), alpha(0.3), framesToLearn(10), frameNumber(0), firstTime(true), showOutput(true) +{ + p = new NPBGSubtractor; + std::cout << "KDE()" << std::endl; +} + +KDE::~KDE() +{ + delete FGImage; + delete p; + std::cout << "~KDE()" << std::endl; +} + +void KDE::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + { + rows = img_input.size().height; + cols = img_input.size().width; + color_channels = img_input.channels(); + + // SequenceLength: number of samples for each pixel. + // TimeWindowSize: Time window for sampling. for example in the call above, the bg will sample 50 points out of 100 frames. + // this rate will affect how fast the model adapt. + // SDEstimationFlag: True means to estimate suitable kernel bandwidth to each pixel, False uses a default value. + // lUseColorRatiosFlag: True means use normalized RGB for color (recommended.) + p->Intialize(rows,cols,color_channels,SequenceLength,TimeWindowSize,SDEstimationFlag,lUseColorRatiosFlag); + // th: 0-1 is the probability threshold for a pixel to be a foregroud. typically make it small as 10e-8. the smaller the value the less false positive and more false negative. + // alpha: 0-1, for color. typically set to 0.3. this affect shadow suppression. + p->SetThresholds(th,alpha); + + FGImage = new unsigned char[rows*cols]; + //FilteredFGImage = new unsigned char[rows*cols]; + FilteredFGImage = 0; + DisplayBuffers = 0; + + img_foreground = cv::Mat::zeros(rows,cols,CV_8UC1); + + frameNumber = 0; + saveConfig(); + firstTime = false; + } + + // Stores the first N frames to build the background model + if(frameNumber < framesToLearn) + { + p->AddFrame(img_input.data); + frameNumber++; + return; + } + + // Build the background model with first 10 frames + if(frameNumber == framesToLearn) + { + p->Estimation(); + frameNumber++; + } + + // Now, we can subtract the background + ((NPBGSubtractor *)p)->NBBGSubtraction(img_input.data,FGImage,FilteredFGImage,DisplayBuffers); + + // At each frame also you can call the update function to adapt the bg + // here you pass a mask where pixels with true value will be masked out of the update. + ((NPBGSubtractor *)p)->Update(FGImage); + + img_foreground.data = FGImage; + + if(showOutput) + cv::imshow("KDE", img_foreground); + + img_foreground.copyTo(img_output); +} + +void KDE::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/KDE.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "framesToLearn", framesToLearn); + cvWriteInt(fs, "SequenceLength", SequenceLength); + cvWriteInt(fs, "TimeWindowSize", TimeWindowSize); + cvWriteInt(fs, "SDEstimationFlag", SDEstimationFlag); + cvWriteInt(fs, "lUseColorRatiosFlag", lUseColorRatiosFlag); + cvWriteReal(fs, "th", th); + cvWriteReal(fs, "alpha", alpha); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void KDE::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/KDE.xml", 0, CV_STORAGE_READ); + + framesToLearn = cvReadIntByName(fs, 0, "framesToLearn", 10); + SequenceLength = cvReadIntByName(fs, 0, "SequenceLength", 50); + TimeWindowSize = cvReadIntByName(fs, 0, "TimeWindowSize", 100); + SDEstimationFlag = cvReadIntByName(fs, 0, "SDEstimationFlag", 1); + lUseColorRatiosFlag = cvReadIntByName(fs, 0, "lUseColorRatiosFlag", 1); + th = cvReadRealByName(fs, 0, "th", 10e-8); + alpha = cvReadRealByName(fs, 0, "alpha", 0.3); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/ae/KDE.h b/package_bgs/ae/KDE.h new file mode 100644 index 0000000000000000000000000000000000000000..39f01dc196d57245faef08b88b2d2b6a5d9367d1 --- /dev/null +++ b/package_bgs/ae/KDE.h @@ -0,0 +1,58 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "NPBGSubtractor.h" +#include "../IBGS.h" + +class KDE : public IBGS +{ +private: + NPBGSubtractor *p; + int rows; + int cols; + int color_channels; + int SequenceLength; + int TimeWindowSize; + int SDEstimationFlag; + int lUseColorRatiosFlag; + double th; + double alpha; + int framesToLearn; + int frameNumber; + bool firstTime; + bool showOutput; + + cv::Mat img_foreground; + unsigned char *FGImage; + unsigned char *FilteredFGImage; + unsigned char **DisplayBuffers; + +public: + KDE(); + ~KDE(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; diff --git a/package_bgs/ae/KernelTable.cpp b/package_bgs/ae/KernelTable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e20d7cfc0c09382bc360bda5b069aa870c39d98 --- /dev/null +++ b/package_bgs/ae/KernelTable.cpp @@ -0,0 +1,116 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* +* +* Copyright 2001 by Ahmed Elgammal All rights reserved. +* +* Permission to use, copy, or modify this software and its documentation +* for educational and research purposes only and without fee is hereby +* granted, provided that this copyright notice and the original authors's +* name appear on all copies and supporting documentation. If individual +* files are separated from this distribution directory structure, this +* copyright notice must be included. For any other uses of this software, +* in original or modified form, including but not limited to distribution +* in whole or in part, specific prior permission must be obtained from +* Author or UMIACS. These programs shall not be used, rewritten, or +* adapted as the basis of a commercial software or hardware product +* without first obtaining appropriate licenses from Author. +* Other than these cases, no part of this software may be used or +* distributed without written permission of the author. +* +* Neither the author nor UMIACS make any representations about the +* suitability of this software for any purpose. It is provided +* "as is" without express or implied warranty. +* +* Ahmed Elgammal +* +* University of Maryland at College Park +* UMIACS +* A.V. Williams Bldg. +* CollegePark, MD 20742 +* E-mail: elgammal@umiacs.umd.edu +* +**/ + +#include "KernelTable.h" +#include <math.h> + +#define PI 3.14159 + +KernelLUTable::KernelLUTable() +{ + std::cout << "KernelLUTable()" << std::endl; +} + +KernelLUTable::~KernelLUTable() +{ + delete kerneltable; + delete kernelsums; + std::cout << "~KernelLUTable()" << std::endl; +} + +KernelLUTable::KernelLUTable(int KernelHalfWidth, double Segmamin, double Segmamax, int Segmabins) +{ + std::cout << "KernelLUTable()" << std::endl; + + double C1,C2,v,segma,sum; + int bin,b; + + minsegma = Segmamin; + maxsegma = Segmamax; + segmabins = Segmabins; + tablehalfwidth = KernelHalfWidth; + + // Generate the Kernel + + // allocate memory for the Kernal Table + kerneltable = new double[segmabins*(2*KernelHalfWidth+1)]; + kernelsums = new double[segmabins]; + + double segmastep = (maxsegma - minsegma) / segmabins; + double y; + + for(segma = minsegma, bin = 0; bin < segmabins; segma += segmastep, bin++) + { + C1 = 1/(sqrt(2*PI)*segma); + C2 = -1/(2*segma*segma); + + b = (2*KernelHalfWidth+1)*bin; + sum = 0; + + for(int x = 0; x <= KernelHalfWidth; x++) + { + y = x/1.0; + v = C1*exp(C2*y*y); + kerneltable[b+KernelHalfWidth+x]=v; + kerneltable[b+KernelHalfWidth-x]=v; + sum += 2*v; + } + + sum -= C1; + + kernelsums[bin] = sum; + + // Normailization + for(int x = 0; x <= KernelHalfWidth; x++) + { + v = kerneltable[b+KernelHalfWidth+x] / sum; + kerneltable[b+KernelHalfWidth+x]=v; + kerneltable[b+KernelHalfWidth-x]=v; + } + } +} diff --git a/package_bgs/ae/KernelTable.h b/package_bgs/ae/KernelTable.h new file mode 100644 index 0000000000000000000000000000000000000000..bc37cc4f0a8befb16cf987f7c0f7e697400b1dff --- /dev/null +++ b/package_bgs/ae/KernelTable.h @@ -0,0 +1,71 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* +* +* Copyright 2001 by Ahmed Elgammal All rights reserved. +* +* Permission to use, copy, or modify this software and its documentation +* for educational and research purposes only and without fee is hereby +* granted, provided that this copyright notice and the original authors's +* name appear on all copies and supporting documentation. If individual +* files are separated from this distribution directory structure, this +* copyright notice must be included. For any other uses of this software, +* in original or modified form, including but not limited to distribution +* in whole or in part, specific prior permission must be obtained from +* Author or UMIACS. These programs shall not be used, rewritten, or +* adapted as the basis of a commercial software or hardware product +* without first obtaining appropriate licenses from Author. +* Other than these cases, no part of this software may be used or +* distributed without written permission of the author. +* +* Neither the author nor UMIACS make any representations about the +* suitability of this software for any purpose. It is provided +* "as is" without express or implied warranty. +* +* Ahmed Elgammal +* +* University of Maryland at College Park +* UMIACS +* A.V. Williams Bldg. +* CollegePark, MD 20742 +* E-mail: elgammal@umiacs.umd.edu +* +**/ + +#ifndef __KERNEL_TABLE__ +#define __KERNEL_TABLE__ + +#include <iostream> + +class KernelLUTable +{ +public: + double minsegma; + double maxsegma; + int segmabins; + int tablehalfwidth; + double *kerneltable; + double *kernelsums; + +public: + KernelLUTable(); + ~KernelLUTable(); + + KernelLUTable(int KernelHalfWidth,double Segmamin,double Segmamax,int Segmabins); +}; + +#endif diff --git a/package_bgs/ae/NPBGSubtractor.cpp b/package_bgs/ae/NPBGSubtractor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0f77a9a225b8ae6a1ae54f716e29575aea5dd50d --- /dev/null +++ b/package_bgs/ae/NPBGSubtractor.cpp @@ -0,0 +1,1160 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* +* +* Copyright 2001 by Ahmed Elgammal All rights reserved. +* +* Permission to use, copy, or modify this software and its documentation +* for educational and research purposes only and without fee is hereby +* granted, provided that this copyright notice and the original authors's +* name appear on all copies and supporting documentation. If individual +* files are separated from this distribution directory structure, this +* copyright notice must be included. For any other uses of this software, +* in original or modified form, including but not limited to distribution +* in whole or in part, specific prior permission must be obtained from +* Author or UMIACS. These programs shall not be used, rewritten, or +* adapted as the basis of a commercial software or hardware product +* without first obtaining appropriate licenses from Author. +* Other than these cases, no part of this software may be used or +* distributed without written permission of the author. +* +* Neither the author nor UMIACS make any representations about the +* suitability of this software for any purpose. It is provided +* "as is" without express or implied warranty. +* +* Ahmed Elgammal +* +* University of Maryland at College Park +* UMIACS +* A.V. Williams Bldg. +* CollegePark, MD 20742 +* E-mail: elgammal@umiacs.umd.edu +* +**/ + +// NPBGSubtractor.cpp: implementation of the NPBGSubtractor class. +// +////////////////////////////////////////////////////////////////////// + +#include "NPBGSubtractor.h" +#include <assert.h> +#include <math.h> +#include <string.h> + +//#ifdef _DEBUG +//#undef THIS_FILE +//static char THIS_FILE[]=__FILE__; +//#define new DEBUG_NEW +//#endif + +void BGR2SnGnRn(unsigned char * in_image, + unsigned char * out_image, + unsigned int rows, + unsigned int cols) +{ + unsigned int i; + unsigned int r1,r2,r3; + unsigned int r,g,b; + double s; + + for(i = 0; i < rows*cols*3; i += 3) + { + b=in_image[i]; + g=in_image[i+1]; + r=in_image[i+2]; + + // calculate color ratios + s = (double) 255 / (double) (b+g+r+30); + + r2 =(unsigned int) ((g+10) * s ); + r3 =(unsigned int) ((r+10) * s ); + + out_image[i] = (unsigned char) (((unsigned int) b+g+r) / 3); + out_image[i+1] = (unsigned char) (r2 > 255 ? 255 : r2) ; + out_image[i+2] = (unsigned char) (r3 > 255 ? 255 : r3) ; + } +} + +void UpdateDiffHist(unsigned char * image1, unsigned char * image2, DynamicMedianHistogram * pHist) +{ + unsigned int j; + int bin,diff; + + unsigned int imagesize = pHist->imagesize; + unsigned char histbins = pHist->histbins; + unsigned char *pAbsDiffHist = pHist->Hist; + + int histbins_1 = histbins-1; + + for(j = 0; j < imagesize; j++) + { + diff = (int) image1[j] - (int) image2[j]; + diff = abs(diff); + // update histogram + bin = (diff < histbins ? diff : histbins_1); + pAbsDiffHist[j*histbins+bin]++; + } +} + +void FindHistMedians(DynamicMedianHistogram * pAbsDiffHist) +{ + unsigned char * Hist = pAbsDiffHist->Hist; + unsigned char * MedianBins = pAbsDiffHist->MedianBins; + unsigned char * AccSum = pAbsDiffHist->AccSum; + unsigned char histsum = pAbsDiffHist->histsum; + unsigned char histbins = pAbsDiffHist->histbins; + unsigned int imagesize = pAbsDiffHist->imagesize; + + int sum; + int bin; + unsigned int histindex; + unsigned char medianCount=histsum/2; + unsigned int j; + + // find medians + for(j = 0; j < imagesize; j++) + { + // find the median + bin=0; + sum=0; + + histindex=j*histbins; + + while(sum < medianCount) + { + sum+=Hist[histindex+bin]; + bin++; + } + + bin--; + + MedianBins[j]=bin; + AccSum[j]=sum; + } +} + +DynamicMedianHistogram BuildAbsDiffHist(unsigned char * pSequence, + unsigned int rows, + unsigned int cols, + unsigned int color_channels, + unsigned int SequenceLength, + unsigned int histbins) +{ + + unsigned int imagesize=rows*cols*color_channels; + unsigned int i; + + DynamicMedianHistogram Hist; + + unsigned char *pAbsDiffHist = new unsigned char[rows*cols*color_channels*histbins]; + unsigned char *pMedianBins = new unsigned char[rows*cols*color_channels]; + unsigned char *pMedianFreq = new unsigned char[rows*cols*color_channels]; + unsigned char *pAccSum = new unsigned char[rows*cols*color_channels]; + + memset(pAbsDiffHist,0,rows*cols*color_channels*histbins); + + Hist.Hist = pAbsDiffHist; + Hist.MedianBins = pMedianBins; + Hist.MedianFreq = pMedianFreq; + Hist.AccSum = pAccSum; + Hist.histbins = histbins; + Hist.imagesize = rows*cols*color_channels; + Hist.histsum = SequenceLength-1; + + unsigned char *image1, *image2; + for(i = 1; i < SequenceLength; i++) + { + // find diff between frame i,i-1; + image1 = pSequence+(i-1)*imagesize; + image2 = pSequence+(i)*imagesize; + + UpdateDiffHist(image1,image2,&Hist); + } + + FindHistMedians(&Hist); + + return Hist; +} + +void EstimateSDsFromAbsDiffHist(DynamicMedianHistogram * pAbsDiffHist, + unsigned char * pSDs, + unsigned int imagesize, + double MinSD, + double MaxSD, + unsigned int kernelbins) +{ + double v; + double kernelbinfactor=(kernelbins-1)/(MaxSD-MinSD); + int medianCount; + int sum; + int bin; + unsigned int histindex; + unsigned int j; + unsigned int x1,x2; + + unsigned char *Hist =pAbsDiffHist->Hist; + unsigned char *MedianBins =pAbsDiffHist->MedianBins; + unsigned char *AccSum =pAbsDiffHist->AccSum; + unsigned char histsum =pAbsDiffHist->histsum; + unsigned char histbins =pAbsDiffHist->histbins; + + medianCount=(histsum)/2 ; + + for(j = 0; j < imagesize; j++) + { + histindex=j*histbins; + + bin=MedianBins[j]; + sum=AccSum[j]; + + x1=sum-Hist[histindex+bin]; + x2=sum; + + // interpolate to get the median + // x1 < 50 % < x2 + + v =1.04 * ((double) bin-(double) (x2-medianCount)/ (double) (x2-x1)); + v=( v <= MinSD ? MinSD : v); + + // convert sd to kernel table bin + + bin=(int) (v>=MaxSD ? kernelbins-1 : floor((v-MinSD)*kernelbinfactor+.5)); + + assert(bin>=0 && bin < kernelbins ); + + pSDs[j]=bin; + } +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +NPBGSubtractor::NPBGSubtractor(){} + +NPBGSubtractor::~NPBGSubtractor() +{ + delete AbsDiffHist.Hist; + delete AbsDiffHist.MedianBins; + delete AbsDiffHist.MedianFreq; + delete AbsDiffHist.AccSum; + delete KernelTable; + delete BGModel->SDbinsImage; + delete BGModel; + delete Pimage1; + delete Pimage2; + delete tempFrame; + delete imageindex->List; + delete imageindex; +} + +int NPBGSubtractor::Intialize(unsigned int prows, + unsigned int pcols, + unsigned int pcolor_channels, + unsigned int SequenceLength, + unsigned int pTimeWindowSize, + unsigned char pSDEstimationFlag, + unsigned char pUseColorRatiosFlag) +{ + + rows=prows; + cols=pcols; + color_channels=pcolor_channels; + imagesize=rows*cols*color_channels; + SdEstimateFlag = pSDEstimationFlag; + UseColorRatiosFlag=pUseColorRatiosFlag; + //SampleSize = SequenceLength; + + AdaptBGFlag = FALSE; + // + SubsetFlag = TRUE; + + UpdateSDRate = 0; + + BGModel = new NPBGmodel(rows,cols,color_channels,SequenceLength,pTimeWindowSize,500); + + Pimage1= new double[rows*cols]; + Pimage2= new double[rows*cols]; + + tempFrame= new unsigned char[rows*cols*3]; + + imageindex = new ImageIndex; + imageindex->List= new unsigned int [rows*cols]; + + // error checking + if (BGModel==NULL) + return 0; + + return 1; +} + +void NPBGSubtractor::AddFrame(unsigned char *ImageBuffer) +{ + if(UseColorRatiosFlag && color_channels==3) + BGR2SnGnRn(ImageBuffer,ImageBuffer,rows,cols); + + BGModel->AddFrame(ImageBuffer); +} + +void NPBGSubtractor::Estimation() +{ + int SampleSize=BGModel->SampleSize; + + memset(BGModel->TemporalMask,0,rows*cols*BGModel->TemporalBufferLength); + + //BGModel->AccMask= new unsigned int [rows*cols]; + memset(BGModel->AccMask,0,rows*cols*sizeof(unsigned int)); + + unsigned char *pSDs = new unsigned char[rows*cols*color_channels]; + + //DynamicMedianHistogram AbsDiffHist; + + int Abshistbins = 20; + + TimeIndex=0; + + // estimate standard deviations + + if(SdEstimateFlag) + { + AbsDiffHist = BuildAbsDiffHist(BGModel->Sequence,rows,cols,color_channels,SampleSize,Abshistbins); + EstimateSDsFromAbsDiffHist(&AbsDiffHist,pSDs,imagesize,SEGMAMIN,SEGMAMAX,SEGMABINS); + } + else + { + unsigned int bin; + bin = (unsigned int) floor(((DEFAULTSEGMA-SEGMAMIN)*SEGMABINS)/(SEGMAMAX-SEGMAMIN)); + memset(pSDs,bin,rows*cols*color_channels*sizeof(unsigned char)); + } + + BGModel->SDbinsImage=pSDs; + + // Generate the Kernel + KernelTable = new KernelLUTable(KERNELHALFWIDTH,SEGMAMIN,SEGMAMAX,SEGMABINS); +} + +/*********************************************************************/ + +void BuildImageIndex(unsigned char * Image, + ImageIndex * imageIndex, + unsigned int rows, + unsigned int cols) +{ + unsigned int i,j; + unsigned int r,c; + unsigned int * image_list; + + j=cols+1; + i=0; + image_list=imageIndex->List; + + for(r = 1; r < rows-1; r++) + { + for(c = 1; c < cols-1; c++) + { + if(Image[j]) + image_list[i++]=j; + + j++; + } + j+=2; + } + + imageIndex->cnt = i; +} + +/*********************************************************************/ + +void HystExpandOperatorIndexed(unsigned char * inImage, + ImageIndex * inIndex, + double * Pimage, + double hyst_th, + unsigned char * outImage, + ImageIndex * outIndex, + unsigned int urows, + unsigned int ucols) +{ + unsigned int * in_list; + unsigned int in_cnt; + unsigned int * out_list; + + int rows,cols; + + int Nbr[9]; + unsigned int i,j; + unsigned int k; + unsigned int idx; + + rows=(int) urows; + cols=(int) ucols; + + in_cnt=inIndex->cnt; + in_list=inIndex->List; + + Nbr[0]=-cols-1; + Nbr[1]=-cols; + Nbr[2]=-cols+1; + Nbr[3]=-1; + Nbr[4]=0; + Nbr[5]=1; + Nbr[6]=cols-1; + Nbr[7]=cols; + Nbr[8]=cols+1; + + memset(outImage,0,rows*cols); + + out_list=outIndex->List; + k=0; + + for(i = 0; i < in_cnt; i++) + { + for(j = 0; j < 9; j++) + { + idx = in_list[i] + Nbr[j]; + + if(Pimage[idx] < hyst_th) + outImage[idx] = 255; + } + } + + // build index for out image + BuildImageIndex(outImage,outIndex,urows,ucols); +} + +/*********************************************************************/ + +void HystShrinkOperatorIndexed(unsigned char * inImage, + ImageIndex * inIndex, + double * Pimage, + double hyst_th, + unsigned char * outImage, + ImageIndex * outIndex, + unsigned int urows, + unsigned int ucols) +{ + unsigned int * in_list; + unsigned int in_cnt; + unsigned int * out_list; + + int rows,cols; + + int Nbr[9]; + unsigned int i,j; + unsigned int k,idx; + + rows=(int) urows; + cols=(int) ucols; + + in_cnt=inIndex->cnt; + in_list=inIndex->List; + + Nbr[0]=-cols-1; + Nbr[1]=-cols; + Nbr[2]=-cols+1; + Nbr[3]=-1; + Nbr[4]=0; + Nbr[5]=1; + Nbr[6]=cols-1; + Nbr[7]=cols; + Nbr[8]=cols+1; + + memset(outImage,0,rows*cols); + + out_list=outIndex->List; + k=0; + + for(i = 0; i < in_cnt; i++) + { + idx = in_list[i]; + j = 0; + + while(j < 9 && inImage[idx+Nbr[j]]) + j++; + + if(j >= 9 || Pimage[idx] <= hyst_th) + outImage[idx]=255; + } + + BuildImageIndex(outImage,outIndex,rows,cols); +} + +/*********************************************************************/ + +void ExpandOperatorIndexed(unsigned char * inImage, + ImageIndex * inIndex, + unsigned char * outImage, + ImageIndex * outIndex, + unsigned int urows, + unsigned int ucols) +{ + unsigned int * in_list; + unsigned int in_cnt; + unsigned int * out_list; + + int rows,cols; + + int Nbr[9]; + unsigned int i,j; + unsigned int k; + unsigned int idx; + + rows=(int) urows; + cols=(int) ucols; + + in_cnt=inIndex->cnt; + in_list=inIndex->List; + + Nbr[0]=-cols-1; + Nbr[1]=-cols; + Nbr[2]=-cols+1; + Nbr[3]=-1; + Nbr[4]=0; + Nbr[5]=1; + Nbr[6]=cols-1; + Nbr[7]=cols; + Nbr[8]=cols+1; + + + memset(outImage,0,rows*cols); + + + out_list=outIndex->List; + k=0; + for (i=0; i<in_cnt;i++) + for (j=0;j<9;j++) { + idx=in_list[i]+Nbr[j]; + outImage[idx]=255; + } + + + // build index for out image + + BuildImageIndex(outImage,outIndex,rows,cols); + +} + +/*********************************************************************/ + +void ShrinkOperatorIndexed(unsigned char * inImage, + ImageIndex * inIndex, + unsigned char * outImage, + ImageIndex * outIndex, + unsigned int urows, + unsigned int ucols) +{ + + unsigned int * in_list; + unsigned int in_cnt; + unsigned int * out_list; + + int rows,cols; + + int Nbr[9]; + unsigned int i,j; + unsigned int k,idx; + + rows=(int) urows; + cols=(int) ucols; + + in_cnt=inIndex->cnt; + in_list=inIndex->List; + + Nbr[0]=-cols-1; + Nbr[1]=-cols; + Nbr[2]=-cols+1; + Nbr[3]=-1; + Nbr[4]=0; + Nbr[5]=1; + Nbr[6]=cols-1; + Nbr[7]=cols; + Nbr[8]=cols+1; + + + memset(outImage,0,rows*cols); + + out_list=outIndex->List; + k=0; + for (i=0; i<in_cnt;i++) { + idx=in_list[i]; + j=0; + + while ( j<9 && inImage[idx+Nbr[j]]){ + j++; + } + + if (j>=9) { + outImage[idx]=255; + } + } + + BuildImageIndex(outImage,outIndex,rows,cols); +} + +/*********************************************************************/ + +void NoiseFilter_o(unsigned char * Image, + unsigned char * ResultIm, + int rows, + int cols, + unsigned char th) +{ + /* assuming input is 1 for on, 0 for off */ + + + int r,c; + unsigned char *p,*n,*nw,*ne,*e,*w,*s,*sw,*se; + unsigned int v; + unsigned int TH; + + unsigned char * ResultPtr; + + TH=255*th; + + memset(ResultIm,0,rows*cols); + + p=Image+cols+1; + ResultPtr=ResultIm+cols+1; + + for(r=1;r<rows-1;r++) + { + for(c=1;c<cols-1;c++) + { + if (*p) + { + n=p-cols; + ne=n+1; + nw=n-1; + e=p+1; + w=p-1; + s=p+cols; + se=s+1; + sw=s-1; + + v=(unsigned int) *nw+*n+*ne+*w+*e+*sw+*s+*se; + + if (v>=TH) + *ResultPtr=255; + else + *ResultPtr=0; + } + p++; + ResultPtr++; + } + p+=2; + ResultPtr+=2; + } +} + +/*********************************************************************/ + +void NPBGSubtractor::SequenceBGUpdate_Pairs(unsigned char * image, + unsigned char * Mask) +{ + unsigned int i,ic; + unsigned char * pSequence =BGModel->Sequence; + unsigned char * PixelQTop =BGModel->PixelQTop; + unsigned int Top =BGModel->Top; + unsigned int rate; + + int TemporalBufferTop =(int) BGModel->TemporalBufferTop; + unsigned char * pTemporalBuffer = BGModel->TemporalBuffer; + unsigned char * pTemporalMask = BGModel->TemporalMask; + int TemporalBufferLength = BGModel->TemporalBufferLength; + + unsigned int * AccMask = BGModel->AccMask; + unsigned int ResetMaskTh = BGModel->ResetMaskTh; + + unsigned char *pAbsDiffHist = AbsDiffHist.Hist; + unsigned char histbins = AbsDiffHist.histbins; + int histbins_1=histbins-1; + + int TimeWindowSize = BGModel->TimeWindowSize; + int SampleSize = BGModel->SampleSize; + + int TemporalBufferNext; + + unsigned int imagebuffersize=rows*cols*color_channels; + unsigned int imagespatialsize=rows*cols; + + unsigned char mask; + + unsigned int histindex; + unsigned char diff; + unsigned char bin; + + static int TBCount=0; + + unsigned char * pTBbase1, * pTBbase2; + unsigned char * pModelbase1, * pModelbase2; + + rate=TimeWindowSize/SampleSize; + rate=(rate > 2) ? rate : 2; + + + TemporalBufferNext=(TemporalBufferTop+1) + % TemporalBufferLength; + + // pointers to Masks : Top and Next + unsigned char * pTMaskTop=pTemporalMask+TemporalBufferTop*imagespatialsize; + unsigned char * pTMaskNext=pTemporalMask+TemporalBufferNext*imagespatialsize; + + // pointers to TB frames: Top and Next + unsigned char * pTBTop=pTemporalBuffer+TemporalBufferTop*imagebuffersize; + unsigned char * pTBNext=pTemporalBuffer+TemporalBufferNext*imagebuffersize; + + if ( ((TimeIndex) % rate == 0) && TBCount >= TemporalBufferLength ) + { + for(i=0,ic=0;i<imagespatialsize;i++,ic+=color_channels) + { + mask= * (pTMaskTop+i) || * (pTMaskNext+i); + + if(!mask) + { + // pointer to TB pixels to be added to the model + pTBbase1=pTBTop+ic; + pTBbase2=pTBNext+ic; + + // pointers to Model pixels to be replaced + pModelbase1=pSequence+PixelQTop[i]*imagebuffersize+ic; + pModelbase2=pSequence+((PixelQTop[i]+1)% SampleSize)*imagebuffersize+ic; + + // update Deviation Histogram + if(SdEstimateFlag) + { + if(color_channels==1) + { + histindex=i*histbins; + + // add new pair from temporal buffer + diff=(unsigned char) abs((int) *pTBbase1 - (int) *pTBbase2); + bin=(diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex+bin]++; + + + // remove old pair from the model + diff=(unsigned char) abs((int) *pModelbase1-(int) *pModelbase2); + bin=(diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex+bin]--; + } + else + { + // color + + // add new pair from temporal buffer + histindex=ic*histbins; + diff=abs(*pTBbase1 - + *pTBbase2); + bin=(diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex+bin]++; + + histindex+=histbins; + diff=abs(*(pTBbase1+1) - + *(pTBbase2+1)); + bin=(diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex+bin]++; + + histindex+=histbins; + diff=abs(*(pTBbase1+2) - + *(pTBbase2+2)); + bin=(diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex+bin]++; + + // remove old pair from the model + histindex=ic*histbins; + + diff=abs(*pModelbase1- + *pModelbase2); + bin=(diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex+bin]--; + + histindex+=histbins; + diff=abs(*(pModelbase1+1)- + *(pModelbase2+1)); + bin=(diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex+bin]--; + + histindex+=histbins; + diff=abs(*(pModelbase1+2)- + *(pModelbase2+2)); + bin=(diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex+bin]--; + } + } + + // add new pair into the model + memcpy(pModelbase1,pTBbase1, color_channels*sizeof(unsigned char)); + + memcpy(pModelbase2,pTBbase2, color_channels*sizeof(unsigned char)); + + PixelQTop[i]=(PixelQTop[i]+2) % SampleSize; + } + } + } // end if (sampling event) + + // update temporal buffer + // add new frame to Temporal buffer. + memcpy(pTBTop,image,imagebuffersize); + + // update AccMask + // update new Mask with information in AccMask + + for (i=0;i<rows*cols;i++) + { + if (Mask[i]) + AccMask[i]++; + else + AccMask[i]=0; + + if (AccMask[i] > ResetMaskTh) + Mask[i]=0; + } + + // add new mask + memcpy(pTMaskTop,Mask,imagespatialsize); + + // advance Temporal buffer pointer + TemporalBufferTop=(TemporalBufferTop+1) % TemporalBufferLength; + + BGModel->TemporalBufferTop=TemporalBufferTop; + + TBCount++; + + // estimate SDs + + if (SdEstimateFlag && UpdateSDRate && ((TimeIndex) % UpdateSDRate == 0)) + { + double MaxSD = KernelTable->maxsegma; + double MinSD = KernelTable->minsegma; + int KernelBins = KernelTable->segmabins; + + unsigned char * pSDs= BGModel->SDbinsImage; + + FindHistMedians(&(AbsDiffHist)); + EstimateSDsFromAbsDiffHist(&(AbsDiffHist),pSDs,imagebuffersize,MinSD,MaxSD,KernelBins); + } + + TimeIndex++; +} + +/*********************************************************************/ + +void DisplayPropabilityImageWithThresholding(double * Pimage, + unsigned char * DisplayImage, + double Threshold, + unsigned int rows, + unsigned int cols) +{ + double p; + + for(unsigned int i=0;i<rows*cols;i++) + { + p = Pimage[i]; + + DisplayImage[i]=(p > Threshold) ? 0 : 255; + } +} + +/*********************************************************************/ + +void NPBGSubtractor::NPBGSubtraction_Subset_Kernel( + unsigned char * image, + unsigned char * FGImage, + unsigned char * FilteredFGImage) +{ + unsigned int i,j; + unsigned char *pSequence =BGModel->Sequence; + + unsigned int SampleSize = BGModel->SampleSize; + + double *kerneltable = KernelTable->kerneltable; + int KernelHalfWidth = KernelTable->tablehalfwidth; + double *KernelSum = KernelTable->kernelsums; + double KernelMaxSigma = KernelTable->maxsegma; + double KernelMinSigma = KernelTable->minsegma; + int KernelBins = KernelTable->segmabins; + unsigned char * SDbins= BGModel->SDbinsImage; + + unsigned char * SaturationImage=FilteredFGImage; + + // default sigmas .. to be removed. + double sigma1; + double sigma2; + double sigma3; + + sigma1=2.25; + sigma2=2.25; + sigma3=2.25; + + double p; + double th; + + double alpha,beta,beta_over_alpha, betau,betau_over_alpha; + + alpha= AlphaValue; + + /* intialize FG image */ + + memset(FGImage,0,rows*cols); + + //Threshold=1; + th = Threshold * SampleSize; + + double sum=0,kernel1,kernel2,kernel3; + int k,g; + + + if (color_channels==1) + { + // gray scale + + int kernelbase; + + for (i=0;i<rows*cols;i++) + { + kernelbase=SDbins[i]*(2*KernelHalfWidth+1); + sum=0; + j=0; + + while (j<SampleSize && sum < th) + { + g=pSequence[j*imagesize+i]; + k= g- image[i] +KernelHalfWidth; + sum+=kerneltable[kernelbase+k]; + j++; + } + + p=sum/j; + Pimage1[i]=p; + } + } + else if (UseColorRatiosFlag && SubsetFlag) + { + // color ratios + + unsigned int ig; + int base; + + int kernelbase1; + int kernelbase2; + int kernelbase3; + + unsigned int kerneltablewidth=2*KernelHalfWidth+1; + + beta=3.0; // minimum bound on the range. + betau=100.0; + + beta_over_alpha = beta / alpha; + betau_over_alpha = betau / alpha; + + + double brightness_lowerbound = 1-alpha; + double brightness_upperbound = 1+alpha; + int x1,x2; + unsigned int SubsampleCount; + + for (i=0,ig=0;i<imagesize;i+=3,ig++) + { + kernelbase1=SDbins[i]*kerneltablewidth; + kernelbase2=SDbins[i+1]*kerneltablewidth; + kernelbase3=SDbins[i+2]*kerneltablewidth; + + sum=0; + j=0; + SubsampleCount=0; + + while (j<SampleSize && sum < th) + { + base=j*imagesize+i; + g=pSequence[base]; + + if (g < beta_over_alpha) + { + x1=(int) (g-beta); + x2=(int) (g+beta); + } + else if (g > betau_over_alpha) + { + x1=(int) (g-betau); + x2=(int) (g+betau); + } + else + { + x1=(int) (g*brightness_lowerbound+0.5); + x2=(int) (g*brightness_upperbound+0.5); + } + + if(x1<image[i] && image[i] <x2) + { + g=pSequence[base+1]; + k= (g- image[i+1]) +KernelHalfWidth; + kernel2=kerneltable[kernelbase2+k]; + + g=pSequence[base+2]; + k= (g- image[i+2]) +KernelHalfWidth; + kernel3=kerneltable[kernelbase3+k]; + + sum+=kernel2*kernel3; + + SubsampleCount++; + } + j++; + } + + p=sum / j; + Pimage1[ig]=p; + } + } + else if (UseColorRatiosFlag && ! SubsetFlag) + { + // color ratios + + unsigned int ig; + int base; + int bin; + + int kernelbase1; + int kernelbase2; + int kernelbase3; + + unsigned int kerneltablewidth=2*KernelHalfWidth+1; + + int gmin,gmax; + double gfactor; + + gmax=200; + gmin=10; + + gfactor = (KernelMaxSigma-KernelMinSigma) / (double) (gmax - gmin); + + for (i=0,ig=0;i<imagesize;i+=3,ig++) + { + + bin=(int) floor(((alpha*16-KernelMinSigma)*KernelBins)/(KernelMaxSigma-KernelMinSigma)); + + kernelbase1=bin*kerneltablewidth; + kernelbase2=SDbins[i+1]*kerneltablewidth; + kernelbase3=SDbins[i+2]*kerneltablewidth; + + sum=0; + j=0; + + while (j<SampleSize && sum < th) + { + base=j*imagesize+i; + g=pSequence[base]; + + if (g < gmin ) + bin=0; + else if (g > gmax) + bin = KernelBins -1 ; + else + bin= (int) ((g-gmin) * gfactor + 0.5); + + kernelbase1=bin*kerneltablewidth; + + k= (g- image[i]) +KernelHalfWidth; + kernel1=kerneltable[kernelbase1+k]; + + g=pSequence[base+1]; + k= (g- image[i+1]) +KernelHalfWidth; + kernel2=kerneltable[kernelbase2+k]; + + g=pSequence[base+2]; + k= (g- image[i+2]) +KernelHalfWidth; + kernel3=kerneltable[kernelbase3+k]; + + sum+=kernel1*kernel2*kernel3; + j++; + } + + p=sum / j; + Pimage1[ig]=p; + } + } + else // RGB color + { + unsigned int ig; + int base; + + int kernelbase1; + int kernelbase2; + int kernelbase3; + unsigned int kerneltablewidth=2*KernelHalfWidth+1; + + for (i=0,ig=0;i<imagesize;i+=3,ig++) + { + // used extimated kernel width to access the right kernel + kernelbase1=SDbins[i]*kerneltablewidth; + kernelbase2=SDbins[i+1]*kerneltablewidth; + kernelbase3=SDbins[i+2]*kerneltablewidth; + + sum=0; + j=0; + while (j<SampleSize && sum < th) + { + base=j*imagesize+i; + g=pSequence[base]; + k= (g- image[i]) +KernelHalfWidth; + kernel1=kerneltable[kernelbase1+k]; + + g=pSequence[base+1]; + k= (g- image[i+1]) +KernelHalfWidth; + kernel2=kerneltable[kernelbase2+k]; + + g=pSequence[base+2]; + k= (g- image[i+2]) +KernelHalfWidth; + kernel3=kerneltable[kernelbase3+k]; + + sum+=kernel1*kernel2*kernel3; + j++; + } + + p=sum/j; + Pimage1[ig]=p; + } + } + + DisplayPropabilityImageWithThresholding(Pimage1,FGImage,Threshold,rows,cols); +} + +/*********************************************************************/ + +void NPBGSubtractor::NBBGSubtraction(unsigned char * Frame, + unsigned char * FGImage, + unsigned char * FilteredFGImage, + unsigned char ** DisplayBuffers) +{ + if(UseColorRatiosFlag) + BGR2SnGnRn(Frame,tempFrame,rows,cols); + else + memcpy(tempFrame,Frame,rows*cols*color_channels); + + NPBGSubtraction_Subset_Kernel(tempFrame,FGImage,FilteredFGImage); + /*NoiseFilter_o(FGImage,DisplayBuffers[3],rows,cols,4); + BuildImageIndex(DisplayBuffers[3],imageindex,rows,cols); + + ExpandOperatorIndexed(DisplayBuffers[3],imageindex,DisplayBuffers[4],imageindex,rows,cols); + ShrinkOperatorIndexed(DisplayBuffers[4],imageindex,FilteredFGImage,imageindex,rows,cols); + + memset(DisplayBuffers[3],0,rows*cols);*/ +} + +void NPBGSubtractor::Update(unsigned char * FGMask) +{ + if(UpdateBGFlag) + SequenceBGUpdate_Pairs(tempFrame,FGMask); +} diff --git a/package_bgs/ae/NPBGSubtractor.h b/package_bgs/ae/NPBGSubtractor.h new file mode 100644 index 0000000000000000000000000000000000000000..e7f379542767bec2b6e47b1b1b74a2574e58ebb9 --- /dev/null +++ b/package_bgs/ae/NPBGSubtractor.h @@ -0,0 +1,154 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* +* +* Copyright 2001 by Ahmed Elgammal All rights reserved. +* +* Permission to use, copy, or modify this software and its documentation +* for educational and research purposes only and without fee is hereby +* granted, provided that this copyright notice and the original authors's +* name appear on all copies and supporting documentation. If individual +* files are separated from this distribution directory structure, this +* copyright notice must be included. For any other uses of this software, +* in original or modified form, including but not limited to distribution +* in whole or in part, specific prior permission must be obtained from +* Author or UMIACS. These programs shall not be used, rewritten, or +* adapted as the basis of a commercial software or hardware product +* without first obtaining appropriate licenses from Author. +* Other than these cases, no part of this software may be used or +* distributed without written permission of the author. +* +* Neither the author nor UMIACS make any representations about the +* suitability of this software for any purpose. It is provided +* "as is" without express or implied warranty. +* +* Ahmed Elgammal +* +* University of Maryland at College Park +* UMIACS +* A.V. Williams Bldg. +* CollegePark, MD 20742 +* E-mail: elgammal@umiacs.umd.edu +* +**/ + +// NPBGSubtractor.h: interface for the NPBGSubtractor class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_NPBGSUBTRACTOR_H__84B0F51E_6E65_41E4_AC01_723B406363C4__INCLUDED_) +#define AFX_NPBGSUBTRACTOR_H__84B0F51E_6E65_41E4_AC01_723B406363C4__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "NPBGmodel.h" +#include "KernelTable.h" + +#define FALSE 0 +#define TRUE 1 + +// kernal look up table settings +#define KERNELHALFWIDTH 255 +#define SEGMAMAX 36.5 +#define SEGMAMIN 0.5 +#define SEGMABINS 80 +#define DEFAULTSEGMA 1.0 + +typedef struct +{ + unsigned char *Hist; + unsigned char *MedianBins; + unsigned char *MedianFreq; + unsigned char *AccSum; + unsigned char histbins; + unsigned char histsum; + unsigned int imagesize; +} DynamicMedianHistogram; + +typedef struct +{ + unsigned int cnt; + unsigned int *List; +} ImageIndex; + +class NPBGSubtractor +{ +private: + unsigned int rows; + unsigned int cols; + unsigned int color_channels; + unsigned int imagesize; + // flags + unsigned char UpdateBGFlag; + unsigned char SdEstimateFlag; + unsigned char UseColorRatiosFlag; + unsigned char AdaptBGFlag; + unsigned char SubsetFlag; + // + int UpdateSDRate; + double Threshold; + double AlphaValue; + unsigned int TimeIndex; + ImageIndex *imageindex; + unsigned char *tempFrame; + KernelLUTable *KernelTable; + NPBGmodel *BGModel; + DynamicMedianHistogram AbsDiffHist; + double *Pimage1; + double *Pimage2; + // + void NPBGSubtraction_Subset_Kernel(unsigned char * image, unsigned char * FGImage, unsigned char * FilteredFGImage); + void SequenceBGUpdate_Pairs(unsigned char * image, unsigned char * Mask); + +public: + NPBGSubtractor(); + virtual ~NPBGSubtractor(); + //~NPBGSubtractor(); + + int Intialize(unsigned int rows, + unsigned int cols, + unsigned int color_channels, + unsigned int SequenceLength, + unsigned int TimeWindowSize, + unsigned char SDEstimationFlag, + unsigned char UseColorRatiosFlag); + + void AddFrame(unsigned char * ImageBuffer); + + void Estimation(); + + void NBBGSubtraction(unsigned char *Frame, + unsigned char *FGImage, + unsigned char *FilteredFGImage, + unsigned char **DisplayBuffers); + + void Update(unsigned char *); + + void SetThresholds(double th, double alpha) + { + Threshold = th; + AlphaValue = alpha; + }; + + void SetUpdateFlag(unsigned int bgflag){ + UpdateBGFlag = bgflag; + }; +}; + +#endif // !defined(AFX_NPBGSUBTRACTOR_H__84B0F51E_6E65_41E4_AC01_723B406363C4__INCLUDED_) diff --git a/package_bgs/ae/NPBGmodel.cpp b/package_bgs/ae/NPBGmodel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2c8b07400992bf4db7810723b6fef930ed806ee3 --- /dev/null +++ b/package_bgs/ae/NPBGmodel.cpp @@ -0,0 +1,127 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* +* +* Copyright 2001 by Ahmed Elgammal All rights reserved. +* +* Permission to use, copy, or modify this software and its documentation +* for educational and research purposes only and without fee is hereby +* granted, provided that this copyright notice and the original authors's +* name appear on all copies and supporting documentation. If individual +* files are separated from this distribution directory structure, this +* copyright notice must be included. For any other uses of this software, +* in original or modified form, including but not limited to distribution +* in whole or in part, specific prior permission must be obtained from +* Author or UMIACS. These programs shall not be used, rewritten, or +* adapted as the basis of a commercial software or hardware product +* without first obtaining appropriate licenses from Author. +* Other than these cases, no part of this software may be used or +* distributed without written permission of the author. +* +* Neither the author nor UMIACS make any representations about the +* suitability of this software for any purpose. It is provided +* "as is" without express or implied warranty. +* +* Ahmed Elgammal +* +* University of Maryland at College Park +* UMIACS +* A.V. Williams Bldg. +* CollegePark, MD 20742 +* E-mail: elgammal@umiacs.umd.edu +* +**/ + +// NPBGmodel.cpp: implementation of the NPBGmodel class. +// +////////////////////////////////////////////////////////////////////// + +#include "NPBGmodel.h" +#include "memory.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +//#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +NPBGmodel::NPBGmodel() +{ + std::cout << "NPBGmodel()" << std::endl; +} + +NPBGmodel::~NPBGmodel() +{ + delete Sequence; + delete PixelQTop; + delete TemporalBuffer; + delete TemporalMask; + delete AccMask; + //delete SDbinsImage; + std::cout << "~NPBGmodel()" << std::endl; +} + +NPBGmodel::NPBGmodel(unsigned int Rows, + unsigned int Cols, + unsigned int ColorChannels, + unsigned int Length, + unsigned int pTimeWindowSize, + unsigned int bg_suppression_time) +{ + std::cout << "NPBGmodel()" << std::endl; + + imagesize = Rows*Cols*ColorChannels; + + rows = Rows; + cols = Cols; + color_channels = ColorChannels; + + SampleSize = Length; + + TimeWindowSize = pTimeWindowSize; + + Sequence = new unsigned char[imagesize*Length]; + Top = 0; + memset(Sequence,0,imagesize*Length); + + PixelQTop = new unsigned char[rows*cols]; + + // temporalBuffer + TemporalBufferLength = (TimeWindowSize/Length > 2 ? TimeWindowSize/Length:2); + TemporalBuffer = new unsigned char[imagesize*TemporalBufferLength]; + TemporalMask = new unsigned char[rows*cols*TemporalBufferLength]; + + TemporalBufferTop = 0; + + AccMask = new unsigned int[rows*cols]; + + ResetMaskTh = bg_suppression_time; +} + +void NPBGmodel::AddFrame(unsigned char *ImageBuffer) +{ + memcpy(Sequence+Top*imagesize,ImageBuffer,imagesize); + Top = (Top + 1) % SampleSize; + + memset(PixelQTop, (unsigned char) Top, rows*cols); + + memcpy(TemporalBuffer,ImageBuffer,imagesize); +} diff --git a/package_bgs/ae/NPBGmodel.h b/package_bgs/ae/NPBGmodel.h new file mode 100644 index 0000000000000000000000000000000000000000..9e285108eec393167c327970aa1c783733c71447 --- /dev/null +++ b/package_bgs/ae/NPBGmodel.h @@ -0,0 +1,111 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* +* +* Copyright 2001 by Ahmed Elgammal All rights reserved. +* +* Permission to use, copy, or modify this software and its documentation +* for educational and research purposes only and without fee is hereby +* granted, provided that this copyright notice and the original authors's +* name appear on all copies and supporting documentation. If individual +* files are separated from this distribution directory structure, this +* copyright notice must be included. For any other uses of this software, +* in original or modified form, including but not limited to distribution +* in whole or in part, specific prior permission must be obtained from +* Author or UMIACS. These programs shall not be used, rewritten, or +* adapted as the basis of a commercial software or hardware product +* without first obtaining appropriate licenses from Author. +* Other than these cases, no part of this software may be used or +* distributed without written permission of the author. +* +* Neither the author nor UMIACS make any representations about the +* suitability of this software for any purpose. It is provided +* "as is" without express or implied warranty. +* +* Ahmed Elgammal +* +* University of Maryland at College Park +* UMIACS +* A.V. Williams Bldg. +* CollegePark, MD 20742 +* E-mail: elgammal@umiacs.umd.edu +* +**/ + +// NPBGmodel.h: interface for the NPBGmodel class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_NPBGMODEL_H__CCAF05D4_D06E_44C2_95D8_979E2249953A__INCLUDED_) +#define AFX_NPBGMODEL_H__CCAF05D4_D06E_44C2_95D8_979E2249953A__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include <iostream> + +class NPBGmodel +{ +private: + unsigned char *Sequence; + unsigned int SampleSize; + unsigned int TimeWindowSize; + + unsigned int rows,cols,color_channels; + unsigned int imagesize; + + unsigned int Top; + unsigned char *PixelQTop; + + //unsigned int *PixelUpdateCounter; + + unsigned char *SDbinsImage; + + unsigned char *TemporalBuffer; + unsigned char TemporalBufferLength; + unsigned char TemporalBufferTop; + unsigned char *TemporalBufferMask; + + unsigned char *TemporalMask; + unsigned char TemporalMaskLength; + unsigned char TemporalMaskTop; + + unsigned int *AccMask; + unsigned int ResetMaskTh; // Max continous duration a pixel can be detected before + // it is forced to be updated... + + double *weights; + +public: + NPBGmodel(); + //~NPBGmodel(); + virtual ~NPBGmodel(); + + NPBGmodel(unsigned int Rows, + unsigned int Cols, + unsigned int ColorChannels, + unsigned int Length, + unsigned int pTimeWindowSize, + unsigned int bg_suppression_time); + + void AddFrame(unsigned char *ImageBuffer); + + friend class NPBGSubtractor; +}; + +#endif // !defined(AFX_NPBGMODEL_H__CCAF05D4_D06E_44C2_95D8_979E2249953A__INCLUDED_) diff --git a/package_bgs/av/TBackground.cpp b/package_bgs/av/TBackground.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2e97d93804b29507cb7eb8b4c95276c7a0497e34 --- /dev/null +++ b/package_bgs/av/TBackground.cpp @@ -0,0 +1,166 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* +* TBackground.cpp +* Framework +* +* Created by Robinault Lionel on 07/12/11. +* +*/ + +#include "TBackground.h" + +TBackground::TBackground(void) +{ + std::cout << "TBackground()" << std::endl; +} + +TBackground::~TBackground(void) +{ + Clear(); + std::cout << "~TBackground()" << std::endl; +} + +void TBackground::Clear(void) +{ +} + +void TBackground::Reset(void) +{ +} + +int TBackground::GetParameterCount(void) +{ + return 0; +} + +std::string TBackground::GetParameterName(int nInd) +{ + return ""; +} + +std::string TBackground::GetParameterValue(int nInd) +{ + return ""; +} + +int TBackground::SetParameterValue(int nInd, std::string csNew) +{ + return 0; +} + +int TBackground::Init(IplImage * pSource) +{ + return 0; +} + +bool TBackground::isInitOk(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask) +{ + bool bResult = TRUE; + int nbl, nbc; + + if(pSource == NULL || pSource->nChannels != 1 || pSource->depth != IPL_DEPTH_8U) + bResult = FALSE; + + if(bResult) + { + nbl = pSource->height; + nbc = pSource->width; + + if(pBackground == NULL || pBackground->width != nbc || pBackground->height != nbl || pBackground->imageSize != pSource->imageSize) + bResult = FALSE; + + if(pMotionMask == NULL || pMotionMask->width != nbc || pMotionMask->height != nbl || pMotionMask->imageSize != pSource->imageSize) + bResult = FALSE; + } + + return bResult; +} + +int TBackground::UpdateBackground(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask) +{ + return 0; +} + +IplImage *TBackground::CreateTestImg() +{ + IplImage *pImage = cvCreateImage(cvSize(256, 256), IPL_DEPTH_8U, 3); + + if(pImage != NULL) + cvSetZero(pImage); + + return pImage; +} + +int TBackground::UpdateTest(IplImage *pSource, IplImage *pBackground, IplImage *pTest, int nX, int nY, int nInd) +{ + int nErr = 0; + CvScalar Color; + unsigned char *ptr; + int l, c; + + if(pTest == NULL || !isInitOk(pSource, pBackground, pSource)) + nErr = 1; + + if(!nErr) + { + if(pTest->width != 256 || pTest->height != 256 || pTest->nChannels != 3) + nErr = 1; + + if(nX < 0 || nX > pSource->width || nY < 0 || nY > pSource->height) + nErr = 1; + + switch(nInd) + { + case 0 : Color = cvScalar(128, 0, 0); break; + case 1 : Color = cvScalar(0, 128, 0); break; + case 2 : Color = cvScalar(0, 0, 128); break; + default : nErr = 1; + } + } + + if(!nErr) + { + // recupere l'indice de la colonne + ptr = (unsigned char *)(pTest->imageData); + c = *ptr; + + // efface la colonne + cvLine(pTest, cvPoint(c, 0), cvPoint(c, 255), cvScalar(0)); + *ptr += 1; + + //recupere la couleur du fond + ptr = (unsigned char *)(pBackground->imageData + pBackground->widthStep * nY); + ptr += nX; + l = *ptr; + + // dessine la couleur + cvLine(pTest, cvPoint(c, l - 5), cvPoint(c, l + 5), Color); + + //recupere la couleur du point + ptr = (unsigned char *)(pSource->imageData + pSource->widthStep * nY); + ptr += nX; + l = *ptr; + + // dessine la couleur + ptr = (unsigned char *)(pTest->imageData + pTest->widthStep * l); + ptr += (c * 3) + nInd; + *ptr = 255; + } + + return nErr; +} diff --git a/package_bgs/av/TBackground.h b/package_bgs/av/TBackground.h new file mode 100644 index 0000000000000000000000000000000000000000..974cab955cdddca8fa8bd1e59345544533eb7125 --- /dev/null +++ b/package_bgs/av/TBackground.h @@ -0,0 +1,51 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* +* TBackground.h +* Framework +* +* Created by Robinault Lionel on 07/12/11. +* +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +class TBackground +{ +public: + TBackground(void); + virtual ~TBackground(void); + + virtual void Clear(void); + virtual void Reset(void); + + virtual int UpdateBackground(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask); + virtual int UpdateTest(IplImage *pSource, IplImage *pBackground, IplImage *pTest, int nX, int nY, int nInd); + virtual IplImage *CreateTestImg(); + + virtual int GetParameterCount(void); + virtual std::string GetParameterName(int nInd); + virtual std::string GetParameterValue(int nInd); + virtual int SetParameterValue(int nInd, std::string csNew); + +protected: + virtual int Init(IplImage * pSource); + virtual bool isInitOk(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask); +}; diff --git a/package_bgs/av/TBackgroundVuMeter.cpp b/package_bgs/av/TBackgroundVuMeter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45976213a3a0ff23f7d855d4f5977b966824d04d --- /dev/null +++ b/package_bgs/av/TBackgroundVuMeter.cpp @@ -0,0 +1,380 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* +* TBackgroundVuMeter.cpp +* Framework +* +* Created by Robinault Lionel on 07/12/11. +* +*/ +#include "TBackgroundVuMeter.h" + +#define PROCESS_PAR_COUNT 3 + +TBackgroundVuMeter::TBackgroundVuMeter(void) + : m_pHist(NULL) + , m_nBinSize(8) + , m_nBinCount(0) + , m_fAlpha(0.995) + , m_fThreshold(0.03) + , m_nCount(0) +{ + std::cout << "TBackgroundVuMeter()" << std::endl; +} + +TBackgroundVuMeter::~TBackgroundVuMeter(void) +{ + Clear(); + std::cout << "~TBackgroundVuMeter()" << std::endl; +} + +void TBackgroundVuMeter::Clear(void) +{ + int i; + + TBackground::Clear(); + + if(m_pHist != NULL) + { + for(i = 0; i < m_nBinCount; ++i) + { + if(m_pHist[i] != NULL) + cvReleaseImage(&m_pHist[i]); + } + + delete m_pHist; + m_pHist = NULL; + m_nBinCount = 0; + } + + m_nCount = 0; +} + +void TBackgroundVuMeter::Reset(void) +{ + int i; + float fVal = 0.0; + + TBackground::Reset(); + + if(m_pHist != NULL) + { + // fVal = (m_nBinCount != 0) ? (float)(1.0 / (double)m_nBinCount) : (float)0.0; + fVal = 0.0; + + for(i = 0; i < m_nBinCount; ++i) + { + if(m_pHist[i] != NULL) + { + cvSetZero(m_pHist[i]); + cvAddS(m_pHist[i], cvScalar(fVal), m_pHist[i]); + } + } + } + + m_nCount = 0; +} + +int TBackgroundVuMeter::GetParameterCount(void) +{ + return TBackground::GetParameterCount() + PROCESS_PAR_COUNT; +} + +std::string TBackgroundVuMeter::GetParameterName(int nInd) +{ + std::string csResult; + int nNb; + + nNb = TBackground::GetParameterCount(); + + if(nInd >= nNb) + { + nInd -= nNb; + + switch(nInd) + { + case 0 : csResult = "Bin size"; break; + case 1 : csResult = "Alpha"; break; + case 2 : csResult = "Threshold"; break; + } + } + else + csResult = TBackground::GetParameterName(nInd); + + return csResult; +} + +std::string TBackgroundVuMeter::GetParameterValue(int nInd) +{ + std::string csResult; + int nNb; + + nNb = TBackground::GetParameterCount(); + + if(nInd >= nNb) + { + nInd -= nNb; + + char buff[100]; + + switch(nInd) + { + case 0 : sprintf(buff, "%d", m_nBinSize); break; + case 1 : sprintf(buff, "%.3f", m_fAlpha); break; + case 2 : sprintf(buff, "%.2f", m_fThreshold); break; + } + + csResult = buff; + } + else + csResult = TBackground::GetParameterValue(nInd); + + return csResult; +} + +int TBackgroundVuMeter::SetParameterValue(int nInd, std::string csNew) +{ + int nErr = 0; + + int nNb; + + nNb = TBackground::GetParameterCount(); + + if(nInd >= nNb) + { + nInd -= nNb; + + switch(nInd) + { + case 0 : SetBinSize(atoi(csNew.c_str())); break; + case 1 : SetAlpha(atof(csNew.c_str())); break; + case 2 : SetThreshold(atof(csNew.c_str())); break; + default : nErr = 1; + } + } + else + nErr = TBackground::SetParameterValue(nInd, csNew); + + return nErr; +} + +int TBackgroundVuMeter::Init(IplImage * pSource) +{ + int nErr = 0; + int i; + int nbl, nbc; + + Clear(); + + nErr = TBackground::Init(pSource); + + if(pSource == NULL) + nErr = 1; + + // calcul le nb de bin + if(!nErr) + { + nbl = pSource->height; + nbc = pSource->width; + m_nBinCount = (m_nBinSize != 0) ? 256 / m_nBinSize : 0; + + if(m_nBinCount <= 0 || m_nBinCount > 256) + nErr = 1; + } + + // creation du tableau de pointeur + if(!nErr) + { + m_pHist = new IplImage *[m_nBinCount]; + + if(m_pHist == NULL) + nErr = 1; + } + + // creation des images + if(!nErr) + { + for(i = 0; i < m_nBinCount; ++i) + { + m_pHist[i] = cvCreateImage(cvSize(nbc, nbl), IPL_DEPTH_32F, 1); + + if(m_pHist[i] == NULL) + nErr = 1; + } + } + + if(!nErr) + Reset(); + else + Clear(); + + return nErr; +} + +bool TBackgroundVuMeter::isInitOk(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask) +{ + bool bResult = TRUE; + int i; + int nbl, nbc; + + bResult = TBackground::isInitOk(pSource, pBackground, pMotionMask); + + if(pSource == NULL) + bResult = FALSE; + + if(m_nBinSize == 0) + bResult = FALSE; + + if(bResult) + { + i = (m_nBinSize != 0) ? 256 / m_nBinSize : 0; + + if(i != m_nBinCount || m_pHist == NULL) + bResult = FALSE; + } + + if(bResult) + { + nbl = pSource->height; + nbc = pSource->width; + + for(i = 0; i < m_nBinCount; ++i) + { + if(m_pHist[i] == NULL || m_pHist[i]->width != nbc || m_pHist[i]->height != nbl) + bResult = FALSE; + } + } + + return bResult; +} + +int TBackgroundVuMeter::UpdateBackground(IplImage *pSource, IplImage *pBackground, IplImage *pMotionMask) +{ + int nErr = 0; + int i, l, c, nbl, nbc; + unsigned char *ptrs, *ptrb, *ptrm; + float *ptr1, *ptr2; + unsigned char v; + + if(!isInitOk(pSource, pBackground, pMotionMask)) + nErr = Init(pSource); + + if(!nErr) + { + m_nCount++; + nbc = pSource->width; + nbl = pSource->height; + v = m_nBinSize; + + // multiplie tout par alpha + for(i = 0; i < m_nBinCount; ++i) + cvConvertScale(m_pHist[i], m_pHist[i], m_fAlpha, 0.0); + + for(l = 0; l < nbl; ++l) + { + ptrs = (unsigned char *)(pSource->imageData + pSource->widthStep * l); + ptrm = (unsigned char *)(pMotionMask->imageData + pMotionMask->widthStep * l); + ptrb = (unsigned char *)(pBackground->imageData + pBackground->widthStep * l); + + for(c = 0; c < nbc; ++c, ptrs++, ptrb++, ptrm++) + { + // recherche le bin � augmenter + i = *ptrs / v; + + if(i < 0 || i >= m_nBinCount) + i = 0; + + ptr1 = (float *)(m_pHist[i]->imageData + m_pHist[i]->widthStep * l); + ptr1 += c; + + *ptr1 += (float)(1.0 - m_fAlpha); + *ptrm = (*ptr1 < m_fThreshold) ? 255 : 0; + + // recherche le bin du fond actuel + i = *ptrb / v; + + if(i < 0 || i >= m_nBinCount) + i = 0; + + ptr2 = (float *)(m_pHist[i]->imageData + m_pHist[i]->widthStep * l); + ptr2 += c; + + if(*ptr2 < *ptr1) + *ptrb = *ptrs; + } + } + + if(m_nCount < 5) + cvSetZero(pMotionMask); + } + + return nErr; +} + +IplImage *TBackgroundVuMeter::CreateTestImg() +{ + IplImage *pImage = NULL; + + if(m_nBinCount > 0) + pImage = cvCreateImage(cvSize(m_nBinCount, 100), IPL_DEPTH_8U, 3); + + if(pImage != NULL) + cvSetZero(pImage); + + return pImage; +} + +int TBackgroundVuMeter::UpdateTest(IplImage *pSource, IplImage *pBackground, IplImage *pTest, int nX, int nY, int nInd) +{ + int nErr = 0; + int i, nbl, nbc; + float *ptrf; + + if(pTest == NULL || !isInitOk(pSource, pBackground, pSource)) + nErr = 1; + + if(!nErr) + { + nbl = pTest->height; + nbc = pTest->width; + + if(nbl != 100 || nbc != m_nBinCount) + nErr = 1; + + if(nX < 0 || nX >= pSource->width || nY < 0 || nY >= pSource->height) + nErr = 1; + } + + if(!nErr) + { + cvSetZero(pTest); + + for(i = 0; i < m_nBinCount; ++i) + { + ptrf = (float *)(m_pHist[i]->imageData + m_pHist[i]->widthStep * nY); + ptrf += nX; + + if(*ptrf >= 0 || *ptrf <= 1.0) { + cvLine(pTest, cvPoint(i, 100), cvPoint(i, (int)(100.0 * (1.0 - *ptrf))), cvScalar(0, 255, 0)); + } + } + + cvLine(pTest, cvPoint(0, (int)(100.0 * (1.0 - m_fThreshold))), cvPoint(m_nBinCount, (int)(100.0 * (1.0 - m_fThreshold))), cvScalar(0, 128, 0)); + } + + return nErr; +} diff --git a/package_bgs/av/TBackgroundVuMeter.h b/package_bgs/av/TBackgroundVuMeter.h new file mode 100644 index 0000000000000000000000000000000000000000..1a19324f08c8d20b4bb2fd972a9920e73b2adae8 --- /dev/null +++ b/package_bgs/av/TBackgroundVuMeter.h @@ -0,0 +1,67 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* +* TBackgroundVuMeter.h +* Framework +* +* Created by Robinault Lionel on 07/12/11. +* +*/ +#pragma once + +#include "TBackground.h" + +class TBackgroundVuMeter : public TBackground +{ +public: + TBackgroundVuMeter(void); + virtual ~TBackgroundVuMeter(void); + + virtual void Clear(void); + virtual void Reset(void); + + virtual int UpdateBackground(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask); + + virtual IplImage *CreateTestImg(); + virtual int UpdateTest(IplImage *pSource, IplImage *pBackground, IplImage *pTest, int nX, int nY, int nInd); + + virtual int GetParameterCount(void); + virtual std::string GetParameterName(int nInd); + virtual std::string GetParameterValue(int nInd); + virtual int SetParameterValue(int nInd, std::string csNew); + + inline void SetBinSize(int nNew) { m_nBinSize = (nNew > 0 && nNew < 255) ? nNew : 8; } + inline double GetBinSize() { return m_nBinSize; } + + inline void SetAlpha(double fNew) { m_fAlpha = (fNew > 0.0 && fNew < 1.0) ? fNew : 0.995; } + inline double GetAlpha() { return m_fAlpha; } + + inline void SetThreshold(double fNew) { m_fThreshold = (fNew > 0.0 && fNew < 1.0) ? fNew : 0.03; } + inline double GetThreshold() { return m_fThreshold; } + +protected: + IplImage **m_pHist; + + int m_nBinCount; + int m_nBinSize; + int m_nCount; + double m_fAlpha; + double m_fThreshold; + + virtual int Init(IplImage * pSource); + virtual bool isInitOk(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask); +}; diff --git a/package_bgs/av/VuMeter.cpp b/package_bgs/av/VuMeter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..29a1477f536a06058f76bc51384c12712b0db761 --- /dev/null +++ b/package_bgs/av/VuMeter.cpp @@ -0,0 +1,116 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "VuMeter.h" + +VuMeter::VuMeter() : firstTime(true), showOutput(true), enableFilter(true), binSize(8), alpha(0.995), threshold(0.03) +{ + std::cout << "VuMeter()" << std::endl; +} + +VuMeter::~VuMeter() +{ + cvReleaseImage(&mask); + cvReleaseImage(&background); + cvReleaseImage(&gray); + + std::cout << "~VuMeter()" << std::endl; +} + +void VuMeter::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + else + frame = new IplImage(img_input); + + loadConfig(); + + if(firstTime) + { + bgs.SetAlpha(alpha); + bgs.SetBinSize(binSize); + bgs.SetThreshold(threshold); + + gray = cvCreateImage(cvGetSize(frame),IPL_DEPTH_8U,1); + cvCvtColor(frame,gray,CV_RGB2GRAY); + + background = cvCreateImage(cvGetSize(gray),IPL_DEPTH_8U,1); + cvCopy(gray, background); + + mask = cvCreateImage(cvGetSize(gray),IPL_DEPTH_8U,1); + cvZero(mask); + + saveConfig(); + } + else + cvCvtColor(frame,gray,CV_RGB2GRAY); + + bgs.UpdateBackground(gray,background,mask); + cv::Mat img_foreground(mask); + cv::Mat img_bkg(background); + + if(enableFilter) + { + cv::erode(img_foreground,img_foreground,cv::Mat()); + cv::medianBlur(img_foreground, img_foreground, 5); + } + + if(showOutput) + { + if(!img_foreground.empty()) + cv::imshow("VuMeter", img_foreground); + + if(!img_bkg.empty()) + cv::imshow("VuMeter Bkg Model", img_bkg); + } + + img_foreground.copyTo(img_output); + img_bkg.copyTo(img_bgmodel); + + delete frame; + firstTime = false; +} + +void VuMeter::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/VuMeter.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "enableFilter", enableFilter); + + cvWriteInt(fs, "binSize", binSize); + cvWriteReal(fs, "alpha", alpha); + cvWriteReal(fs, "threshold", threshold); + + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void VuMeter::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/VuMeter.xml", 0, CV_STORAGE_READ); + + enableFilter = cvReadIntByName(fs, 0, "enableFilter", true); + + binSize = cvReadIntByName(fs, 0, "binSize", 8); + alpha = cvReadRealByName(fs, 0, "alpha", 0.995); + threshold = cvReadRealByName(fs, 0, "threshold", 0.03); + + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/av/VuMeter.h b/package_bgs/av/VuMeter.h new file mode 100644 index 0000000000000000000000000000000000000000..7884ff68aa5ac41eb42225b1d6defa2ddcda3b58 --- /dev/null +++ b/package_bgs/av/VuMeter.h @@ -0,0 +1,53 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "TBackgroundVuMeter.h" +#include "../IBGS.h" + +class VuMeter : public IBGS +{ +private: + TBackgroundVuMeter bgs; + + IplImage *frame; + IplImage *gray; + IplImage *background; + IplImage *mask; + + bool firstTime; + bool showOutput; + bool enableFilter; + + int binSize; + double alpha; + double threshold; + +public: + VuMeter(); + ~VuMeter(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; diff --git a/package_bgs/ck/LbpMrf.cpp b/package_bgs/ck/LbpMrf.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3d068c253505783976837916c3e4611bbed63bb --- /dev/null +++ b/package_bgs/ck/LbpMrf.cpp @@ -0,0 +1,87 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. + +Csaba, Kertész: Texture-Based Foreground Detection, International Journal of Signal Processing, +Image Processing and Pattern Recognition (IJSIP), Vol. 4, No. 4, 2011. + +*/ +#include "LbpMrf.h" + +#include "MotionDetection.hpp" + +LbpMrf::LbpMrf() : firstTime(true), Detector(NULL), showOutput(true) +{ + std::cout << "LbpMrf()" << std::endl; + Detector = new MotionDetection(); + Detector->SetMode(MotionDetection::md_LBPHistograms); +} + +LbpMrf::~LbpMrf() +{ + std::cout << "~LbpMrf()" << std::endl; + delete Detector; + Detector = NULL; +} + +void LbpMrf::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + { + saveConfig(); + } + + IplImage TempImage(img_input); + MEImage InputImage(img_input.cols, img_input.rows, img_input.channels()); + MEImage OutputImage(img_input.cols, img_input.rows, img_input.channels()); + + InputImage.SetIplImage((void*)&TempImage); + + Detector->DetectMotions(InputImage); + Detector->GetMotionsMask(OutputImage); + img_output = (IplImage*)OutputImage.GetIplImage(); + bitwise_not(img_output, img_bgmodel); + + if(showOutput) + { + cv::imshow("LBP-MRF FG", img_output); + cv::imshow("LBP-MRF BG", img_bgmodel); + } + + firstTime = false; +} + +void LbpMrf::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/LbpMrf.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void LbpMrf::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/LbpMrf.xml", 0, CV_STORAGE_READ); + + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/ck/LbpMrf.h b/package_bgs/ck/LbpMrf.h new file mode 100644 index 0000000000000000000000000000000000000000..6fd72673a16d0f649b58d08b9f16b6ca5b786c2d --- /dev/null +++ b/package_bgs/ck/LbpMrf.h @@ -0,0 +1,44 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <opencv2/opencv.hpp> + +#include "../IBGS.h" + +class MotionDetection; + +class LbpMrf : public IBGS +{ +private: + bool firstTime; + MotionDetection* Detector; + cv::Mat img_foreground; + cv::Mat img_segmentation; + bool showOutput; + +public: + LbpMrf(); + ~LbpMrf(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; diff --git a/package_bgs/ck/MEDefs.cpp b/package_bgs/ck/MEDefs.cpp new file mode 100644 index 0000000000000000000000000000000000000000..93473537cbd5b7de0ce6ffc1dcc17f380004b91f --- /dev/null +++ b/package_bgs/ck/MEDefs.cpp @@ -0,0 +1,40 @@ +/* + * This file is part of the AiBO+ project + * + * Copyright (C) 2005-2013 Csaba Kertész (csaba.kertesz@gmail.com) + * + * AiBO+ is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * AiBO+ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include "MEDefs.hpp" + +#include <math.h> + +float MERound(float number) +{ + double FracPart = 0.0; + double IntPart = 0.0; + float Ret = 0.0; + + FracPart = modf((double)number, &IntPart); + if (number >= 0) + { + Ret = (float)(FracPart >= 0.5 ? IntPart+1 : IntPart); + } else { + Ret = (float)(FracPart <= -0.5 ? IntPart-1 : IntPart); + } + return Ret; +} diff --git a/package_bgs/ck/MEDefs.hpp b/package_bgs/ck/MEDefs.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d5bc24637555a67639842a9269ea758d605db9dd --- /dev/null +++ b/package_bgs/ck/MEDefs.hpp @@ -0,0 +1,83 @@ +/* + * This file is part of the AiBO+ project + * + * Copyright (C) 2005-2013 Csaba Kertész (csaba.kertesz@gmail.com) + * + * AiBO+ is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * AiBO+ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef MEDefs_hpp +#define MEDefs_hpp + +/** + * @addtogroup mindeye + * @{ + */ + +/// Pi value +#ifndef ME_PI_VALUE +#define ME_PI_VALUE 3.14159265 +#endif + +/*! Process state */ +typedef enum { + ps_Min = 0, /*!< Minimum value */ + ps_Uninitialized = ps_Min, /*!< Uninitialized state */ + ps_Initialized, /*!< Initialized state */ + ps_InProgress, /*!< In progress state */ + ps_Successful, /*!< Successful state */ + ps_Max = ps_Successful /*!< Maximum value */ +} MEProcessStateType; + +template <typename T> +const T& MEMin(const T& a, const T& b) +{ + if (a < b) + return a; + return b; +} + +template <typename T> +const T& MEMax(const T& a, const T& b) +{ + if (a < b) + return b; + return a; +} + +template <typename T> +const T& MEBound(const T& min, const T& val, const T& max) +{ + return MEMax(min, MEMin(max, val)); +} + +/*! + * @brief Round a float number + * + * @param number number to round + * + * @return New float number + * + * This method rounds a float number, if the fraction is .5 or lower + * then it rounds down, otherwise up. + * + */ + +float MERound(float number); + +/** @} */ + +#endif diff --git a/package_bgs/ck/MEHistogram.cpp b/package_bgs/ck/MEHistogram.cpp new file mode 100644 index 0000000000000000000000000000000000000000..126fd259fefb6ee853fe354235972cd6a4e964ff --- /dev/null +++ b/package_bgs/ck/MEHistogram.cpp @@ -0,0 +1,508 @@ +/* + * This file is part of the AiBO+ project + * + * Copyright (C) 2005-2013 Csaba Kertész (csaba.kertesz@gmail.com) + * + * AiBO+ is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * AiBO+ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + * Some histogram stretch codes are based on how Gimp does it, the same + * GPL 2 license applies for the following authors: + * + * The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + */ + +#include "MEHistogram.hpp" + +#if defined(__MINGW32__) || defined(__MINGW64__) +#include <cv.h> +#else +#include <opencv/cv.h> +#endif + +#include "MEDefs.hpp" +#include "MEImage.hpp" + +MEHistogram::MEHistogram() +{ + Clear(); +} + + +MEHistogram::~MEHistogram() +{ +} + + +void MEHistogram::Clear() +{ + memset(&HistogramData, 0, 256*sizeof(int)); +} + + +bool MEHistogram::operator==(MEHistogram& histogram) const +{ + bool ret = true; + + for (int i = 255; i >= 0; --i) + { + if (HistogramData[i] != histogram.HistogramData[i]) + { + ret = false; + break; + } + } + return ret; +} + + +void MEHistogram::Calculate(MEImage& image, int channel, HistogramType mode) +{ + int Channel = (channel < 1) ? 1 : ((channel > image.GetLayers()) ? image.GetLayers() : channel); + + if (mode == h_Overwrite) + { + Clear(); + } + + unsigned char *ImageData = image.GetImageData(); + int rowStart = 0; + + for (int i = image.GetHeight()-1; i >= 0; i--) + { + for (int i1 = (image.GetWidth()-1)*image.GetLayers()+Channel-1; i1 >= Channel-1; i1 -= image.GetLayers()) + { + HistogramData[ImageData[rowStart+i1]]++; + } + rowStart += image.GetRowWidth(); + } +} + + +void MEHistogram::Calculate(MEImage& image, HistogramType mode) +{ + if (mode == h_Overwrite) + { + Clear(); + } + + unsigned char *ImageData = image.GetImageData(); + int RowStart = 0; + int RowWidth = image.GetRowWidth(); + + for (int i = image.GetHeight()-1; i >= 0; i--) + { + for (int i1 = image.GetWidth()*image.GetLayers()-1; i1 >= 0; i1--) + { + HistogramData[ImageData[RowStart+i1]]++; + } + RowStart += RowWidth; + } +} + + +void MEHistogram::Calculate(MEImage& image, int channel, int x0, int y0, int x1, int y1) +{ + int Channel = (channel < 1) ? 1 : ((channel > image.GetLayers()) ? image.GetLayers() : channel); + unsigned char *ImageData = image.GetImageData(); + int RowStart = 0; + int RowWidth = image.GetRowWidth(); + int X0 = x0 > x1 ? x1 : x0; + int Y0 = y0 > y1 ? y1 : y0; + int X1 = x0 < x1 ? x1 : x0; + int Y1 = y0 < y1 ? y1 : y0; + + Clear(); + + // Compute the correct region coordinates and check them + X0 = X0 < 0 ? 0 : X0; + Y0 = Y0 < 0 ? 0 : Y0; + X1 = X1 > image.GetWidth()-1 ? image.GetWidth()-1 : X1; + Y1 = Y1 > image.GetHeight()-1 ? image.GetHeight()-1 : Y1; + RowStart = Y0*image.GetRowWidth(); + + for (int i = Y1; i >= Y0; --i) + { + for (int i1 = X1*image.GetLayers()+Channel-1; i1 >= X0*image.GetLayers()+Channel-1; + i1 -= image.GetLayers()) + { + HistogramData[ImageData[RowStart+i1]]++; + } + RowStart += RowWidth; + } +} + + +int MEHistogram::GetPeakIndex() const +{ + int PeakIndex = 0; + int PeakValue = 0; + + for (int i = 0; i < 256; i++) + { + if (PeakValue < HistogramData[i]) + { + PeakValue = HistogramData[i]; + PeakIndex = i; + } + } + return PeakIndex; +} + + +int MEHistogram::GetLowestLimitIndex(int threshold) const +{ + int MinIndex = 0; + + for (int i = 0; i < 256; i++) + { + if (threshold <= HistogramData[i]) + { + MinIndex = i; + break; + } + } + return MinIndex; +} + + +int MEHistogram::GetHighestLimitIndex(int threshold) const +{ + int MaxIndex = 255; + + for (int i = 255; i >= 0; i--) + { + if (threshold <= HistogramData[i]) + { + MaxIndex = i; + break; + } + } + return MaxIndex; +} + + +int MEHistogram::GetPowerAmount(int minindex, int maxindex) const +{ + int ValueAmount = 0; + int MinIndex = (minindex > 255) ? 255 : ((minindex < 0) ? 0 : minindex); + int MaxIndex = (maxindex > 255) ? 255 : ((maxindex < 0) ? 0 : maxindex); + + if (MinIndex > MaxIndex) + { + int TempInt = MinIndex; + MinIndex = MaxIndex; + MaxIndex = TempInt; + } + + for (int i = MinIndex; i <= MaxIndex; i++) + { + ValueAmount += HistogramData[i]; + } + return ValueAmount; +} + + +int MEHistogram::GetCentroidIndex() const +{ + int ValueAmount = GetPowerAmount(0, 255); + int WeightedValueAmount = 1; + int CentroidIndex = 0; + + // Calculate the normal and weighted amount of histogram values + for (int i = 0; i < 256; i++) + { + WeightedValueAmount += i*HistogramData[i]; + } + // Calculate the centroid point of the histogram + CentroidIndex = WeightedValueAmount / ValueAmount; + return CentroidIndex; +} + + +bool MEHistogram::Stretch(StretchType mode) +{ + int MinIndex = -1; + int MaxIndex = -1; + int Percent = -1; + double Percentage = 0.0; + double NextPercentage = 0.0; + double Count = 0.0; + double NewCount = 0.0; + bool Ret = true; + + switch (mode) + { + case s_OwnMode: + Percent = 20; + MinIndex = GetLowestLimitIndex(Percent); + MaxIndex = GetHighestLimitIndex(Percent); + + while ((abs(MaxIndex-MinIndex) < 52) && (Percent > 1)) + { + Percent = Percent / 2; + MinIndex = GetLowestLimitIndex(Percent); + MaxIndex = GetHighestLimitIndex(Percent); + + // The calculation gives wrong answer back + if (MinIndex == 0 && MaxIndex == 255) + { + MinIndex = 128; + MaxIndex = 128; + Ret = false; + } + } + break; + + case s_GimpMode: + Count = GetPowerAmount(0, 255); + NewCount = 0; + + for (int i = 0; i < 255; i++) + { + double Value = 0.0; + double NextValue = 0.0; + + Value = HistogramData[i]; + NextValue = HistogramData[i+1]; + NewCount += Value; + Percentage = NewCount / Count; + NextPercentage = (NewCount+NextValue) / Count; + + if (fabs(Percentage-0.006) < fabs(NextPercentage-0.006)) + { + MinIndex = i+1; + break; + } + } + NewCount = 0.0; + for (int i = 255; i > 0; i--) + { + double Value = 0.0; + double NextValue = 0.0; + + Value = HistogramData[i]; + NextValue = HistogramData[i-1]; + NewCount += Value; + Percentage = NewCount / Count; + NextPercentage = (NewCount+NextValue) / Count; + + if (fabs(Percentage-0.006) < fabs(NextPercentage-0.006)) + { + MaxIndex = i-1; + break; + } + } + break; + + default: + break; + } + + if (MaxIndex <= MinIndex) + { + MinIndex = 0; + MaxIndex = 255; + Ret = false; + } + if (MaxIndex-MinIndex <= 10 || + (MaxIndex-MinIndex <= 20 && (float)GetPowerAmount(MinIndex, MaxIndex) / GetPowerAmount(0, 255) < 0.20)) + { + MinIndex = 0; + MaxIndex = 255; + Ret = false; + } + if (Ret) + { + unsigned char TransformedHistogram[256]; + + for (int i = 0; i < 256; ++i) + { + TransformedHistogram[i] = (unsigned char)MEBound(0, 255*(i-MinIndex) / (MaxIndex-MinIndex), 255); + } + for (int i = 0; i < 256; ++i) + { + HistogramData[i] = TransformedHistogram[i]; + } + } + return Ret; +} + + +MEHistogramTransform::MEHistogramTransform() : ChannelMode(p_SeparateChannels), +StretchMode(MEHistogram::s_GimpMode), DiscreteStretchingDone(false) +{ +} + + +MEHistogramTransform::~MEHistogramTransform() +{ +} + + +void MEHistogramTransform::HistogramStretch(MEImage& image) +{ + SetStretchProcessingMode(p_SeparateChannels, MEHistogram::s_GimpMode); + HistogramStretch(image, t_Continuous); +} + + +void MEHistogramTransform::HistogramStretch(MEImage& image, TransformType time_mode) +{ + if (time_mode == t_Continuous) + { + DiscreteStretchingDone = false; + } + + if (ChannelMode == p_Average || image.GetLayers() == 1) + { + if (time_mode == t_Continuous || (time_mode == t_Discrete && !DiscreteStretchingDone)) + { + RedChannel.Calculate(image, 1, MEHistogram::h_Overwrite); + + for (int l = 1; l < image.GetLayers(); l++) + { + RedChannel.Calculate(image, l+1, MEHistogram::h_Add); + } + RedChannel.Stretch(StretchMode); + if (time_mode == t_Discrete && !DiscreteStretchingDone) + { + DiscreteStretchingDone = true; + } + } + unsigned char *ImageData = image.GetImageData(); + int RowStart = 0; + int RowWidth = image.GetRowWidth(); + + for (int i = image.GetHeight()-1; i >= 0; i--) + { + for (int i1 = image.GetWidth()*image.GetLayers()-1; i1 >= 0; i1--) + { + ImageData[RowStart+i1] = RedChannel.HistogramData[ImageData[RowStart+i1]]; + } + RowStart += RowWidth; + } + } else + if (ChannelMode == p_SeparateChannels) + { + if (time_mode == t_Continuous || (time_mode == t_Discrete && !DiscreteStretchingDone)) + { + RedChannel.Calculate(image, 1, MEHistogram::h_Overwrite); + GreenChannel.Calculate(image, 2, MEHistogram::h_Overwrite); + BlueChannel.Calculate(image, 3, MEHistogram::h_Overwrite); + RedChannel.Stretch(StretchMode); + GreenChannel.Stretch(StretchMode); + BlueChannel.Stretch(StretchMode); + if (time_mode == t_Discrete && !DiscreteStretchingDone) + { + DiscreteStretchingDone = true; + } + } + unsigned char *ImageData = image.GetImageData(); + int RowStart = 0; + int RowWidth = image.GetRowWidth(); + + for (int i = image.GetHeight()-1; i >= 0; i--) + { + for (int i1 = image.GetWidth()*image.GetLayers()-3; i1 >= 0; i1 -= 3) + { + ImageData[RowStart+i1] = RedChannel.HistogramData[ImageData[RowStart+i1]]; + ImageData[RowStart+i1+1] = GreenChannel.HistogramData[ImageData[RowStart+i1+1]]; + ImageData[RowStart+i1+2] = BlueChannel.HistogramData[ImageData[RowStart+i1+2]]; + } + RowStart += RowWidth; + } + } +} + + +void MEHistogramTransform::HistogramEqualize(MEImage& image) +{ + DiscreteStretchingDone = false; + IplImage* cvDest8bitImg = NULL; + IplImage* cvDestImg = NULL; + + switch (image.GetLayers()) + { + case 1: + // Grayscale image + cvDest8bitImg = cvCreateImage(cvSize(image.GetWidth(), image.GetHeight()), 8, 1); + cvEqualizeHist((IplImage*)image.GetIplImage(), cvDest8bitImg); + image.SetIplImage((void*)cvDest8bitImg); + cvReleaseImage(&cvDest8bitImg); + break; + + case 3: + // RGB image + cvDestImg = cvCreateImage(cvSize(image.GetWidth(), image.GetHeight()), 8, 3); + IplImage *cvR, *cvG, *cvB; + + cvR = cvCreateImage(cvSize(image.GetWidth(), image.GetHeight()), 8, 1); + cvG = cvCreateImage(cvSize(image.GetWidth(), image.GetHeight()), 8, 1); + cvB = cvCreateImage(cvSize(image.GetWidth(), image.GetHeight()), 8, 1); + + cvSplit((IplImage*)image.GetIplImage(), cvR, cvG, cvB, NULL); + cvEqualizeHist(cvR, cvR); + cvEqualizeHist(cvG, cvG); + cvEqualizeHist(cvB, cvB); + cvMerge(cvR, cvG, cvB, NULL, cvDestImg); + + image.SetIplImage((void*)cvDestImg); + cvReleaseImage(&cvR); + cvReleaseImage(&cvG); + cvReleaseImage(&cvB); + cvReleaseImage(&cvDestImg); + break; + + default: + break; + } +} + + +void MEHistogramTransform::SetStretchProcessingMode(ProcessingType new_channel_mode, + MEHistogram::StretchType new_stretch_mode) +{ + DiscreteStretchingDone = false; + + switch(new_channel_mode) + { + case p_SeparateChannels: + ChannelMode = new_channel_mode; + break; + + case p_Average: + ChannelMode = new_channel_mode; + break; + + default: + break; + } + + switch(new_stretch_mode) + { + case MEHistogram::s_OwnMode: + StretchMode = new_stretch_mode; + break; + + case MEHistogram::s_GimpMode: + StretchMode = new_stretch_mode; + break; + + default: + break; + } +} diff --git a/package_bgs/ck/MEHistogram.hpp b/package_bgs/ck/MEHistogram.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5b2b47f00d44716df0b1307a3631992e8c7342b0 --- /dev/null +++ b/package_bgs/ck/MEHistogram.hpp @@ -0,0 +1,348 @@ +/* + * This file is part of the AiBO+ project + * + * Copyright (C) 2005-2013 Csaba Kertész (csaba.kertesz@gmail.com) + * + * AiBO+ is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * AiBO+ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef MEHistogram_hpp +#define MEHistogram_hpp + +/** + * @addtogroup mindeye + * @{ + */ + +class MEImage; + +/** + * MEHistogram + * @brief The class provides basic histogram operations + */ +class MEHistogram +{ +public: + + /// Types of histogram calculation + typedef enum { + h_Min = 0, /*!< Minimum value */ + h_Overwrite = h_Min, /*!< Overwrite */ + h_Add, /*!< Add */ + h_Max = h_Add /*!< Maximum value */ + } HistogramType; + + /// Types of histogram stretching + typedef enum { + s_Min = 0, /*!< Minimum value */ + s_OwnMode = s_Min, /*!< Own mode */ + s_GimpMode, /*!< Gimp mode */ + s_Max = s_GimpMode /*!< Maximum value */ + } StretchType; + + /// Constructor of class + MEHistogram(); + /// Destructor of class + ~MEHistogram(); + + /*! + * @brief Clear histogram data + * + * Clear histogram data. + * + */ + + void Clear(); + + /*! + * @brief Equality (==) operator + * + * @param histogram Histogram to be compared + * + * @return True if the two histograms are equal. + * + * Compare two histograms. + * + */ + + bool operator==(MEHistogram& histogram) const; + + /*! + * @brief Calculate the histogram of one color channel + * + * @param image Given image for the calculations + * @param channel Selected color channel for calculation (Range: 1..x) + * @param mode The mode of calculation. + * + * The method calculates the histograms of a color channel. + * There is two different type of the function: + * + * - h_Add: Add the data to the existing histogram. + * - h_Overwrite: Clear the histogram data before the + * calculation. + * + */ + + void Calculate(MEImage& image, int channel, HistogramType mode); + + /*! + * @brief Calculate the average histogram of an image + * + * @param image Given image for the calculations + * @param mode Histogram calculation mode + * + * The method calculates the average histogram of an image. + * + */ + + void Calculate(MEImage& image, HistogramType mode = h_Overwrite); + + /*! + * @brief Calculate the histogram of an image region + * + * @param image Given image for the calculations + * @param channel Selected color channel for calculation (Range: 1..x) + * @param x0 x0 coordinate of the region + * @param y0 y0 coordinate of the region + * @param x1 x1 coordinate of the region + * @param y1 y1 coordinate of the region + * + * The method calculates the average histogram of an image region + * (x0,y0)-(x1,y1). + * + */ + + void Calculate(MEImage& image, int channel, int x0, int y0, int x1, int y1); + + /*! + * @brief Get the index of maximum value of the histogram + * + * @return Index number + * + * Function gives an index value back where is the highest + * peak of the histogram. + * + */ + + int GetPeakIndex() const; + + /*! + * @brief Get the lowest histogram index with an threshold value + * + * @param threshold Specified threshold (in percent: 0..100 %) + * + * @return Index number + * + * Function gives the lowest index back whose value reaches + * an threshold value calculated by (counted pixel number / + * 10*threshold / 100). + * + */ + + int GetLowestLimitIndex(int threshold) const; + + /*! + * @brief Get the highest histogram index with an threshold value + * + * @param threshold Specified threshold (in percent: 0..100 %) + * + * @return Index number + * + * Function gives the highest index back whose value reaches + * an threshold value calculated by (counted pixel number / + * 10*threshold / 100). + * + */ + + int GetHighestLimitIndex(int threshold) const; + + /*! + * @brief Get the amount of the histogram values in an interval + * + * @param minindex Minimal index of the interval + * @param maxindex Maximal index of the interval + * + * @return Amount of the values + * + * Function calculates the amount of the histogram values + * in a given interval. + * + */ + + int GetPowerAmount(int min_index, int max_index) const; + + /*! + * @brief Get index value of the centroid point of the histogram + * + * @return Index number + * + * Function calculates the centre of area of the histogram and + * gives the index number back. + * + */ + + int GetCentroidIndex() const; + + /*! + * @brief Stretch the histogram + * + * @param mode Mode of the histogram stretching + * + * @return True if successful, otherwise false. + * + * The function selects and stretches the main power + * interval of the histogram. The following calculation + * modes are available: + * + * - s_OwnMode: The calculation of the power + * interval is selected by functions Histogram::GetHistogramLowestLimitIndex() + * and Histogram::GetHistogramHighestLimitIndex() where the + * threshold is 20, 10, 5, 2, 1 in order. The power range will + * be selected if the length is at least 52 long or the used + * threshold reaches the 1 value. + * - s_GimpMode: The minimum index of power interval is + * specified by the first fulfilled abs(percentage[i]-0.006) < + * fabs(percentage[i+1]-0.006) where the percentage[i] means + * the amount of the histogram values in the interval [0, i]. + * The maximum index is specified by the first fulfilled + * (from the end of the histogram) abs(percentage[i]-0.006) < + * fabs(percentage[i-1]-0.006) where the percentage[i] means + * the amount of the histogram values in the interval [i, 255]. + * + * The stretch operation is rejected if the power interval is + * less than 10 or less than 20 and the percentage[min_index, max_index] + * / percentage[0, 255] < 0.2. + * + */ + + bool Stretch(StretchType mode); + + /// Histogram spectrum + int HistogramData[256]; +}; + + +/** + * MEHistogramTransform + * @brief The class provides histogram operations + */ +class MEHistogramTransform +{ +public: + /// Types of histogram processing + typedef enum { + p_Min = 0, /*!< Minimum value */ + p_SeparateChannels = p_Min, /*!< Separate channels */ + p_Average, /*!< Average */ + p_Max = p_Average /*!< Maximum value */ + } ProcessingType; + + /// Types of histogram transformations + typedef enum { + t_Min = 0, /*!< Minimum value */ + t_Continuous = t_Min, /*!< Continuous */ + t_Discrete, /*!< Discrete */ + t_Max = t_Discrete /*!< Maximum value */ + } TransformType; + + /// Constructor of class + MEHistogramTransform(); + /// Destructor of class + ~MEHistogramTransform(); + + /*! + * @brief Histogram stretching an image + * + * @param image Source image to stretch + * + * The function stretches the histogram of the given image with + * default parameters: process the color channels separately + * and continuously. + * + */ + + void HistogramStretch(MEImage& image); + + /*! + * @brief Histogram stretching with specified parameters + * + * @param image Source image to stretch + * @param time_mode Mode of the histogram stretching + * + * The function transformations the histogram of the image. + * There is some different possibilities to make the operation: + * + * - t_Continuous: The function always stretches the + * image at each call of the method. + * - t_Discrete: A histogram is calculated at the first + * call of the function and all further images will be + * stretched by this initial histogram. + * + */ + + void HistogramStretch(MEImage& image, TransformType time_mode); + + /*! + * @brief Histogram equalization on an image + * + * @param image Source image to equalize + * + * The source image is transformed by histogram + * equalization. + * + */ + + void HistogramEqualize(MEImage& image); + + /*! + * @brief Set the process mode of the histogram transformation + * + * @param new_channel_mode New mode of processing channels + * @param new_stretch_mode New mode of histogram stretching + * + * The process mode of histogram transformation can be + * set by this method. Two process modes are available for + * processing channels: + * + * - p_SeparateChannels: The class processes the color channels + * separately. + * - p_Average: The color channels are averaged + * in the histogram operations. + * + * Two process modes are usable for histogram stretching: + * s_OwnMode and s_GimpMode. See Histogram::Stretch() + * for more details. + * + */ + + void SetStretchProcessingMode(ProcessingType new_channel_mode, MEHistogram::StretchType new_stretch_mode); + +private: + /// Type of the process of histograms + ProcessingType ChannelMode; + /// Stretch mode + MEHistogram::StretchType StretchMode; + /// Histograms for red, green and blue color channels + MEHistogram RedChannel, GreenChannel, BlueChannel; + /// Histogram for average calculation + MEHistogram AverageChannel; + /// Continuous histogram stretch is done already + bool DiscreteStretchingDone; +}; + +/** @} */ + +#endif diff --git a/package_bgs/ck/MEImage.cpp b/package_bgs/ck/MEImage.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a625a9686d42ba27f63aa115ec9343ed2d63ea6 --- /dev/null +++ b/package_bgs/ck/MEImage.cpp @@ -0,0 +1,1472 @@ +/* + * This file is part of the AiBO+ project + * + * Copyright (C) 2005-2013 Csaba Kertész (csaba.kertesz@gmail.com) + * + * AiBO+ is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * AiBO+ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include "MEImage.hpp" + +#if defined(__MINGW32__) || defined(__MINGW64__) +#include <cv.h> +#include <highgui.h> +#else +#include <opencv/cv.h> +#include <opencv/highgui.h> +#endif + +#include "MEDefs.hpp" + +#define ME_CAST_TO_IPLIMAGE(image_ptr) ((IplImage*)image_ptr) +#define ME_RELEASE_IPLIMAGE(image_ptr) \ + cvReleaseImage((IplImage**)&image_ptr); \ + image_ptr = NULL; + +// RGB to YUV transform +const float RGBtoYUVMatrix[3][3] = + {{ 0.299, 0.587, 0.114 }, + { -0.147, -0.289, 0.436 }, + { 0.615, -0.515, -0.100 }}; + +// RGB to YIQ transform +const float RGBtoYIQMatrix[3][3] = + {{ 0.299, 0.587, 0.114 }, + { 0.596, -0.274, -0.322 }, + { 0.212, -0.523, 0.311 }}; + +MEImage::MEImage(int width, int height, int layers) : cvImg(NULL) +{ + _Init(width, height, layers); +} + + +MEImage::MEImage(const MEImage& other) : cvImg(NULL) +{ + _Copy(other); +} + + +MEImage::~MEImage() +{ + if (ME_CAST_TO_IPLIMAGE(cvImg)) + { + ME_RELEASE_IPLIMAGE(cvImg); + } +} + + +void MEImage::Clear() +{ + cvSetZero(ME_CAST_TO_IPLIMAGE(cvImg)); +} + + +void MEImage::GetLayer(MEImage& new_layer, int layer_number) const +{ + int LayerNumber = layer_number; + + if ((new_layer.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width) || + (new_layer.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height) || + (new_layer.GetLayers() != 1)) + { + new_layer.Realloc(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height, 1); + } + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels < LayerNumber) + { + printf("The given layer number is too large (%d > %d)\n", + LayerNumber, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + + LayerNumber = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; + } + if (LayerNumber <= 0) + { + printf("The given layer number is too small (%d <= 0)\n", LayerNumber); + LayerNumber = 1; + } + + cvSetImageCOI(ME_CAST_TO_IPLIMAGE(cvImg), LayerNumber); + cvCopy(ME_CAST_TO_IPLIMAGE(cvImg), (IplImage*)new_layer.GetIplImage(), NULL); + cvSetImageCOI(ME_CAST_TO_IPLIMAGE(cvImg), 0); +} + + +void MEImage::SetLayer(MEImage& layer, int layer_number) +{ + int LayerNumber = layer_number; + + if (layer.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + layer.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height) + { + printf("The dimensions of the layer and " + "destination image is different (%dx%d <> %dx%d)\n", + layer.GetWidth(), layer.GetHeight(), ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height); + return; + } + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels < LayerNumber) + { + printf("The given layer number is too large (%d > %d)\n", + LayerNumber, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + LayerNumber = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; + } + if (LayerNumber <= 0) + { + printf("The given layer number is too small (%d <= 0)\n", LayerNumber); + LayerNumber = 1; + } + if (layer.GetLayers() != 1) + { + printf("The layer image has not one color channel (1 != %d)\n", + layer.GetLayers()); + return; + } + cvSetImageCOI(ME_CAST_TO_IPLIMAGE(cvImg), LayerNumber); + cvCopy((IplImage*)layer.GetIplImage(), ME_CAST_TO_IPLIMAGE(cvImg), NULL); + cvSetImageCOI(ME_CAST_TO_IPLIMAGE(cvImg), 0); +} + + +void MEImage::CopyImageData(unsigned char* data) +{ + memcpy(ME_CAST_TO_IPLIMAGE(cvImg)->imageData, data, ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); +} + + +void* MEImage::GetIplImage() const +{ + return (void*)ME_CAST_TO_IPLIMAGE(cvImg); +} + + +void MEImage::SetIplImage(void* image) +{ + if (ME_CAST_TO_IPLIMAGE(cvImg)) + { + ME_RELEASE_IPLIMAGE(cvImg); + } + cvImg = cvCloneImage((IplImage*)image); + // Correct the origin of the image + if (ME_CAST_TO_IPLIMAGE(cvImg)->origin == 1) + { + MirrorVertical(); + ME_CAST_TO_IPLIMAGE(cvImg)->origin = 0; + } +} + + +bool MEImage::operator==(const MEImage& image) +{ + return Equal(image); +} + + +bool MEImage::operator!=(const MEImage& image) +{ + return !operator==(image); +} + + +MEImage& MEImage::operator=(const MEImage& other_image) +{ + if (&other_image == this) + return *this; + + _Copy(other_image); + return *this; +} + + +int MEImage::GetWidth() const +{ + return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->width : 0; +} + + +int MEImage::GetRowWidth() const +{ + return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->widthStep : 0; +} + + +int MEImage::GetHeight() const +{ + return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->height : 0; +} + + +int MEImage::GetLayers() const +{ + return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->nChannels : 0; +} + + +int MEImage::GetPixelDataNumber() const +{ + return ME_CAST_TO_IPLIMAGE(cvImg) ? GetWidth()*GetHeight()*GetLayers() : 0; +} + + +unsigned char* MEImage::GetImageData() const +{ + return ME_CAST_TO_IPLIMAGE(cvImg) ? (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData : NULL; +} + + +void MEImage::SetData(unsigned char* image_data, int width, int height, int channels) +{ + _Init(width, height, channels); + + for (int y = height-1; y >= 0; --y) + { + int Start = GetRowWidth()*y; + int Start2 = width*channels*y; + + memcpy(&ME_CAST_TO_IPLIMAGE(cvImg)->imageData[Start], &image_data[Start2], width*channels); + } +} + + +float MEImage::GetRatio() const +{ + return ME_CAST_TO_IPLIMAGE(cvImg) ? (float)ME_CAST_TO_IPLIMAGE(cvImg)->height/(float)ME_CAST_TO_IPLIMAGE(cvImg)->width : 0.0; +} + + +void MEImage::Realloc(int width, int height) +{ + Realloc(width, height, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); +} + + +void MEImage::Realloc(int width, int height, int layers) +{ + _Init(width, height, layers); +} + + +void MEImage::Resize(int new_width, int new_height) +{ + if (new_height < 1) + { + printf("Invalid new height: %d < 1\n", new_height); + return; + } + if (new_width < 1) + { + printf("Invalid new width: %d < 1\n", new_width); + return; + } + IplImage* TempImg = cvCreateImage(cvSize(new_width, new_height), 8, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + + cvResize(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_INTER_NN); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; +} + + +void MEImage::ResizeScaleX(int new_width) +{ + if (new_width < 1) + { + printf("Invalid new width: %d < 1\n", new_width); + return; + } + Resize(new_width, (int)((float)new_width*GetRatio())); +} + + +void MEImage::ResizeScaleY(int new_height) +{ + if (new_height < 1) + { + printf("Invalid new height: %d < 1\n", new_height); + return; + } + Resize((int)((float)new_height*1/GetRatio()), new_height); +} + + +void MEImage::MirrorHorizontal() +{ + cvFlip(ME_CAST_TO_IPLIMAGE(cvImg), NULL, 1); +} + + +void MEImage::MirrorVertical() +{ + cvFlip(ME_CAST_TO_IPLIMAGE(cvImg), NULL, 0); +} + + +void MEImage::Crop(int x1, int y1, int x2, int y2) +{ + int NewX1 = x1; + int NewY1 = y1; + int NewX2 = x2; + int NewY2 = y2; + + NewX1 = (NewX1 < 0) ? 0 : NewX1; + NewX1 = (NewX1 > ME_CAST_TO_IPLIMAGE(cvImg)->width) ? ME_CAST_TO_IPLIMAGE(cvImg)->width : NewX1; + NewY1 = (NewY1 < 0) ? 0 : NewY1; + NewY1 = (NewY1 > ME_CAST_TO_IPLIMAGE(cvImg)->height) ? ME_CAST_TO_IPLIMAGE(cvImg)->height : NewY1; + + NewX2 = (NewX2 < 0) ? 0 : NewX2; + NewX2 = (NewX2 > ME_CAST_TO_IPLIMAGE(cvImg)->width) ? ME_CAST_TO_IPLIMAGE(cvImg)->width : NewX2; + NewY2 = (NewY2 < 0) ? 0 : NewY2; + NewY2 = (NewY2 > ME_CAST_TO_IPLIMAGE(cvImg)->height) ? ME_CAST_TO_IPLIMAGE(cvImg)->height : NewY2; + + if ((NewX2-NewX1) <= 0) + { + printf("Invalid new width: %d <= 0\n", NewX2-NewX1); + return; + } + if ((NewY2-NewY1) <= 0) + { + printf("Invalid new height: %d <= 0\n", NewY2-NewY1); + return; + } + IplImage* TempImg = cvCreateImage(cvSize(NewX2-NewX1, NewY2-NewY1), 8, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + + cvSetImageROI(ME_CAST_TO_IPLIMAGE(cvImg), cvRect(NewX1, NewY1, NewX2-NewX1, NewY2-NewY1)); + cvCopy(ME_CAST_TO_IPLIMAGE(cvImg), TempImg); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; +} + + +void MEImage::CopyImageInside(int x, int y, MEImage& source_image) +{ + int NewX = x; + int NewY = y; + int PasteLengthX = source_image.GetWidth(); + int PasteLengthY = source_image.GetHeight(); + + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != source_image.GetLayers()) + { + if (source_image.GetLayers() == 1 && ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 3) + { + source_image.ConvertGrayscaleToRGB(); + } + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 1 && source_image.GetLayers() == 3) + { + source_image.ConvertToGrayscale(g_OpenCV); + } + } + if (NewX < 0) + NewX = 0; + if (NewX > ME_CAST_TO_IPLIMAGE(cvImg)->width) + NewX = ME_CAST_TO_IPLIMAGE(cvImg)->width; + if (NewY < 0) + NewY = 0; + if (NewY > ME_CAST_TO_IPLIMAGE(cvImg)->height) + NewY = ME_CAST_TO_IPLIMAGE(cvImg)->height; + if (NewX+PasteLengthX > ME_CAST_TO_IPLIMAGE(cvImg)->width) + PasteLengthX = ME_CAST_TO_IPLIMAGE(cvImg)->width-NewX; + if (NewY+PasteLengthY > ME_CAST_TO_IPLIMAGE(cvImg)->height) + PasteLengthY = ME_CAST_TO_IPLIMAGE(cvImg)->height-NewY; + + if (PasteLengthX != source_image.GetWidth() || + PasteLengthY != source_image.GetHeight()) + { + source_image.Resize(PasteLengthX, PasteLengthY); + } + cvSetImageROI(ME_CAST_TO_IPLIMAGE(cvImg), cvRect(NewX, NewY, PasteLengthX, PasteLengthY)); + cvCopy((IplImage*)source_image.GetIplImage(), ME_CAST_TO_IPLIMAGE(cvImg)); + cvResetImageROI(ME_CAST_TO_IPLIMAGE(cvImg)); +} + + +void MEImage::Erode(int iterations) +{ + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), + 8, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + + cvErode(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, NULL, iterations); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; +} + + +void MEImage::Dilate(int iterations) +{ + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), + 8, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + + cvDilate(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, NULL, iterations); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; +} + + +void MEImage::Smooth() +{ + SmoothAdvanced(s_Median, 3); +} + + +void MEImage::SmoothAdvanced(SmoothType filtermode, int filtersize) +{ + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + + switch (filtermode) + { + case s_Blur: + cvSmooth(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_BLUR, filtersize, filtersize, 0); + break; + case s_Median: + cvSmooth(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_MEDIAN, filtersize, 0, 0); + break; + case s_Gaussian: + cvSmooth(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_GAUSSIAN, filtersize, filtersize, 0); + break; + default: + cvSmooth(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_MEDIAN, filtersize, 0, 0); + break; + } + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; +} + + +void MEImage::Canny() +{ + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels > 1) + { + ConvertToGrayscale(g_OpenCV); + } + + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCanny(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, 800, 1100, 5); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; +} + + +void MEImage::Laplace() +{ + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 1) + { + ConvertToGrayscale(g_OpenCV); + } + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), + IPL_DEPTH_16S, 1); + cvLaplace(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, 3); + cvConvertScale(TempImg, ME_CAST_TO_IPLIMAGE(cvImg), 1, 0); + ME_RELEASE_IPLIMAGE(cvImg); +} + + +void MEImage::Quantize(int levels) +{ + if (levels <= 0) + { + printf("Level number is too small (%d <= 0)\n", levels); + return; + } + if (levels > 256) + { + printf("Level number is too large (%d > 256)\n", levels); + return; + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height-1; i >= 0; --i) + { + ImageData[i] = ImageData[i] / (256 / levels)*(256 / levels); + } +} + + +void MEImage::Threshold(int threshold_limit) +{ + if (threshold_limit < 0) + { + printf("Threshold number is too small (%d <= 0)\n", threshold_limit); + return; + } + if (threshold_limit > 255) + { + printf("Threshold number is too large (%d > 255)\n", threshold_limit); + return; + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height-1; i >= 0; --i) + { + if (ImageData[i] < threshold_limit) + { + ImageData[i] = 0; + } + } +} + + +void MEImage::AdaptiveThreshold() +{ + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 1) + { + ConvertToGrayscale(g_OpenCV); + } + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvAdaptiveThreshold(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, 25, + CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 7, -7); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; +} + + +void MEImage::ThresholdByMask(MEImage& mask_image) +{ + if (mask_image.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + mask_image.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height) + { + printf("Image properties are different\n"); + return; + } + if (mask_image.GetLayers() != 3 && ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 3) + { + mask_image.ConvertGrayscaleToRGB(); + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* MaskImageData = mask_image.GetImageData(); + + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height-1; i >= 0; --i) + { + if (MaskImageData[i] == 0) + { + ImageData[i] = 0; + } + } +} + + +void MEImage::ColorSpace(ColorSpaceConvertType mode) +{ + IplImage* TempImg = NULL; + unsigned char* ImageData = NULL; + int WidthStep = 0; + int RowStart = 0; + + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 1) + { + printf("No sense to convert: source image is greyscale\n"); + ConvertGrayscaleToRGB(); + } + switch (mode) + { + case csc_RGBtoXYZCIED65: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2XYZ); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_XYZCIED65toRGB: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_XYZ2RGB); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_RGBtoHSV: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2HSV); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_HSVtoRGB: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_HSV2RGB); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_RGBtoHLS: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2HLS); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_HLStoRGB: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_HLS2RGB); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_RGBtoCIELab: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2Lab); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_CIELabtoRGB: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_Lab2RGB); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_RGBtoCIELuv: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2Luv); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_CIELuvtoRGB: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_Luv2RGB); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_RGBtoYUV: + ComputeColorSpace(csc_RGBtoYUV); + break; + + case csc_RGBtoYIQ: + ComputeColorSpace(csc_RGBtoYIQ); + break; + + case csc_RGBtorgI: + ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + RowStart = 0; + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height-1; y >= 0; --y) + { + for (int x = (ME_CAST_TO_IPLIMAGE(cvImg)->width-1)*3; x >= 0; x -= 3) + { + int r = 0; + int g = 0; + int I = 0; + + I = (int)ImageData[RowStart+x]+(int)ImageData[RowStart+x+1]+(int)ImageData[RowStart+x+2]; + r = (int)((float)ImageData[RowStart+x] / I*255); + g = (int)((float)ImageData[RowStart+x+1] / I*255); + ImageData[RowStart+x] = (unsigned char)r; + ImageData[RowStart+x+1] = (unsigned char)g; + ImageData[RowStart+x+2] = (unsigned char)(I / 3); + } + RowStart += WidthStep; + } + break; + + default: + break; + } +} + + +void MEImage::ConvertToGrayscale(GrayscaleType grayscale_mode) +{ + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 1) + { + printf("Image is already grayscale\n"); + return; + } + IplImage* TempImg = NULL; + unsigned char* ImgData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* ImageData = NULL; + + switch (grayscale_mode) + { + case g_Average: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, 1); + ImageData = (unsigned char*)TempImg->imageData; + + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height-3; i >= 0; i -= 3) + { + ImageData[i / 3] = (ImgData[i]+ImgData[i+1]+ImgData[i+2]) / 3; + } + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case g_OpenCV: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, 1); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2GRAY); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + default: + break; + } +} + + +void MEImage::ConvertGrayscaleToRGB() +{ + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 1) + { + return; + } + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, 3); + + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_GRAY2RGB); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; +} + + +void MEImage::ConvertBGRToRGB() +{ + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 3) + { + return; + } + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), ME_CAST_TO_IPLIMAGE(cvImg), CV_RGB2BGR); +} + + +void MEImage::LBP(LBPType mode) +{ + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels > 1) + { + ConvertToGrayscale(g_OpenCV); + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, 1); + unsigned char* TempImgData = (unsigned char*)TempImg->imageData; + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int WidthStep_2 = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*2; + + cvSetZero(TempImg); + switch (mode) + { + case lbp_Normal: + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*(ME_CAST_TO_IPLIMAGE(cvImg)->height-2)-1; i >= ME_CAST_TO_IPLIMAGE(cvImg)->widthStep+1; --i) + { + TempImgData[i] = + (ImageData[i] <= ImageData[i-ME_CAST_TO_IPLIMAGE(cvImg)->widthStep-1])+ + ((ImageData[i] <= ImageData[i-ME_CAST_TO_IPLIMAGE(cvImg)->widthStep])*2)+ + ((ImageData[i] <= ImageData[i-ME_CAST_TO_IPLIMAGE(cvImg)->widthStep+1])*4)+ + ((ImageData[i] <= ImageData[i-1])*8)+ + ((ImageData[i] <= ImageData[i+1])*16)+ + ((ImageData[i] <= ImageData[i+ME_CAST_TO_IPLIMAGE(cvImg)->widthStep-1])*32)+ + ((ImageData[i] <= ImageData[i+ME_CAST_TO_IPLIMAGE(cvImg)->widthStep])*64)+ + ((ImageData[i] <= ImageData[i+ME_CAST_TO_IPLIMAGE(cvImg)->widthStep+1])*128); + } + break; + + case lbp_Special: + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*(ME_CAST_TO_IPLIMAGE(cvImg)->height-3)-2; i >= ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*2+2; --i) + { + int CenterPixel = (ImageData[i+1]+ImageData[i-1]+ + ImageData[i-WidthStep]+ImageData[i+WidthStep]) / 4; + TempImgData[i] = ((CenterPixel <= (ImageData[i-(WidthStep_2)-2]+ + ImageData[i-(WidthStep_2)-1]+ + ImageData[i-WidthStep-2]+ + ImageData[i-WidthStep-1]) / 4))+ + ((CenterPixel <= (ImageData[i-WidthStep]+ + ImageData[i-(WidthStep_2)]) / 2)*2)+ + ((CenterPixel <= ((ImageData[i-(WidthStep_2)+2]+ + ImageData[i-(WidthStep_2)+1]+ + ImageData[i-WidthStep+2]+ + ImageData[i-WidthStep+1]) / 4))*4)+ + ((CenterPixel <= (ImageData[i-1]+ + ImageData[i-2]) / 2)*8)+ + ((CenterPixel <= (ImageData[i+1]+ + ImageData[i+2]) / 2)*16)+ + ((CenterPixel <= ((ImageData[i+(WidthStep_2)-2]+ + ImageData[i+(WidthStep_2)-1]+ + ImageData[i+WidthStep-2]+ + ImageData[i+WidthStep-1]) / 4))*32)+ + ((CenterPixel <= (ImageData[i+WidthStep]+ + ImageData[i-WidthStep_2]) / 2)*64)+ + ((CenterPixel <= ((ImageData[i+(WidthStep_2)+2]+ + ImageData[i+(WidthStep_2)+1]+ + ImageData[i+WidthStep+2]+ + ImageData[i+WidthStep+1]) / 4))*128); + } + break; + + default: + break; + } + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; +} + + +void MEImage::Binarize(int threshold) +{ + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep-1; i >= 0; --i) + { + if (ImageData[i] >= threshold) + { + ImageData[i] = 255; + } else { + ImageData[i] = 0; + } + } +} + + +void MEImage::Subtract(MEImage& source, SubtractModeType mode) +{ + if (source.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + source.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || + source.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) + { + printf("Image properties are different.\n"); + return; + } + unsigned char* ImageData = NULL; + unsigned char* DstData = NULL; + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int RowStart = 0; + + switch (mode) + { + case sub_Normal: + ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + DstData = source.GetImageData(); + RowStart = 0; + + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height-1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels-1; x >= 0; --x) + { + ImageData[RowStart+x] = + ImageData[RowStart+x]-DstData[RowStart+x] < 0 ? 0 : + ImageData[RowStart+x]-DstData[RowStart+x]; + } + RowStart += WidthStep; + } + break; + + case sub_Absolut: + ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + DstData = source.GetImageData(); + RowStart = 0; + + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height-1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels-1; x >= 0; --x) + { + ImageData[RowStart+x] = ImageData[RowStart+x]- + DstData[RowStart+x] < 0 ? -ImageData[RowStart+x]+ + DstData[RowStart+x] : ImageData[RowStart+x]-DstData[RowStart+x]; + } + RowStart += WidthStep; + } + break; + + default: + break; + } +} + + +void MEImage::Multiple(MEImage& source, MultiplicationType mode) +{ + if (source.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + source.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || + source.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) + { + printf("Image properties are different.\n"); + return; + } + float Result = 0.0; + IplImage* TempImg = NULL; + unsigned char* ImageData = NULL; + unsigned char* ImageData2 = NULL; + unsigned char* ImageData3 = NULL; + unsigned char* DstData = NULL; + + switch (mode) + { + case m_Normal: + Result = 0; + ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + DstData = source.GetImageData(); + + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep-1; i >= 0; --i) + { + if ((ImageData[i] >= 128) && (DstData[i] >= 128)) + { + Result = (float)ImageData[i]/128*(float)DstData[i]/128; + + if (Result >= 1) + { + ImageData[i] = 255; + } else { + ImageData[i] = 0; + } + } else { + ImageData[i] = 0; + } + } + break; + + case m_Neighbourhood: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + ImageData2 = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + DstData = source.GetImageData(); + ImageData3 = (unsigned char*)TempImg->imageData; + + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height-1; y >= 0; --y) + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width-1; x >= 0; --x) + for (int l = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels-1; l >= 0; --l) + { + if (((DstData[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+ + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+l] == 255) || + (ImageData2[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+ + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+l] == 255)) && + (NeighbourhoodCounter(x-2, y-2, n_5x5) > 3) && + (source.NeighbourhoodCounter(x-2, y-2, n_5x5) > 3)) + { + ImageData3[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+ + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+l] = 255; + } + } + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + default: + break; + } +} + + +void MEImage::Addition(MEImage& source, AdditionType mode) +{ + if (source.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + source.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || + source.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) + { + printf("Image properties are different.\n"); + return; + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* DstData = source.GetImageData(); + + switch (mode) + { + case a_Average: + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep-1; i >= 0; --i) + { + ImageData[i] = (ImageData[i]+DstData[i]) / 2; + } + break; + + case a_Union: + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep-1; i >= 0; --i) + { + if (DstData[i] > ImageData[i]) + { + ImageData[i] = DstData[i]; + } + } + break; + + default: + break; + } +} + + +void MEImage::EliminateSinglePixels() +{ + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* DstData = (unsigned char*)TempImg->imageData; + int sum = 0; + int xy = 0; + int ywidth = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height-1; y >= 0; --y) + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width-1; x >= 0; --x) + { + xy = y*ywidth+x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; + + for (int l = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels-1; l >= 0; --l) + { + if ((ImageData[xy+l] > 0) && (x > 0) && (y > 0) && (x < ME_CAST_TO_IPLIMAGE(cvImg)->width-1) && (y < ME_CAST_TO_IPLIMAGE(cvImg)->height-1)) + { + sum = (ImageData[xy-ywidth-ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+l] > 0)+ + (ImageData[xy-ywidth+l] > 0)+ + (ImageData[xy-ywidth+ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+l] > 0)+ + (ImageData[xy-ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+l] > 0)+ + (ImageData[xy+ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+l] > 0)+ + (ImageData[xy+ywidth-ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+l] > 0)+ + (ImageData[xy+ywidth+l] > 0)+ + (ImageData[xy+ywidth+ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+l] > 0); + + if (sum > 3) + { + DstData[xy+l] = 255; + } else { + DstData[xy+l] = 0; + } + } else { + DstData[xy+l] = 0; + } + } + } + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; +} + + +float MEImage::DifferenceAreas(MEImage& reference, int difference) const +{ + if (reference.GetWidth() != GetWidth() || + reference.GetHeight() != GetHeight() || + reference.GetLayers() != GetLayers()) + { + printf("Image dimensions or channels are different\n"); + return -1.0; + } + float PixelDiff = 0.0; + int Pixels = 0; + unsigned char* OrigImgData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* RefImgData = reference.GetImageData(); + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int RowStart = 0; + + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height-1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels-1; x >= 0; --x) + { + if (abs(OrigImgData[RowStart+x]-RefImgData[RowStart+x]) > difference) + Pixels++; + } + RowStart += WidthStep; + } + PixelDiff = (float)Pixels / (ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep)*100; + return PixelDiff; +} + + +int MEImage::AverageDifference(MEImage& reference) const +{ + if (reference.GetWidth() != GetWidth() || + reference.GetHeight() != GetHeight() || + reference.GetLayers() != GetLayers()) + { + printf("Image dimensions or channels are different\n"); + return -1; + } + int Difference = 0; + unsigned char* OrigImgData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* RefImgData = reference.GetImageData(); + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int RowStart = 0; + + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height-1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels-1; x >= 0; --x) + { + Difference += abs(OrigImgData[RowStart+x]-RefImgData[RowStart+x]); + } + RowStart += WidthStep; + } + Difference = Difference / (ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep); + return Difference; +} + + +void MEImage::Minimum(MEImage& image) +{ + if (image.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + image.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || + image.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) + { + printf("Image properties are different\n"); + return; + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* SecData = image.GetImageData(); + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int RowStart = 0; + + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height-1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels-1; x >= 0; --x) + { + ImageData[RowStart+x] = ImageData[RowStart+x] > SecData[RowStart+x] ? + SecData[RowStart+x] : ImageData[RowStart+x]; + } + RowStart += WidthStep; + } +} + + +float MEImage::AverageBrightnessLevel() const +{ + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int RowStart = 0; + int BrightnessLevel = 0; + + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height-1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels-1; x >= 0; --x) + { + BrightnessLevel += (int)ImageData[RowStart+x]; + } + RowStart += WidthStep; + } + return BrightnessLevel / (GetWidth()*GetHeight()*GetLayers()); +} + + +bool MEImage::Equal(const MEImage& reference) const +{ + return Equal(reference, 1); +} + + +bool MEImage::Equal(const MEImage& reference, int maxabsdiff) const +{ + bool Ret = true; + + if (reference.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + reference.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || + reference.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) + { + printf("Image properties are different\n"); + return false; + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* RefData = reference.GetImageData(); + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int RowStart = 0; + + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height-1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels-1; x >= 0; --x) + { + if (abs(ImageData[RowStart+x]-RefData[RowStart+x]) >= maxabsdiff) + { + Ret = false; + return Ret; + } + } + RowStart += WidthStep; + } + return Ret; +} + + +unsigned char MEImage::GrayscalePixel(int x, int y) const +{ + int NewX = x; + int NewY = y; + + NewX = NewX < 0 ? 0 : NewX; + NewX = NewX > ME_CAST_TO_IPLIMAGE(cvImg)->width-1 ? ME_CAST_TO_IPLIMAGE(cvImg)->width-1 : NewX; + NewY = NewY < 0 ? 0 : NewY; + NewY = NewY > ME_CAST_TO_IPLIMAGE(cvImg)->height-1 ? ME_CAST_TO_IPLIMAGE(cvImg)->height-1 : NewY; + + float Sum = 0; + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + + for (int l = 0; l < ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; l++) + { + Sum = Sum + (int)ImageData[NewY*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+NewX*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+l]; + } + Sum = Sum / ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; + return (unsigned char)(Sum); +} + + +int MEImage::NeighbourhoodCounter(int startx, int starty, + NeighbourhoodType neighbourhood) const +{ + int IterX = 0; + int IterY = 0; + int Counter = 0; + + // Determine the iteration numbers + switch (neighbourhood) + { + case n_2x2: + IterX = 2; + IterY = 2; + break; + + case n_3x3: + IterX = 3; + IterY = 3; + break; + + case n_3x2: + IterX = 2; + IterY = 3; + break; + + case n_5x5: + IterX = 5; + IterY = 5; + break; + + case n_7x7: + IterX = 7; + IterY = 7; + break; + + default: + IterX = 3; + IterY = 3; + break; + } + + int NewStartX = startx ; + int NewStartY = starty; + + NewStartX = startx < 0 ? 0 : startx; + NewStartX = startx >= ME_CAST_TO_IPLIMAGE(cvImg)->width-IterX ? ME_CAST_TO_IPLIMAGE(cvImg)->width-IterX-1 : startx; + NewStartY = starty < 0 ? 0 : starty; + NewStartY = starty >= ME_CAST_TO_IPLIMAGE(cvImg)->height-IterY ? ME_CAST_TO_IPLIMAGE(cvImg)->height-IterY-1 : starty; + + int Value = 0; + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + + for (int x = NewStartX; x < NewStartX+IterX; x++) + for (int y = NewStartY; y < NewStartY+IterY; y++) + { + Value = ((int)ImageData[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels]+ + (int)ImageData[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+1]+ + (int)ImageData[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels+2]) / 3; + + if (Value == 255) + { + Counter++; + } + } + return Counter; +} + + +void MEImage::GradientVector(bool smooth, int x, int y, int mask_size, int& result_x, int& result_y) +{ + int Results[8]; + int DiagonalMaskSize = (int)((float)mask_size / sqrt(2)); + + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels > 1) + { + ConvertToGrayscale(g_OpenCV); + } + if (smooth) + { + SmoothAdvanced(s_Gaussian, mask_size*3-(mask_size*3-1) % 2); + } + + Results[0] = (int)GrayscalePixel(x,y)-(int)GrayscalePixel(x,y-mask_size); + Results[1] = (int)GrayscalePixel(x,y)-(int)GrayscalePixel(x+DiagonalMaskSize,y-DiagonalMaskSize); + Results[2] = (int)GrayscalePixel(x,y)-(int)GrayscalePixel(x+mask_size,y); + Results[3] = (int)GrayscalePixel(x,y)-(int)GrayscalePixel(x+DiagonalMaskSize,y+DiagonalMaskSize); + Results[4] = (int)GrayscalePixel(x,y)-(int)GrayscalePixel(x,y+mask_size); + Results[5] = (int)GrayscalePixel(x,y)-(int)GrayscalePixel(x-DiagonalMaskSize,y+DiagonalMaskSize); + Results[6] = (int)GrayscalePixel(x,y)-(int)GrayscalePixel(x-mask_size,y); + Results[7] = (int)GrayscalePixel(x,y)-(int)GrayscalePixel(x+DiagonalMaskSize,y-DiagonalMaskSize); + + result_x = (DiagonalMaskSize*Results[1]+mask_size*Results[2]+ + DiagonalMaskSize*Results[3]-DiagonalMaskSize*Results[5]- + mask_size*Results[6]+DiagonalMaskSize*Results[7]) / 256; + result_y = (-mask_size*Results[0]-DiagonalMaskSize*Results[1]+ + DiagonalMaskSize*Results[3]+mask_size*Results[4]+ + DiagonalMaskSize*Results[5]-DiagonalMaskSize*Results[7]) / 256; +} + + +void MEImage::GradientVisualize(int vector_x, int vector_y) +{ + if (vector_x <= 0) + { + printf("vectorx: wrong parameter (%d <= 0)\n", vector_x); + return; + } + if (vector_y <= 0) + { + printf("vectory: wrong parameter (%d <= 0)\n", vector_y); + return; + } + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels > 1) + { + ConvertToGrayscale(g_OpenCV); + } + + int masksize = (ME_CAST_TO_IPLIMAGE(cvImg)->width < ME_CAST_TO_IPLIMAGE(cvImg)->height) ? + ME_CAST_TO_IPLIMAGE(cvImg)->width / (vector_x+1) : + ME_CAST_TO_IPLIMAGE(cvImg)->height / (vector_y+1); + + SmoothAdvanced(s_Gaussian, masksize*2-1); + for (int i = 1; i < vector_x; i++) + for (int i1 = 1; i1 < vector_y; i1++) + { + int Resultx = 0, Resulty = 0; + int x = (int)(((float)ME_CAST_TO_IPLIMAGE(cvImg)->width*i / (vector_x))); + int y = (int)(((float)ME_CAST_TO_IPLIMAGE(cvImg)->height*i1 / (vector_y))); + + GradientVector(false, x, y, (int)(0.707*masksize), Resultx, Resulty); + + CvPoint Point1; + CvPoint Point2; + + Point1.x = x-Resultx / 2; + Point1.y = y-Resulty / 2; + Point2.x = x+Resultx / 2; + Point2.y = y+Resulty / 2; + cvLine(ME_CAST_TO_IPLIMAGE(cvImg), Point1, Point2, CV_RGB(255, 255, 255), 1, 8); + } +} + + +bool MEImage::_Copy(const MEImage& other_image) +{ + if (&other_image == this) + return true; + + if (ME_CAST_TO_IPLIMAGE(cvImg)) + { + ME_RELEASE_IPLIMAGE(cvImg); + } + cvImg = cvCloneImage((IplImage*)other_image.GetIplImage()); + return true; +} + + +void MEImage::_Init(int width, int height, int layers) +{ + if (width < 1) + { + printf("Given width for the new image is too small (%d <= 0)\n", width); + return; + } + if (height < 1) + { + printf("Given height for the new image is (%d <= 0)\n", height); + return; + } + if ((layers != 1) && (layers != 3)) + { + printf("Only one or three (%d != 1 or 3) layer allowed\n", layers); + return; + } + + if (ME_CAST_TO_IPLIMAGE(cvImg)) + { + ME_RELEASE_IPLIMAGE(cvImg); + } + cvImg = cvCreateImage(cvSize(width, height), 8, layers); +} + + +void MEImage::ComputeColorSpace(ColorSpaceConvertType mode) +{ + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 3) + { + printf("Image has to have three color channels (%d != 3)\n", ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + return; + } + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + + for (int i = 0; i < 3; i++) + for (int i1 = 0; i1 < 3; i1++) + { + if (mode == csc_RGBtoYUV) + TransformMatrix[i][i1] = RGBtoYUVMatrix[i][i1]; + if (mode == csc_RGBtoYIQ) + TransformMatrix[i][i1] = RGBtoYIQMatrix[i][i1]; + } + float x = 0.0; + float y = 0.0; + float z = 0.0; + float xmin = 0.0; + float xmax = 0.0; + float ymin = 0.0; + float ymax = 0.0; + float zmin = 0.0; + float zmax = 0.0; + + if (mode == csc_RGBtoYUV) + { + xmin = 0.0; + xmax = 255.0; + ymin = -111.18; + ymax = 111.18; + zmin = -156.825; + zmax = 156.825; + } + if (mode == csc_RGBtoYIQ) + { + xmin = 0.0; + xmax = 255.0; + ymin = -151.98; + ymax = 151.98; + zmin = -133.365; + zmax = 133.365; + } + unsigned char* SrcData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* DstData = (unsigned char*)TempImg->imageData; + + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height-1; i >= 0; i-=3) + { + x = (float)SrcData[i]*TransformMatrix[0][0]+ + (float)SrcData[i+1]*TransformMatrix[0][1]+ + (float)SrcData[i+2]*TransformMatrix[0][2]; + y = (float)SrcData[i]*TransformMatrix[1][0]+ + (float)SrcData[i+1]*TransformMatrix[1][1]+ + (float)SrcData[i+2]*TransformMatrix[1][2]; + z = (float)SrcData[i]*TransformMatrix[2][0]+ + (float)SrcData[i+1]*TransformMatrix[2][1]+ + (float)SrcData[i+2]*TransformMatrix[2][2]; + + x = xmax-xmin != 0.0 ? 255.0 : (x-xmin) / (xmax-xmin)*255.0; + y = ymax-ymin != 0.0 ? 255.0 : (y-xmin) / (ymax-ymin)*255.0; + z = zmax-zmin != 0.0 ? 255.0 : (z-xmin) / (zmax-zmin)*255.0; + + DstData[i] = (unsigned char)MEBound(0, (int)x, 255); + DstData[i+1] = (unsigned char)MEBound(0, (int)y, 255); + DstData[i+2] = (unsigned char)MEBound(0, (int)z, 255); + } + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; +} diff --git a/package_bgs/ck/MEImage.hpp b/package_bgs/ck/MEImage.hpp new file mode 100644 index 0000000000000000000000000000000000000000..41ada3ba71e086fd1b6ef0d1517f7ff34261f7bb --- /dev/null +++ b/package_bgs/ck/MEImage.hpp @@ -0,0 +1,999 @@ +/* + * This file is part of the AiBO+ project + * + * Copyright (C) 2005-2013 Csaba Kertész (csaba.kertesz@gmail.com) + * + * AiBO+ is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * AiBO+ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef MEImage_H +#define MEImage_H + +/** + * @addtogroup mindeye + * @{ + */ + +/** + * MEImage + * @brief Basic image functions + */ +class MEImage +{ +public: + /// Types of LBP operator + typedef enum { + lbp_Min = 0, /*!< Minimum value */ + lbp_Normal = lbp_Min, /*!< Normal LBP pattern */ + lbp_Special, /*!< Special LBP pattern */ + lbp_Max = lbp_Special /*!< Maximum value */ + } LBPType; + + /// Types of image subtraction + typedef enum { + sub_Min = 0, /*!< Minimum value */ + sub_Normal = sub_Min, /*!< Normal */ + sub_Absolut, /*!< Absolut */ + sub_Max = sub_Absolut /*!< Maximum value */ + } SubtractModeType; + + /// Types of image addition + typedef enum { + a_Min = 0, /*!< Minimum value */ + a_Average = a_Min, /*!< Average */ + a_Union, /*!< Union */ + a_Max = a_Union /*!< Maximum value */ + } AdditionType; + + /// Types of image multiplication + typedef enum { + m_Min = 0, /*!< Minimum value */ + m_Normal = m_Min, /*!< Normal */ + m_Neighbourhood, /*!< Neighbourhood */ + m_Max = m_Neighbourhood /*!< Maximum value */ + } MultiplicationType; + + /// Types of grayscale conversation + typedef enum { + g_Min = 0, /*!< Minimum value */ + g_Average = g_Min, /*!< Average */ + g_OpenCV, /*!< OpenCV */ + g_Max = g_OpenCV /*!< Maximum value */ + } GrayscaleType; + + /// Types of pixel neighbourhoods + typedef enum { + n_Min = 0, /*!< Minimum value */ + n_2x2 = n_Min, /*!< 2x2 */ + n_3x2, /*!< 3x2 */ + n_3x3, /*!< 3x3 */ + n_5x5, /*!< 5x5 */ + n_7x7, /*!< 7x7 */ + n_Max = n_7x7 /*!< Maximum value */ + } NeighbourhoodType; + + /// Types of special pixels + typedef enum { + p_Min = 0, /*!< Minimum value */ + p_Minimum = p_Min, /*!< Minimum */ + p_Maximum, /*!< Maximum */ + p_Counter, /*!< Counter */ + p_Max = p_Counter /*!< Maximum value */ + } PixelType; + + /// Types of smooth operation + typedef enum { + s_Min = 0, /*!< Minimum value */ + s_Blur = s_Min, /*!< Blur */ + s_Gaussian, /*!< Gaussian */ + s_Median, /*!< Medium */ + s_Max = s_Median /*!< Maximum value */ + } SmoothType; + + /// Types of color space conversions + typedef enum { + csc_Min = 0, /*!< Minimum value */ + csc_RGBtoXYZCIED65 = csc_Min, /*!< RGB to XYZCIED65 */ + csc_XYZCIED65toRGB, /*!< XYZCIED65 to RGB */ + csc_RGBtoHSV, /*!< RGB to HSV */ + csc_HSVtoRGB, /*!< HSV to RGB */ + csc_RGBtoHLS, /*!< RGB to HLS */ + csc_HLStoRGB, /*!< HLS to RGB */ + csc_RGBtoCIELab, /*!< RGB to CIELab */ + csc_CIELabtoRGB, /*!< CIELab to RGB */ + csc_RGBtoCIELuv, /*!< RGB to CIELuv */ + csc_CIELuvtoRGB, /*!< CIELuv to RGB */ + csc_RGBtoYUV, /*!< RGB to YUV */ + csc_RGBtoYIQ, /*!< RGB to YIQ */ + csc_RGBtorgI, /*!< RGB to rgI */ + csc_Max = csc_RGBtorgI /*!< Maximum value */ + } ColorSpaceConvertType; + + /*! + * @brief Class constructor + * + * @param width Image width + * @param height Image height + * @param layers Layers + * + * Class constructor with the possibility to specify the image width, + * height and the layers. The default options are 16x16x1. + * + */ + + MEImage(int width = 16, int height = 16, int layers = 1); + + /*! + * @brief Class constructor + * + * @param other Other image + * + * Class constructor with the possibility to specify the image width, + * height and the layers. The default options are 16x16x1. + * + */ + + MEImage(const MEImage& other); + /// Destructor of class + ~MEImage(); + + /* + ------------------------------------------------------------------- + Basic functions + ------------------------------------------------------------------- + */ + + /*! + * @brief Clear image + * + * This function clears image by filling all image data with zero + * value. + * + */ + + void Clear(); + + /*! + * @brief Get an color layer of image + * + * @param new_layer new image of layer + * @param layernumber number of layer which will be copied + * + * Copy an image layer (R, G or B) to @a new_layer image. @a new_layer has to + * have only one color layer (greyscale). If @a new_layer is not + * greyscale or it has got different width or height like source image + * than function reallocates it with appropriate features before + * copying image data. + * + */ + + void GetLayer(MEImage& new_layer, int layernumber) const; + + /*! + * @brief Copy a new color layer to image + * + * @param new_layer image data of new color layer + * @param layernumber number of layer where image data will copy + * + * Copy a new image layer from @a new_layer image. @a new_layer has to + * have only one color layer (greyscale). If @a new_layer is not + * greyscale or it has got different width or height like source image + * than function halts with an error message. + * + */ + + void SetLayer(MEImage& new_layer, int layernumber); + + /*! + * @brief Copy image data to a pointer + * + * @param data pointer where image data will be copied + * + * Function in order to acquire image data to an external + * (unsigned char*) pointer. + * + */ + + void CopyImageData(unsigned char* data); + + /*! + * @brief Get a pointer to the internal IplImage + * + * @return Pointer to the IplImage + * + * This function returns the internal IplImage of the class. The + * image data can not be modified. + * + */ + + void* GetIplImage() const; + + /*! + * @brief Set the internal IplImage + * + * @param image Pointer to the IplImage + * + * This function sets the internal IplImage of the class. + * + */ + + void SetIplImage(void* image); + + /*! + * @brief Handle operator == for MEImage + * + * @param image image to check + * + * @return true if the images are equal otherwise false. + * + * The operator checks the equality of two images. + * + */ + + bool operator==(const MEImage& image); + + /*! + * @brief Handle operator != for MEImage + * + * @param image image to check + * + * @return true if the images are not equal otherwise false. + * + * The operator checks the non-equality of two images. + * + */ + + bool operator!=(const MEImage& image); + + /*! + * @brief Handle operator = for MEImage + * + * @param other_image image to copy operation + * + * @return Reference to the actual instance. + * + * Copy image data to @a other_image image. Function calls only + * _Copy() directly. + * + */ + + MEImage& operator=(const MEImage& other_image); + + /*! + * @brief Get the width of the image + * + * @return Width of the image + * + * Get the width of the image. + * + */ + + int GetWidth() const; + + /*! + * @brief Get the height of the image + * + * @return Height of the image + * + * Get the height of the image. + */ + + int GetHeight() const; + + /*! + * @brief Get the length of a pixel row of the image + * + * @return Length of a pixel row + * + * Get the row width of the image. + * + */ + + int GetRowWidth() const; + + /*! + * @brief Get the number of color layers of the image + * + * @return Number of color layer of the image + * + * Get the number of color layer of the image. + * + */ + + int GetLayers() const; + + /*! + * @brief Get the number of the image pixel data + * + * @return Number of the image pixel data + * + * Get the number of the image pixel data. + * + */ + + int GetPixelDataNumber() const; + + /*! + * @brief Get the image data + * + * @return Pointer to the image data + * + * Get a pointer to the image. + * + */ + + unsigned char* GetImageData() const; + + /*! + * @brief Set the image data + * + * @param image_data New image data + * @param width New image width + * @param height New image height + * @param channels New image color channels + * + * Get a pointer to the image. + * + */ + + void SetData(unsigned char* image_data, int width, int height, int channels); + + /*! + * @brief Get ratio of image width and height + * + * @return float ratio of image dimensions + * + * Function calculates ratio of image width and height with + * following equation: ratio = height / width. + */ + + float GetRatio() const; + + /* + ------------------------------------------------------------------- + Basic image manipulation + ------------------------------------------------------------------- + */ + + /*! + * @brief Reallocate image data + * + * @param width New width of the image + * @param height New height of the image + * + * Image data will be reallocated with new dimensions @a width + * and @a height. Number of color channels is not changed. + * + */ + + void Realloc(int width, int height); + + /*! + * @brief Reallocate image data + * + * @param width New width of the image + * @param height New height of the image + * @param layers Number of color channels of the image + * + * Image data will be reallocated with new dimensions @a width, + * @a height and new number of color channels @a layers. + * + */ + + void Realloc(int width, int height, int layers); + + /*! + * @brief Resize image + * + * @param newwidth new width of image + * @param newheight new height of image + * + * Resize image to @a newwidth width and @a newheight + * height dimensions. + * + */ + + void Resize(int newwidth, int newheight); + + /*! + * @brief Resize image with new width + * + * @param newwidth new width of image + * + * Image is resized with only new width information therefore + * fit to original ratio. + * + */ + + void ResizeScaleX(int newwidth); + + /*! + * @brief Resize image with new height + * + * @param newheight new height of image + * + * Image is resized with only new height information therefore + * fit to original ratio. + * + */ + + void ResizeScaleY(int newheight); + + /*! + * @brief Reverse image in horizontal direction + * + * Function makes a mirror transformation on image in horizontal + * direction. + * + */ + + void MirrorHorizontal(); + + /*! + * @brief Reverse image in vertical direction + * + * Function makes a mirror transformation on image in vertical + * direction. + * + */ + + void MirrorVertical(); + + /*! + * @brief Crop image + * + * @param x1, y1 coordinates of top-left point of rectangle + * @param x2, y2 coordinates of bottom-right point of rectangle + * + * Crop the image in a smaller piece whose dimensions are + * specified as a rectangle. Top-left and bottom-right + * coordinates of rectangle are (x1, y1) and (x2, y2) wherefrom + * comes that the width of the new image is x2-x1 and height is + * y2-y1. + * + */ + + void Crop(int x1, int y1, int x2, int y2); + + /*! + * @brief Copy all image data from an other picture + * + * @param x0 x coordinate to paste the new image data + * @param y0 y coordinate to paste the new image data + * @param source_image source image + * + * Function copies all image data from @a source_image + * to the given coordinate (x0,y0). + * + */ + + void CopyImageInside(int x0, int y0, MEImage& source_image); + + /* + ------------------------------------------------------------------- + Image processing functions + ------------------------------------------------------------------- + */ + + /*! + * @brief Erode function + * + * @param iterations iterations of erode method + * + * Method makes an erode filter on an image @a iterations + * times with standard 3x3 matrix size. + * + */ + + void Erode(int iterations); + + /*! + * @brief Dilate function + * + * @param iterations iterations of dilate method + * + * Method makes an dilate filter on an image + * @a iterations times with standard 3x3 matrix size. + * + */ + + void Dilate(int iterations); + + /*! + * @brief Smooth function + * + * Method smooths with median filter and standard 3x3 matrix size. + * (Median filter works fine and fast.) + * + */ + + void Smooth(); + + /*! + * @brief Smooth function with defined parameters + * + * @param filtermode type of smooth method + * @param filtersize the size of the convolution matrix + * + * Method smooths with median filter and the given matrix + * size (@a filtersize x @a filtersize). There are more + * types of smooth function (@a filtermode): + * + * - s_Blur: Blur filter. + * - s_Gaussian: Gaussian filter. + * - s_Median: Median filter. + * + */ + + void SmoothAdvanced(SmoothType filtermode, int filtersize); + + /*! + * @brief Canny function + * + * Canny operator is usable for edge detection. Function makes + * this operation with standard 3x3 matrix + * size. Canny has two threshold value which are set to zero + * in this function by default. + * + */ + + void Canny(); + + /*! + * @brief Laplace function + * + * Laplace operator is usable for edge detection like Canny. + * This function makes a laplace filter with + * standard 3x3 matrix size. After calculating destination image will + * be converted from 16 bit back to 8 bit. + * + */ + + void Laplace(); + + /*! + * @brief Image quantisation + * + * @param levels level of quantisation + * + * Quantize an image with @a levels level. It means by 16 + * level color range 0-255 quantizes to 0-15, by 4 level to 0-63 etc. + * + */ + + void Quantize(int levels); + + /*! + * @brief Threshold a picture + * + * @param threshold_limit limit for threshold + * + * Threshold an image with @a threshold_limit limit. Value range + * of @a threshold_limit is between 0-255. E.g. by value 160 functions + * will eliminate all color values under 160 with black color + * (color value zero). + * + */ + + void Threshold(int threshold_limit); + + /*! + * @brief Adaptive threshold function + * + * This function does adaptive threshold function. + * + */ + + void AdaptiveThreshold(); + + /*! + * @brief Threshold a picture by a mask image + * + * @param mask_image mask image for thresholding + * + * Threshold an image with a mask image @a mask_image. + * + */ + + void ThresholdByMask(MEImage& mask_image); + + /*! + * @brief Convert an image into a new color space + * + * @param transformation Definition of color transformation + * + * This function converts an image from a specified color space + * to an other. + * Current supported conversions (@a transformation): + * - csc_RGBtoXYZCIED65: RGB to XYZ (D65 reference light), + * - csc_XYZCIED65toRGB: XYZ to RGB (D65 reference light), + * - csc_RGBtoHSV: RGB to HSV, + * - csc_HSVtoRGB: HSV to RGB, + * - csc_RGBtoHLS: RGB to HSV, + * - csc_HLStoRGB: HSV to RGB, + * - csc_RGBtoCIELab: RGB to CIELab, + * - csc_CIELabtoRGB: CIELuv to RGB, + * - csc_RGBtoCIELuv: RGB to CIELuv, + * - csc_CIELuvtoRGB: CIELuv to RGB, + * - csc_RGBtoYUV: RGB to YUV color space, + * - csc_RGBtoYIQ: RGB to YIQ color space. + * + */ + + void ColorSpace(ColorSpaceConvertType transformation); + + /*! + * @brief Convert an image to grayscale + * + * @param grayscale_mode mode of grayscale conversation + * + * The function converts the image to grayscale version + * (one color channel after the conversion). There is four + * different ways to convert the image to grayscale what we + * can define with @a grayscale_mode: + * + * - g_Average: It computes the average grayscale + * values of the pixels with arithmetical average. + * - g_OpenCV: It computes the average grayscale + * values by help of the values of the Y channel. + * + */ + + void ConvertToGrayscale(GrayscaleType grayscale_mode = g_OpenCV); + + /*! + * @brief Convert a grayscale image to RGB + * + * The function converts the grayscale image to RGB version. + * (It copies the info from a single color channel to + * three color channel.) + * + */ + + void ConvertGrayscaleToRGB(); + + /*! + * @brief Change the red and blue components of every pixels + * + * Function changes the red component with the blue of + * every pixels. (Simple conversion from RGB->BGR.) + * + */ + + void ConvertBGRToRGB(); + + /*! + * @brief Compute an LBP filter on the image + * + * @param mode The LBP operator type + * + * The function converts the image to binary version over the + * threshold value. + * + */ + + void LBP(LBPType mode = lbp_Special); + + /*! + * @brief Binarize an image + * + * @param threshold Threshold value + * + * The function converts the image to binary version over the + * threshold value. + * + */ + + void Binarize(int threshold); + + /*! + * @brief Subtract an image from the internal picture + * + * @param source Source image for subtraction + * @param mode Calculation mode of difference feature + * + * Function generates a difference image between two image: + * the internal picture of this class and @a source_image. + * The calculation mode is determined by @a mode parameter. + * Function supports the following modes: + * + * - sub_Normal: Simple subtraction between each + * correspondent pixel (per color channels). The result values + * are converted to absolute value and normalized to + * range 0-255. + * + */ + + void Subtract(MEImage& source, SubtractModeType mode); + + /*! + * @brief Multiple an image with the internal picture + * + * @param source Second source image for multiplication + * @param mode Multiplication mode + * + * Function multiples an image with the internal image of this class and + * the result is stored in the internal image. The implemented calculation + * modes: + * + * - m_Normal: It multiples the corresponding pixel values + * of the two images. The original pixel values are divided by 128 and + * multiplied together. If the result is at least 1 then the new pixel value + * is 255 otherwise 0. + * - m_Neighbourhood: It multiples all pixel values of its + * 3x3 neighbourhood separately (see the method at MULTIPLICATION_NORMAL) + * and the new pixel value is 255 if at least two pixel is active in the + * 3x3 neighbourhood otherwise 0. + * + */ + + void Multiple(MEImage& source, MultiplicationType mode); + + /*! + * @brief Addition of an image and the internal picture + * + * @param source second source image for addition method + * @param mode the declaration of the used addition mode + * + * Function makes an addition operation between an image and the internal + * image of this class and the result is stored in the internal image. + * Supported modes: + * + * - a_Average: It sums the average of the corresponding pixels + * of each pictures. + * - a_Union: It sums the union of the corresponding pixels + * of each pictures. + * + */ + + void Addition(MEImage& source, AdditionType mode); + + /*! + * @brief Eliminate the single pixels from a binary image + * + * Function eliminates such a pixels which do not have neighbour pixels with + * 255 value in a 3x3 neighbourhood. The image should be converted to binary + * version. + * + */ + + void EliminateSinglePixels(); + + /*! + * @brief Calculate an area difference feature between two images + * + * @param reference Reference image + * @param difference Difference + * + * @return The percentage of image areas representing the conditions + * + * Function calculates a similarity feature between two pictures. + * Counts the number of the pixels whose intensity difference is + * higher than @a difference. (Range: 0..100) + * + */ + + float DifferenceAreas(MEImage& reference, int difference) const; + + /*! + * @brief Calculate an average difference between two images + * + * @param reference Reference image + * + * @return Average difference of the pixels + * + * Function calculates a similarity feature between + * two images. It returns a simple sum of the absolute difference + * of each pixel in the two images and averaged by the pixel number. + * (Range: 0..255) + * + */ + + int AverageDifference(MEImage& reference) const; + + /*! + * @brief Calculate minimum of image data + * + * @param image Second image + * + * Function calculates the minimum of current and given image. + * + */ + + void Minimum(MEImage& image); + + /*! + * @brief Calculate average brightness level + * + * @return Brightness level in range 0-255. + * + * Function calculates the average brightness level of the image. + * + */ + + float AverageBrightnessLevel() const; + + /*! + * @brief Check the equalization with a reference image + * + * @param reference Reference image + * + * @return true in case of binary equalization, otherwise false. + * + * Function calculates the binary difference between + * the image and the reference image. + * + */ + + bool Equal(const MEImage& reference) const; + + /*! + * @brief Check the equalization with a reference image + * + * @param reference Reference image + * @param maxabsdiff Maximal absolute difference + * + * @return true in case of equalization, otherwise false. + * + * Function checks the difference between the image and + * the reference image. Two pixels are equal in a range of + * a maximal absolute difference. + * + */ + + bool Equal(const MEImage& reference, int maxabsdiff) const; + + /*! + * @brief Get the grayscale value of a pixel + * + * @param x X coordinate of the pixel + * @param y Y coordinate of the pixel + * + * @return grayscale value of the pixel + * + * The method gives the grayscale value of a pixel back. If + * the image has 3 color channels (e.g. RGB) then Y value of + * YIQ/YUV color space will be calculated otherwise normal + * averaged grayscale value. + * + */ + + unsigned char GrayscalePixel(int x, int y) const; + + /*! + * @brief Count the number of neighbourhood pixels with maximum intensity + * + * @param startx X coordinate of the top-left pixel + * @param starty Y coordinate of the top-left pixel + * @param neighbourhood Specific subset of pixels + * + * @return number of the pixels with maximum intensity. + * + * The method counts the number of the pixels with maximum + * intensity (255) in a specified subset of pixels. + * The grayscale values of the pixels are used in the counter + * process. The following neighbourhood forms are allowed with + * the @a neighbourhood parameter: + * + * - n_2X2: Simple 2x2 matrix. + * - n_3X3: Simple 3x3 matrix. + * - n_3x2: Simple 3x2 matrix. + * + */ + + int NeighbourhoodCounter(int startx, int starty, NeighbourhoodType neighbourhood) const; + + /*! + * @brief Calculate the gradient vector in a point + * + * @param smooth compute smooth filter + * @param x X coordinate of the point + * @param y Y coordinate of the point + * @param mask_size The mask size to calculate the gradient + * + * @param result_x X component of the calculated vector + * @param result_y Y component of the calculated vector + * + * The method calculates the gradient vector in a given point. + * The image is preprocessed with a Gauss filter to smooth the + * image content. The filter size of the Gauss filter depends on + * mask size of the gradient vector: filter size = mask size*3. + * Eight points are assigned to the initial point to compute + * a vector sum: (x, y-mask_size), (x+mask_size/√2, y-mask_size/√2), + * (x+mask_size, y), (x+mask_size/√2, y+mask_size/√2), (x, y+mask_size), + * (x-mask_size/√2, y+mask_size/√2), (x-mask_size, y), (x-mask_size/√2, y-mask_size/√2). + * The lengths of all vectors equalize with the mask size. + * After that each vector is multiplied with the gradient difference between + * its two end points. The results are summarized and normalized by + * the mask size. + * + */ + + void GradientVector(bool smooth, int x, int y, int mask_size, int& result_x, int& result_y); + + /*! + * @brief Visualize gradient vectors + * + * @param vector_x Number of points horizontally + * @param vector_y Number of points vertically + * + * This function draws a wire (@a vector_x * @a vector_y) with + * gradient vectors. + * + */ + + void GradientVisualize(int vector_x, int vector_y); + +private: + + /* + ------------------------------------------------------------------- + Internal methods + ------------------------------------------------------------------- + */ + + /*! + * @brief Copy image data + * + * @param other_image Input image with new image data + * + * @return true if it is successful, otherwise false. + * + * Copy image data from @a other_image to MEImage image data. + * + */ + + bool _Copy(const MEImage& other_image); + + /*! + * @brief Inherent initialization function + * + * @param width Width of the image + * @param height Height of the image + * @param layer Number of color channels of the image + * + * Initialization function of MEImage class which allocates + * memory to internal MEImage image and sets its properties. + * + */ + + void _Init(int width, int height, int layer); + + /*! + * @brief Compute an image to a different color space + * + * @param mode Mode of the conversion + * + * Currently, the internal function allows to use a few + * mode to convert an image between color spaces. + * Current supported conversions (@a mode): + * - RGBtoYUV: RGB to YUV color space, + * - RGBtoYIQ: RGB to YIQ color space. + * + */ + + void ComputeColorSpace(ColorSpaceConvertType mode); + +private: + /// This matrix stores the matrix of the actual color space transform + float TransformMatrix[3][3]; + /// The OpenCV image which contains the image data + void* cvImg; +}; + +/** @} */ + +#endif diff --git a/package_bgs/ck/MotionDetection.cpp b/package_bgs/ck/MotionDetection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9cb8e79bcb4e8059d99ded840906f9020da6f394 --- /dev/null +++ b/package_bgs/ck/MotionDetection.cpp @@ -0,0 +1,1425 @@ +/* + * This file is part of the AiBO+ project + * + * Copyright (C) 2005-2013 Csaba Kertész (csaba.kertesz@gmail.com) + * + * AiBO+ is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * AiBO+ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + * Paper: Csaba, Kertész: Texture-Based Foreground Detection, International Journal of Signal Processing, + * Image Processing and Pattern Recognition (IJSIP), Vol. 4, No. 4, 2011. + */ + +#include "MotionDetection.hpp" + +#include "graph.h" + +#if defined(__MINGW32__) || defined(__MINGW64__) +#include <cvaux.h> +#else +#include <opencv/cvaux.h> +#endif + +#include "MEHistogram.hpp" +#include "MEImage.hpp" + +// Pyramid picture for the tracking +IplImage *HUOFPyramid; +// Pyramid picture for the tracking +IplImage *HUOFPrevPyramid; + +// Struct for histogram update data of a pixel +struct MEPixelDataType +{ + float BackgroundRate; + int LifeCycle; + float *Weights; + bool *BackgroundHistogram; + float **Histograms; + float *PreviousHistogram; +}; + +MotionDetection::MotionDetection(DetectorType mode) : + MDMode(md_NotDefined), MDDataState(ps_Uninitialized), Frames(0), ReadyMask(false), + HUColorSpace(MEImage::csc_RGBtoCIELuv), HULBPMode(MEImage::lbp_Special), + HUHistogramsPerPixel(3), HUHistogramArea(5), HUHistogramBins(8), + HUImageWidth(-1), HUImageHeight(-1), HULBPPixelData(NULL), + HUPrThres(0.75), HUBackgrThres(0.95), HUHistLRate(0.01), HUWeightsLRate(0.01), + HUSamplePixels(-1), HUDesiredSamplePixels(-1), HUMinCutWeight(8.0), + HUOFDataState(ps_Uninitialized), HUOFPointsNumber(-1), + HUOFCamMovementX(0), MaxTrackedPoints(0), HUOFFrames(-1), + HUOFCamMovement(false) +{ + HUOFPyramid = NULL; + HUOFPrevPyramid = NULL; + HUOFPoints[0] = NULL; + HUOFPoints[1] = NULL; + SetMode(mode); +} + + +MotionDetection::~MotionDetection() +{ + if (MDMode != md_NotDefined) + { + ReleaseData(); + } +} + + +void MotionDetection::SetMode(DetectorType newmode) +{ + if (MDMode != md_NotDefined && MDMode != newmode) + { + ReleaseData(); + Frames = 0; + HUOFFrames = -1; + HUOFCamMovement = false; + HUOFCamMovementX = 0; + ReadyMask = false; + } + + switch (newmode) + { + case md_LBPHistograms: + MDMode = md_LBPHistograms; + break; + + case md_DLBPHistograms: + MDMode = md_DLBPHistograms; + break; + + default: + MDMode = md_LBPHistograms; + break; + } +} + + +float MotionDetection::GetParameter(ParametersType param) const +{ + float ret = 0.0; + + switch (param) + { + case mdp_HUProximityThreshold: + ret = (float)HUPrThres; + break; + + case mdp_HUBackgroundThreshold: + ret = (float)HUBackgrThres; + break; + + case mdp_HUHistogramLearningRate: + ret = (float)HUHistLRate; + break; + + case mdp_HUWeightsLearningRate: + ret = (float)HUWeightsLRate; + break; + + case mdp_HUMinCutWeight: + ret = (float)HUMinCutWeight; + break; + + case mdp_HUDesiredSamplePixels: + ret = (float)HUDesiredSamplePixels; + break; + + case mdp_HUHistogramsPerPixel: + ret = (float)HUHistogramsPerPixel; + break; + + case mdp_HUHistogramArea: + ret = (float)HUHistogramArea; + break; + + case mdp_HUHistogramBins: + ret = (float)HUHistogramBins; + break; + + case mdp_HUColorSpace: + ret = (float)HUColorSpace; + break; + + case mdp_HULBPMode: + ret = (float)HULBPMode; + break; + + default: + break; + } + return ret; +} + + +void MotionDetection::SetParameter(ParametersType param, float value) +{ + switch (param) + { + case mdp_HUProximityThreshold: + HUPrThres = (float)value; + break; + + case mdp_HUBackgroundThreshold: + HUBackgrThres = (float)value; + break; + + case mdp_HUHistogramLearningRate: + HUHistLRate = (float)value; + break; + + case mdp_HUWeightsLearningRate: + HUWeightsLRate = (float)value; + break; + + case mdp_HUMinCutWeight: + HUMinCutWeight = (float)value; + break; + + case mdp_HUDesiredSamplePixels: + HUDesiredSamplePixels = (int)value; + break; + + case mdp_HUHistogramsPerPixel: + HUHistogramsPerPixel = (MDDataState == ps_Uninitialized) ? (int)value : HUHistogramsPerPixel; + break; + + case mdp_HUHistogramArea: + HUHistogramArea = (MDDataState == ps_Uninitialized) ? (int)value : HUHistogramArea; + break; + + case mdp_HUHistogramBins: + HUHistogramBins = (MDDataState == ps_Uninitialized) ? (int)value : HUHistogramBins; + break; + + case mdp_HUColorSpace: + HUColorSpace = (MDDataState == ps_Uninitialized) ? (int)value : HUColorSpace; + break; + + case mdp_HULBPMode: + HULBPMode = (MDDataState == ps_Uninitialized) ? (int)value : HULBPMode; + break; + + default: + break; + } +} + + +void MotionDetection::DetectMotions(MEImage& image) +{ + switch (MDMode) + { + case md_LBPHistograms: + case md_DLBPHistograms: + DetectMotionsHU(image); + break; + + default: + break; + } +} + + +void MotionDetection::GetMotionsMask(MEImage& mask_image) +{ + if (ReadyMask) + { + mask_image = MaskImage; + } + + switch (MDMode) + { + case md_LBPHistograms: + case md_DLBPHistograms: + GetMotionsMaskHU(MaskImage); + break; + + default: + break; + } + + ReadyMask = true; + mask_image = MaskImage; +} + + +void MotionDetection::CalculateResults(MEImage& referenceimage, int& tnegatives, int& tpositives, + int& ttnegatives, int& ttpositives) +{ + if (MDDataState != ps_Successful) + { + printf("No data for calculation.\n"); + return; + } + + if (referenceimage.GetLayers() != 1) + referenceimage.ConvertToGrayscale(MEImage::g_OpenCV); + + referenceimage.Binarize(1); + + MEImage mask_image; + + GetMotionsMask(mask_image); + + if ((mask_image.GetWidth() != referenceimage.GetWidth()) || + (mask_image.GetHeight() != referenceimage.GetHeight())) + { + printf("Different resolutions of mask<->reference image.\n"); + return; + } + + unsigned char* RefMaskImgData = referenceimage.GetImageData(); + unsigned char* MaskImgData = mask_image.GetImageData(); + int RowStart = 0; + int RowWidth = referenceimage.GetRowWidth(); + + int TrueNegatives = 0; + int TruePositives = 0; + int TotalTrueNegatives = 0; + int TotalTruePositives = 0; + + int ImageFrame = 0; + + if (MDMode == md_LBPHistograms || md_DLBPHistograms) + { + ImageFrame = HUHistogramArea / 2; + } + + for (int y = referenceimage.GetHeight()-ImageFrame-1; y >= ImageFrame; --y) + { + for (int x = referenceimage.GetWidth()-ImageFrame-1; x >= ImageFrame; --x) + { + TrueNegatives += + (RefMaskImgData[RowStart+x] == 0) && + (MaskImgData[RowStart+x] == 0); + TotalTrueNegatives += (RefMaskImgData[RowStart+x] == 0); + TruePositives += + (RefMaskImgData[RowStart+x] == 255) && + (MaskImgData[RowStart+x] == 255); + TotalTruePositives += (RefMaskImgData[RowStart+x] == 255); + } + RowStart += RowWidth; + } + + tnegatives = TrueNegatives; + ttnegatives = TotalTrueNegatives; + tpositives = TruePositives; + ttpositives = TotalTruePositives; +} + + +void MotionDetection::ReleaseData() +{ + if (MDMode == md_LBPHistograms || MDMode == md_DLBPHistograms) + { + ReleaseHUData(); + } +} + + +void MotionDetection::InitHUData(int imagewidth, int imageheight) +{ + if ((HUImageWidth != imagewidth-HUHistogramArea+1) || + (HUImageHeight != imageheight-HUHistogramArea+1) || + (MDDataState == ps_Uninitialized)) + { + if (MDDataState != ps_Uninitialized) + { + ReleaseHUData(); + } + + MDDataState = ps_Initialized; + + HUImageWidth = imagewidth-HUHistogramArea+1; + HUImageHeight = imageheight-HUHistogramArea+1; + + HULBPPixelData = new MEPixelDataType**[HUImageWidth / 2]; + + for (int i = 0; i < HUImageWidth / 2; ++i) + { + HULBPPixelData[i] = new MEPixelDataType*[HUImageHeight]; + } + + for (int i = 0; i < HUImageWidth / 2; ++i) + for (int i1 = 0; i1 < HUImageHeight; ++i1) + { + HULBPPixelData[i][i1] = new MEPixelDataType; + HULBPPixelData[i][i1]->Weights = new float[HUHistogramsPerPixel]; + HULBPPixelData[i][i1]->BackgroundHistogram = new bool[HUHistogramsPerPixel]; + HULBPPixelData[i][i1]->Histograms = new float*[HUHistogramsPerPixel]; + for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) + HULBPPixelData[i][i1]->Histograms[i2] = new float[HUHistogramBins]; + HULBPPixelData[i][i1]->PreviousHistogram = new float[HUHistogramBins]; + } + + // Allocate auxiliary variables + HUMaskColumnAddDel = new int*[HUHistogramArea]; + for (int i = 0; i < HUHistogramArea; ++i) + HUMaskColumnAddDel[i] = new int[2]; + + HUMaskRowAddDel = new int*[HUHistogramArea]; + for (int i = 0; i < HUHistogramArea; ++i) + HUMaskRowAddDel[i] = new int[2]; + + // Generate sample mask + SetSampleMaskHU(sm_Circle, HUDesiredSamplePixels); + + // Init HU optical flow data + if (MDMode == md_DLBPHistograms) + InitHUOFData(imagewidth, imageheight); + + ClearHUData(); + } +} + + +void MotionDetection::InitHUOFData(int imagewidth, int imageheight) +{ + if (HUOFDataState != ps_Uninitialized) + { + ReleaseHUOFData(); + } + + if (HUOFDataState == ps_Uninitialized) + { + HUOFPointsNumber = imagewidth*imageheight / 1000; + HUOFPyramid = cvCreateImage(cvSize(imagewidth, imageheight), 8, 1); + HUOFPrevPyramid = cvCreateImage(cvSize(imagewidth, imageheight), 8, 1); + HUOFPoints[0] = (CvPoint2D32f*)cvAlloc(HUOFPointsNumber*sizeof(HUOFPoints[0][0])); + HUOFPoints[1] = (CvPoint2D32f*)cvAlloc(HUOFPointsNumber*sizeof(HUOFPoints[1][0])); + } +} + + +void MotionDetection::ReleaseHUData() +{ + if (MDDataState != ps_Uninitialized) + { + for (int i = 0; i < HUImageWidth / 2; i++) + for (int i1 = 0; i1 < HUImageHeight; i1++) + { + delete[] HULBPPixelData[i][i1]->PreviousHistogram; + for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) + delete[] HULBPPixelData[i][i1]->Histograms[i2]; + delete[] HULBPPixelData[i][i1]->Histograms; + delete[] HULBPPixelData[i][i1]->BackgroundHistogram; + delete[] HULBPPixelData[i][i1]->Weights; + delete HULBPPixelData[i][i1]; + } + + for (int i = 0; i < HUImageWidth / 2; i++) + { + delete[] HULBPPixelData[i]; + } + delete[] HULBPPixelData; + + if (MDMode == md_DLBPHistograms) + ReleaseHUOFData(); + + HUImageWidth = -1; + HUImageHeight = -1; + HULBPPixelData = NULL; + MDDataState = ps_Uninitialized; + + // Release auxiliary variables + for (int i = 0; i < HUHistogramArea; ++i) + delete[] HUMaskColumnAddDel[i]; + delete[] HUMaskColumnAddDel; + + for (int i = 0; i < HUHistogramArea; ++i) + delete[] HUMaskRowAddDel[i]; + delete[] HUMaskRowAddDel; + + HUMaskColumnAddDel = NULL; + HUMaskRowAddDel = NULL; + } +} + + +void MotionDetection::ReleaseHUOFData() +{ + if (MDDataState != ps_Uninitialized) + { + if (HUOFPyramid) + { + cvReleaseImage(&HUOFPyramid); + HUOFPyramid = NULL; + } + if (HUOFPrevPyramid) + { + cvReleaseImage(&HUOFPrevPyramid); + HUOFPrevPyramid = NULL; + } + if (HUOFPoints[0]) + { + cvFree(&HUOFPoints[0]); + HUOFPoints[0] = NULL; + } + if (HUOFPoints[1]) + { + cvFree(&HUOFPoints[1]); + HUOFPoints[1] = NULL; + } + HUOFDataState = ps_Uninitialized; + } +} + + +void MotionDetection::ClearHUData() +{ + if (MDDataState != ps_Uninitialized) + { + for (int i = (HUImageWidth / 2)-1; i >= 0; --i) + for (int i1 = HUImageHeight-1; i1 >= 0; --i1) + { + for (int i2 = HUHistogramsPerPixel-1; i2 >= 0; --i2) + { + memset(HULBPPixelData[i][i1]->Histograms[i2], 0, + HUHistogramBins*sizeof(float)); + HULBPPixelData[i][i1]->Weights[i2] = 1.0 / HUHistogramsPerPixel; + HULBPPixelData[i][i1]->BackgroundHistogram[i2] = true; + } + HULBPPixelData[i][i1]->BackgroundRate = 1.0; + HULBPPixelData[i][i1]->LifeCycle = 0; + } + MDDataState = ps_Initialized; + } +} + + +void MotionDetection::DetectMotionsHU(MEImage& image) +{ + unsigned char *ImgData = NULL; + MEImage newimage = image; + float DiffAreas = 0; + + // Init the histogram update data structures if needs be + if ((MDDataState == ps_Uninitialized) || + (HUImageWidth != newimage.GetWidth()-HUHistogramArea+1) || + (HUImageHeight != newimage.GetHeight()-HUHistogramArea+1)) + { + InitHUData(newimage.GetWidth(), newimage.GetHeight()); + } + + if (newimage.GetLayers() == 1) + { + newimage.ConvertGrayscaleToRGB(); + } + + MEImage blueimage = newimage; + blueimage.ColorSpace(MEImage::csc_RGBtoCIELuv); + + if (HUColorSpace != -1) + { + newimage.ColorSpace((MEImage::ColorSpaceConvertType)HUColorSpace); + } + + if (Frames == 0) + { + MEImage BlueLayer; + blueimage.GetLayer(BlueLayer, 1); + BlueLayer.Resize(32, 24); + PreviousBlueLayer = BlueLayer; + } + + Frames++; + + // Detect the fast, big changes in the scene + MEImage BlueLayer; + blueimage.GetLayer(BlueLayer, 1); + BlueLayer.Resize(32, 24); + DiffAreas = BlueLayer.DifferenceAreas(PreviousBlueLayer, 12); + + if (DiffAreas > 80) + { + MDDataState = ps_Initialized; + if (MDMode == md_DLBPHistograms) + HUOFDataState = ps_Initialized; + printf("Frame: %d - big changes in the scene (%f)", Frames, DiffAreas); + Frames = 1; + HUOFFrames = -1; + } + PreviousBlueLayer = BlueLayer; + + if (Frames == 1) + { + CurrentImage = image; + PreviousImage = CurrentImage; + } else + if (Frames > 1) + { + PreviousImage = CurrentImage; + CurrentImage = image; + // Optical flow correction of the camera movements + if (MDMode == md_DLBPHistograms) + { + OpticalFlowCorrection(); + } + } + + newimage.ConvertToGrayscale(MEImage::g_OpenCV); + + if (HULBPMode != -1) + { + newimage.LBP((MEImage::LBPType)HULBPMode); + } + + // Set some auxiliary variables + ImgData = newimage.GetImageData(); + int DivisionOperator = (int)(log(256 / HUHistogramBins) / log(2))+1; + + // Downscale the image + for (int i = newimage.GetRowWidth()*newimage.GetHeight()-1; i >= 0; --i) + { + ImgData[i] >>= DivisionOperator; + } + + UpdateModelHU(newimage, HULBPPixelData); + + // Change the state of the HU data structures + if (MDDataState == ps_Initialized) + { + MDDataState = ps_Successful; + } + HUOFCamMovement = false; + ReadyMask = false; +} + + +void MotionDetection::UpdateModelHU(MEImage& image, MEPixelDataType*** model) +{ + float CurrentHistogram[HUHistogramBins], CurrentHistogram2[HUHistogramBins]; + unsigned char *ImgData = image.GetImageData(); + int RowWidth = image.GetRowWidth(); + int RowStart = (HUImageHeight-1)*RowWidth; + + memset(CurrentHistogram, 0, sizeof(CurrentHistogram)); + // Calculate the first histogram + for (int y = HUHistogramArea-1; y >= 0; --y) + { + for (int x = HUHistogramArea-1; x >= 0; --x) + { + if ((HUMaskRowAddDel[y][1] > x) && (HUMaskRowAddDel[y][0] <= x) && + (HUMaskColumnAddDel[x][1] > y) && (HUMaskColumnAddDel[x][0] <= y)) + { + CurrentHistogram[ImgData[RowStart+HUImageWidth-1+x]]++; + } + } + RowStart += RowWidth; + } + + // This cycle generates the last row of histograms + for (int y = HUImageHeight-1; y >= 0; --y) + { + if (HUImageHeight-1 > y) + { + // Delete and add a pixel column from the histogram data + for (int i = HUHistogramArea-1; i >= 0; --i) + { + if (HUMaskColumnAddDel[i][0] != -1) + CurrentHistogram[ImgData[RowWidth*(y+HUMaskColumnAddDel[i][0])+HUImageWidth-1+i]]++; + if (HUMaskColumnAddDel[i][1] != -1) + CurrentHistogram[ImgData[RowWidth*(y+HUMaskColumnAddDel[i][1])+HUImageWidth-1+i]]--; + } + } + + if (y % 2 == HUImageWidth % 2) + { + MEPixelDataType* PixelData = model[(HUImageWidth-1) / 2][y]; + + // Allocate and initialize the pixel data if needs be + if (!PixelData) + { + // Memory allocation + PixelData = new MEPixelDataType; + PixelData->Weights = new float[HUHistogramsPerPixel]; + PixelData->BackgroundHistogram = new bool[HUHistogramsPerPixel]; + PixelData->Histograms = new float*[HUHistogramsPerPixel]; + for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) + PixelData->Histograms[i2] = new float[HUHistogramBins]; + PixelData->PreviousHistogram = new float[HUHistogramBins]; + + for (int i = HUHistogramsPerPixel-1; i >= 0; --i) + { + memcpy(PixelData->Histograms[i], CurrentHistogram, sizeof(CurrentHistogram)); + PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel; + PixelData->BackgroundHistogram[i] = true; + } + PixelData->BackgroundRate = 1.0; + PixelData->LifeCycle = 0; + memcpy(PixelData->PreviousHistogram, CurrentHistogram, sizeof(CurrentHistogram)); + + model[(HUImageWidth-1) / 2][y] = PixelData; + } else { + bool InitHistograms = (MDDataState == ps_Initialized); + + if (MDDataState != ps_Initialized && HUOFCamMovement) + { + // Histogram intersection between the previous and the current histogram + float Difference = 0.0; + for (int i1 = HUHistogramBins-1; i1 >= 0; --i1) + { + Difference += (float)(CurrentHistogram[i1] < PixelData->PreviousHistogram[i1] ? + CurrentHistogram[i1] : PixelData->PreviousHistogram[i1]); + } + Difference /= HUSamplePixels; + + if (Difference < HUBackgrThres) + InitHistograms = true; + } + if (InitHistograms) + { + // Copy the histogram data to the HU data structures + for (int i = HUHistogramsPerPixel-1; i >= 0; --i) + { + memcpy(PixelData->Histograms[i], CurrentHistogram, sizeof(CurrentHistogram)); + PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel; + PixelData->BackgroundHistogram[i] = true; + } + memcpy(PixelData->PreviousHistogram, CurrentHistogram, sizeof(CurrentHistogram)); + PixelData->BackgroundRate = 1.0; + PixelData->LifeCycle = 0; + } else { + // Update the HU data structures + UpdateHUPixelData(PixelData, CurrentHistogram); + + if (MDMode == md_DLBPHistograms) + { + memcpy(PixelData->PreviousHistogram, CurrentHistogram, sizeof(CurrentHistogram)); + } + } + } + } + + // Copy the histogram + memcpy(CurrentHistogram2, CurrentHistogram, sizeof(CurrentHistogram)); + + // This cycle generates a column of histograms + for (int x = HUImageWidth-2; x >= 0; --x) + { + RowStart = RowWidth*y; + + // Delete and add a pixel column from the histogram data + for (int i = HUHistogramArea-1; i >= 0; --i) + { + if (HUMaskRowAddDel[i][0] != -1) + CurrentHistogram2[ImgData[RowStart+x+HUMaskRowAddDel[i][0]]]++; + if (HUMaskRowAddDel[i][1] != -1) + CurrentHistogram2[ImgData[RowStart+x+HUMaskRowAddDel[i][1]]]--; + + RowStart += RowWidth; + } + if (x % 2 == 0) + { + MEPixelDataType* PixelData = model[x / 2][y]; + + // Allocate and initialize the pixel data if needs be + if (!PixelData) + { + // Memory allocation + PixelData = new MEPixelDataType; + PixelData->Weights = new float[HUHistogramsPerPixel]; + PixelData->BackgroundHistogram = new bool[HUHistogramsPerPixel]; + PixelData->Histograms = new float*[HUHistogramsPerPixel]; + for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) + PixelData->Histograms[i2] = new float[HUHistogramBins]; + PixelData->PreviousHistogram = new float[HUHistogramBins]; + + for (int i = HUHistogramsPerPixel-1; i >= 0; --i) + { + memcpy(PixelData->Histograms[i], CurrentHistogram2, sizeof(CurrentHistogram2)); + PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel; + PixelData->BackgroundHistogram[i] = true; + } + PixelData->BackgroundRate = 1.0; + PixelData->LifeCycle = 0; + model[x / 2][y] = PixelData; + memcpy(PixelData->PreviousHistogram, CurrentHistogram2, sizeof(CurrentHistogram2)); + } else { + bool InitHistograms = (MDDataState == ps_Initialized); + + if (MDDataState != ps_Initialized && HUOFCamMovement) + { + // Histogram intersection between the previous and the current histogram + float Difference = 0.0; + for (int i1 = HUHistogramBins-1; i1 >= 0; --i1) + { + Difference += (float)(CurrentHistogram2[i1] < PixelData->PreviousHistogram[i1] ? + CurrentHistogram2[i1] : PixelData->PreviousHistogram[i1]); + } + Difference /= HUSamplePixels; + + if (Difference < HUBackgrThres) + InitHistograms = true; + } + if (InitHistograms) + { + // Copy the histogram data to the HU data structures + for (int i = HUHistogramsPerPixel-1; i >= 0; --i) + { + memcpy(PixelData->Histograms[i], CurrentHistogram2, sizeof(CurrentHistogram2)); + PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel; + PixelData->BackgroundHistogram[i] = true; + } + memcpy(PixelData->PreviousHistogram, CurrentHistogram2, sizeof(CurrentHistogram2)); + PixelData->BackgroundRate = 1.0; + PixelData->LifeCycle = 0; + } else { + // Update the HU data structures + UpdateHUPixelData(PixelData, CurrentHistogram2); + + if (MDMode == md_DLBPHistograms) + { + memcpy(PixelData->PreviousHistogram, CurrentHistogram2, sizeof(CurrentHistogram2)); + } + } + } + } + + } + } +} + + +void MotionDetection::UpdateHUPixelData(MEPixelDataType* PixelData, const float *histogram) +{ + int MaxIndex = 0; + float MaxValue = -1; + bool Replace = true; + float IntersectionResults[HUHistogramsPerPixel]; + + PixelData->LifeCycle++; + PixelData->BackgroundRate = 0.0; + + // Compute intersection between the currect and older histograms + for (int i = HUHistogramsPerPixel-1; i >= 0; --i) + { + // Histogram intersection + float Difference = 0.0; + for (int i1 = HUHistogramBins-1; i1 >= 0; --i1) + { + Difference += (float)histogram[i1] < PixelData->Histograms[i][i1] ? + (float)histogram[i1] : PixelData->Histograms[i][i1]; + } + + IntersectionResults[i] = (float)Difference / (float)(HUSamplePixels); + + if (PixelData->BackgroundHistogram[i] && + IntersectionResults[i] > PixelData->BackgroundRate) + { + PixelData->BackgroundRate = IntersectionResults[i]; + } + + if (MaxValue < IntersectionResults[i]) + { + MaxValue = IntersectionResults[i]; + MaxIndex = i; + } + + Replace = Replace && (IntersectionResults[i] < HUPrThres); + } + + // Replace the histogram with the lowest weight + if (Replace) + { + // Find the histogram with minimal weight + int MinIndex = 0; + float MinValue = PixelData->Weights[0]; + for (int i1 = HUHistogramsPerPixel-1; i1 > 0; --i1) + { + if (MinValue > PixelData->Weights[i1]) + { + MinValue = PixelData->Weights[i1]; + MinIndex = i1; + } + } + + PixelData->Weights[MinIndex] = 0.01; + for (int i1 = HUHistogramBins-1; i1 >= 0; --i1) + PixelData->Histograms[MinIndex][i1] = (float)histogram[i1]; + PixelData->BackgroundHistogram[MinIndex] = 0; + + // Normalize the weights + float sum = 0; + for (int i1 = HUHistogramsPerPixel-1; i1 >= 0; --i1) + sum += PixelData->Weights[i1]; + + for (int i1 = HUHistogramsPerPixel-1; i1 >= 0; --i1) + PixelData->Weights[i1] = PixelData->Weights[i1] / sum; + + return; + } + + float LearningRate = HUHistLRate; + + if (PixelData->LifeCycle < 100) + LearningRate += (float)(100-PixelData->LifeCycle) / 100; + else + if (MDMode == md_DLBPHistograms && HUOFFrames != -1 && HUOFFrames < 40) + LearningRate += (HUOFFrames < 80 ? 0.05 : 0); + + // Match was found -> Update the histogram of the best match + for (int i = HUHistogramBins-1; i >= 0; --i) + { + PixelData->Histograms[MaxIndex][i] *= (1.0-LearningRate); + PixelData->Histograms[MaxIndex][i] += LearningRate*(float)histogram[i]; + } + + LearningRate = HUWeightsLRate; + if (PixelData->LifeCycle < 100) + LearningRate += (float)(100-PixelData->LifeCycle) / 100; + else + if (MDMode == md_DLBPHistograms && HUOFFrames != -1 && HUOFFrames < 40) + LearningRate += (HUOFFrames < 80 ? 0.05 : 0); + + // Update the weights of the histograms + for (int i = HUHistogramsPerPixel-1; i >= 0; --i) + { + PixelData->Weights[i] = + (LearningRate*(i == MaxIndex)+(1.0-LearningRate)*PixelData->Weights[i]); + } + + // Order and select the background histograms + float Weights[HUHistogramsPerPixel][2]; + + for (int i = HUHistogramsPerPixel-1; i >= 0; --i) + { + Weights[i][0] = (float)i; + Weights[i][1] = PixelData->Weights[i]; + } + + for (int i1 = HUHistogramsPerPixel-1; i1 >= 2; --i1) + for (int i = i1; i >= 1; --i) + { + if (Weights[i][1] <= Weights[i-1][1]) + { + float tmp = Weights[i][0]; + float tmp2 = Weights[i][1]; + + Weights[i][0] = Weights[i-1][0]; + Weights[i][1] = Weights[i-1][1]; + + Weights[i-1][0] = tmp; + Weights[i-1][1] = tmp2; + } + } + + float Sum = 0; + int i = 0; + + for (i = HUHistogramsPerPixel-1; i >= 0; --i) + { + Sum += Weights[i][1]; + PixelData->BackgroundHistogram[(int)Weights[i][0]] = true; + + if (Sum > HUBackgrThres) + break; + } + for (int i1 = i-1; i1 >= 0; --i1) + { + PixelData->BackgroundHistogram[(int)Weights[i1][0]] = false; + } +} + + +void MotionDetection::OpticalFlowCorrection() +{ + IplImage *PreviousGray = NULL, *CurrentGray = NULL; + char* PointsStatus = (char*)cvAlloc(HUOFPointsNumber); + int i = 0, i1 = 0; + + if (HUOFFrames != -1) + HUOFFrames++; + + // Convert the images into grayscale + if (CurrentImage.GetLayers() > 1) + { + CurrentGray = cvCreateImage(cvGetSize(CurrentImage.GetIplImage()), IPL_DEPTH_8U, 1); + cvCvtColor(CurrentImage.GetIplImage(), CurrentGray, CV_BGR2GRAY); + } else + CurrentGray = (IplImage*)CurrentImage.GetIplImage(); + if (PreviousImage.GetLayers() > 1) + { + PreviousGray = cvCreateImage(cvGetSize(CurrentImage.GetIplImage()), IPL_DEPTH_8U, 1); + cvCvtColor(PreviousImage.GetIplImage(), PreviousGray, CV_BGR2GRAY); + } else + PreviousGray = (IplImage*)PreviousImage.GetIplImage(); + + if (HUOFDataState != ps_Successful) + { + printf("Search new corners\n"); + IplImage* TempEig = cvCreateImage(cvGetSize(CurrentGray), 32, 1 ); + IplImage* Temp = cvCreateImage(cvGetSize(CurrentGray), 32, 1 ); + double MinDistance = (CurrentImage.GetWidth()+CurrentImage.GetHeight()) / 20; + HUOFPointsNumber = MaxTrackedPoints = CurrentImage.GetWidth()*CurrentImage.GetHeight() / 1000; + + // Search good trackable points + cvGoodFeaturesToTrack(PreviousGray, TempEig, Temp, + HUOFPoints[0], &HUOFPointsNumber, + 0.01, MinDistance, NULL, 3); + MaxTrackedPoints = HUOFPointsNumber; + // Release temporary images + cvReleaseImage(&TempEig); + cvReleaseImage(&Temp); + // Realloc the point status array + if (PointsStatus) + { + cvFree(&PointsStatus); + PointsStatus = NULL; + } + + if (MaxTrackedPoints < 2) + { + HUOFDataState = ps_Initialized; + HUOFPointsNumber = CurrentImage.GetWidth()*CurrentImage.GetHeight() / 1000; + return; + } else + HUOFDataState = ps_Successful; + PointsStatus = (char*)cvAlloc(HUOFPointsNumber); + } + + cvCalcOpticalFlowPyrLK(PreviousGray, CurrentGray, HUOFPrevPyramid, HUOFPyramid, + HUOFPoints[0], HUOFPoints[1], HUOFPointsNumber, + cvSize(10, 10), 3, PointsStatus, NULL, + cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 5, 1), 0); + + // Count the distances of the tracked points + int Distances[HUOFPointsNumber][3]; + int DistanceMax = 0; + for (i = 0; i < HUOFPointsNumber; ++i) + { + int DiffX = (int)MERound(HUOFPoints[1][i].x-HUOFPoints[0][i].x); + int DiffY = (int)MERound(HUOFPoints[1][i].y-HUOFPoints[0][i].y); + if ((PointsStatus[i] == 1) && !((DiffX == 0) && (DiffY == 0))) + { + bool found = false; + // Create a list from the differences to count them + for (i1 = 0; i1 < DistanceMax; ++i1) + { + if ((Distances[i1][0] == DiffX) && + (Distances[i1][1] == DiffY)) + { + Distances[i1][2]++; + found = true; + break; + } + } + if ((!found) && !((DiffX == 0) && (DiffY == 0))) + { + Distances[DistanceMax][0] = (int)MERound(HUOFPoints[1][i].x-HUOFPoints[0][i].x); + Distances[DistanceMax][1] = (int)MERound(HUOFPoints[1][i].y-HUOFPoints[0][i].y); + Distances[DistanceMax][2] = 1; + DistanceMax++; + } + } + } + + // Sort the results + for (int i1 = DistanceMax-1; i1 >= 2; --i1) + for (int i = i1; i >= 1; --i) + { + if ((Distances[i][2] > Distances[i-1][2]) || + ((Distances[i][2] == Distances[i-1][2]) && + (abs(Distances[i][0])+abs(Distances[i][1]) < + abs(Distances[i-1][0])+abs(Distances[i-1][1])))) + { + int tmp = Distances[i][0]; + int tmp2 = Distances[i][1]; + int tmp3 = Distances[i][2]; + + Distances[i][0] = Distances[i-1][0]; + Distances[i][1] = Distances[i-1][1]; + Distances[i][2] = Distances[i-1][2]; + + Distances[i-1][0] = tmp; + Distances[i-1][1] = tmp2; + Distances[i-1][2] = tmp3; + } + } + + float MoveX = 0.0; + float MoveY = 0.0; + int SampleNums = 0; + float DistanceMeasure = 0.0; + + // Calculate the final camera movement + for (i = 0; i < DistanceMax; ++i) + { + if ((Distances[i][2] <= MaxTrackedPoints / 10)) + break; + + if (i > 0) + { + DistanceMeasure += (Distances[i][0]-Distances[i-1][0])*(Distances[i][0]-Distances[i-1][0]); + DistanceMeasure += (Distances[i][1]-Distances[i-1][1])*(Distances[i][1]-Distances[i-1][1]); + } + + MoveX += Distances[i][0]*Distances[i][2]; + MoveY += Distances[i][1]*Distances[i][2]; + SampleNums += Distances[i][2]; + } + + if (SampleNums > 0) + { + MoveX = MERound(MoveX / SampleNums); + MoveY = MERound(MoveY / SampleNums); + } + + if (!((MoveX == 0) && (MoveY == 0)) && + (SampleNums > MaxTrackedPoints / 2)) + { + HUOFCamMovementX += (int)MoveX; + int HUOFCamMovementY = (int)MoveY; + int MaxX = (HUImageWidth / 2)-1; + int MaxY = HUImageHeight-1; +/* + printf("-----------\n"); + + for (i = 0; i < DistanceMax; ++i) + printf("%d: %d,%d\n", Distances[i][2], Distances[i][0], Distances[i][1]); + + printf("FINAL: %d,%d,%1.2f\n", (int)MoveX, (int)MoveY, DistanceMeasure); + printf("-----------\n"); + printf("Camera movement: %d,%d,%d (max: %d, current: %d)\n", + SampleNums, HUOFCamMovementX, HUOFCamMovementY, MaxTrackedPoints, HUOFPointsNumber); +*/ + HUOFFrames = 0; + HUOFCamMovement = true; + + if (!(HUOFCamMovementY == 0 && HUOFCamMovementX >= -1 && HUOFCamMovementX <= 1)) + { + MEPixelDataType* PreviousData[MaxX+1][MaxY+1]; + + // Camera movement being happened + for (int y = MaxY; y >= 0; --y) + for (int x = MaxX; x >= 0; --x) + { + PreviousData[x][y] = NULL; + } + + // Move the LBP data to new locations + for (int y = MaxY; y >= 0; --y) + for (int x = MaxX; x >= 0; --x) + { + int NewX = x+(HUOFCamMovementX / 2); + int NewY = y+HUOFCamMovementY; + + if (NewX >= 0 && NewX <= MaxX && + NewY >= 0 && NewY <= MaxY) + { + if (HULBPPixelData[NewX][NewY]) + { + PreviousData[NewX][NewY] = HULBPPixelData[NewX][NewY]; + HULBPPixelData[NewX][NewY] = NULL; + if (PreviousData[x][y]) + { + HULBPPixelData[NewX][NewY] = PreviousData[x][y]; + PreviousData[x][y] = NULL; + } else { + HULBPPixelData[NewX][NewY] = HULBPPixelData[x][y]; + HULBPPixelData[x][y] = NULL; + } + } else { + if (PreviousData[x][y]) + { + HULBPPixelData[NewX][NewY] = PreviousData[x][y]; + PreviousData[x][y] = NULL; + } else { + HULBPPixelData[NewX][NewY] = HULBPPixelData[x][y]; + HULBPPixelData[x][y] = NULL; + } + } + } else { + if (HULBPPixelData[x][y]) + { + delete[] HULBPPixelData[x][y]->PreviousHistogram; + for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) + delete[] HULBPPixelData[x][y]->Histograms[i2]; + delete[] HULBPPixelData[x][y]->Histograms; + delete[] HULBPPixelData[x][y]->BackgroundHistogram; + delete[] HULBPPixelData[x][y]->Weights; + delete HULBPPixelData[x][y]; + HULBPPixelData[x][y] = NULL; + } + } + } + // Release unused data + for (int y = MaxY; y >= 0; --y) + for (int x = MaxX; x >= 0; --x) + { + if (PreviousData[x][y]) + { + delete[] PreviousData[x][y]->PreviousHistogram; + for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) + delete[] PreviousData[x][y]->Histograms[i2]; + delete[] PreviousData[x][y]->Histograms; + delete[] PreviousData[x][y]->BackgroundHistogram; + delete[] PreviousData[x][y]->Weights; + delete PreviousData[x][y]; + PreviousData[x][y] = NULL; + } + } + HUOFCamMovementX = HUOFCamMovementX % 1; + } + } + + i1 = 0; + // Throw the missed points away + for (i = 0; i < HUOFPointsNumber; ++i) + { + if (PointsStatus[i] == 1) + { + HUOFPoints[0][i1] = HUOFPoints[1][i]; + i1++; + } + } + HUOFPointsNumber -= i+1-i1; + + if (HUOFPointsNumber < MaxTrackedPoints / 2) + { + printf("Re-init the optical flow\n"); + HUOFDataState = ps_Initialized; + HUOFPointsNumber = CurrentImage.GetWidth()*CurrentImage.GetHeight() / 1000; + } + // Free memory + if (PreviousGray != PreviousImage.GetIplImage()) + cvReleaseImage(&PreviousGray); + if (CurrentGray != CurrentImage.GetIplImage()) + cvReleaseImage(&CurrentGray); + cvFree(&PointsStatus); +} + + +void MotionDetection::GetMotionsMaskHU(MEImage& mask_image) +{ + if (MDDataState != ps_Successful) + { + mask_image.Clear(); + return; + } + + // Reallocate the mask image if needs be + if ((HUImageWidth+HUHistogramArea-1 != mask_image.GetWidth()) || + (HUImageHeight+HUHistogramArea-1 != mask_image.GetHeight()) || + (mask_image.GetLayers() != 1)) + { + mask_image.Realloc(HUImageWidth+HUHistogramArea-1, + HUImageHeight+HUHistogramArea-1, 1); + } + mask_image.Clear(); + // Generate the mask image + unsigned char* MaskImgData = mask_image.GetImageData(); + int RowStart = (mask_image.GetHeight()-HUHistogramArea / 2)*mask_image.GetRowWidth(); + int RowWidth = mask_image.GetRowWidth(); + + // Generate a graph about the histogram data + Graph::node_id Nodes[HUImageWidth / 2][HUImageHeight]; + Graph *LBPGraph = new Graph(); + + for (int x = (HUImageWidth / 2)-1; x >= 0; --x) + for (int y = HUImageHeight-1; y >= 0; --y) + { + Nodes[x][y] = LBPGraph->add_node(); + } + + for (int x = (HUImageWidth / 2)-1; x >= 0; --x) + for (int y = HUImageHeight-1; y >= 0; --y) + { + LBPGraph->set_tweights(Nodes[x][y], 1, + (short int)(HUMinCutWeight*(1-HULBPPixelData[x][y]->BackgroundRate))); + + if (x > 0 && y > 0) + { + LBPGraph->add_edge(Nodes[x][y], Nodes[x-1][y], 1, 1); + LBPGraph->add_edge(Nodes[x][y], Nodes[x][y-1], 1, 1); + } + } + + LBPGraph->maxflow(); + + for (int x = (HUImageWidth / 2)-1; x >= 0; --x) + for (int y = HUImageHeight-1; y >= 0; --y) + { + if (LBPGraph->what_segment(Nodes[x][y]) == Graph::SINK) + HULBPPixelData[x][y]->BackgroundRate = 0.0; + else + HULBPPixelData[x][y]->BackgroundRate = 1.0; + } + + delete LBPGraph; + LBPGraph = NULL; + for (int y = HUImageHeight-1; y >= 0; --y) + { + for (int x = HUImageWidth-1; x >= 0; --x) + { + if (y % 2 == (x+1) % 2) + MaskImgData[RowStart+x+(HUHistogramArea / 2)] = + (HULBPPixelData[x / 2][y]->BackgroundRate == 0.0) ? 255 : 0; + else { + MaskImgData[RowStart+x+(HUHistogramArea / 2)] = + ((int)(x > 1 && HULBPPixelData[(x / 2)-1][y]->BackgroundRate == 0.0)+ + (int)(x < mask_image.GetWidth()-HUHistogramArea-1 && + HULBPPixelData[(x / 2)+1][y]->BackgroundRate == 0.0)+ + (int)(y > 0 && HULBPPixelData[x / 2][y-1]->BackgroundRate == 0.0)+ + (int)(y < mask_image.GetHeight()-HUHistogramArea && + HULBPPixelData[x / 2][y+1]->BackgroundRate == 0.0) > 1) + ? 255 : 0; + } + } + RowStart -= RowWidth; + } + + cvFloodFill(mask_image.GetIplImage(), cvPoint(0, 0), cvScalar(128, 128, 128, 128), + cvScalar(0, 0, 0, 0), cvScalar(0, 0, 0, 0)); + for (int i = ((IplImage*)mask_image.GetIplImage())->widthStep*((IplImage*)mask_image.GetIplImage())->height-1; i >= 0; --i) + { + if (MaskImgData[i] == 128) + { + MaskImgData[i] = 0; + } else + if (MaskImgData[i] == 0) + { + MaskImgData[i] = 255; + } + } + // Apply an erode operator + mask_image.Erode(1); +} + + +void MotionDetection::SetSampleMaskHU(SampleMaskType mask_type, int desiredarea) +{ + if (HUMaskColumnAddDel == NULL || HUMaskRowAddDel == NULL) + { + printf("Auxiliary variables are NULL\n"); + return; + } + + // Generate a mask for computing the histograms + IplImage *MaskImage = cvCreateImage(cvSize(HUHistogramArea, HUHistogramArea), 8, 1); + int DesiredArea = desiredarea <= 0 ? HUHistogramBins*2 : desiredarea; + int CalculationMask[HUHistogramArea][HUHistogramArea]; + int SquareSide = (int)MERound(sqrt(DesiredArea)); + int CircleRadius = (int)MERound(sqrt((float)DesiredArea / ME_PI_VALUE)); + int EllipseA = (int)MERound(HUHistogramArea / 2+1); + int EllipseB = (int)MERound(DesiredArea / (EllipseA*1.2*ME_PI_VALUE)); + + cvSetZero(MaskImage); + + switch (mask_type) + { + case sm_Circle: + cvCircle(MaskImage, cvPoint(HUHistogramArea / 2, HUHistogramArea / 2), + CircleRadius, CV_RGB(1, 1, 1), -1); + break; + + case sm_Square: + cvRectangle(MaskImage, + cvPoint(HUHistogramArea / 2-SquareSide / 2, HUHistogramArea / 2-SquareSide / 2), + cvPoint(HUHistogramArea / 2+SquareSide / 2, HUHistogramArea / 2+SquareSide / 2), + CV_RGB(1, 1, 1), -1); + break; + + case sm_Ellipse: + cvEllipse(MaskImage, cvPoint(HUHistogramArea / 2, HUHistogramArea / 2), + cvSize(EllipseA, EllipseB), 45, 0, 360, + CV_RGB(1, 1, 1), -1); + break; + + case sm_RandomPixels: + HUSamplePixels = 0; + while (HUSamplePixels != DesiredArea) + { + int i = rand() % HUHistogramArea; + int j = rand() % HUHistogramArea; + + if (MaskImage->imageData[i*MaskImage->widthStep+j] == 0) + { + MaskImage->imageData[i*MaskImage->widthStep+j] = 1; + HUSamplePixels++; + } + } + break; + + default: + cvCircle(MaskImage, cvPoint(HUHistogramArea / 2, HUHistogramArea / 2), + (int)MERound(sqrt((float)DesiredArea / ME_PI_VALUE)), CV_RGB(1, 1, 1), -1); + break; + } + + HUSamplePixels = 0; + memset(CalculationMask, 0, sizeof(CalculationMask)); + + for (int i = 0; i < HUHistogramArea; ++i) + for (int i1 = 0; i1 < HUHistogramArea; ++i1) + { + if (MaskImage->imageData[i*MaskImage->widthStep+i1] != 0) + { + HUSamplePixels++; + CalculationMask[i][i1] = 1; + } else { + CalculationMask[i][i1] = 0; + } + } + + // Fill an auxiliary variable for fast computing with data + for (int i = 0; i < HUHistogramArea; ++i) + { + HUMaskColumnAddDel[i][0] = -1; + for (int i1 = 0; i1 < HUHistogramArea; ++i1) + { + if (CalculationMask[i][i1] != 0) + { + HUMaskColumnAddDel[i][0] = i1; + break; + } + } + HUMaskColumnAddDel[i][1] = -1; + for (int i1 = HUHistogramArea-1; i1 >= 0; --i1) + { + if (CalculationMask[i][i1] != 0) + { + HUMaskColumnAddDel[i][1] = i1+1; + break; + } + } + } + // Fill an auxiliary variable for fast computing with data + for (int i = 0; i < HUHistogramArea; ++i) + { + HUMaskRowAddDel[i][0] = -1; + for (int i1 = 0; i1 < HUHistogramArea; ++i1) + { + if (CalculationMask[i1][i] != 0) + { + HUMaskRowAddDel[i][0] = i1; + break; + } + } + HUMaskRowAddDel[i][1] = -1; + for (int i1 = HUHistogramArea-1; i1 >= 0; --i1) + { + if (CalculationMask[i1][i] != 0) + { + HUMaskRowAddDel[i][1] = i1+1; + break; + } + } + } + // Freeing memory + cvReleaseImage(&MaskImage); +} + diff --git a/package_bgs/ck/MotionDetection.hpp b/package_bgs/ck/MotionDetection.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6e11521d493b40ef78ce069d0ad854ac627a4748 --- /dev/null +++ b/package_bgs/ck/MotionDetection.hpp @@ -0,0 +1,401 @@ +/* + * This file is part of the AiBO+ project + * + * Copyright (C) 2005-2013 Csaba Kertész (csaba.kertesz@gmail.com) + * + * AiBO+ is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * AiBO+ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef MotionDetection_hpp +#define MotionDetection_hpp + +/** + * @addtogroup mindeye + * @{ + */ + +#include "MEDefs.hpp" +#include "MEImage.hpp" + +class CvBGStatModel; +class CvPoint2D32f; + +// Struct for histogram update data of a pixel +struct MEPixelDataType; + +/** + * MotionDetection + * @brief Extract moving objects from image sequence + */ +class MotionDetection +{ +public: + + /// Types of motion detection + typedef enum + { + md_Min = 0, /*!< Minimum value */ + md_NotDefined = md_Min, /*!< Not defined */ + md_DLBPHistograms, /*!< Dynamic LBP */ + md_LBPHistograms, /*!< Normal LBP */ + md_Max = md_LBPHistograms /*!< Maximum value */ + } DetectorType; + + /// Types of sample mask + typedef enum + { + sm_Min = 0, /*!< Minimum value */ + sm_Circle = sm_Min, /*!< Circle */ + sm_Square, /*!< Square */ + sm_Ellipse, /*!< Ellipse */ + sm_RandomPixels, /*!< Random pixels */ + sm_Max = sm_RandomPixels /*!< Maximum value */ + } SampleMaskType; + + /// Types of motion detection parameters + typedef enum + { + mdp_Min = 0, /*!< Minimum value */ + mdp_HUProximityThreshold = mdp_Min, /*!< Proximity threshold */ + mdp_HUBackgroundThreshold, /*!< Background threshold */ + mdp_HUHistogramLearningRate, /*!< Histogram learning rate */ + mdp_HUWeightsLearningRate, /*!< Weights learning rate */ + mdp_HUMinCutWeight, /*!< Minimum cut weight */ + mdp_HUDesiredSamplePixels, /*!< Desired sample pixels */ + mdp_HUHistogramsPerPixel, /*!< Histogram per pixel */ + mdp_HUHistogramArea, /*!< Histogram area */ + mdp_HUHistogramBins, /*!< Histogram bins */ + mdp_HUColorSpace, /*!< Color space */ + mdp_HULBPMode, /*!< LBP mode */ + mdp_Max = mdp_HULBPMode /*!< Maximum value */ + } ParametersType; + + /*! + * @brief Class constructor + * + * @param mode Detection mode + * + * Class constructor with the possibility to specify the detection mode. + * The default is dynamic LBP. + * + */ + + MotionDetection(DetectorType mode = md_DLBPHistograms); + /// Destructor of class + ~MotionDetection(); + + /* + ------------------------------------------------------------------- + Motion methods + ------------------------------------------------------------------- + */ + + /*! + * @brief Set the mode of the motion detection + * + * @param newmode New mode of detection + * + * Set the mode of the motion detection. + * + */ + + void SetMode(DetectorType newmode); + + /*! + * @brief Get a parameter value of the motion detection + * + * @param param Parameter of the detection + * + * @return Queried value + * + * Get the value of a parameter of the motion detection. + * + */ + + float GetParameter(ParametersType param) const; + + /*! + * @brief Set a parameter of the motion detection + * + * @param param Parameter of the detection + * @param value New value + * + * Set a new value to a parameter of the motion detection. + * + */ + + void SetParameter(ParametersType param, float value); + + /*! + * @brief Detect the motions on an image + * + * @param image Image to process + * + * The function designed to search motions in image streams + * thus it needs to process the image sequence frame by frame. + * It processes an image from this sequence and searches moving blobs + * on that. + * + */ + + void DetectMotions(MEImage& image); + + /*! + * @brief Get mask image with detected motions + * + * @param mask_image Result mask image + * + * The function creates a mask image on which the objects are + * indicated by white blobs. + * + */ + + void GetMotionsMask(MEImage& mask_image); + + /*! + * @brief Calculate results of the motion detection + * + * @param referenceimage Reference mask image + * @param tnegatives True negative pixels + * @param tpositives True positive pixels + * @param ttnegatives Total true negative pixels + * @param ttpositives Total true positive pixels + * + * The function calculates the results of the motion detection + * between the current motion mask and a given reference mask + * image. + * + */ + + void CalculateResults(MEImage& referenceimage, int& tnegatives, int& tpositives, + int& ttnegatives, int& ttpositives); + +private: + + /*! + * @brief Release data structures + * + * Function releases the data structures. + * + */ + + void ReleaseData(); + + /* + ------------------------------------------------------------------- + Histogram update methods + ------------------------------------------------------------------- + */ + + /*! + * @brief Init HU data structures + * + * @param imagewidth Image width for HU to process + * @param imageheight Image height for HU to process + * + * Function allocates/re-allocates the HU data structures and they + * are cleared if needs be. + * + */ + + void InitHUData(int imagewidth, int imageheight); + + /*! + * @brief Init HU optical flow data structures + * + * @param imagewidth Image width for HU to process + * @param imageheight Image height for HU to process + * + * Function allocates/re-allocates the HU optical flow + * data structures. + * + */ + + void InitHUOFData(int imagewidth, int imageheight); + + /*! + * @brief Release HU data structures + * + * Function releases the HU data structures. + * + */ + + void ReleaseHUData(); + + /*! + * @brief Release HU optical flow data structures + * + * Function releases the HU optical flow data structures. + * + */ + + void ReleaseHUOFData(); + + /*! + * @brief Clear HU data structures + * + * Function clears the HU data structures. + * + */ + + void ClearHUData(); + + /*! + * @brief Get mask image with detected motions by histogram update + * + * @param mask_image Result mask image + * + * The function creates a mask image on which the objects are + * indicated by white blobs. + * + */ + + void GetMotionsMaskHU(MEImage& mask_image); + + /*! + * @brief Set the sample mask + * + * @param mask_type Type of the mask + * @param desiredarea The desired area size of the mask + * + * The function creates a sample mask with a desired form + * (square, circle, ellipse, random pixels) and size. + * + */ + + void SetSampleMaskHU(SampleMaskType mask_type, int desiredarea); + + /*! + * @brief Detect the motions on an image with histogram update + * + * @param image Image to process + * + * The function designed to search motions in image streams + * thus it needs to process the image sequence frame by frame. + * It processes an image from this sequence and searches moving blobs + * on that. It uses histogram update method. + * + */ + + void DetectMotionsHU(MEImage& image); + + /*! + * @brief Update a model + * + * @param image Image to process + * @param model Model to update + * + * The function updates a histogram model of the image. + * + */ + + void UpdateModelHU(MEImage& image, MEPixelDataType*** model); + + /*! + * @brief Update the HU data structure for one pixel + * + * @param pixeldata Pixel data + * @param histogram Current histogram + * + * This method updates the HU data for one pixel. + * + */ + + void UpdateHUPixelData(MEPixelDataType* pixeldata, const float *histogram); + + /*! + * @brief Optical flow correction of the camera movements + * + * The function trackes some points on the scene if a camera movement is + * detected, then the LBP pixel data is corrected. + * + */ + + void OpticalFlowCorrection(); + +private: + // GENERAL VARIABLES + /// Motion detection type + DetectorType MDMode; + /// State of the data structures + MEProcessStateType MDDataState; + /// Processed number in the image sequence + int Frames; + /// Store the current image + MEImage CurrentImage; + /// Store the previous image + MEImage PreviousImage; + /// Store the current mask image + MEImage MaskImage; + /// Store the current mask image + bool ReadyMask; + // HISTOGRAM UPDATE VARIABLES + /// Color space (-1 = no conversion) + int HUColorSpace; + /// LBP calculation mode (-1 = no conversion) + int HULBPMode; + /// Histograms per pixel + int HUHistogramsPerPixel; + /// Histogram area + int HUHistogramArea; + /// Histogram bins + int HUHistogramBins; + /// Image width for histogram update + int HUImageWidth; + /// Image height for histogram update + int HUImageHeight; + /// Data of the LBP histograms + MEPixelDataType ***HULBPPixelData; + /// Store the previous blue layer + MEImage PreviousBlueLayer; + /// Histogram proximity threshold + float HUPrThres; + /// Background selection threshold + float HUBackgrThres; + /// Histogram learning rate + float HUHistLRate; + /// Weights learning rate + float HUWeightsLRate; + /// Pixel number used to calculate the histograms + int HUSamplePixels; + /// The desired pixel number used to calculate the histograms (-1 = Auto) + int HUDesiredSamplePixels; + /// Min cut weight + float HUMinCutWeight; + /// Auxiliary variable for computing the histograms in a column + int **HUMaskColumnAddDel; + /// Auxiliary variable for computing the histograms in a row + int **HUMaskRowAddDel; + // OPTICAL FLOW VARIABLES + /// State of the optical flow + MEProcessStateType HUOFDataState; + /// Number of the tracked points with optical flow + int HUOFPointsNumber; + /// Tracked points + CvPoint2D32f* HUOFPoints[2]; + /// The rest x component of previous camera movement + int HUOFCamMovementX; + /// Maximum tracked points detected in one cycle + int MaxTrackedPoints; + /// Processed frame number with optical flow in the image sequence + int HUOFFrames; + /// Indicator of a new camera movement + bool HUOFCamMovement; +}; + +/** @} */ + +#endif diff --git a/package_bgs/ck/README.TXT b/package_bgs/ck/README.TXT new file mode 100644 index 0000000000000000000000000000000000000000..d76c6a3438d3b26fb98b3355f6359b2474ccea34 --- /dev/null +++ b/package_bgs/ck/README.TXT @@ -0,0 +1,135 @@ +################################################################### +# # +# MAXFLOW - software for computing mincut/maxflow in a graph # +# Version 2.2 # +# http://www.cs.cornell.edu/People/vnk/software.html # +# # +# Yuri Boykov (yuri@csd.uwo.ca) # +# Vladimir Kolmogorov (vnk@cs.cornell.edu) # +# 2001 # +# # +################################################################### + +1. Introduction. + +This software library implements the maxflow algorithm +described in + + An Experimental Comparison of Min-Cut/Max-Flow Algorithms + for Energy Minimization in Vision. + Yuri Boykov and Vladimir Kolmogorov. + In IEEE Transactions on Pattern Analysis and Machine Intelligence (PAMI), + September 2004 + +This algorithm was developed by Yuri Boykov and Vladimir Kolmogorov +at Siemens Corporate Research. To make it available for public use, +it was later reimplemented by Vladimir Kolmogorov based on open publications. + +If you use this software for research purposes, you should cite +the aforementioned paper in any resulting publication. + +Tested under windows, Visual C++ 6.0 compiler and unix (SunOS 5.8 +and RedHat Linux 7.0, GNU c++ compiler). + +################################################################## + +2. License. + + Copyright 2001 Vladimir Kolmogorov (vnk@cs.cornell.edu), Yuri Boykov (yuri@csd.uwo.ca). + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +################################################################## + +3. Graph representation. + +There are two versions of the algorithm using different +graph representations (adjacency list and forward star). +The former one uses more than twice as much memory as the +latter one but is 10-20% faster. + +Memory allocation (assuming that all capacities are 'short' - 2 bytes): + + | Nodes | Arcs +------------------------------------------ +Adjacency list | *24 bytes | *14 bytes +Forward star | *28 bytes | 6 bytes + +(* means that often it should be rounded up to be a multiple of 4 +- some compilers (e.g. Visual C++) seem to round up elements +of arrays unless the are structures containing only char[].) + +Note that arcs are always added in pairs - in forward and reverse directions. +Arcs between nodes and terminals (the source and the sink) are +not stored as arcs, but rather as a part of nodes. + +The assumption for the forward star representation is that +the maximum number of arcs per node (except the source +and the sink) is much less than ARC_BLOCK_SIZE (1024 by default). + +Both versions have the same interface. + +################################################################## + +4. Example usage. + +This section shows how to use the library to compute +a minimum cut on the following graph: + + SOURCE + / \ + 1/ \2 + / 3 \ + node0 -----> node1 + | <----- | + | 4 | + \ / + 5\ /6 + \ / + SINK + +/////////////////////////////////////////////////// + +#include <stdio.h> +#include "graph.h" + +void main() +{ + Graph::node_id nodes[2]; + Graph *g = new Graph(); + + nodes[0] = g -> add_node(); + nodes[1] = g -> add_node(); + g -> set_tweights(nodes[0], 1, 5); + g -> set_tweights(nodes[1], 2, 6); + g -> add_edge(nodes[0], nodes[1], 3, 4); + + Graph::flowtype flow = g -> maxflow(); + + printf("Flow = %d\n", flow); + printf("Minimum cut:\n"); + if (g->what_segment(nodes[0]) == Graph::SOURCE) + printf("node0 is in the SOURCE set\n"); + else + printf("node0 is in the SINK set\n"); + if (g->what_segment(nodes[1]) == Graph::SOURCE) + printf("node1 is in the SOURCE set\n"); + else + printf("node1 is in the SINK set\n"); + + delete g; +} + +/////////////////////////////////////////////////// diff --git a/package_bgs/ck/block.h b/package_bgs/ck/block.h new file mode 100644 index 0000000000000000000000000000000000000000..a243ed1da41b47512afa8ddf9912a275b04cf76a --- /dev/null +++ b/package_bgs/ck/block.h @@ -0,0 +1,286 @@ +/* block.h */ +/* + Copyright 2001 Vladimir Kolmogorov (vnk@cs.cornell.edu), Yuri Boykov (yuri@csd.uwo.ca). + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/* + Template classes Block and DBlock + Implement adding and deleting items of the same type in blocks. + + If there there are many items then using Block or DBlock + is more efficient than using 'new' and 'delete' both in terms + of memory and time since + (1) On some systems there is some minimum amount of memory + that 'new' can allocate (e.g., 64), so if items are + small that a lot of memory is wasted. + (2) 'new' and 'delete' are designed for items of varying size. + If all items has the same size, then an algorithm for + adding and deleting can be made more efficient. + (3) All Block and DBlock functions are inline, so there are + no extra function calls. + + Differences between Block and DBlock: + (1) DBlock allows both adding and deleting items, + whereas Block allows only adding items. + (2) Block has an additional operation of scanning + items added so far (in the order in which they were added). + (3) Block allows to allocate several consecutive + items at a time, whereas DBlock can add only a single item. + + Note that no constructors or destructors are called for items. + + Example usage for items of type 'MyType': + + /////////////////////////////////////////////////// + #include "block.h" + #define BLOCK_SIZE 1024 + typedef struct { int a, b; } MyType; + MyType *ptr, *array[10000]; + + ... + + Block<MyType> *block = new Block<MyType>(BLOCK_SIZE); + + // adding items + for (int i=0; i<sizeof(array); i++) + { + ptr = block -> New(); + ptr -> a = ptr -> b = rand(); + } + + // reading items + for (ptr=block->ScanFirst(); ptr; ptr=block->ScanNext()) + { + printf("%d %d\n", ptr->a, ptr->b); + } + + delete block; + + ... + + DBlock<MyType> *dblock = new DBlock<MyType>(BLOCK_SIZE); + + // adding items + for (int i=0; i<sizeof(array); i++) + { + array[i] = dblock -> New(); + } + + // deleting items + for (int i=0; i<sizeof(array); i+=2) + { + dblock -> Delete(array[i]); + } + + // adding items + for (int i=0; i<sizeof(array); i++) + { + array[i] = dblock -> New(); + } + + delete dblock; + + /////////////////////////////////////////////////// + + Note that DBlock deletes items by marking them as + empty (i.e., by adding them to the list of free items), + so that this memory could be used for subsequently + added items. Thus, at each moment the memory allocated + is determined by the maximum number of items allocated + simultaneously at earlier moments. All memory is + deallocated only when the destructor is called. +*/ + +#ifndef __BLOCK_H__ +#define __BLOCK_H__ + +#include <stdlib.h> +#include <stdio.h> + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + +template <class Type> class Block +{ +public: + /* Constructor. Arguments are the block size and + (optionally) the pointer to the function which + will be called if allocation failed; the message + passed to this function is "Not enough memory!" */ + Block(int size, void (*err_function)(char *) = NULL) { first = last = NULL; block_size = size; error_function = err_function; } + + /* Destructor. Deallocates all items added so far */ + ~Block() { while (first) { block *next = first -> next; delete first; first = next; } } + + /* Allocates 'num' consecutive items; returns pointer + to the first item. 'num' cannot be greater than the + block size since items must fit in one block */ + Type *New(int num = 1) + { + Type *t; + + if (!last || last->current + num > last->last) + { + if (last && last->next) last = last -> next; + else + { + block *next = (block *) new char [sizeof(block) + (block_size-1)*sizeof(Type)]; + if (!next) { fprintf(stderr, "Not enough memory!"); exit(1); } + if (last) last -> next = next; + else first = next; + last = next; + last -> current = & ( last -> data[0] ); + last -> last = last -> current + block_size; + last -> next = NULL; + } + } + + t = last -> current; + last -> current += num; + return t; + } + + /* Returns the first item (or NULL, if no items were added) */ + Type *ScanFirst() + { + scan_current_block = first; + if (!scan_current_block) return NULL; + scan_current_data = & ( scan_current_block -> data[0] ); + return scan_current_data ++; + } + + /* Returns the next item (or NULL, if all items have been read) + Can be called only if previous ScanFirst() or ScanNext() + call returned not NULL. */ + Type *ScanNext() + { + if (scan_current_data >= scan_current_block -> current) + { + scan_current_block = scan_current_block -> next; + if (!scan_current_block) return NULL; + scan_current_data = & ( scan_current_block -> data[0] ); + } + return scan_current_data ++; + } + + /* Marks all elements as empty */ + void Reset() + { + block *b; + if (!first) return; + for (b=first; ; b=b->next) + { + b -> current = & ( b -> data[0] ); + if (b == last) break; + } + last = first; + } + +/***********************************************************************/ + +private: + + typedef struct block_st + { + Type *current, *last; + struct block_st *next; + Type data[1]; + } block; + + int block_size; + block *first; + block *last; + + block *scan_current_block; + Type *scan_current_data; + + void (*error_function)(char *); +}; + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + +template <class Type> class DBlock +{ +public: + /* Constructor. Arguments are the block size and + (optionally) the pointer to the function which + will be called if allocation failed; the message + passed to this function is "Not enough memory!" */ + DBlock(int size, void (*err_function)(char *) = NULL) { first = NULL; first_free = NULL; block_size = size; error_function = err_function; } + + /* Destructor. Deallocates all items added so far */ + ~DBlock() { while (first) { block *next = first -> next; delete first; first = next; } } + + /* Allocates one item */ + Type *New() + { + block_item *item; + + if (!first_free) + { + block *next = first; + first = (block *) new char [sizeof(block) + (block_size-1)*sizeof(block_item)]; + if (!first) { fprintf(stderr, "Not enough memory!"); exit(1); } + first_free = & (first -> data[0] ); + for (item=first_free; item<first_free+block_size-1; item++) + item -> next_free = item + 1; + item -> next_free = NULL; + first -> next = next; + } + + item = first_free; + first_free = item -> next_free; + return (Type *) item; + } + + /* Deletes an item allocated previously */ + void Delete(Type *t) + { + ((block_item *) t) -> next_free = first_free; + first_free = (block_item *) t; + } + +/***********************************************************************/ + +private: + + typedef union block_item_st + { + Type t; + block_item_st *next_free; + } block_item; + + typedef struct block_st + { + struct block_st *next; + block_item data[1]; + } block; + + int block_size; + block *first; + block_item *first_free; + + void (*error_function)(char *); +}; + + +#endif + diff --git a/package_bgs/ck/graph.cpp b/package_bgs/ck/graph.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fb4cf9ff752514a402eedb9acf0ab061347395af --- /dev/null +++ b/package_bgs/ck/graph.cpp @@ -0,0 +1,80 @@ +/* graph.cpp */ +/* + Copyright 2001 Vladimir Kolmogorov (vnk@cs.cornell.edu), Yuri Boykov (yuri@csd.uwo.ca). + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include <stdio.h> +#include "graph.h" + +Graph::Graph(void (*err_function)(char *)) +{ + error_function = err_function; + node_block = new Block<node>(NODE_BLOCK_SIZE, error_function); + arc_block = new Block<arc>(NODE_BLOCK_SIZE, error_function); + flow = 0; +} + +Graph::~Graph() +{ + delete node_block; + delete arc_block; +} + +Graph::node_id Graph::add_node() +{ + node *i = node_block -> New(); + + i -> first = NULL; + i -> tr_cap = 0; + + return (node_id) i; +} + +void Graph::add_edge(node_id from, node_id to, captype cap, captype rev_cap) +{ + arc *a, *a_rev; + + a = arc_block -> New(2); + a_rev = a + 1; + + a -> sister = a_rev; + a_rev -> sister = a; + a -> next = ((node*)from) -> first; + ((node*)from) -> first = a; + a_rev -> next = ((node*)to) -> first; + ((node*)to) -> first = a_rev; + a -> head = (node*)to; + a_rev -> head = (node*)from; + a -> r_cap = cap; + a_rev -> r_cap = rev_cap; +} + +void Graph::set_tweights(node_id i, captype cap_source, captype cap_sink) +{ + flow += (cap_source < cap_sink) ? cap_source : cap_sink; + ((node*)i) -> tr_cap = cap_source - cap_sink; +} + +void Graph::add_tweights(node_id i, captype cap_source, captype cap_sink) +{ + register captype delta = ((node*)i) -> tr_cap; + if (delta > 0) cap_source += delta; + else cap_sink -= delta; + flow += (cap_source < cap_sink) ? cap_source : cap_sink; + ((node*)i) -> tr_cap = cap_source - cap_sink; +} diff --git a/package_bgs/ck/graph.h b/package_bgs/ck/graph.h new file mode 100644 index 0000000000000000000000000000000000000000..fdae5661b6b9a2ea56317a79e6c680503475f015 --- /dev/null +++ b/package_bgs/ck/graph.h @@ -0,0 +1,180 @@ +/* graph.h */ +/* + This software library implements the maxflow algorithm + described in + + An Experimental Comparison of Min-Cut/Max-Flow Algorithms + for Energy Minimization in Vision. + Yuri Boykov and Vladimir Kolmogorov. + In IEEE Transactions on Pattern Analysis and Machine Intelligence (PAMI), + September 2004 + + This algorithm was developed by Yuri Boykov and Vladimir Kolmogorov + at Siemens Corporate Research. To make it available for public use, + it was later reimplemented by Vladimir Kolmogorov based on open publications. + + If you use this software for research purposes, you should cite + the aforementioned paper in any resulting publication. +*/ + +/* + Copyright 2001 Vladimir Kolmogorov (vnk@cs.cornell.edu), Yuri Boykov (yuri@csd.uwo.ca). + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/* + For description, example usage, discussion of graph representation + and memory usage see README.TXT. +*/ + +#ifndef __GRAPH_H__ +#define __GRAPH_H__ + +#include "block.h" + +/* + Nodes, arcs and pointers to nodes are + added in blocks for memory and time efficiency. + Below are numbers of items in blocks +*/ +#define NODE_BLOCK_SIZE 512 +#define ARC_BLOCK_SIZE 1024 +#define NODEPTR_BLOCK_SIZE 128 + +class Graph +{ +public: + typedef enum + { + SOURCE = 0, + SINK = 1 + } termtype; /* terminals */ + + /* Type of edge weights. + Can be changed to char, int, float, double, ... */ + typedef short captype; + /* Type of total flow */ + typedef int flowtype; + + typedef void * node_id; + + /* interface functions */ + + /* Constructor. Optional argument is the pointer to the + function which will be called if an error occurs; + an error message is passed to this function. If this + argument is omitted, exit(1) will be called. */ + Graph(void (*err_function)(char *) = NULL); + + /* Destructor */ + ~Graph(); + + /* Adds a node to the graph */ + node_id add_node(); + + /* Adds a bidirectional edge between 'from' and 'to' + with the weights 'cap' and 'rev_cap' */ + void add_edge(node_id from, node_id to, captype cap, captype rev_cap); + + /* Sets the weights of the edges 'SOURCE->i' and 'i->SINK' + Can be called at most once for each node before any call to 'add_tweights'. + Weights can be negative */ + void set_tweights(node_id i, captype cap_source, captype cap_sink); + + /* Adds new edges 'SOURCE->i' and 'i->SINK' with corresponding weights + Can be called multiple times for each node. + Weights can be negative */ + void add_tweights(node_id i, captype cap_source, captype cap_sink); + + /* After the maxflow is computed, this function returns to which + segment the node 'i' belongs (Graph::SOURCE or Graph::SINK) */ + termtype what_segment(node_id i); + + /* Computes the maxflow. Can be called only once. */ + flowtype maxflow(); + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + +private: + /* internal variables and functions */ + + struct arc_st; + + /* node structure */ + typedef struct node_st + { + arc_st *first; /* first outcoming arc */ + + arc_st *parent; /* node's parent */ + node_st *next; /* pointer to the next active node + (or to itself if it is the last node in the list) */ + int TS; /* timestamp showing when DIST was computed */ + int DIST; /* distance to the terminal */ + short is_sink; /* flag showing whether the node is in the source or in the sink tree */ + + captype tr_cap; /* if tr_cap > 0 then tr_cap is residual capacity of the arc SOURCE->node + otherwise -tr_cap is residual capacity of the arc node->SINK */ + } node; + + /* arc structure */ + typedef struct arc_st + { + node_st *head; /* node the arc points to */ + arc_st *next; /* next arc with the same originating node */ + arc_st *sister; /* reverse arc */ + + captype r_cap; /* residual capacity */ + } arc; + + /* 'pointer to node' structure */ + typedef struct nodeptr_st + { + node_st *ptr; + nodeptr_st *next; + } nodeptr; + + Block<node> *node_block; + Block<arc> *arc_block; + DBlock<nodeptr> *nodeptr_block; + + void (*error_function)(char *); /* this function is called if a error occurs, + with a corresponding error message + (or exit(1) is called if it's NULL) */ + + flowtype flow; /* total flow */ + +/***********************************************************************/ + + node *queue_first[2], *queue_last[2]; /* list of active nodes */ + nodeptr *orphan_first, *orphan_last; /* list of pointers to orphans */ + int TIME; /* monotonically increasing global counter */ + +/***********************************************************************/ + + /* functions for processing active list */ + void set_active(node *i); + node *next_active(); + + void maxflow_init(); + void augment(arc *middle_arc); + void process_source_orphan(node *i); + void process_sink_orphan(node *i); +}; + +#endif diff --git a/package_bgs/ck/maxflow.cpp b/package_bgs/ck/maxflow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8812a4a85d016049b70351c5aca936acca21868f --- /dev/null +++ b/package_bgs/ck/maxflow.cpp @@ -0,0 +1,514 @@ +/* maxflow.cpp */ +/* + Copyright 2001 Vladimir Kolmogorov (vnk@cs.cornell.edu), Yuri Boykov (yuri@csd.uwo.ca). + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include <stdio.h> +#include "graph.h" + +/* + special constants for node->parent +*/ +#define TERMINAL ( (arc *) 1 ) /* to terminal */ +#define ORPHAN ( (arc *) 2 ) /* orphan */ + +#define INFINITE_D 1000000000 /* infinite distance to the terminal */ + +/***********************************************************************/ + +/* + Functions for processing active list. + i->next points to the next node in the list + (or to i, if i is the last node in the list). + If i->next is NULL iff i is not in the list. + + There are two queues. Active nodes are added + to the end of the second queue and read from + the front of the first queue. If the first queue + is empty, it is replaced by the second queue + (and the second queue becomes empty). +*/ + +inline void Graph::set_active(node *i) +{ + if (!i->next) + { + /* it's not in the list yet */ + if (queue_last[1]) queue_last[1] -> next = i; + else queue_first[1] = i; + queue_last[1] = i; + i -> next = i; + } +} + +/* + Returns the next active node. + If it is connected to the sink, it stays in the list, + otherwise it is removed from the list +*/ +inline Graph::node * Graph::next_active() +{ + node *i; + + while ( 1 ) + { + if (!(i=queue_first[0])) + { + queue_first[0] = i = queue_first[1]; + queue_last[0] = queue_last[1]; + queue_first[1] = NULL; + queue_last[1] = NULL; + if (!i) return NULL; + } + + /* remove it from the active list */ + if (i->next == i) queue_first[0] = queue_last[0] = NULL; + else queue_first[0] = i -> next; + i -> next = NULL; + + /* a node in the list is active iff it has a parent */ + if (i->parent) return i; + } +} + +/***********************************************************************/ + +void Graph::maxflow_init() +{ + node *i; + + queue_first[0] = queue_last[0] = NULL; + queue_first[1] = queue_last[1] = NULL; + orphan_first = NULL; + + for (i=node_block->ScanFirst(); i; i=node_block->ScanNext()) + { + i -> next = NULL; + i -> TS = 0; + if (i->tr_cap > 0) + { + /* i is connected to the source */ + i -> is_sink = 0; + i -> parent = TERMINAL; + set_active(i); + i -> TS = 0; + i -> DIST = 1; + } + else if (i->tr_cap < 0) + { + /* i is connected to the sink */ + i -> is_sink = 1; + i -> parent = TERMINAL; + set_active(i); + i -> TS = 0; + i -> DIST = 1; + } + else + { + i -> parent = NULL; + } + } + TIME = 0; +} + +/***********************************************************************/ + +void Graph::augment(arc *middle_arc) +{ + node *i; + arc *a; + captype bottleneck; + nodeptr *np; + + + /* 1. Finding bottleneck capacity */ + /* 1a - the source tree */ + bottleneck = middle_arc -> r_cap; + for (i=middle_arc->sister->head; ; i=a->head) + { + a = i -> parent; + if (a == TERMINAL) break; + if (bottleneck > a->sister->r_cap) bottleneck = a -> sister -> r_cap; + } + if (bottleneck > i->tr_cap) bottleneck = i -> tr_cap; + /* 1b - the sink tree */ + for (i=middle_arc->head; ; i=a->head) + { + a = i -> parent; + if (a == TERMINAL) break; + if (bottleneck > a->r_cap) bottleneck = a -> r_cap; + } + if (bottleneck > - i->tr_cap) bottleneck = - i -> tr_cap; + + + /* 2. Augmenting */ + /* 2a - the source tree */ + middle_arc -> sister -> r_cap += bottleneck; + middle_arc -> r_cap -= bottleneck; + for (i=middle_arc->sister->head; ; i=a->head) + { + a = i -> parent; + if (a == TERMINAL) break; + a -> r_cap += bottleneck; + a -> sister -> r_cap -= bottleneck; + if (!a->sister->r_cap) + { + /* add i to the adoption list */ + i -> parent = ORPHAN; + np = nodeptr_block -> New(); + np -> ptr = i; + np -> next = orphan_first; + orphan_first = np; + } + } + i -> tr_cap -= bottleneck; + if (!i->tr_cap) + { + /* add i to the adoption list */ + i -> parent = ORPHAN; + np = nodeptr_block -> New(); + np -> ptr = i; + np -> next = orphan_first; + orphan_first = np; + } + /* 2b - the sink tree */ + for (i=middle_arc->head; ; i=a->head) + { + a = i -> parent; + if (a == TERMINAL) break; + a -> sister -> r_cap += bottleneck; + a -> r_cap -= bottleneck; + if (!a->r_cap) + { + /* add i to the adoption list */ + i -> parent = ORPHAN; + np = nodeptr_block -> New(); + np -> ptr = i; + np -> next = orphan_first; + orphan_first = np; + } + } + i -> tr_cap += bottleneck; + if (!i->tr_cap) + { + /* add i to the adoption list */ + i -> parent = ORPHAN; + np = nodeptr_block -> New(); + np -> ptr = i; + np -> next = orphan_first; + orphan_first = np; + } + + + flow += bottleneck; +} + +/***********************************************************************/ + +void Graph::process_source_orphan(node *i) +{ + node *j; + arc *a0, *a0_min = NULL, *a; + nodeptr *np; + int d, d_min = INFINITE_D; + + /* trying to find a new parent */ + for (a0=i->first; a0; a0=a0->next) + if (a0->sister->r_cap) + { + j = a0 -> head; + if (!j->is_sink && (a=j->parent)) + { + /* checking the origin of j */ + d = 0; + while ( 1 ) + { + if (j->TS == TIME) + { + d += j -> DIST; + break; + } + a = j -> parent; + d ++; + if (a==TERMINAL) + { + j -> TS = TIME; + j -> DIST = 1; + break; + } + if (a==ORPHAN) { d = INFINITE_D; break; } + j = a -> head; + } + if (d<INFINITE_D) /* j originates from the source - done */ + { + if (d<d_min) + { + a0_min = a0; + d_min = d; + } + /* set marks along the path */ + for (j=a0->head; j->TS!=TIME; j=j->parent->head) + { + j -> TS = TIME; + j -> DIST = d --; + } + } + } + } + + if ((i->parent = a0_min)) + { + i -> TS = TIME; + i -> DIST = d_min + 1; + } + else + { + /* no parent is found */ + i -> TS = 0; + + /* process neighbors */ + for (a0=i->first; a0; a0=a0->next) + { + j = a0 -> head; + if (!j->is_sink && (a=j->parent)) + { + if (a0->sister->r_cap) set_active(j); + if (a!=TERMINAL && a!=ORPHAN && a->head==i) + { + /* add j to the adoption list */ + j -> parent = ORPHAN; + np = nodeptr_block -> New(); + np -> ptr = j; + if (orphan_last) orphan_last -> next = np; + else orphan_first = np; + orphan_last = np; + np -> next = NULL; + } + } + } + } +} + +void Graph::process_sink_orphan(node *i) +{ + node *j; + arc *a0, *a0_min = NULL, *a; + nodeptr *np; + int d, d_min = INFINITE_D; + + /* trying to find a new parent */ + for (a0=i->first; a0; a0=a0->next) + if (a0->r_cap) + { + j = a0 -> head; + if (j->is_sink && (a=j->parent)) + { + /* checking the origin of j */ + d = 0; + while ( 1 ) + { + if (j->TS == TIME) + { + d += j -> DIST; + break; + } + a = j -> parent; + d ++; + if (a==TERMINAL) + { + j -> TS = TIME; + j -> DIST = 1; + break; + } + if (a==ORPHAN) { d = INFINITE_D; break; } + j = a -> head; + } + if (d<INFINITE_D) /* j originates from the sink - done */ + { + if (d<d_min) + { + a0_min = a0; + d_min = d; + } + /* set marks along the path */ + for (j=a0->head; j->TS!=TIME; j=j->parent->head) + { + j -> TS = TIME; + j -> DIST = d --; + } + } + } + } + + if ((i->parent = a0_min)) + { + i -> TS = TIME; + i -> DIST = d_min + 1; + } + else + { + /* no parent is found */ + i -> TS = 0; + + /* process neighbors */ + for (a0=i->first; a0; a0=a0->next) + { + j = a0 -> head; + if (j->is_sink && (a=j->parent)) + { + if (a0->r_cap) set_active(j); + if (a!=TERMINAL && a!=ORPHAN && a->head==i) + { + /* add j to the adoption list */ + j -> parent = ORPHAN; + np = nodeptr_block -> New(); + np -> ptr = j; + if (orphan_last) orphan_last -> next = np; + else orphan_first = np; + orphan_last = np; + np -> next = NULL; + } + } + } + } +} + +/***********************************************************************/ + +Graph::flowtype Graph::maxflow() +{ + node *i, *j, *current_node = NULL; + arc *a; + nodeptr *np, *np_next; + + maxflow_init(); + nodeptr_block = new DBlock<nodeptr>(NODEPTR_BLOCK_SIZE, error_function); + + while ( 1 ) + { + if ((i=current_node)) + { + i -> next = NULL; /* remove active flag */ + if (!i->parent) i = NULL; + } + if (!i) + { + if (!(i = next_active())) break; + } + + /* growth */ + if (!i->is_sink) + { + /* grow source tree */ + for (a=i->first; a; a=a->next) + if (a->r_cap) + { + j = a -> head; + if (!j->parent) + { + j -> is_sink = 0; + j -> parent = a -> sister; + j -> TS = i -> TS; + j -> DIST = i -> DIST + 1; + set_active(j); + } + else if (j->is_sink) break; + else if (j->TS <= i->TS && + j->DIST > i->DIST) + { + /* heuristic - trying to make the distance from j to the source shorter */ + j -> parent = a -> sister; + j -> TS = i -> TS; + j -> DIST = i -> DIST + 1; + } + } + } + else + { + /* grow sink tree */ + for (a=i->first; a; a=a->next) + if (a->sister->r_cap) + { + j = a -> head; + if (!j->parent) + { + j -> is_sink = 1; + j -> parent = a -> sister; + j -> TS = i -> TS; + j -> DIST = i -> DIST + 1; + set_active(j); + } + else if (!j->is_sink) { a = a -> sister; break; } + else if (j->TS <= i->TS && + j->DIST > i->DIST) + { + /* heuristic - trying to make the distance from j to the sink shorter */ + j -> parent = a -> sister; + j -> TS = i -> TS; + j -> DIST = i -> DIST + 1; + } + } + } + + TIME ++; + + if (a) + { + i -> next = i; /* set active flag */ + current_node = i; + + /* augmentation */ + augment(a); + /* augmentation end */ + + /* adoption */ + while ((np=orphan_first)) + { + np_next = np -> next; + np -> next = NULL; + + while ((np=orphan_first)) + { + orphan_first = np -> next; + i = np -> ptr; + nodeptr_block -> Delete(np); + if (!orphan_first) orphan_last = NULL; + if (i->is_sink) process_sink_orphan(i); + else process_source_orphan(i); + } + + orphan_first = np_next; + } + /* adoption end */ + } + else current_node = NULL; + } + + delete nodeptr_block; + + return flow; +} + +/***********************************************************************/ + +Graph::termtype Graph::what_segment(node_id i) +{ + if (((node*)i)->parent && !((node*)i)->is_sink) return SOURCE; + return SINK; +} + diff --git a/package_bgs/db/IndependentMultimodalBGS.cpp b/package_bgs/db/IndependentMultimodalBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c05ee5cb0684117637cebfa23d4989c7177d92a6 --- /dev/null +++ b/package_bgs/db/IndependentMultimodalBGS.cpp @@ -0,0 +1,54 @@ +#include "IndependentMultimodalBGS.h" + +IndependentMultimodalBGS::IndependentMultimodalBGS() : firstTime(true), fps(10), showOutput(true){ + pIMBS = new BackgroundSubtractorIMBS(fps); +} +IndependentMultimodalBGS::~IndependentMultimodalBGS(){ + delete pIMBS; +} + +void IndependentMultimodalBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if (firstTime) + saveConfig(); + + //get the fgmask and update the background model + pIMBS->apply(img_input, img_foreground); + + //get background image + pIMBS->getBackgroundImage(img_background); + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + if (showOutput) + { + cv::imshow("IMBS FG", img_foreground); + cv::imshow("IMBS BG", img_background); + } + + firstTime = false; +} + +void IndependentMultimodalBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/IndependentMultimodalBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void IndependentMultimodalBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/IndependentMultimodalBGS.xml", 0, CV_STORAGE_READ); + + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/db/IndependentMultimodalBGS.h b/package_bgs/db/IndependentMultimodalBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..b6f9a5de9abd15441a2ad7a1127427f45df07628 --- /dev/null +++ b/package_bgs/db/IndependentMultimodalBGS.h @@ -0,0 +1,29 @@ +#pragma once + +#include <cv.h> +#include <highgui.h> + +#include "imbs.hpp" + +#include "../IBGS.h" + +class IndependentMultimodalBGS : public IBGS +{ +private: + BackgroundSubtractorIMBS* pIMBS; + int fps; + bool firstTime; + cv::Mat img_foreground; + cv::Mat img_background; + bool showOutput; + +public: + IndependentMultimodalBGS(); + ~IndependentMultimodalBGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; \ No newline at end of file diff --git a/package_bgs/db/imbs.cpp b/package_bgs/db/imbs.cpp new file mode 100644 index 0000000000000000000000000000000000000000..715d699c117c4dab3c41a270d8f28791e31c8852 --- /dev/null +++ b/package_bgs/db/imbs.cpp @@ -0,0 +1,748 @@ +/* +* IMBS Background Subtraction Library +* +* This file imbs.hpp contains the C++ OpenCV based implementation for +* IMBS algorithm described in +* D. D. Bloisi and L. Iocchi +* "Independent Multimodal Background Subtraction" +* In Proc. of the Third Int. Conf. on Computational Modeling of Objects +* Presented in Images: Fundamentals, Methods and Applications, pp. 39-44, 2012. +* Please, cite the above paper if you use IMBS. +* +* This software is provided without any warranty about its usability. +* It is for educational purposes and should be regarded as such. +* +* Written by Domenico D. Bloisi +* +* Please, report suggestions/comments/bugs to +* domenico.bloisi@gmail.com +* +*/ + +#include "imbs.hpp" + +using namespace std; +using namespace cv; + +BackgroundSubtractorIMBS::BackgroundSubtractorIMBS() +{ + fps = 0.; + fgThreshold = 15; + associationThreshold = 5; + samplingPeriod = 250.;//500.ms + minBinHeight = 2; + numSamples = 10; //30 + alpha = 0.65; + beta = 1.15; + tau_s = 60.; + tau_h = 40.; + minArea = 30.; + persistencePeriod = samplingPeriod*numSamples/3.;//ms + + initial_tick_count = (double)getTickCount(); + + //morphological Opening and closing + morphologicalFiltering = false; +} + +BackgroundSubtractorIMBS::BackgroundSubtractorIMBS( + double fps, + unsigned int fgThreshold, + unsigned int associationThreshold, + double samplingPeriod, + unsigned int minBinHeight, + unsigned int numSamples, + double alpha, + double beta, + double tau_s, + double tau_h, + double minArea, + double persistencePeriod, + bool morphologicalFiltering) +{ + this->fps = fps; + this->fgThreshold = fgThreshold; + this->persistencePeriod = persistencePeriod; + if(minBinHeight <= 1){ + this->minBinHeight = 1; + } + else { + this->minBinHeight = minBinHeight; + } + this->associationThreshold = associationThreshold; + this->samplingPeriod = samplingPeriod;//ms + this->minBinHeight = minBinHeight; + this->numSamples = numSamples; + this->alpha = alpha; + this->beta = beta; + this->tau_s = tau_s; + this->tau_h = tau_h; + this->minArea = minArea; + + if(fps == 0.) + initial_tick_count = (double)getTickCount(); + else + initial_tick_count = 0; + + //morphological Opening and closing + this->morphologicalFiltering = morphologicalFiltering; +} + +BackgroundSubtractorIMBS::~BackgroundSubtractorIMBS() +{ + delete[] bgBins; + delete[] bgModel; + delete[] persistenceMap; +} + +void BackgroundSubtractorIMBS::initialize(Size frameSize, int frameType) +{ + /*cout << "INPUT: WIDTH " << frameSize.width << " HEIGHT " << frameSize.height << + " FPS " << fps << endl; + cout << endl;*/ + + this->frameSize = frameSize; + this->frameType = frameType; + this->numPixels = frameSize.width*frameSize.height; + + persistenceMap = new unsigned int[numPixels]; + for(int i = 0; i < numPixels; i++) { + persistenceMap[i] = 0; + } + + bgBins = new Bins[numPixels]; + bgModel = new BgModel[numPixels]; + maxBgBins = numSamples / minBinHeight; + + timestamp = 0.;//ms + prev_timestamp = 0.;//ms + prev_bg_frame_time = 0; + bg_frame_counter = 0; + bg_reset = false; + prev_area = 0; + sudden_change = false; + + SHADOW_LABEL = 80; + PERSISTENCE_LABEL = 180; + FOREGROUND_LABEL = 255; + + fgmask.create(frameSize, CV_8UC1); + fgfiltered.create(frameSize, CV_8UC1); + persistenceImage = Mat::zeros(frameSize, CV_8UC1); + bgSample.create(frameSize, CV_8UC3); + bgImage = Mat::zeros(frameSize, CV_8UC3); + + //initial message to be shown until the first fg mask is computed + initialMsgGray = Mat::zeros(frameSize, CV_8UC1); + putText(initialMsgGray, "Creating", Point(10,20), FONT_HERSHEY_SIMPLEX, 0.4, CV_RGB(255, 255, 255)); + putText(initialMsgGray, "initial", Point(10,40), FONT_HERSHEY_SIMPLEX, 0.4, CV_RGB(255, 255, 255)); + putText(initialMsgGray, "background...", Point(10,60), FONT_HERSHEY_SIMPLEX, 0.4, CV_RGB(255, 255, 255)); + + initialMsgRGB = Mat::zeros(frameSize, CV_8UC3); + putText(initialMsgRGB, "Creating", Point(10,20), FONT_HERSHEY_SIMPLEX, 0.4, CV_RGB(255, 255, 255)); + putText(initialMsgRGB, "initial", Point(10,40), FONT_HERSHEY_SIMPLEX, 0.4, CV_RGB(255, 255, 255)); + putText(initialMsgRGB, "background...", Point(10,60), FONT_HERSHEY_SIMPLEX, 0.4, CV_RGB(255, 255, 255)); + + if(minBinHeight <= 1){ + minBinHeight = 1; + } + + for(unsigned int p = 0; p < numPixels; ++p) + { + bgBins[p].binValues = new Vec3b[numSamples]; + bgBins[p].binHeights = new uchar[numSamples]; + bgBins[p].isFg = new bool[numSamples]; + + bgModel[p].values = new Vec3b[maxBgBins]; + bgModel[p].isValid = new bool[maxBgBins]; + bgModel[p].isValid[0] = false; + bgModel[p].isFg = new bool[maxBgBins]; + bgModel[p].counter = new uchar[maxBgBins]; + } +} + +void BackgroundSubtractorIMBS::apply(InputArray _frame, OutputArray _fgmask, double learningRate) +{ + frame = _frame.getMat(); + + CV_Assert(frame.depth() == CV_8U); + CV_Assert(frame.channels() == 3); + + bool needToInitialize = nframes == 0 || frame.type() != frameType; + if( needToInitialize ) { + initialize(frame.size(), frame.type()); + } + + _fgmask.create(frameSize, CV_8UC1); + fgmask = _fgmask.getMat(); + fgmask = Scalar(0); + + //get current time + prev_timestamp = timestamp; + if(fps == 0.) { + timestamp = getTimestamp();//ms + } + else { + timestamp += 1000./fps;//ms + } + + //check for global changes + if(sudden_change) { + changeBg(); + } + + //wait for the first model to be generated + if(bgModel[0].isValid[0]) { + getFg(); + hsvSuppression(); + filterFg(); + } + //update the bg model + updateBg(); + + //show an initial message if the first bg is not yet ready + if(!bgModel[0].isValid[0]) { + initialMsgGray.copyTo(fgmask); + initialMsgRGB.copyTo(bgImage); + } + ++nframes; +} + +void BackgroundSubtractorIMBS::updateBg() { + if(bg_reset) { + if(bg_frame_counter > numSamples - 1) { + bg_frame_counter = numSamples - 1; + } + } + + if(prev_bg_frame_time > timestamp) { + prev_bg_frame_time = timestamp; + } + + if(bg_frame_counter == numSamples - 1) { + createBg(bg_frame_counter); + bg_frame_counter = 0; + } + else { //bg_frame_counter < (numSamples - 1) + + if((timestamp - prev_bg_frame_time) >= samplingPeriod) + { + //get a new sample for creating the bg model + prev_bg_frame_time = timestamp; + frame.copyTo(bgSample); + createBg(bg_frame_counter); + bg_frame_counter++; + } + } +} + +double BackgroundSubtractorIMBS::getTimestamp() { + return ((double)getTickCount() - initial_tick_count)*1000./getTickFrequency(); +} + +void BackgroundSubtractorIMBS::hsvSuppression() { + + uchar h_i, s_i, v_i; + uchar h_b, s_b, v_b; + float h_diff, s_diff, v_ratio; + + Mat bgrPixel(cv::Size(1, 1), CV_8UC3); + + vector<Mat> imHSV; + cv::split(convertImageRGBtoHSV(frame), imHSV); + + for(unsigned int p = 0; p < numPixels; ++p) { + if(fgmask.data[p]) { + + h_i = imHSV[0].data[p]; + s_i = imHSV[1].data[p]; + v_i = imHSV[2].data[p]; + + for(unsigned int n = 0; n < maxBgBins; ++n) { + if(!bgModel[p].isValid[n]) { + break; + } + + if(bgModel[p].isFg[n]) { + continue; + } + + bgrPixel.at<cv::Vec3b>(0,0) = bgModel[p].values[n]; + + cv::Mat hsvPixel = convertImageRGBtoHSV(bgrPixel); + + h_b = hsvPixel.at<cv::Vec3b>(0,0)[0]; + s_b = hsvPixel.at<cv::Vec3b>(0,0)[1]; + v_b = hsvPixel.at<cv::Vec3b>(0,0)[2]; + + v_ratio = (float)v_i / (float)v_b; + s_diff = std::abs(s_i - s_b); + h_diff = std::min( std::abs(h_i - h_b), 255 - std::abs(h_i - h_b)); + + if( h_diff <= tau_h && + s_diff <= tau_s && + v_ratio >= alpha && + v_ratio < beta) + { + fgmask.data[p] = SHADOW_LABEL; + break; + } + }//for + }//if + }//numPixels +} + +void BackgroundSubtractorIMBS::createBg(unsigned int bg_sample_number) { + if(!bgSample.data) { + //cerr << "createBg -- an error occurred: " << + // " unable to retrieve frame no. " << bg_sample_number << endl; + + //TODO vedere gestione errori + abort(); + } + //aux variable + Vec3b currentPixel; + //split bgSample in channels + cv::split(bgSample, bgSampleBGR); + //create a statistical model for each pixel (a set of bins of variable size) + for(unsigned int p = 0; p < numPixels; ++p) { + //create an initial bin for each pixel from the first sample (bg_sample_number = 0) + if(bg_sample_number == 0) { + for(int k = 0; k < 3; ++k) { + bgBins[p].binValues[0][k] = bgSampleBGR[k].data[p]; + } + bgBins[p].binHeights[0] = 1; + for(unsigned int s = 1; s < numSamples; ++s) { + bgBins[p].binHeights[s] = 0; + } + //if the sample pixel is from foreground keep track of that situation + if(fgmask.data[p] == FOREGROUND_LABEL) { + bgBins[p].isFg[0] = true; + } + else { + bgBins[p].isFg[0] = false; + } + }//if(bg_sample_number == 0) + else { //bg_sample_number > 0 + for(int k = 0; k < 3; ++k) { + currentPixel[k] = bgSampleBGR[k].data[p]; + } + int den = 0; + for(unsigned int s = 0; s < bg_sample_number; ++s) { + //try to associate the current pixel values to an existing bin + if( std::abs(currentPixel[2] - bgBins[p].binValues[s][2]) <= associationThreshold && + std::abs(currentPixel[1] - bgBins[p].binValues[s][1]) <= associationThreshold && + std::abs(currentPixel[0] - bgBins[p].binValues[s][0]) <= associationThreshold ) + { + den = (bgBins[p].binHeights[s] + 1); + for(int k = 0; k < 3; ++k) { + bgBins[p].binValues[s][k] = + (bgBins[p].binValues[s][k] * bgBins[p].binHeights[s] + currentPixel[k]) / den; + } + bgBins[p].binHeights[s]++; //increment the height of the bin + if(fgmask.data[p] == FOREGROUND_LABEL) { + bgBins[p].isFg[s] = true; + } + break; + } + //if the association is not possible, create a new bin + else if(bgBins[p].binHeights[s] == 0) { + bgBins[p].binValues[s] = currentPixel; + bgBins[p].binHeights[s]++; + if(fgmask.data[p] == FOREGROUND_LABEL) { + bgBins[p].isFg[s] = true; + } + else { + bgBins[p].isFg[s] = false; + } + break; + } + else continue; + }//for(unsigned int s = 0; s <= bg_sample_number; ++s) + + //if all samples have been processed + //it is time to compute the fg mask + if(bg_sample_number == (numSamples - 1)) { + unsigned int index = 0; + int max_height = -1; + for(unsigned int s = 0; s < numSamples; ++s){ + if(bgBins[p].binHeights[s] == 0) { + bgModel[p].isValid[index] = false; + break; + } + if(index == maxBgBins) { + break; + } + else if(bgBins[p].binHeights[s] >= minBinHeight) { + if(fgmask.data[p] == PERSISTENCE_LABEL) { + for(unsigned int n = 0; n < maxBgBins; n++) { + if(!bgModel[p].isValid[n]) { + break; + } + unsigned int d = std::max((int)std::abs(bgModel[p].values[n][0] - bgBins[p].binValues[s][0]), + std::abs(bgModel[p].values[n][1] - bgBins[p].binValues[s][1]) ); + d = std::max((int)d, std::abs(bgModel[p].values[n][2] - bgBins[p].binValues[s][2]) ); + if(d < fgThreshold){ + bgModel[p].isFg[n] = false; + bgBins[p].isFg[s] = false; + } + } + } + + if(bgBins[p].binHeights[s] > max_height) { + max_height = bgBins[p].binHeights[s]; + + for(int k = 0; k < 3; ++k) { + bgModel[p].values[index][k] = bgModel[p].values[0][k]; + } + bgModel[p].isValid[index] = true; + bgModel[p].isFg[index] = bgModel[p].isFg[0]; + bgModel[p].counter[index] = bgModel[p].counter[0]; + + for(int k = 0; k < 3; ++k) { + bgModel[p].values[0][k] = bgBins[p].binValues[s][k]; + } + bgModel[p].isValid[0] = true; + bgModel[p].isFg[0] = bgBins[p].isFg[s]; + bgModel[p].counter[0] = bgBins[p].binHeights[s]; + } + else { + for(int k = 0; k < 3; ++k) { + bgModel[p].values[index][k] = bgBins[p].binValues[s][k]; + } + bgModel[p].isValid[index] = true; + bgModel[p].isFg[index] = bgBins[p].isFg[s]; + bgModel[p].counter[index] = bgBins[p].binHeights[s]; + } + ++index; + } + } //for all numSamples + }//bg_sample_number == (numSamples - 1) + }//else --> if(frame_number == 0) + }//numPixels + + if(bg_sample_number == (numSamples - 1)) { + //std::cout << "new bg created" << std::endl; + persistenceImage = Scalar(0); + + bg_reset = false; + if(sudden_change) { + numSamples *= 3.; + samplingPeriod *= 2.; + sudden_change = false; + } + + for(int i = 0; i < numPixels; i++) { + persistenceMap[i] = 0; + } + + unsigned int p = 0; + for(int i = 0; i < bgImage.rows; ++i) { + for(int j = 0; j < bgImage.cols; ++j, ++p) { + bgImage.at<cv::Vec3b>(i,j) = bgModel[p].values[0]; + } + } + } +} + +void BackgroundSubtractorIMBS::getFg() { + fgmask = Scalar(0); + cv::split(frame, frameBGR); + + bool isFg = true; + bool conditionalUpdated = false; + unsigned int d = 0; + for(unsigned int p = 0; p < numPixels; ++p) { + isFg = true; + conditionalUpdated = false; + d = 0; + for(unsigned int n = 0; n < maxBgBins; ++n) { + if(!bgModel[p].isValid[n]) { + if(n == 0) { + isFg = false; + } + break; + } + else { //the model is valid + d = std::max( + (int)std::abs(bgModel[p].values[n][0] - frameBGR[0].data[p]), + std::abs(bgModel[p].values[n][1] - frameBGR[1].data[p]) ); + d = std::max( + (int)d, std::abs(bgModel[p].values[n][2] - frameBGR[2].data[p]) ); + if(d < fgThreshold){ + //check if it is a potential background pixel + //from stationary object + if(bgModel[p].isFg[n]) { + conditionalUpdated = true; + break; + } + else { + isFg = false; + persistenceMap[p] = 0; + } + } + } + } + if(isFg) { + if(conditionalUpdated) { + fgmask.data[p] = PERSISTENCE_LABEL; + persistenceMap[p] += (timestamp - prev_timestamp); + if(persistenceMap[p] > persistencePeriod) { + for(unsigned int n = 0; n < maxBgBins; ++n) { + if(!bgModel[p].isValid[n]) { + break; + } + bgModel[p].isFg[n] = false; + } + } + } + else { + fgmask.data[p] = FOREGROUND_LABEL; + persistenceMap[p] = 0; + } + } + } +} + +void BackgroundSubtractorIMBS::areaThresholding() +{ + double maxArea = 0.6 * numPixels; + + std::vector < std::vector<Point> > contours; + Mat tmpBinaryImage = fgfiltered.clone(); + findContours(tmpBinaryImage, contours, RETR_LIST, CHAIN_APPROX_NONE); + + tmpBinaryImage = Scalar(0); + + for (size_t contourIdx = 0; contourIdx < contours.size(); ++contourIdx) + { + Moments moms = moments(Mat(contours[contourIdx])); + double area = moms.m00; + if (area < minArea || area >= maxArea) + continue; + else { + drawContours( tmpBinaryImage, contours, contourIdx, Scalar(255), CV_FILLED ); + } + } + for(int i = 0; i < fgfiltered.rows; ++i) { + for(int j = 0; j < fgfiltered.cols; ++j) { + if(!tmpBinaryImage.at<uchar>(i,j)) { + fgfiltered.at<uchar>(i,j) = 0; + } + } + } +} + +// Create a HSV image from the RGB image using the full 8-bits, since OpenCV only allows Hues up to 180 instead of 255. +// ref: "http://cs.haifa.ac.il/hagit/courses/ist/Lectures/Demos/ColorApplet2/t_convert.html" +// Remember to free the generated HSV image. +Mat BackgroundSubtractorIMBS::convertImageRGBtoHSV(const Mat& imageRGB) +{ + float fR, fG, fB; + float fH, fS, fV; + const float FLOAT_TO_BYTE = 255.0f; + const float BYTE_TO_FLOAT = 1.0f / FLOAT_TO_BYTE; + + // Create a blank HSV image + Mat imageHSV(imageRGB.size(), CV_8UC3); + //if (!imageHSV || imageRGB->depth != 8 || imageRGB->nChannels != 3) { + //printf("ERROR in convertImageRGBtoHSV()! Bad input image.\n"); + //exit(1); + //} + + int h = imageRGB.rows; // Pixel height. + int w = imageRGB.cols; // Pixel width. + //int rowSizeRGB = imageRGB->widthStep; // Size of row in bytes, including extra padding. + //char *imRGB = imageRGB->imageData; // Pointer to the start of the image pixels. + //int rowSizeHSV = imageHSV->widthStep; // Size of row in bytes, including extra padding. + //char *imHSV = imageHSV->imageData; // Pointer to the start of the image pixels. + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + // Get the RGB pixel components. NOTE that OpenCV stores RGB pixels in B,G,R order. + //uchar *pRGB = (uchar*)(imRGB + y*rowSizeRGB + x*3); + int bB = imageRGB.at<Vec3b>(y,x)[0]; //*(uchar*)(pRGB+0); // Blue component + int bG = imageRGB.at<Vec3b>(y,x)[1]; //*(uchar*)(pRGB+1); // Green component + int bR = imageRGB.at<Vec3b>(y,x)[2]; //*(uchar*)(pRGB+2); // Red component + + // Convert from 8-bit integers to floats. + fR = bR * BYTE_TO_FLOAT; + fG = bG * BYTE_TO_FLOAT; + fB = bB * BYTE_TO_FLOAT; + + // Convert from RGB to HSV, using float ranges 0.0 to 1.0. + float fDelta; + float fMin, fMax; + int iMax; + // Get the min and max, but use integer comparisons for slight speedup. + if (bB < bG) { + if (bB < bR) { + fMin = fB; + if (bR > bG) { + iMax = bR; + fMax = fR; + } + else { + iMax = bG; + fMax = fG; + } + } + else { + fMin = fR; + fMax = fG; + iMax = bG; + } + } + else { + if (bG < bR) { + fMin = fG; + if (bB > bR) { + fMax = fB; + iMax = bB; + } + else { + fMax = fR; + iMax = bR; + } + } + else { + fMin = fR; + fMax = fB; + iMax = bB; + } + } + fDelta = fMax - fMin; + fV = fMax; // Value (Brightness). + if (iMax != 0) { // Make sure its not pure black. + fS = fDelta / fMax; // Saturation. + float ANGLE_TO_UNIT = 1.0f / (6.0f * fDelta); // Make the Hues between 0.0 to 1.0 instead of 6.0 + if (iMax == bR) { // between yellow and magenta. + fH = (fG - fB) * ANGLE_TO_UNIT; + } + else if (iMax == bG) { // between cyan and yellow. + fH = (2.0f/6.0f) + ( fB - fR ) * ANGLE_TO_UNIT; + } + else { // between magenta and cyan. + fH = (4.0f/6.0f) + ( fR - fG ) * ANGLE_TO_UNIT; + } + // Wrap outlier Hues around the circle. + if (fH < 0.0f) + fH += 1.0f; + if (fH >= 1.0f) + fH -= 1.0f; + } + else { + // color is pure Black. + fS = 0; + fH = 0; // undefined hue + } + + // Convert from floats to 8-bit integers. + int bH = (int)(0.5f + fH * 255.0f); + int bS = (int)(0.5f + fS * 255.0f); + int bV = (int)(0.5f + fV * 255.0f); + + // Clip the values to make sure it fits within the 8bits. + if (bH > 255) + bH = 255; + if (bH < 0) + bH = 0; + if (bS > 255) + bS = 255; + if (bS < 0) + bS = 0; + if (bV > 255) + bV = 255; + if (bV < 0) + bV = 0; + + // Set the HSV pixel components. + imageHSV.at<Vec3b>(y, x)[0] = bH; // H component + imageHSV.at<Vec3b>(y, x)[1] = bS; // S component + imageHSV.at<Vec3b>(y, x)[2] = bV; // V component + } + } + return imageHSV; +} + +void BackgroundSubtractorIMBS::getBackgroundImage(OutputArray backgroundImage) const +{ + bgImage.copyTo(backgroundImage); +} + +void BackgroundSubtractorIMBS::filterFg() { + + unsigned int cnt = 0; + for(unsigned int p = 0; p < numPixels; ++p) { + if(fgmask.data[p] == (uchar)255) { + fgfiltered.data[p] = 255; + cnt++; + } + else { + fgfiltered.data[p] = 0; + } + } + + if(cnt > numPixels*0.5) { + sudden_change = true; + } + + if(morphologicalFiltering) { + cv::Mat element3(3,3,CV_8U,cv::Scalar(1)); + cv::morphologyEx(fgfiltered, fgfiltered, cv::MORPH_OPEN, element3); + cv::morphologyEx(fgfiltered, fgfiltered, cv::MORPH_CLOSE, element3); + } + + areaThresholding(); + + for(unsigned int p = 0; p < numPixels; ++p) { + if(fgmask.data[p] == PERSISTENCE_LABEL) { + fgfiltered.data[p] = PERSISTENCE_LABEL; + } + else if(fgmask.data[p] == SHADOW_LABEL) { + fgfiltered.data[p] = SHADOW_LABEL; + } + } + + fgfiltered.copyTo(fgmask); +} + +void BackgroundSubtractorIMBS::changeBg() { + + std::cout << "\n\n\n\nWARNING: changeBg\n\n\n\n\n" << std::endl; + + //samplingPeriod /= 2.; + //numSamples /= 2.; + //bg_reset = true; + //cout << "qua" << endl; + + if(!bg_reset) { + numSamples /= 3.; + samplingPeriod /= 2.; + bg_frame_counter = 0; + bg_reset = true; + } +} + +void BackgroundSubtractorIMBS::getBgModel(BgModel bgModel_copy[], int size) { + if(size != numPixels) { + return; + } + for(int i = 0; i < numPixels; ++i){ + bgModel_copy[i].values = new Vec3b[maxBgBins]; + bgModel_copy[i].isValid = new bool[maxBgBins]; + bgModel_copy[i].isValid[0] = false; + bgModel_copy[i].isFg = new bool[maxBgBins]; + bgModel_copy[i].counter = new uchar[maxBgBins]; + } + for(unsigned int p = 0; p < numPixels; ++p) { + for(unsigned int n = 0; n < maxBgBins; ++n) { + if(!bgModel[p].isValid[n]) { + break; + } + bgModel_copy[p].values[n] = bgModel[p].values[n]; + bgModel_copy[p].isValid[n] = bgModel[p].isValid[n]; + bgModel_copy[p].isFg[n] = bgModel[p].isFg[n]; + bgModel_copy[p].counter[n] = bgModel[p].counter[n]; + } + } +} diff --git a/package_bgs/db/imbs.hpp b/package_bgs/db/imbs.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fd7faf17ce44bb26a5345984b5768df1a6bf8072 --- /dev/null +++ b/package_bgs/db/imbs.hpp @@ -0,0 +1,178 @@ +/* +* IMBS Background Subtraction Library +* +* This file imbs.hpp contains the C++ OpenCV based implementation for +* IMBS algorithm described in +* D. D. Bloisi and L. Iocchi +* "Independent Multimodal Background Subtraction" +* In Proc. of the Third Int. Conf. on Computational Modeling of Objects +* Presented in Images: Fundamentals, Methods and Applications, pp. 39-44, 2012. +* Please, cite the above paper if you use IMBS. +* +* This software is provided without any warranty about its usability. +* It is for educational purposes and should be regarded as such. +* +* Written by Domenico D. Bloisi +* +* Please, report suggestions/comments/bugs to +* domenico.bloisi@gmail.com +* +*/ + +#ifndef __IMBS_HPP__ +#define __IMBS_HPP__ + +//OPENCV +#include <opencv2/core/core.hpp> +#include <opencv2/highgui/highgui.hpp> +#include <opencv2/imgproc/imgproc.hpp> +#include <opencv2/features2d/features2d.hpp> +//C++ +#include <iostream> +#include <vector> + +using namespace cv; +using namespace std; + +class BackgroundSubtractorIMBS +{ +public: + //! the default constructor + BackgroundSubtractorIMBS(); + //! the full constructor + BackgroundSubtractorIMBS(double fps, + unsigned int fgThreshold=15, + unsigned int associationThreshold=5, + double samplingPeriod=500., + unsigned int minBinHeight=2, + unsigned int numSamples=30, + double alpha=0.65, + double beta=1.15, + double tau_s=60., + double tau_h=40., + double minArea=30., + double persistencePeriod=10000., + bool morphologicalFiltering=false + ); + //! the destructor + ~BackgroundSubtractorIMBS(); + //! the update operator + void apply(InputArray image, OutputArray fgmask, double learningRate=-1.); + + //! computes a background image which shows only the highest bin for each pixel + void getBackgroundImage(OutputArray backgroundImage) const; + + //! re-initiaization method + void initialize(Size frameSize, int frameType); + +private: + //method for creating the background model + void createBg(unsigned int bg_sample_number); + //method for updating the background model + void updateBg(); + //method for computing the foreground mask + void getFg(); + //method for suppressing shadows and highlights + void hsvSuppression(); + //method for refining foreground mask + void filterFg(); + //method for filtering out blobs smaller than a given area + void areaThresholding(); + //method for getting the current time + double getTimestamp(); + //method for converting from RGB to HSV + Mat convertImageRGBtoHSV(const Mat& imageRGB); + //method for changing the bg in case of sudden changes + void changeBg(); + + //current input RGB frame + Mat frame; + vector<Mat> frameBGR; + //frame size + Size frameSize; + //frame type + int frameType; + //total number of pixels in frame + unsigned int numPixels; + //current background sample + Mat bgSample; + vector<Mat> bgSampleBGR; + //current background image which shows only the highest bin for each pixel + //(just for displaying purposes) + Mat bgImage; + //current foreground mask + Mat fgmask; + + Mat fgfiltered; + + //number of fps + double fps; + //time stamp in milliseconds (ms) + double timestamp; + //previous time stamp in milliseconds (ms) + double prev_timestamp; + double initial_tick_count; + //initial message to be shown until the first bg model is ready + Mat initialMsgGray; + Mat initialMsgRGB; + + //struct for modeling the background values for a single pixel + typedef struct { + Vec3b* binValues; + uchar* binHeights; + bool* isFg; + } Bins; + + Bins* bgBins; +public: + //struct for modeling the background values for the entire frame + typedef struct { + Vec3b* values; + bool* isValid; + bool* isFg; + uchar* counter; + } BgModel; +private: + BgModel* bgModel; + + //SHADOW SUPPRESSION PARAMETERS + float alpha; + float beta; + uchar tau_s; + uchar tau_h; + + unsigned int minBinHeight; + unsigned int numSamples; + unsigned int samplingPeriod; + unsigned long prev_bg_frame_time; + unsigned int bg_frame_counter; + unsigned int associationThreshold; + unsigned int maxBgBins; + unsigned int nframes; + + double minArea; + bool bg_reset; + unsigned int persistencePeriod; + bool prev_area; + bool sudden_change; + unsigned int fgThreshold; + uchar SHADOW_LABEL; + uchar PERSISTENCE_LABEL; + uchar FOREGROUND_LABEL; + //persistence map + unsigned int* persistenceMap; + Mat persistenceImage; + + bool morphologicalFiltering; + +public: + unsigned int getMaxBgBins() { + return maxBgBins; + } + unsigned int getFgThreshold() { + return fgThreshold; + } + void getBgModel(BgModel bgModel_copy[], int size); +}; + +#endif //__IMBS_HPP__ diff --git a/package_bgs/dp/AdaptiveMedianBGS.cpp b/package_bgs/dp/AdaptiveMedianBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..16b3988c17c67cc60531f2cb9ea135d1336c6adc --- /dev/null +++ b/package_bgs/dp/AdaptiveMedianBGS.cpp @@ -0,0 +1,140 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* AdaptiveMedianBGS.cpp +* +* Purpose: Implementation of the simple adaptive median background +* subtraction algorithm described in: +* "Segmentation and tracking of piglets in images" +* by McFarlane and Schofield +* +* Author: Donovan Parks, September 2007 +* +******************************************************************************/ + +#include <iostream> +#include <stdlib.h> +#include <cmath> + +#include "AdaptiveMedianBGS.h" + +using namespace Algorithms::BackgroundSubtraction; + +void AdaptiveMedianBGS::Initalize(const BgsParams& param) +{ + m_params = (AdaptiveMedianParams&)param; + + m_median = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); + cvSet(m_median.Ptr(), CV_RGB(BACKGROUND,BACKGROUND,BACKGROUND)); +} + +RgbImage* AdaptiveMedianBGS::Background() +{ + return &m_median; +} + +void AdaptiveMedianBGS::InitModel(const RgbImage& data) +{ + // initialize the background model + for (unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + m_median(r,c) = data(r,c); + } + } +} + +void AdaptiveMedianBGS::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) +{ + if(frame_num % m_params.SamplingRate() == 1) + { + // update background model + for (unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + // perform conditional updating only if we are passed the learning phase + if(update_mask(r,c) == BACKGROUND || frame_num < m_params.LearningFrames()) + { + for(int ch = 0; ch < NUM_CHANNELS; ++ch) + { + if(data(r,c,ch) > m_median(r,c,ch)) + { + m_median(r,c,ch)++; + } + else if(data(r,c,ch) < m_median(r,c,ch)) + { + m_median(r,c,ch)--; + } + } + } + } + } + } +} + +void AdaptiveMedianBGS::SubtractPixel(int r, int c, const RgbPixel& pixel, + unsigned char& low_threshold, unsigned char& high_threshold) +{ + // perform background subtraction + low_threshold = high_threshold = FOREGROUND; + + int diffR = abs(pixel(0) - m_median(r,c,0)); + int diffG = abs(pixel(1) - m_median(r,c,1)); + int diffB = abs(pixel(2) - m_median(r,c,2)); + + if(diffR <= m_params.LowThreshold() && diffG <= m_params.LowThreshold() && diffB <= m_params.LowThreshold()) + { + low_threshold = BACKGROUND; + } + + if(diffR <= m_params.HighThreshold() && diffG <= m_params.HighThreshold() && diffB <= m_params.HighThreshold()) + { + high_threshold = BACKGROUND; + } +} + +/////////////////////////////////////////////////////////////////////////////// +//Input: +// data - a pointer to the image data +//Output: +// output - a pointer to the data of a gray value image +// (the memory should already be reserved) +// values: 255-foreground, 0-background +/////////////////////////////////////////////////////////////////////////////// +void AdaptiveMedianBGS::Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask) +{ + unsigned char low_threshold, high_threshold; + + // update each pixel of the image + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + // perform background subtraction + SubtractPixel(r, c, data(r,c), low_threshold, high_threshold); + + // setup silhouette mask + low_threshold_mask(r,c) = low_threshold; + high_threshold_mask(r,c) = high_threshold; + } + } +} + diff --git a/package_bgs/dp/AdaptiveMedianBGS.h b/package_bgs/dp/AdaptiveMedianBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..3ea25af0ff3bf2c8b2956294015617347b17c672 --- /dev/null +++ b/package_bgs/dp/AdaptiveMedianBGS.h @@ -0,0 +1,89 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* AdaptiveMedianBGS.hpp +* +* Purpose: Implementation of the simple adaptive median background +* subtraction algorithm described in: +* "Segmentation and tracking of piglets in images" +* by McFarlane and Schofield +* +* Author: Donovan Parks, September 2007 + +Example: + Algorithms::BackgroundSubtraction::AdaptiveMedianParams params; + params.SetFrameSize(width, height); + params.LowThreshold() = 40; + params.HighThreshold() = 2*params.LowThreshold(); + params.SamplingRate() = 7; + params.LearningFrames() = 30; + + Algorithms::BackgroundSubtraction::AdaptiveMedianBGS bgs; + bgs.Initalize(params); +******************************************************************************/ + +#include "Bgs.h" + +namespace Algorithms +{ + namespace BackgroundSubtraction + { + // --- Parameters used by the Adaptive Median BGS algorithm --- + class AdaptiveMedianParams : public BgsParams + { + public: + unsigned char &LowThreshold() { return m_low_threshold; } + unsigned char &HighThreshold() { return m_high_threshold; } + + int &SamplingRate() { return m_samplingRate; } + int &LearningFrames() { return m_learning_frames; } + + private: + unsigned char m_low_threshold; + unsigned char m_high_threshold; + + int m_samplingRate; + int m_learning_frames; + }; + + + // --- Adaptive Median BGS algorithm --- + class AdaptiveMedianBGS : public Bgs + { + public: + virtual ~AdaptiveMedianBGS() {} + + void Initalize(const BgsParams& param); + + void InitModel(const RgbImage& data); + void Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask); + void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); + + RgbImage* Background(); + + private: + void SubtractPixel(int r, int c, const RgbPixel& pixel, + unsigned char& low_threshold, unsigned char& high_threshold); + + AdaptiveMedianParams m_params; + + RgbImage m_median; + }; + }; +}; diff --git a/package_bgs/dp/Bgs.h b/package_bgs/dp/Bgs.h new file mode 100644 index 0000000000000000000000000000000000000000..c1ae23c25b90d1b5795afaef2e3d96f641eaac60 --- /dev/null +++ b/package_bgs/dp/Bgs.h @@ -0,0 +1,67 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* Bgs.hpp +* +* Purpose: Base class for BGS algorithms. +* +* Author: Donovan Parks, October 2007 +* +******************************************************************************/ + +#ifndef BGS_H_ +#define BGS_H_ + +#include "Image.h" +#include "BgsParams.h" + +namespace Algorithms +{ + namespace BackgroundSubtraction + { + class Bgs + { + public: + static const int BACKGROUND = 0; + static const int FOREGROUND = 255; + + virtual ~Bgs() {} + + // Initialize any data required by the BGS algorithm. Should be called once before calling + // any of the following functions. + virtual void Initalize(const BgsParams& param) = 0; + + // Initialize the background model. Typically, the background model is initialized using the first + // frame of the incoming video stream, but alternatives are possible. + virtual void InitModel(const RgbImage& data) = 0; + + // Subtract the current frame from the background model and produce a binary foreground mask using + // both a low and high threshold value. + virtual void Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask) = 0; + + // Update the background model. Only pixels set to background in update_mask are updated. + virtual void Update(int frame_num, const RgbImage& data, const BwImage& update_mask) = 0; + + // Return the current background model. + virtual RgbImage *Background() = 0; + }; + }; +}; + +#endif \ No newline at end of file diff --git a/package_bgs/dp/BgsParams.h b/package_bgs/dp/BgsParams.h new file mode 100644 index 0000000000000000000000000000000000000000..c3bad830f254eb6c6ba45750610abab8283fed2c --- /dev/null +++ b/package_bgs/dp/BgsParams.h @@ -0,0 +1,59 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* BgsParams.hpp +* +* Purpose: Base class for BGS parameters. Any parameters common to all BGS +* algorithms should be specified directly in this class. +* +* Author: Donovan Parks, May 2008 +* +******************************************************************************/ + +#ifndef BGS_PARAMS_H_ +#define BGS_PARAMS_H_ + +namespace Algorithms +{ + namespace BackgroundSubtraction + { + class BgsParams + { + public: + virtual ~BgsParams() {} + + virtual void SetFrameSize(unsigned int width, unsigned int height) + { + m_width = width; + m_height = height; + m_size = width*height; + } + + unsigned int &Width() { return m_width; } + unsigned int &Height() { return m_height; } + unsigned int &Size() { return m_size; } + + protected: + unsigned int m_width; + unsigned int m_height; + unsigned int m_size; + }; + }; +}; + +#endif \ No newline at end of file diff --git a/package_bgs/dp/DPAdaptiveMedianBGS.cpp b/package_bgs/dp/DPAdaptiveMedianBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d7ebcd19ab53def6396097f32ba5865ddd2f520b --- /dev/null +++ b/package_bgs/dp/DPAdaptiveMedianBGS.cpp @@ -0,0 +1,104 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "DPAdaptiveMedianBGS.h" + +DPAdaptiveMedianBGS::DPAdaptiveMedianBGS() : firstTime(true), frameNumber(0), showOutput(true), threshold(40), samplingRate(7), learningFrames(30) +{ + std::cout << "DPAdaptiveMedianBGS()" << std::endl; +} + +DPAdaptiveMedianBGS::~DPAdaptiveMedianBGS() +{ + std::cout << "~DPAdaptiveMedianBGS()" << std::endl; +} + +void DPAdaptiveMedianBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + frame = new IplImage(img_input); + + if(firstTime) + frame_data.ReleaseMemory(false); + frame_data = frame; + + if(firstTime) + { + int width = img_input.size().width; + int height = img_input.size().height; + + lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + params.SetFrameSize(width, height); + params.LowThreshold() = threshold; + params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing + params.SamplingRate() = samplingRate; + params.LearningFrames() = learningFrames; + + bgs.Initalize(params); + bgs.InitModel(frame_data); + } + + bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask); + lowThresholdMask.Clear(); + bgs.Update(frameNumber, frame_data, lowThresholdMask); + + cv::Mat foreground(highThresholdMask.Ptr()); + + if(showOutput) + cv::imshow("Adaptive Median (McFarlane&Schofield)", foreground); + + foreground.copyTo(img_output); + + delete frame; + firstTime = false; + frameNumber++; +} + +void DPAdaptiveMedianBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPAdaptiveMedianBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "threshold", threshold); + cvWriteInt(fs, "samplingRate", samplingRate); + cvWriteInt(fs, "learningFrames", learningFrames); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void DPAdaptiveMedianBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPAdaptiveMedianBGS.xml", 0, CV_STORAGE_READ); + + threshold = cvReadIntByName(fs, 0, "threshold", 40); + samplingRate = cvReadIntByName(fs, 0, "samplingRate", 7); + learningFrames = cvReadIntByName(fs, 0, "learningFrames", 30); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/dp/DPAdaptiveMedianBGS.h b/package_bgs/dp/DPAdaptiveMedianBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..05ac9c3364be2b0a9689af12b1116c4e4c9d3ede --- /dev/null +++ b/package_bgs/dp/DPAdaptiveMedianBGS.h @@ -0,0 +1,56 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "AdaptiveMedianBGS.h" + +using namespace Algorithms::BackgroundSubtraction; + +class DPAdaptiveMedianBGS : public IBGS +{ +private: + bool firstTime; + long frameNumber; + IplImage* frame; + RgbImage frame_data; + + AdaptiveMedianParams params; + AdaptiveMedianBGS bgs; + BwImage lowThresholdMask; + BwImage highThresholdMask; + + int threshold; + int samplingRate; + int learningFrames; + bool showOutput; + +public: + DPAdaptiveMedianBGS(); + ~DPAdaptiveMedianBGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/dp/DPEigenbackgroundBGS.cpp b/package_bgs/dp/DPEigenbackgroundBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6d2c6ba12cdf426b49831559f72bf443a5f904c5 --- /dev/null +++ b/package_bgs/dp/DPEigenbackgroundBGS.cpp @@ -0,0 +1,106 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "DPEigenbackgroundBGS.h" + +DPEigenbackgroundBGS::DPEigenbackgroundBGS() : firstTime(true), frameNumber(0), showOutput(true), threshold(225), historySize(20), embeddedDim(10) +{ + std::cout << "DPEigenbackgroundBGS()" << std::endl; +} + +DPEigenbackgroundBGS::~DPEigenbackgroundBGS() +{ + std::cout << "~DPEigenbackgroundBGS()" << std::endl; +} + +void DPEigenbackgroundBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + frame = new IplImage(img_input); + + if(firstTime) + frame_data.ReleaseMemory(false); + frame_data = frame; + + if(firstTime) + { + int width = img_input.size().width; + int height = img_input.size().height; + + lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + params.SetFrameSize(width, height); + params.LowThreshold() = threshold; //15*15; + params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing + //params.HistorySize() = 100; + params.HistorySize() = historySize; + //params.EmbeddedDim() = 20; + params.EmbeddedDim() = embeddedDim; + + bgs.Initalize(params); + bgs.InitModel(frame_data); + } + + bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask); + lowThresholdMask.Clear(); + bgs.Update(frameNumber, frame_data, lowThresholdMask); + + cv::Mat foreground(highThresholdMask.Ptr()); + + if(showOutput) + cv::imshow("Eigenbackground (Oliver)", foreground); + + foreground.copyTo(img_output); + + delete frame; + firstTime = false; + frameNumber++; +} + +void DPEigenbackgroundBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPEigenbackgroundBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "threshold", threshold); + cvWriteInt(fs, "historySize", historySize); + cvWriteInt(fs, "embeddedDim", embeddedDim); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void DPEigenbackgroundBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPEigenbackgroundBGS.xml", 0, CV_STORAGE_READ); + + threshold = cvReadIntByName(fs, 0, "threshold", 225); + historySize = cvReadIntByName(fs, 0, "historySize", 20); + embeddedDim = cvReadIntByName(fs, 0, "embeddedDim", 10); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/dp/DPEigenbackgroundBGS.h b/package_bgs/dp/DPEigenbackgroundBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..df558fe45cdbea23199685eafc5e2d44a54fec01 --- /dev/null +++ b/package_bgs/dp/DPEigenbackgroundBGS.h @@ -0,0 +1,56 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "Eigenbackground.h" + +using namespace Algorithms::BackgroundSubtraction; + +class DPEigenbackgroundBGS : public IBGS +{ +private: + bool firstTime; + long frameNumber; + IplImage* frame; + RgbImage frame_data; + + EigenbackgroundParams params; + Eigenbackground bgs; + BwImage lowThresholdMask; + BwImage highThresholdMask; + + int threshold; + int historySize; + int embeddedDim; + bool showOutput; + +public: + DPEigenbackgroundBGS(); + ~DPEigenbackgroundBGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/dp/DPGrimsonGMMBGS.cpp b/package_bgs/dp/DPGrimsonGMMBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0d6ad42fa6c59c4a356dccdf0ce054e8ceeaed42 --- /dev/null +++ b/package_bgs/dp/DPGrimsonGMMBGS.cpp @@ -0,0 +1,105 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "DPGrimsonGMMBGS.h" + +DPGrimsonGMMBGS::DPGrimsonGMMBGS() : firstTime(true), frameNumber(0), showOutput(true), threshold(9.0), alpha(0.01), gaussians(3) +{ + std::cout << "DPGrimsonGMMBGS()" << std::endl; +} + +DPGrimsonGMMBGS::~DPGrimsonGMMBGS() +{ + std::cout << "~DPGrimsonGMMBGS()" << std::endl; +} + +void DPGrimsonGMMBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + frame = new IplImage(img_input); + + if(firstTime) + frame_data.ReleaseMemory(false); + frame_data = frame; + + if(firstTime) + { + int width = img_input.size().width; + int height = img_input.size().height; + + lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + params.SetFrameSize(width, height); + params.LowThreshold() = threshold; //3.0f*3.0f; + params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing + //params.Alpha() = 0.001f; + params.Alpha() = alpha; //0.01f; + params.MaxModes() = gaussians; //3; + + bgs.Initalize(params); + bgs.InitModel(frame_data); + } + + bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask); + lowThresholdMask.Clear(); + bgs.Update(frameNumber, frame_data, lowThresholdMask); + + cv::Mat foreground(highThresholdMask.Ptr()); + + if(showOutput) + cv::imshow("GMM (Grimson)", foreground); + + foreground.copyTo(img_output); + + delete frame; + firstTime = false; + frameNumber++; +} + +void DPGrimsonGMMBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPGrimsonGMMBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteReal(fs, "threshold", threshold); + cvWriteReal(fs, "alpha", alpha); + cvWriteInt(fs, "gaussians", gaussians); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void DPGrimsonGMMBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPGrimsonGMMBGS.xml", 0, CV_STORAGE_READ); + + threshold = cvReadRealByName(fs, 0, "threshold", 9.0); + alpha = cvReadRealByName(fs, 0, "alpha", 0.01); + gaussians = cvReadIntByName(fs, 0, "gaussians", 3); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/dp/DPGrimsonGMMBGS.h b/package_bgs/dp/DPGrimsonGMMBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..37e2a33ab31e791ddb6371ad37e4f77d64ce71ed --- /dev/null +++ b/package_bgs/dp/DPGrimsonGMMBGS.h @@ -0,0 +1,56 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "GrimsonGMM.h" + +using namespace Algorithms::BackgroundSubtraction; + +class DPGrimsonGMMBGS : public IBGS +{ +private: + bool firstTime; + long frameNumber; + IplImage* frame; + RgbImage frame_data; + + GrimsonParams params; + GrimsonGMM bgs; + BwImage lowThresholdMask; + BwImage highThresholdMask; + + double threshold; + double alpha; + int gaussians; + bool showOutput; + +public: + DPGrimsonGMMBGS(); + ~DPGrimsonGMMBGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/dp/DPMeanBGS.cpp b/package_bgs/dp/DPMeanBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..13260b469c9d8eb50d05d9218513b46983162a37 --- /dev/null +++ b/package_bgs/dp/DPMeanBGS.cpp @@ -0,0 +1,105 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "DPMeanBGS.h" + +DPMeanBGS::DPMeanBGS() : firstTime(true), frameNumber(0), threshold(2700), alpha(1e-6f), learningFrames(30), showOutput(true) +{ + std::cout << "DPMeanBGS()" << std::endl; +} + +DPMeanBGS::~DPMeanBGS() +{ + std::cout << "~DPMeanBGS()" << std::endl; +} + +void DPMeanBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + frame = new IplImage(img_input); + + if(firstTime) + frame_data.ReleaseMemory(false); + frame_data = frame; + + if(firstTime) + { + int width = img_input.size().width; + int height = img_input.size().height; + + lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + params.SetFrameSize(width, height); + params.LowThreshold() = threshold; //3*30*30; // 2700 + params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing + //params.Alpha() = 1e-6f; + params.Alpha() = alpha; + params.LearningFrames() = learningFrames;//30; + + bgs.Initalize(params); + bgs.InitModel(frame_data); + } + + bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask); + lowThresholdMask.Clear(); + bgs.Update(frameNumber, frame_data, lowThresholdMask); + + cv::Mat foreground(highThresholdMask.Ptr()); + + if(showOutput) + cv::imshow("Temporal Mean (Donovan Parks)", foreground); + + foreground.copyTo(img_output); + + delete frame; + firstTime = false; + frameNumber++; +} + +void DPMeanBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPMeanBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "threshold", threshold); + cvWriteReal(fs, "alpha", alpha); + cvWriteInt(fs, "learningFrames", learningFrames); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void DPMeanBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPMeanBGS.xml", 0, CV_STORAGE_READ); + + threshold = cvReadIntByName(fs, 0, "threshold", 2700); + alpha = cvReadRealByName(fs, 0, "alpha", 1e-6f); + learningFrames = cvReadIntByName(fs, 0, "learningFrames", 30); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/dp/DPMeanBGS.h b/package_bgs/dp/DPMeanBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..b119ac4d6667478d37ceab0e234032f915834649 --- /dev/null +++ b/package_bgs/dp/DPMeanBGS.h @@ -0,0 +1,56 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "MeanBGS.h" + +using namespace Algorithms::BackgroundSubtraction; + +class DPMeanBGS : public IBGS +{ +private: + bool firstTime; + long frameNumber; + IplImage* frame; + RgbImage frame_data; + + MeanParams params; + MeanBGS bgs; + BwImage lowThresholdMask; + BwImage highThresholdMask; + + int threshold; + double alpha; + int learningFrames; + bool showOutput; + +public: + DPMeanBGS(); + ~DPMeanBGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/dp/DPPratiMediodBGS.cpp b/package_bgs/dp/DPPratiMediodBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..65c3ef3bc6e5115422bced7ab003face6381302a --- /dev/null +++ b/package_bgs/dp/DPPratiMediodBGS.cpp @@ -0,0 +1,107 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "DPPratiMediodBGS.h" + +DPPratiMediodBGS::DPPratiMediodBGS() : firstTime(true), frameNumber(0), threshold(30), samplingRate(5), historySize(16), weight(5), showOutput(true) +{ + std::cout << "DPPratiMediodBGS()" << std::endl; +} + +DPPratiMediodBGS::~DPPratiMediodBGS() +{ + std::cout << "~DPPratiMediodBGS()" << std::endl; +} + +void DPPratiMediodBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + frame = new IplImage(img_input); + + if(firstTime) + frame_data.ReleaseMemory(false); + frame_data = frame; + + if(firstTime) + { + int width = img_input.size().width; + int height = img_input.size().height; + + lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + params.SetFrameSize(width, height); + params.LowThreshold() = threshold; + params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing + params.SamplingRate() = samplingRate; + params.HistorySize() = historySize; + params.Weight() = weight; + + bgs.Initalize(params); + bgs.InitModel(frame_data); + } + + bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask); + lowThresholdMask.Clear(); + bgs.Update(frameNumber, frame_data, lowThresholdMask); + + cv::Mat foreground(highThresholdMask.Ptr()); + + if(showOutput) + cv::imshow("Temporal Median (Cucchiara&Calderara)", foreground); + + foreground.copyTo(img_output); + + delete frame; + firstTime = false; + frameNumber++; +} + +void DPPratiMediodBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPPratiMediodBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "threshold", threshold); + cvWriteInt(fs, "samplingRate", samplingRate); + cvWriteInt(fs, "historySize", historySize); + cvWriteInt(fs, "weight", weight); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void DPPratiMediodBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPPratiMediodBGS.xml", 0, CV_STORAGE_READ); + + threshold = cvReadIntByName(fs, 0, "threshold", 30); + samplingRate = cvReadIntByName(fs, 0, "samplingRate", 5); + historySize = cvReadIntByName(fs, 0, "historySize", 16); + weight = cvReadIntByName(fs, 0, "weight", 5); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/dp/DPPratiMediodBGS.h b/package_bgs/dp/DPPratiMediodBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..657dc1b3e51b95cf4033329344ce3aba037fa57f --- /dev/null +++ b/package_bgs/dp/DPPratiMediodBGS.h @@ -0,0 +1,57 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "PratiMediodBGS.h" + +using namespace Algorithms::BackgroundSubtraction; + +class DPPratiMediodBGS : public IBGS +{ +private: + bool firstTime; + long frameNumber; + IplImage* frame; + RgbImage frame_data; + + PratiParams params; + PratiMediodBGS bgs; + BwImage lowThresholdMask; + BwImage highThresholdMask; + + int threshold; + int samplingRate; + int historySize; + int weight; + bool showOutput; + +public: + DPPratiMediodBGS(); + ~DPPratiMediodBGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/dp/DPTextureBGS.cpp b/package_bgs/dp/DPTextureBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d34342e1151fb8c174a9291781622a63fc10a0a --- /dev/null +++ b/package_bgs/dp/DPTextureBGS.cpp @@ -0,0 +1,156 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "DPTextureBGS.h" + +DPTextureBGS::DPTextureBGS() : firstTime(true), showOutput(true) + //, enableFiltering(true) +{ + std::cout << "DPTextureBGS()" << std::endl; +} + +DPTextureBGS::~DPTextureBGS() +{ + delete[] bgModel; // ~10Kb (25.708-15.968) + delete[] modeArray; + delete[] curTextureHist; // ~10Kb (16-6.396) + //cvReleaseStructuringElement(&dilateElement); + //cvReleaseStructuringElement(&erodeElement); + image.ReleaseImage(); + fgMask.ReleaseImage(); + tempMask.ReleaseImage(); + texture.ReleaseImage(); + std::cout << "~DPTextureBGS()" << std::endl; +} + +void DPTextureBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + frame = new IplImage(img_input); + + if(firstTime) + { + width = img_input.size().width; + height = img_input.size().height; + size = width * height; + + // input image + image = cvCreateImage(cvSize(width, height), 8, 3); + cvCopy(frame, image.Ptr()); + + // foreground masks + fgMask = cvCreateImage(cvSize(width, height), 8, 1); + tempMask = cvCreateImage(cvSize(width, height), 8, 1); + cvZero(fgMask.Ptr()); + cvZero(tempMask.Ptr()); + + // create background model + bgModel = new TextureArray[size]; + texture = cvCreateImage(cvSize(width, height), 8, 3); + cvZero(texture.Ptr()); + modeArray = new unsigned char[size]; + curTextureHist = new TextureHistogram[size]; + + // initialize background model + bgs.LBP(image, texture); + bgs.Histogram(texture, curTextureHist); + for(int y = REGION_R+TEXTURE_R; y < height-REGION_R-TEXTURE_R; ++y) + { + for(int x = REGION_R+TEXTURE_R; x < width-REGION_R-TEXTURE_R; ++x) + { + int index = x+y*width; + + for(int m = 0; m < NUM_MODES; ++m) + { + for(int i = 0; i < NUM_BINS; ++i) + { + bgModel[index].mode[m].r[i] = curTextureHist[index].r[i]; + bgModel[index].mode[m].g[i] = curTextureHist[index].g[i]; + bgModel[index].mode[m].b[i] = curTextureHist[index].b[i]; + } + } + } + } + + //dilateElement = cvCreateStructuringElementEx(7, 7, 3, 3, CV_SHAPE_RECT); + //erodeElement = cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT); + + saveConfig(); + firstTime = false; + } + + cvCopy(frame, image.Ptr()); + + // perform background subtraction + bgs.LBP(image, texture); + bgs.Histogram(texture, curTextureHist); + bgs.BgsCompare(bgModel, curTextureHist, modeArray, THRESHOLD, fgMask); + + //if(enableFiltering) + //{ + // // size filtering + // ConnectedComponents cc; + // CBlobResult largeBlobs; + // cc.SetImage(&fgMask); + // cc.Find(127); + // cc.FilterMinArea(size/30, largeBlobs); + // fgMask.Clear(); + // cc.ColorBlobs(fgMask.Ptr(), largeBlobs, CV_RGB(255,255,255)); + // largeBlobs.ClearBlobs(); + + // // morphological operators + // cvDilate(fgMask.Ptr(), fgMask.Ptr(), dilateElement, 1); + // cvErode(fgMask.Ptr(), fgMask.Ptr(), erodeElement, 1); + //} + + cv::Mat foreground(fgMask.Ptr()); + if(!foreground.empty()) + foreground.copyTo(img_output); + + if(showOutput) + cv::imshow("Texture BGS (Donovan Parks)", foreground); + + // update background subtraction + bgs.UpdateModel(fgMask, bgModel, curTextureHist, modeArray); + + delete frame; +} + +void DPTextureBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPTextureBGS.xml", 0, CV_STORAGE_WRITE); + + //cvWriteReal(fs, "alpha", alpha); + //cvWriteInt(fs, "enableFiltering", enableFiltering); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void DPTextureBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPTextureBGS.xml", 0, CV_STORAGE_READ); + + //alpha = cvReadRealByName(fs, 0, "alpha", 1e-6f); + //enableFiltering = cvReadIntByName(fs, 0, "enableFiltering", true); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/dp/DPTextureBGS.h b/package_bgs/dp/DPTextureBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..daa6a554481811cf3fa1218f16e5612359cc1579 --- /dev/null +++ b/package_bgs/dp/DPTextureBGS.h @@ -0,0 +1,60 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "TextureBGS.h" +//#include "ConnectedComponents.h" + +class DPTextureBGS : public IBGS +{ +private: + bool firstTime; + bool showOutput; + + int width; + int height; + int size; + TextureBGS bgs; + IplImage* frame; + RgbImage image; + BwImage fgMask; + BwImage tempMask; + TextureArray* bgModel; + RgbImage texture; + unsigned char* modeArray; + TextureHistogram* curTextureHist; + //ConnectedComponents cc; + //CBlobResult largeBlobs; + //IplConvKernel* dilateElement; + //IplConvKernel* erodeElement; + //bool enableFiltering; + +public: + DPTextureBGS(); + ~DPTextureBGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; diff --git a/package_bgs/dp/DPWrenGABGS.cpp b/package_bgs/dp/DPWrenGABGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d7241b1db6ce9ef0c219b5fbaa28e7cb2abc2963 --- /dev/null +++ b/package_bgs/dp/DPWrenGABGS.cpp @@ -0,0 +1,105 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "DPWrenGABGS.h" + +DPWrenGABGS::DPWrenGABGS() : firstTime(true), frameNumber(0), threshold(12.25f), alpha(0.005f), learningFrames(30), showOutput(true) +{ + std::cout << "DPWrenGABGS()" << std::endl; +} + +DPWrenGABGS::~DPWrenGABGS() +{ + std::cout << "~DPWrenGABGS()" << std::endl; +} + +void DPWrenGABGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + frame = new IplImage(img_input); + + if(firstTime) + frame_data.ReleaseMemory(false); + frame_data = frame; + + if(firstTime) + { + int width = img_input.size().width; + int height = img_input.size().height; + + lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + params.SetFrameSize(width, height); + params.LowThreshold() = threshold; //3.5f*3.5f; + params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing + params.Alpha() = alpha; //0.005f; + params.LearningFrames() = learningFrames; //30; + + bgs.Initalize(params); + bgs.InitModel(frame_data); + } + + bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask); + lowThresholdMask.Clear(); + bgs.Update(frameNumber, frame_data, lowThresholdMask); + + cv::Mat foreground(highThresholdMask.Ptr()); + + if(showOutput) + cv::imshow("Gaussian Average (Wren)", foreground); + + foreground.copyTo(img_output); + + delete frame; + firstTime = false; + frameNumber++; +} + +void DPWrenGABGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPWrenGABGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteReal(fs, "threshold", threshold); + cvWriteReal(fs, "alpha", alpha); + cvWriteInt(fs, "learningFrames", learningFrames); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void DPWrenGABGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPWrenGABGS.xml", 0, CV_STORAGE_READ); + + threshold = cvReadRealByName(fs, 0, "threshold", 12.25f); + alpha = cvReadRealByName(fs, 0, "alpha", 0.005f); + learningFrames = cvReadIntByName(fs, 0, "learningFrames", 30); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} + diff --git a/package_bgs/dp/DPWrenGABGS.h b/package_bgs/dp/DPWrenGABGS.h new file mode 100644 index 0000000000000000000000000000000000000000..7e545c5fe0038a560b452f9dc778267239aa2f66 --- /dev/null +++ b/package_bgs/dp/DPWrenGABGS.h @@ -0,0 +1,56 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "WrenGA.h" + +using namespace Algorithms::BackgroundSubtraction; + +class DPWrenGABGS : public IBGS +{ +private: + bool firstTime; + long frameNumber; + IplImage* frame; + RgbImage frame_data; + + WrenParams params; + WrenGA bgs; + BwImage lowThresholdMask; + BwImage highThresholdMask; + + double threshold; + double alpha; + int learningFrames; + bool showOutput; + +public: + DPWrenGABGS(); + ~DPWrenGABGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/dp/DPZivkovicAGMMBGS.cpp b/package_bgs/dp/DPZivkovicAGMMBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..16f991cc96ed1d43683b455764464ff1c4dd1ab8 --- /dev/null +++ b/package_bgs/dp/DPZivkovicAGMMBGS.cpp @@ -0,0 +1,104 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "DPZivkovicAGMMBGS.h" + +DPZivkovicAGMMBGS::DPZivkovicAGMMBGS() : firstTime(true), frameNumber(0), showOutput(true), threshold(25.0f), alpha(0.001f), gaussians(3) +{ + std::cout << "DPZivkovicAGMMBGS()" << std::endl; +} + +DPZivkovicAGMMBGS::~DPZivkovicAGMMBGS() +{ + std::cout << "~DPZivkovicAGMMBGS()" << std::endl; +} + +void DPZivkovicAGMMBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + frame = new IplImage(img_input); + + if(firstTime) + frame_data.ReleaseMemory(false); + frame_data = frame; + + if(firstTime) + { + int width = img_input.size().width; + int height = img_input.size().height; + + lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + params.SetFrameSize(width, height); + params.LowThreshold() = threshold; //5.0f*5.0f; + params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing + params.Alpha() = alpha; //0.001f; + params.MaxModes() = gaussians; //3; + + bgs.Initalize(params); + bgs.InitModel(frame_data); + } + + bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask); + lowThresholdMask.Clear(); + bgs.Update(frameNumber, frame_data, lowThresholdMask); + + cv::Mat foreground(highThresholdMask.Ptr()); + + if(showOutput) + cv::imshow("Gaussian Mixture Model (Zivkovic)", foreground); + + foreground.copyTo(img_output); + + delete frame; + firstTime = false; + frameNumber++; +} + +void DPZivkovicAGMMBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPZivkovicAGMMBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteReal(fs, "threshold", threshold); + cvWriteReal(fs, "alpha", alpha); + cvWriteInt(fs, "gaussians", gaussians); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void DPZivkovicAGMMBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/DPZivkovicAGMMBGS.xml", 0, CV_STORAGE_READ); + + threshold = cvReadRealByName(fs, 0, "threshold", 25.0f); + alpha = cvReadRealByName(fs, 0, "alpha", 0.001f); + gaussians = cvReadIntByName(fs, 0, "gaussians", 3); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/dp/DPZivkovicAGMMBGS.h b/package_bgs/dp/DPZivkovicAGMMBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..d213341e745ee0e6fd771660933a73a56da8f46e --- /dev/null +++ b/package_bgs/dp/DPZivkovicAGMMBGS.h @@ -0,0 +1,56 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "ZivkovicAGMM.h" + +using namespace Algorithms::BackgroundSubtraction; + +class DPZivkovicAGMMBGS : public IBGS +{ +private: + bool firstTime; + long frameNumber; + IplImage* frame; + RgbImage frame_data; + + ZivkovicParams params; + ZivkovicAGMM bgs; + BwImage lowThresholdMask; + BwImage highThresholdMask; + + double threshold; + double alpha; + int gaussians; + bool showOutput; + +public: + DPZivkovicAGMMBGS(); + ~DPZivkovicAGMMBGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/dp/Eigenbackground.cpp b/package_bgs/dp/Eigenbackground.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d2c3ef3d85a26d0653e7b17f3486a4e4affa903 --- /dev/null +++ b/package_bgs/dp/Eigenbackground.cpp @@ -0,0 +1,190 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* Eigenbackground.cpp +* +* Purpose: Implementation of the Eigenbackground background subtraction +* algorithm developed by Oliver et al. +* +* Author: Donovan Parks, September 2007 +* +* "A Bayesian Computer Vision System for Modeling Human Interactions" +* Nuria Oliver, Barbara Rosario, Alex P. Pentland 2000 +* +******************************************************************************/ + +#include "Eigenbackground.h" + +using namespace Algorithms::BackgroundSubtraction; + +Eigenbackground::Eigenbackground() +{ + m_pcaData = NULL; + m_pcaAvg = NULL; + m_eigenValues = NULL; + m_eigenVectors = NULL; +} + +Eigenbackground::~Eigenbackground() +{ + if(m_pcaData != NULL) cvReleaseMat(&m_pcaData); + if(m_pcaAvg != NULL) cvReleaseMat(&m_pcaAvg); + if(m_eigenValues != NULL) cvReleaseMat(&m_eigenValues); + if(m_eigenVectors != NULL) cvReleaseMat(&m_eigenVectors); +} + +void Eigenbackground::Initalize(const BgsParams& param) +{ + m_params = (EigenbackgroundParams&)param; + + m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); + m_background.Clear(); +} + +void Eigenbackground::InitModel(const RgbImage& data) +{ + if(m_pcaData != NULL) cvReleaseMat(&m_pcaData); + if(m_pcaAvg != NULL) cvReleaseMat(&m_pcaAvg); + if(m_eigenValues != NULL) cvReleaseMat(&m_eigenValues); + if(m_eigenVectors != NULL) cvReleaseMat(&m_eigenVectors); + + m_pcaData = cvCreateMat(m_params.HistorySize(), m_params.Size()*3, CV_8UC1); + + m_background.Clear(); +} + +void Eigenbackground::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) +{ + // the eigenbackground model is not updated (serious limitation!) +} + +void Eigenbackground::Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask) +{ + // create eigenbackground + if(frame_num == m_params.HistorySize()) + { + // create the eigenspace + m_pcaAvg = cvCreateMat( 1, m_pcaData->cols, CV_32F ); + m_eigenValues = cvCreateMat( m_pcaData->rows, 1, CV_32F ); + m_eigenVectors = cvCreateMat( m_pcaData->rows, m_pcaData->cols, CV_32F ); + cvCalcPCA(m_pcaData, m_pcaAvg, m_eigenValues, m_eigenVectors, CV_PCA_DATA_AS_ROW); + + int index = 0; + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + for(int ch = 0; ch < m_background.Ptr()->nChannels; ++ch) + { + m_background(r,c,0) = static_cast<unsigned char>(cvmGet(m_pcaAvg,0,index)+0.5); + index++; + } + } + } + } + + if(frame_num >= m_params.HistorySize()) + { + // project new image into the eigenspace + int w = data.Ptr()->width; + int h = data.Ptr()->height; + int ch = data.Ptr()->nChannels; + CvMat* dataPt = cvCreateMat(1, w*h*ch, CV_8UC1); + CvMat data_row; + cvGetRow(dataPt, &data_row, 0); + cvReshape(&data_row, &data_row, 3, data.Ptr()->height); + cvCopy(data.Ptr(), &data_row); + + CvMat* proj = cvCreateMat(1, m_params.EmbeddedDim(), CV_32F); + cvProjectPCA(dataPt, m_pcaAvg, m_eigenVectors, proj); + + // reconstruct point + CvMat* result = cvCreateMat(1, m_pcaData->cols, CV_32F); + cvBackProjectPCA(proj, m_pcaAvg, m_eigenVectors, result); + + // calculate Euclidean distance between new image and its eigenspace projection + int index = 0; + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + double dist = 0; + bool bgLow = true; + bool bgHigh = true; + for(int ch = 0; ch < 3; ++ch) + { + dist = (data(r,c,ch) - cvmGet(result,0,index))*(data(r,c,ch) - cvmGet(result,0,index)); + if(dist > m_params.LowThreshold()) + bgLow = false; + if(dist > m_params.HighThreshold()) + bgHigh = false; + index++; + } + + if(!bgLow) + { + low_threshold_mask(r,c) = FOREGROUND; + } + else + { + low_threshold_mask(r,c) = BACKGROUND; + } + + if(!bgHigh) + { + high_threshold_mask(r,c) = FOREGROUND; + } + else + { + high_threshold_mask(r,c) = BACKGROUND; + } + } + } + + cvReleaseMat(&result); + cvReleaseMat(&proj); + cvReleaseMat(&dataPt); + } + else + { + // set entire image to background since there is not enough information yet + // to start performing background subtraction + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + low_threshold_mask(r,c) = BACKGROUND; + high_threshold_mask(r,c) = BACKGROUND; + } + } + } + + UpdateHistory(frame_num, data); +} + +void Eigenbackground::UpdateHistory(int frame_num, const RgbImage& new_frame) +{ + if(frame_num < m_params.HistorySize()) + { + CvMat src_row; + cvGetRow(m_pcaData, &src_row, frame_num); + cvReshape(&src_row, &src_row, 3, new_frame.Ptr()->height); + cvCopy(new_frame.Ptr(), &src_row); + } +} diff --git a/package_bgs/dp/Eigenbackground.h b/package_bgs/dp/Eigenbackground.h new file mode 100644 index 0000000000000000000000000000000000000000..b86ac92a66bfeb6b07c694f1599b61f2bdb9b5cf --- /dev/null +++ b/package_bgs/dp/Eigenbackground.h @@ -0,0 +1,101 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* Eigenbackground.hpp +* +* Purpose: Implementation of the Eigenbackground background subtraction +* algorithm developed by Oliver et al. +* +* Author: Donovan Parks, September 2007 +* +* "A Bayesian Computer Vision System for Modeling Human Interactions" +* Nuria Oliver, Barbara Rosario, Alex P. Pentland 2000 + +Example: +Algorithms::BackgroundSubtraction::EigenbackgroundParams params; +params.SetFrameSize(width, height); +params.LowThreshold() = 15*15; +params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing +params.HistorySize() = 100; +params.EmbeddedDim() = 20; + +Algorithms::BackgroundSubtraction::Eigenbackground bgs; +bgs.Initalize(params); +******************************************************************************/ + +#ifndef _ELGAMMAL_H_ +#define _ELGAMMAL_H_ + +#include "Bgs.h" + +namespace Algorithms +{ + namespace BackgroundSubtraction + { + // --- Parameters used by the Mean BGS algorithm --- + class EigenbackgroundParams : public BgsParams + { + public: + float &LowThreshold() { return m_low_threshold; } + float &HighThreshold() { return m_high_threshold; } + + int &HistorySize() { return m_history_size; } + int &EmbeddedDim() { return m_dim; } + + private: + // A pixel will be classified as foreground if the squared distance of any + // color channel is greater than the specified threshold + float m_low_threshold; + float m_high_threshold; + + int m_history_size; // number frames used to create eigenspace + int m_dim; // eigenspace dimensionality + }; + + // --- Eigenbackground BGS algorithm --- + class Eigenbackground : public Bgs + { + public: + Eigenbackground(); + ~Eigenbackground(); + + void Initalize(const BgsParams& param); + + void InitModel(const RgbImage& data); + void Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask); + void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); + + RgbImage* Background() { return &m_background; } + + private: + void UpdateHistory(int frameNum, const RgbImage& newFrame); + + EigenbackgroundParams m_params; + + CvMat* m_pcaData; + CvMat* m_pcaAvg; + CvMat* m_eigenValues; + CvMat* m_eigenVectors; + + RgbImage m_background; + }; + }; +}; + +#endif \ No newline at end of file diff --git a/package_bgs/dp/Error.cpp b/package_bgs/dp/Error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1bd87db824e308d63181f5ea6cc32a6304d6129f --- /dev/null +++ b/package_bgs/dp/Error.cpp @@ -0,0 +1,57 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* Error.cpp +* +* Purpose: Error checking routines. +* +* Author: Donovan Parks, July 2007 +* +******************************************************************************/ + +#include <iostream> +#include <fstream> + +#include "Error.h" + +using namespace std; + +ofstream traceFile; + +bool Error(const char* msg, const char* code, int data) +{ + cerr << code << ": " << msg << endl; + + return false; +} + +bool TraceInit(const char* filename) +{ + traceFile.open(filename); + return traceFile.is_open(); +} + +void Trace(const char* msg) +{ + traceFile << msg << endl; +} + +void TraceClose() +{ + traceFile.close(); +} diff --git a/package_bgs/dp/Error.h b/package_bgs/dp/Error.h new file mode 100644 index 0000000000000000000000000000000000000000..cdbb222c4bdc9ec92b5c2aad3bd02c06fd39c2b9 --- /dev/null +++ b/package_bgs/dp/Error.h @@ -0,0 +1,36 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* Error.h +* +* Purpose: Error checking routines. +* +* Author: Donovan Parks, July 2007 +* +******************************************************************************/ + +#ifndef ERROR_H +#define ERROR_H + +bool Error(const char* msg, const char* code, int data); + +bool TraceInit(const char* filename); +void Trace(const char* msg); +void TraceClose(); + +#endif diff --git a/package_bgs/dp/GrimsonGMM.cpp b/package_bgs/dp/GrimsonGMM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5788dc9d1c77d992ac145167f51c8da1694703fe --- /dev/null +++ b/package_bgs/dp/GrimsonGMM.cpp @@ -0,0 +1,332 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* GrimsonGMM.cpp +* +* Purpose: Implementation of the Gaussian mixture model (GMM) background +* subtraction described in: +* "Adaptive background mixture models for real-time tracking" +* by Chris Stauffer and W.E.L Grimson +* +* Author: Donovan Parks, September 2007 +* +* This code is based on code by Z. Zivkovic's written for his enhanced GMM +* background subtraction algorithm: +* +* "Improved adaptive Gausian mixture model for background subtraction" +* Z.Zivkovic +* International Conference Pattern Recognition, UK, August, 2004 +* +* +* "Efficient Adaptive Density Estimapion per Image Pixel for the +* Task of Background Subtraction" +* Z.Zivkovic, F. van der Heijden +* Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006. +* +* Zivkovic's code can be obtained at: www.zoranz.net +******************************************************************************/ + +#include "GrimsonGMM.h" + +using namespace Algorithms::BackgroundSubtraction; + +int compareGMM(const void* _gmm1, const void* _gmm2) +{ + GMM gmm1 = *(GMM*)_gmm1; + GMM gmm2 = *(GMM*)_gmm2; + + if(gmm1.significants < gmm2.significants) + return 1; + else if(gmm1.significants == gmm2.significants) + return 0; + else + return -1; +} + +GrimsonGMM::GrimsonGMM() +{ + m_modes = NULL; +} + +GrimsonGMM::~GrimsonGMM() +{ + if(m_modes != NULL) + delete[] m_modes; +} + +void GrimsonGMM::Initalize(const BgsParams& param) +{ + m_params = (GrimsonParams&)param; + + // Tbf - the threshold + m_bg_threshold = 0.75f; // 1-cf from the paper + + // Tgenerate - the threshold + m_variance = 36.0f; // sigma for the new mode + + // GMM for each pixel + m_modes = new GMM[m_params.Size()*m_params.MaxModes()]; + + // used modes per pixel + m_modes_per_pixel = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1); + + m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); +} + +RgbImage* GrimsonGMM::Background() +{ + return &m_background; +} + +void GrimsonGMM::InitModel(const RgbImage& data) +{ + m_modes_per_pixel.Clear(); + + for(unsigned int i = 0; i < m_params.Size()*m_params.MaxModes(); ++i) + { + m_modes[i].weight = 0; + m_modes[i].variance = 0; + m_modes[i].muR = 0; + m_modes[i].muG = 0; + m_modes[i].muB = 0; + m_modes[i].significants = 0; + } +} + +void GrimsonGMM::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) +{ + // it doesn't make sense to have conditional updates in the GMM framework +} + +void GrimsonGMM::SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char& numModes, + unsigned char& low_threshold, unsigned char& high_threshold) +{ + // calculate distances to the modes (+ sort???) + // here we need to go in descending order!!! + long pos; + bool bFitsPDF=false; + bool bBackgroundLow=false; + bool bBackgroundHigh=false; + + float fOneMinAlpha = 1-m_params.Alpha(); + + float totalWeight = 0.0f; + + // calculate number of Gaussians to include in the background model + int backgroundGaussians = 0; + double sum = 0.0; + for(int i = 0; i < numModes; ++i) + { + if(sum < m_bg_threshold) + { + backgroundGaussians++; + sum += m_modes[posPixel+i].weight; + } + else + { + break; + } + } + + // update all distributions and check for match with current pixel + for (int iModes=0; iModes < numModes; iModes++) + { + pos=posPixel+iModes; + float weight = m_modes[pos].weight; + + // fit not found yet + if (!bFitsPDF) + { + //check if it belongs to some of the modes + //calculate distance + float var = m_modes[pos].variance; + float muR = m_modes[pos].muR; + float muG = m_modes[pos].muG; + float muB = m_modes[pos].muB; + + float dR=muR - pixel(0); + float dG=muG - pixel(1); + float dB=muB - pixel(2); + + // calculate the squared distance + float dist = (dR*dR + dG*dG + dB*dB); + + if(dist < m_params.HighThreshold()*var && iModes < backgroundGaussians) + bBackgroundHigh = true; + + // a match occurs when the pixel is within sqrt(fTg) standard deviations of the distribution + if(dist < m_params.LowThreshold()*var) + { + bFitsPDF=true; + + // check if this Gaussian is part of the background model + if(iModes < backgroundGaussians) + bBackgroundLow = true; + + //update distribution + float k = m_params.Alpha()/weight; + weight = fOneMinAlpha*weight + m_params.Alpha(); + m_modes[pos].weight = weight; + m_modes[pos].muR = muR - k*(dR); + m_modes[pos].muG = muG - k*(dG); + m_modes[pos].muB = muB - k*(dB); + + //limit the variance + float sigmanew = var + k*(dist-var); + m_modes[pos].variance = sigmanew < 4 ? 4 : sigmanew > 5*m_variance ? 5*m_variance : sigmanew; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + else + { + weight = fOneMinAlpha*weight; + if (weight < 0.0) + { + weight=0.0; + numModes--; + } + + m_modes[pos].weight = weight; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + } + else + { + weight = fOneMinAlpha*weight; + if (weight < 0.0) + { + weight=0.0; + numModes--; + } + m_modes[pos].weight = weight; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + + totalWeight += weight; + } + + // renormalize weights so they add to one + double invTotalWeight = 1.0 / totalWeight; + for (int iLocal = 0; iLocal < numModes; iLocal++) + { + m_modes[posPixel + iLocal].weight *= (float)invTotalWeight; + m_modes[posPixel + iLocal].significants = m_modes[posPixel + iLocal].weight + / sqrt(m_modes[posPixel + iLocal].variance); + } + + // Sort significance values so they are in desending order. + qsort(&m_modes[posPixel], numModes, sizeof(GMM), compareGMM); + + // make new mode if needed and exit + if (!bFitsPDF) + { + if (numModes < m_params.MaxModes()) + { + numModes++; + } + else + { + // the weakest mode will be replaced + } + + pos = posPixel + numModes-1; + + m_modes[pos].muR = pixel.ch[0]; + m_modes[pos].muG = pixel.ch[1]; + m_modes[pos].muB = pixel.ch[2]; + m_modes[pos].variance = m_variance; + m_modes[pos].significants = 0; // will be set below + + if (numModes==1) + m_modes[pos].weight = 1; + else + m_modes[pos].weight = m_params.Alpha(); + + //renormalize weights + int iLocal; + float sum = 0.0; + for (iLocal = 0; iLocal < numModes; iLocal++) + { + sum += m_modes[posPixel+ iLocal].weight; + } + + double invSum = 1.0/sum; + for (iLocal = 0; iLocal < numModes; iLocal++) + { + m_modes[posPixel + iLocal].weight *= (float)invSum; + m_modes[posPixel + iLocal].significants = m_modes[posPixel + iLocal].weight + / sqrt(m_modes[posPixel + iLocal].variance); + + } + } + + // Sort significance values so they are in desending order. + qsort(&(m_modes[posPixel]), numModes, sizeof(GMM), compareGMM); + + if(bBackgroundLow) + { + low_threshold = BACKGROUND; + } + else + { + low_threshold = FOREGROUND; + } + + if(bBackgroundHigh) + { + high_threshold = BACKGROUND; + } + else + { + high_threshold = FOREGROUND; + } +} + +/////////////////////////////////////////////////////////////////////////////// +//Input: +// data - a pointer to the data of a RGB image of the same size +//Output: +// output - a pointer to the data of a gray value image of the same size +// (the memory should already be reserved) +// values: 255-foreground, 125-shadow, 0-background +/////////////////////////////////////////////////////////////////////////////// +void GrimsonGMM::Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask) +{ + unsigned char low_threshold, high_threshold; + long posPixel; + + // update each pixel of the image + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + // update model + background subtract + posPixel=(r*m_params.Width()+c)*m_params.MaxModes(); + + SubtractPixel(posPixel, data(r,c), m_modes_per_pixel(r,c), low_threshold, high_threshold); + + low_threshold_mask(r,c) = low_threshold; + high_threshold_mask(r,c) = high_threshold; + + m_background(r,c,0) = (unsigned char)m_modes[posPixel].muR; + m_background(r,c,1) = (unsigned char)m_modes[posPixel].muG; + m_background(r,c,2) = (unsigned char)m_modes[posPixel].muB; + } + } +} + diff --git a/package_bgs/dp/GrimsonGMM.h b/package_bgs/dp/GrimsonGMM.h new file mode 100644 index 0000000000000000000000000000000000000000..2d11a286962e37fc4467ec7decda61209f64a4c1 --- /dev/null +++ b/package_bgs/dp/GrimsonGMM.h @@ -0,0 +1,150 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* GrimsonGMM.cpp +* +* Purpose: Implementation of the Gaussian mixture model (GMM) background +* subtraction described in: +* "Adaptive background mixture models for real-time tracking" +* by Chris Stauffer and W.E.L Grimson +* +* Author: Donovan Parks, September 2007 +* +* This code is based on code by Z. Zivkovic's written for his enhanced GMM +* background subtraction algorithm: +* +* "Improved adaptive Gausian mixture model for background subtraction" +* Z.Zivkovic +* International Conference Pattern Recognition, UK, August, 2004 +* +* +* "Efficient Adaptive Density Estimapion per Image Pixel for the +* Task of Background Subtraction" +* Z.Zivkovic, F. van der Heijden +* Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006. +* +* Zivkovic's code can be obtained at: www.zoranz.net + +Example: + Algorithms::BackgroundSubtraction::GrimsonParams params; + params.SetFrameSize(width, height); + params.LowThreshold() = 3.0f*3.0f; + params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing + params.Alpha() = 0.001f; + params.MaxModes() = 3; + + Algorithms::BackgroundSubtraction::GrimsonGMM bgs; + bgs.Initalize(params); +******************************************************************************/ + +#ifndef GRIMSON_GMM_ +#define GRIMSON_GMM_ + +#include "Bgs.h" + +namespace Algorithms +{ + namespace BackgroundSubtraction + { + typedef struct GMMGaussian + { + float variance; + float muR; + float muG; + float muB; + float weight; + float significants; // this is equal to weight / standard deviation and is used to + // determine which Gaussians should be part of the background model + } GMM; + + // --- User adjustable parameters used by the Grimson GMM BGS algorithm --- + class GrimsonParams : public BgsParams + { + public: + float &LowThreshold() { return m_low_threshold; } + float &HighThreshold() { return m_high_threshold; } + + float &Alpha() { return m_alpha; } + int &MaxModes() { return m_max_modes; } + + private: + // Threshold on the squared dist. to decide when a sample is close to an existing + // components. If it is not close to any a new component will be generated. + // Smaller threshold values lead to more generated components and higher threshold values + // lead to a small number of components but they can grow too large. + // + // It is usual easiest to think of these thresholds as being the number of variances away + // from the mean of a pixel before it is considered to be from the foreground. + float m_low_threshold; + float m_high_threshold; + + // alpha - speed of update - if the time interval you want to average over is T + // set alpha=1/T. + float m_alpha; + + // Maximum number of modes (Gaussian components) that will be used per pixel + int m_max_modes; + }; + + // --- Grimson GMM BGS algorithm --- + class GrimsonGMM : public Bgs + { + public: + GrimsonGMM(); + ~GrimsonGMM(); + + void Initalize(const BgsParams& param); + + void InitModel(const RgbImage& data); + void Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask); + void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); + + RgbImage* Background(); + + private: + void SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char& numModes, + unsigned char& lowThreshold, unsigned char& highThreshold); + + // User adjustable parameters + GrimsonParams m_params; + + // Threshold when the component becomes significant enough to be included into + // the background model. It is the TB = 1-cf from the paper. So I use cf=0.1 => TB=0.9 + // For alpha=0.001 it means that the mode should exist for approximately 105 frames before + // it is considered foreground + float m_bg_threshold; //1-cf from the paper + + // Initial variance for the newly generated components. + // It will will influence the speed of adaptation. A good guess should be made. + // A simple way is to estimate the typical standard deviation from the images. + float m_variance; + + // Dynamic array for the mixture of Gaussians + GMM* m_modes; + + // Number of Gaussian components per pixel + BwImage m_modes_per_pixel; + + // Current background model + RgbImage m_background; + }; + }; +}; + +#endif diff --git a/package_bgs/dp/Image.cpp b/package_bgs/dp/Image.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f00febda47ff02b170ddbed9e9d5ed9bf1d4214e --- /dev/null +++ b/package_bgs/dp/Image.cpp @@ -0,0 +1,76 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* Image.hpp +* +* Purpose: C++ wrapper for OpenCV IplImage which supports simple and +* efficient access to the image data +* +* Author: Donovan Parks, September 2007 +* +* Based on code from: +* http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.hpptml +******************************************************************************/ + +#include "Image.h" + +ImageBase::~ImageBase() +{ + if(imgp != NULL && m_bReleaseMemory) + cvReleaseImage(&imgp); + imgp = NULL; +} + +void DensityFilter(BwImage& image, BwImage& filtered, int minDensity, unsigned char fgValue) +{ + for(int r = 1; r < image.Ptr()->height-1; ++r) + { + for(int c = 1; c < image.Ptr()->width-1; ++c) + { + int count = 0; + if(image(r,c) == fgValue) + { + if(image(r-1,c-1) == fgValue) + count++; + if(image(r-1,c) == fgValue) + count++; + if(image(r-1,c+1) == fgValue) + count++; + if(image(r,c-1) == fgValue) + count++; + if(image(r,c+1) == fgValue) + count++; + if(image(r+1,c-1) == fgValue) + count++; + if(image(r+1,c) == fgValue) + count++; + if(image(r+1,c+1) == fgValue) + count++; + + if(count < minDensity) + filtered(r,c) = 0; + else + filtered(r,c) = fgValue; + } + else + { + filtered(r,c) = 0; + } + } + } +} \ No newline at end of file diff --git a/package_bgs/dp/Image.h b/package_bgs/dp/Image.h new file mode 100644 index 0000000000000000000000000000000000000000..40d327b2fec50d685e3562f8720f46f7484490f6 --- /dev/null +++ b/package_bgs/dp/Image.h @@ -0,0 +1,364 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* Image.h +* +* Purpose: C++ wrapper for OpenCV IplImage which supports simple and +* efficient access to the image data +* +* Author: Donovan Parks, September 2007 +* +* Based on code from: +* http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html +******************************************************************************/ + +#ifndef _IMAGE_H_ +#define _IMAGE_H_ + +#include <cv.h> +#include <cxcore.h> + +// --- Image Iterator --------------------------------------------------------- + +template <class T> +class ImageIterator +{ +public: + ImageIterator(IplImage* image, int x=0, int y=0, int dx= 0, int dy=0) : + i(x), j(y), i0(0) + { + data = reinterpret_cast<T*>(image->imageData); + step = image->widthStep / sizeof(T); + + nl= image->height; + if ((y+dy)>0 && (y+dy) < nl) + nl= y+dy; + + if (y<0) + j=0; + + data += step*j; + + nc = image->width; + if ((x+dx) > 0 && (x+dx) < nc) + nc = x+dx; + + nc *= image->nChannels; + if (x>0) + i0 = x*image->nChannels; + i = i0; + + nch = image->nChannels; + } + + + /* has next ? */ + bool operator!() const { return j < nl; } + + /* next pixel */ + ImageIterator& operator++() + { + i++; + if (i >= nc) + { + i=i0; + j++; + data += step; + } + return *this; + } + + ImageIterator& operator+=(int s) + { + i+=s; + if (i >= nc) + { + i=i0; + j++; + data += step; + } + return *this; + } + + /* pixel access */ + T& operator*() { return data[i]; } + + const T operator*() const { return data[i]; } + + const T neighbor(int dx, int dy) const + { + return *(data+dy*step+i+dx); + } + + T* operator&() const { return data+i; } + + /* current pixel coordinates */ + int column() const { return i/nch; } + int line() const { return j; } + +private: + int i, i0,j; + T* data; + int step; + int nl, nc; + int nch; +}; + +// --- Constants -------------------------------------------------------------- + +const unsigned char NUM_CHANNELS = 3; + +// --- Pixel Types ------------------------------------------------------------ + +class RgbPixel +{ +public: + RgbPixel() {;} + RgbPixel(unsigned char _r, unsigned char _g, unsigned char _b) + { + ch[0] = _r; ch[1] = _g; ch[2] = _b; + } + + RgbPixel& operator=(const RgbPixel& rhs) + { + ch[0] = rhs.ch[0]; ch[1] = rhs.ch[1]; ch[2] = rhs.ch[2]; + return *this; + } + + inline unsigned char& operator()(const int _ch) + { + return ch[_ch]; + } + + inline unsigned char operator()(const int _ch) const + { + return ch[_ch]; + } + + unsigned char ch[3]; +}; + +class RgbPixelFloat +{ +public: + RgbPixelFloat() {;} + RgbPixelFloat(float _r, float _g, float _b) + { + ch[0] = _r; ch[1] = _g; ch[2] = _b; + } + + RgbPixelFloat& operator=(const RgbPixelFloat& rhs) + { + ch[0] = rhs.ch[0]; ch[1] = rhs.ch[1]; ch[2] = rhs.ch[2]; + return *this; + } + + inline float& operator()(const int _ch) + { + return ch[_ch]; + } + + inline float operator()(const int _ch) const + { + return ch[_ch]; + } + + float ch[3]; +}; + +// --- Image Types ------------------------------------------------------------ + +class ImageBase +{ +public: + ImageBase(IplImage* img = NULL) { imgp = img; m_bReleaseMemory = true; } + ~ImageBase(); + + void ReleaseMemory(bool b) { m_bReleaseMemory = b; } + + IplImage* Ptr() { return imgp; } + const IplImage* Ptr() const { return imgp; } + + void ReleaseImage() + { + cvReleaseImage(&imgp); + } + + void operator=(IplImage* img) + { + imgp = img; + } + + // copy-constructor + ImageBase(const ImageBase& rhs) + { + // it is very inefficent if this copy-constructor is called + assert(false); + } + + // assignment operator + ImageBase& operator=(const ImageBase& rhs) + { + // it is very inefficent if operator= is called + assert(false); + + return *this; + } + + virtual void Clear() = 0; + +protected: + IplImage* imgp; + bool m_bReleaseMemory; +}; + +class RgbImage : public ImageBase +{ +public: + RgbImage(IplImage* img = NULL) : ImageBase(img) { ; } + + virtual void Clear() + { + cvZero(imgp); + } + + void operator=(IplImage* img) + { + imgp = img; + } + + // channel-level access using image(row, col, channel) + inline unsigned char& operator()(const int r, const int c, const int ch) + { + return (unsigned char &)imgp->imageData[r*imgp->widthStep+c*imgp->nChannels+ch]; + } + + inline const unsigned char& operator()(const int r, const int c, const int ch) const + { + return (unsigned char &)imgp->imageData[r*imgp->widthStep+c*imgp->nChannels+ch]; + } + + // RGB pixel-level access using image(row, col) + inline RgbPixel& operator()(const int r, const int c) + { + return (RgbPixel &)imgp->imageData[r*imgp->widthStep+c*imgp->nChannels]; + } + + inline const RgbPixel& operator()(const int r, const int c) const + { + return (RgbPixel &)imgp->imageData[r*imgp->widthStep+c*imgp->nChannels]; + } +}; + +class RgbImageFloat : public ImageBase +{ +public: + RgbImageFloat(IplImage* img = NULL) : ImageBase(img) { ; } + + virtual void Clear() + { + cvZero(imgp); + } + + void operator=(IplImage* img) + { + imgp = img; + } + + // channel-level access using image(row, col, channel) + inline float& operator()(const int r, const int c, const int ch) + { + return (float &)imgp->imageData[r*imgp->widthStep+(c*imgp->nChannels+ch)*sizeof(float)]; + } + + inline float operator()(const int r, const int c, const int ch) const + { + return (float)imgp->imageData[r*imgp->widthStep+(c*imgp->nChannels+ch)*sizeof(float)]; + } + + // RGB pixel-level access using image(row, col) + inline RgbPixelFloat& operator()(const int r, const int c) + { + return (RgbPixelFloat &)imgp->imageData[r*imgp->widthStep+c*imgp->nChannels*sizeof(float)]; + } + + inline const RgbPixelFloat& operator()(const int r, const int c) const + { + return (RgbPixelFloat &)imgp->imageData[r*imgp->widthStep+c*imgp->nChannels*sizeof(float)]; + } +}; + +class BwImage : public ImageBase +{ +public: + BwImage(IplImage* img = NULL) : ImageBase(img) { ; } + + virtual void Clear() + { + cvZero(imgp); + } + + void operator=(IplImage* img) + { + imgp = img; + } + + // pixel-level access using image(row, col) + inline unsigned char& operator()(const int r, const int c) + { + return (unsigned char &)imgp->imageData[r*imgp->widthStep+c]; + } + + inline unsigned char operator()(const int r, const int c) const + { + return (unsigned char)imgp->imageData[r*imgp->widthStep+c]; + } +}; + +class BwImageFloat : public ImageBase +{ +public: + BwImageFloat(IplImage* img = NULL) : ImageBase(img) { ; } + + virtual void Clear() + { + cvZero(imgp); + } + + void operator=(IplImage* img) + { + imgp = img; + } + + // pixel-level access using image(row, col) + inline float& operator()(const int r, const int c) + { + return (float &)imgp->imageData[r*imgp->widthStep+c*sizeof(float)]; + } + + inline float operator()(const int r, const int c) const + { + return (float)imgp->imageData[r*imgp->widthStep+c*sizeof(float)]; + } +}; + +// --- Image Functions -------------------------------------------------------- + +void DensityFilter(BwImage& image, BwImage& filtered, int minDensity, unsigned char fgValue); + +#endif \ No newline at end of file diff --git a/package_bgs/dp/MeanBGS.cpp b/package_bgs/dp/MeanBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b8df8958dec2e8fd1a0735861134be91b3b7d25e --- /dev/null +++ b/package_bgs/dp/MeanBGS.cpp @@ -0,0 +1,131 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* MeanBGS.h +* +* Purpose: Implementation of a simple temporal mean background +* subtraction algorithm. +* +* Author: Donovan Parks, September 2007 +* +******************************************************************************/ + +#include "MeanBGS.h" + +using namespace Algorithms::BackgroundSubtraction; + +void MeanBGS::Initalize(const BgsParams& param) +{ + m_params = (MeanParams&)param; + + m_mean = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_32F, 3); + m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); +} + +void MeanBGS::InitModel(const RgbImage& data) +{ + for (unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + for(int ch = 0; ch < NUM_CHANNELS; ++ch) + { + m_mean(r,c,ch) = (float)data(r,c,ch); + } + } + } +} + +void MeanBGS::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) +{ + // update background model + for (unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + // perform conditional updating only if we are passed the learning phase + if(update_mask(r,c) == BACKGROUND || frame_num < m_params.LearningFrames()) + { + // update B/G model + float mean; + for(int ch = 0; ch < NUM_CHANNELS; ++ch) + { + mean = m_params.Alpha() * m_mean(r,c,ch) + (1.0f-m_params.Alpha()) * data(r,c,ch); + m_mean(r,c,ch) = mean; + m_background(r,c,ch) = (unsigned char)(mean + 0.5); + } + } + } + } +} + +void MeanBGS::SubtractPixel(int r, int c, const RgbPixel& pixel, + unsigned char& low_threshold, + unsigned char& high_threshold) +{ + // calculate distance to sample point + float dist = 0; + for(int ch = 0; ch < NUM_CHANNELS; ++ch) + { + dist += (pixel(ch)-m_mean(r,c,ch))*(pixel(ch)-m_mean(r,c,ch)); + } + + // determine if sample point is F/G or B/G pixel + low_threshold = BACKGROUND; + if(dist > m_params.LowThreshold()) + { + low_threshold = FOREGROUND; + } + + high_threshold = BACKGROUND; + if(dist > m_params.HighThreshold()) + { + high_threshold = FOREGROUND; + } +} + +/////////////////////////////////////////////////////////////////////////////// +//Input: +// data - a pointer to the data of a RGB image of the same size +//Output: +// output - a pointer to the data of a gray value image of the same size +// values: 255-foreground, 0-background +/////////////////////////////////////////////////////////////////////////////// +void MeanBGS::Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask) +{ + unsigned char low_threshold, high_threshold; + + // update each pixel of the image + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + // perform background subtraction + update background model + SubtractPixel(r, c, data(r,c), low_threshold, high_threshold); + + // setup silhouette mask + low_threshold_mask(r,c) = low_threshold; + high_threshold_mask(r,c) = high_threshold; + } + } +} + + + + diff --git a/package_bgs/dp/MeanBGS.h b/package_bgs/dp/MeanBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..881beb70fcdb6796aff3039f8dec8d1282c2bf86 --- /dev/null +++ b/package_bgs/dp/MeanBGS.h @@ -0,0 +1,98 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* MeanBGS.hpp +* +* Purpose: Implementation of a simple temporal mean background +* subtraction algorithm. +* +* Author: Donovan Parks, September 2007 +* + +Example: +Algorithms::BackgroundSubtraction::MeanParams params; +params.SetFrameSize(width, height); +params.LowThreshold() = 3*30*30; +params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing +params.Alpha() = 1e-6f; +params.LearningFrames() = 30; + +Algorithms::BackgroundSubtraction::MeanBGS bgs; +bgs.Initalize(params); +******************************************************************************/ + +#include "Bgs.h" + +namespace Algorithms +{ + namespace BackgroundSubtraction + { + + // --- Parameters used by the Mean BGS algorithm --- + class MeanParams : public BgsParams + { + public: + unsigned int &LowThreshold() { return m_low_threshold; } + unsigned int &HighThreshold() { return m_high_threshold; } + + float &Alpha() { return m_alpha; } + int &LearningFrames() { return m_learning_frames; } + + private: + // A pixel is considered to be from the background if the squared distance between + // it and the background model is less than the threshold. + unsigned int m_low_threshold; + unsigned int m_high_threshold; + + float m_alpha; + int m_learning_frames; + }; + + + // --- Mean BGS algorithm --- + class MeanBGS : public Bgs + { + public: + virtual ~MeanBGS() {} + + void Initalize(const BgsParams& param); + + void InitModel(const RgbImage& data); + void Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask); + void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); + + RgbImage* Background() { return &m_background; } + + private: + void SubtractPixel(int r, int c, const RgbPixel& pixel, + unsigned char& lowThreshold, unsigned char& highThreshold); + + MeanParams m_params; + + RgbImageFloat m_mean; + RgbImage m_background; + }; + + }; +}; + + + + + diff --git a/package_bgs/dp/PratiMediodBGS.cpp b/package_bgs/dp/PratiMediodBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2ef44c0f5a71314e7ee799e987f2b0b74014ce8d --- /dev/null +++ b/package_bgs/dp/PratiMediodBGS.cpp @@ -0,0 +1,276 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* PratiMediodBGS.h +* +* Purpose: Implementation of the temporal median background +* subtraction algorithm described in: +* +* [1] "Detecting Moving Objects, Shosts, and Shadows in Video Stream" +* by R. Cucchiara et al (2003) +* +* [2] "Reliable Background Suppression for Complex Scenes" +* by S. Calderara et al (2006) +* +* Author: Donovan Parks, September 2007 +* +* Please note that this is not an implementation of the complete system +* given in the above papers. It simply implements the temporal media background +* subtraction algorithm. +******************************************************************************/ + +#include "PratiMediodBGS.h" + +using namespace Algorithms::BackgroundSubtraction; + +PratiMediodBGS::PratiMediodBGS() +{ + m_median_buffer = NULL; +} + +PratiMediodBGS::~PratiMediodBGS() +{ + if(m_median_buffer != NULL) + delete[] m_median_buffer; +} + +void PratiMediodBGS::Initalize(const BgsParams& param) +{ + m_params = (PratiParams&)param; + + m_mask_low_threshold = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1); + m_mask_high_threshold = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1); + + m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); + + m_median_buffer = new MEDIAN_BUFFER[m_params.Size()]; +} + +void PratiMediodBGS::InitModel(const RgbImage& data) +{ + // there is no need to initialize the mode since it needs a buffer of frames + // before it can performing background subtraction +} + +void PratiMediodBGS::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) +{ + // update the image buffer with the new frame and calculate new median values + if(frame_num % m_params.SamplingRate() == 0) + { + if(m_median_buffer[0].dist.size() == m_params.HistorySize()) + { + // subtract distance to sample being removed from all distances + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + int i = r*m_params.Width()+c; + + if(update_mask(r,c) == BACKGROUND) + { + int oldPos = m_median_buffer[i].pos; + for(unsigned int s = 0; s < m_median_buffer[i].pixels.size(); ++s) + { + int maxDist = 0; + for(int ch = 0; ch < NUM_CHANNELS; ++ch) + { + int tempDist = abs(m_median_buffer[i].pixels.at(oldPos)(ch) + - m_median_buffer[i].pixels.at(s)(ch)); + if(tempDist > maxDist) + maxDist = tempDist; + } + + m_median_buffer[i].dist.at(s) -= maxDist; + } + + int dist; + UpdateMediod(r, c, data, dist); + m_median_buffer[i].dist.at(oldPos) = dist; + m_median_buffer[i].pixels.at(oldPos) = data(r,c); + m_median_buffer[i].pos++; + if(m_median_buffer[i].pos >= m_params.HistorySize()) + m_median_buffer[i].pos = 0; + } + } + } + } + else + { + // calculate sum of L-inf distances for new point and + // add distance from each sample point to this point to their L-inf sum + int dist; + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + int index = r*m_params.Width()+c; + UpdateMediod(r, c, data, dist); + m_median_buffer[index].dist.push_back(dist); + m_median_buffer[index].pos = 0; + m_median_buffer[index].pixels.push_back(data(r,c)); + } + } + } + } +} + +void PratiMediodBGS::UpdateMediod(int r, int c, const RgbImage& new_frame, int& dist) +{ + // calculate sum of L-inf distances for new point and + // add distance from each sample point to this point to their L-inf sum + unsigned int i = (r*m_params.Width()+c); + + m_median_buffer[i].medianDist = INT_MAX; + + int L_inf_dist = 0; + for(unsigned int s = 0; s < m_median_buffer[i].dist.size(); ++s) + { + int maxDist = 0; + for(int ch = 0; ch < NUM_CHANNELS; ++ch) + { + int tempDist = abs(m_median_buffer[i].pixels.at(s)(ch) - new_frame(r,c,ch)); + if(tempDist > maxDist) + maxDist = tempDist; + } + + // check if point from this frame in the image buffer is the median + m_median_buffer[i].dist.at(s) += maxDist; + if(m_median_buffer[i].dist.at(s) < m_median_buffer[i].medianDist) + { + m_median_buffer[i].medianDist = m_median_buffer[i].dist.at(s); + m_median_buffer[i].median = m_median_buffer[i].pixels.at(s); + } + + L_inf_dist += maxDist; + } + + dist = L_inf_dist; + + // check if the new point is the median + if(L_inf_dist < m_median_buffer[i].medianDist) + { + m_median_buffer[i].medianDist = L_inf_dist; + m_median_buffer[i].median = new_frame(r,c); + } +} + +void PratiMediodBGS::Combine(const BwImage& low_mask, const BwImage& high_mask, BwImage& output) +{ + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + output(r,c) = BACKGROUND; + + if(r == 0 || c == 0 || r == m_params.Height()-1 || c == m_params.Width()-1) + continue; + + if(high_mask(r,c) == FOREGROUND) + { + output(r,c) = FOREGROUND; + } + else if(low_mask(r,c) == FOREGROUND) + { + // consider the pixel to be a F/G pixel if it is 8-connected to + // a F/G pixel in the high mask + // check if there is an 8-connected foreground pixel + if(high_mask(r-1,c-1)) + output(r,c) = FOREGROUND; + else if(high_mask(r-1,c)) + output(r,c) = FOREGROUND; + else if(high_mask(r-1,c+1)) + output(r,c) = FOREGROUND; + else if(high_mask(r,c-1)) + output(r,c) = FOREGROUND; + else if(high_mask(r,c+1)) + output(r,c) = FOREGROUND; + else if(high_mask(r+1,c-1)) + output(r,c) = FOREGROUND; + else if(high_mask(r+1,c)) + output(r,c) = FOREGROUND; + else if(high_mask(r+1,c+1)) + output(r,c) = FOREGROUND; + } + } + } +} + +void PratiMediodBGS::CalculateMasks(int r, int c, const RgbPixel& pixel) +{ + int pos = r*m_params.Width()+c; + + // calculate l-inf distance between current value and median value + unsigned char dist = 0; + for(int ch = 0; ch < NUM_CHANNELS; ++ch) + { + int tempDist = abs(pixel(ch) - m_median_buffer[pos].median(ch)); + if(tempDist > dist) + dist = tempDist; + } + m_background(r,c) = m_median_buffer[pos].median; + + // check if pixel is a B/G or F/G pixel according to the low threshold B/G model + m_mask_low_threshold(r,c) = BACKGROUND; + if(dist > m_params.LowThreshold()) + { + m_mask_low_threshold(r,c) = FOREGROUND; + } + + // check if pixel is a B/G or F/G pixel according to the high threshold B/G model + m_mask_high_threshold(r,c)= BACKGROUND; + if(dist > m_params.HighThreshold()) + { + m_mask_high_threshold(r,c) = FOREGROUND; + } +} + +/////////////////////////////////////////////////////////////////////////////// +//Input: +// data - a pointer to the data of a RGB image of the same size +//Output: +// output - a pointer to the data of a gray value image of the same size +// values: 255-foreground, 0-background +/////////////////////////////////////////////////////////////////////////////// +void PratiMediodBGS::Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mark, BwImage& high_threshold_mark) +{ + if(frame_num < m_params.HistorySize()) + { + low_threshold_mark.Clear(); + high_threshold_mark.Clear(); + return; + } + + // update each pixel of the image + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + // need at least one frame of data before we can start calculating the masks + CalculateMasks(r, c, data(r,c)); + } + } + + // combine low and high threshold masks + Combine(m_mask_low_threshold, m_mask_high_threshold, low_threshold_mark); + Combine(m_mask_low_threshold, m_mask_high_threshold, high_threshold_mark); +} + + + + diff --git a/package_bgs/dp/PratiMediodBGS.h b/package_bgs/dp/PratiMediodBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..0b373d7c45f49c28b3c7c04cacfd9e222b998bef --- /dev/null +++ b/package_bgs/dp/PratiMediodBGS.h @@ -0,0 +1,142 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* PratiMediodBGS.hpp +* +* Purpose: Implementation of the temporal median background +* subtraction algorithm described in: +* +* [1] "Detecting Moving Objects, Shosts, and Shadows in Video Stream" +* by R. Cucchiara et al (2003) +* +* [2] "Reliable Background Suppression for Complex Scenes" +* by S. Calderara et al (2006) +* +* Author: Donovan Parks, September 2007 +* +* Please note that this is not an implementation of the complete system +* given in the above papers. It simply implements the temporal media background +* subtraction algorithm. + +Example: +Algorithms::BackgroundSubtraction::PratiParams params; +params.SetFrameSize(width, height); +params.LowThreshold() = 30; +params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing +params.SamplingRate() = 5; +params.HistorySize() = 16; +params.Weight() = 5; + +Algorithms::BackgroundSubtraction::PratiMediodBGS bgs; +bgs.Initalize(params); +******************************************************************************/ + +#ifndef PRATI_MEDIA_BGS_H +#define PRATI_MEDIA_BGS_H + +#include <vector> +#include "Bgs.h" + +namespace Algorithms +{ + namespace BackgroundSubtraction + { + // --- Parameters used by the Prati Mediod BGS algorithm --- + class PratiParams : public BgsParams + { + public: + unsigned int &LowThreshold() { return m_low_threshold; } + unsigned int &HighThreshold() { return m_high_threshold; } + + int &Weight() { return m_weight; } + int &SamplingRate() { return m_sampling_rate; } + int &HistorySize() { return m_history_size; } + + private: + // The low threshold is used to supress noise. The high thresohld is used + // to find pixels highly likely to be foreground. This implementation uses an L-inf + // distance measure and a pixel p is considered F/G if D(I(p), B(p)) > threshold. + // The two threshold maps are combined as in [2]. + unsigned int m_low_threshold; + unsigned int m_high_threshold; + + // The weight parameter controls the amount of influence given to previous background samples + // see w_b in equation (2) of [1] + // in [2] this value is set to 1 + int m_weight; + + // Number of samples to consider when calculating temporal mediod value + int m_history_size; + + // Rate at which to obtain new samples + int m_sampling_rate; + }; + + // --- Prati Mediod BGS algorithm --- + class PratiMediodBGS : public Bgs + { + private: + // sum of L-inf distances from a sample point to all other sample points + struct MEDIAN_BUFFER + { + std::vector<RgbPixel> pixels; // vector of pixels at give location in image + std::vector<int> dist; // distance from pixel to all other pixels + int pos; // current position in circular buffer + + RgbPixel median; // median at this pixel location + int medianDist; // distance from median pixel to all other pixels + }; + + public: + PratiMediodBGS(); + ~PratiMediodBGS(); + + void Initalize(const BgsParams& param); + + void InitModel(const RgbImage& data); + void Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask); + void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); + + RgbImage* Background() { return &m_background; } + + private: + MEDIAN_BUFFER* m_median_buffer; + + void CalculateMasks(int r, int c, const RgbPixel& pixel); + void Combine(const BwImage& low_mask, const BwImage& high_mask, BwImage& output); + void UpdateMediod(int r, int c, const RgbImage& new_frame, int& dist); + + PratiParams m_params; + + RgbImage m_background; + + BwImage m_mask_low_threshold; + BwImage m_mask_high_threshold; + }; + + }; +}; + +#endif + + + + + + diff --git a/package_bgs/dp/TextureBGS.cpp b/package_bgs/dp/TextureBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ccac3715b782809f966155a317a529717a5c311 --- /dev/null +++ b/package_bgs/dp/TextureBGS.cpp @@ -0,0 +1,153 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "TextureBGS.h" + +TextureBGS::TextureBGS(){} +TextureBGS::~TextureBGS(){} + +void TextureBGS::LBP(RgbImage& image, RgbImage& texture) +{ + for(int y = TEXTURE_R; y < image.Ptr()->height-TEXTURE_R; ++y) + { + for(int x = TEXTURE_R; x < image.Ptr()->width-TEXTURE_R; ++x) + { + for(int ch = 0; ch < NUM_CHANNELS; ++ch) + { + unsigned char textureCode = 0; + int centerValue = (int)image(y, x, ch); + + // this only works for a texture radius of 2 + if(centerValue - (int)image(y-2, x, ch) + HYSTERSIS >= 0) + textureCode += 1; + + if(centerValue - (int)image(y-1, x-2, ch) + HYSTERSIS >= 0) + textureCode += 2; + + if(centerValue - (int)image(y-1, x+2, ch) + HYSTERSIS >= 0) + textureCode += 4; + + if(centerValue - (int)image(y+1, x-2, ch) + HYSTERSIS >= 0) + textureCode += 8; + + if(centerValue - (int)image(y+1, x+2, ch) + HYSTERSIS >= 0) + textureCode += 16; + + if(centerValue - (int)image(y+2, x, ch) + HYSTERSIS >= 0) + textureCode += 32; + + texture(y,x,ch) = textureCode; + } + } + } +} + +void TextureBGS::Histogram(RgbImage& texture, TextureHistogram* curTextureHist) +{ + // calculate histogram within a 2*REGION_R square + for(int y = REGION_R+TEXTURE_R; y < texture.Ptr()->height-REGION_R-TEXTURE_R; ++y) + { + for(int x = REGION_R+TEXTURE_R; x < texture.Ptr()->width-REGION_R-TEXTURE_R; ++x) + { + int index = x+y*(texture.Ptr()->width); + + // clear histogram + for(int i = 0; i < NUM_BINS; ++i) + { + curTextureHist[index].r[i] = 0; + curTextureHist[index].g[i] = 0; + curTextureHist[index].b[i] = 0; + } + + // calculate histogram + for(int j = -REGION_R; j <= REGION_R; ++j) + { + for(int i = -REGION_R; i <= REGION_R; ++i) + { + curTextureHist[index].r[texture(y+j,x+i,2)]++; + curTextureHist[index].g[texture(y+j,x+i,1)]++; + curTextureHist[index].b[texture(y+j,x+i,0)]++; + } + } + } + } +} + +int TextureBGS::ProximityMeasure(TextureHistogram& bgTexture, TextureHistogram& curTextureHist) +{ + int proximity = 0; + for(int i = 0; i < NUM_BINS; ++i) + { + proximity += std::min(bgTexture.r[i], curTextureHist.r[i]); + proximity += std::min(bgTexture.g[i], curTextureHist.g[i]); + proximity += std::min(bgTexture.b[i], curTextureHist.b[i]); + } + + return proximity; +} + +void TextureBGS::BgsCompare(TextureArray* bgModel, TextureHistogram* curTextureHist, + unsigned char* modeArray, float threshold, BwImage& fgMask) +{ + cvZero(fgMask.Ptr()); + + for(int y = REGION_R+TEXTURE_R; y < fgMask.Ptr()->height-REGION_R-TEXTURE_R; ++y) + { + for(int x = REGION_R+TEXTURE_R; x < fgMask.Ptr()->width-REGION_R-TEXTURE_R; ++x) + { + int index = x+y*(fgMask.Ptr()->width); + + // find closest matching texture in background model + int maxProximity = -1; + + for(int m = 0; m < NUM_MODES; ++m) + { + int proximity = ProximityMeasure(bgModel[index].mode[m], curTextureHist[index]); + + if(proximity > maxProximity) + { + maxProximity = proximity; + modeArray[index] = m; + } + } + + if(maxProximity < threshold) + fgMask(y,x) = 255; + } + } +} + +void TextureBGS::UpdateModel(BwImage& fgMask, TextureArray* bgModel, + TextureHistogram* curTextureHist, unsigned char* modeArray) +{ + for(int y = REGION_R+TEXTURE_R; y < fgMask.Ptr()->height-REGION_R-TEXTURE_R; ++y) + { + for(int x = REGION_R+TEXTURE_R; x < fgMask.Ptr()->width-REGION_R-TEXTURE_R; ++x) + { + int index = x+y*(fgMask.Ptr()->width); + + if(fgMask(x,y) == 0) + { + for(int i = 0; i < NUM_BINS; ++i) + { + bgModel[index].mode[modeArray[index]].r[i] + = (unsigned char)(ALPHA*curTextureHist[index].r[i] + + (1-ALPHA)*bgModel[index].mode[modeArray[index]].r[i] + 0.5); + } + } + } + } +} diff --git a/package_bgs/dp/TextureBGS.h b/package_bgs/dp/TextureBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..573e6f9af7992833f310dba72aa9a4e5e72bc771 --- /dev/null +++ b/package_bgs/dp/TextureBGS.h @@ -0,0 +1,55 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <math.h> +#include "Image.h" + +const int REGION_R = 5; // Note: the code currently assumes this value is <= 7 +const int TEXTURE_POINTS = 6; // Note: the code currently assumes this value is 6 +const int TEXTURE_R = 2; // Note: the code currently assumes this value is 2 +const int NUM_BINS = 64; // 2^TEXTURE_POINTS +const int HYSTERSIS = 3; +const double ALPHA = 0.05f; +const double THRESHOLD = 0.5*(REGION_R+REGION_R+1)*(REGION_R+REGION_R+1)*NUM_CHANNELS; +const int NUM_MODES = 1; // The paper describes how multiple modes can be maintained, +// but this implementation does not fully support more than one + +struct TextureHistogram +{ + unsigned char r[NUM_BINS]; // histogram for red channel + unsigned char g[NUM_BINS]; // histogram for green channel + unsigned char b[NUM_BINS]; // histogram for blue channel +}; + +struct TextureArray +{ + TextureHistogram mode[NUM_MODES]; +}; + +class TextureBGS +{ +public: + TextureBGS(); + ~TextureBGS(); + + void LBP(RgbImage& image, RgbImage& texture); + void Histogram(RgbImage& texture, TextureHistogram* curTextureHist); + int ProximityMeasure(TextureHistogram& bgTexture, TextureHistogram& curTextureHist); + void BgsCompare(TextureArray* bgModel, TextureHistogram* curTextureHist, + unsigned char* modeArray, float threshold, BwImage& fgMask); + void UpdateModel(BwImage& fgMask, TextureArray* bgModel, + TextureHistogram* curTextureHist, unsigned char* modeArray); +}; diff --git a/package_bgs/dp/WrenGA.cpp b/package_bgs/dp/WrenGA.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8ae10dd9cb93626827d2b8e86d14857c50e8a768 --- /dev/null +++ b/package_bgs/dp/WrenGA.cpp @@ -0,0 +1,174 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* WrenGA.h +* +* Purpose: Implementation of the running Gaussian average background +* subtraction algorithm described in: +* "Pfinder: real-time tracking of the human body" +* by C. Wren et al (1997) +* +* Author: Donovan Parks, September 2007 +* +* Please note that this is not an implementation of Pfinder. It implements +* a simple background subtraction algorithm where each pixel is represented +* by a single Gaussian and update using a simple weighting function. +******************************************************************************/ + +#include "WrenGA.h" + +using namespace Algorithms::BackgroundSubtraction; + +WrenGA::WrenGA() +{ + m_gaussian = NULL; +} + +WrenGA::~WrenGA() +{ + if(m_gaussian != NULL) + delete[] m_gaussian; +} + +void WrenGA::Initalize(const BgsParams& param) +{ + m_params = (WrenParams&)param; + + m_variance = 36.0f; + + // GMM for each pixel + m_gaussian = new GAUSSIAN[m_params.Size()]; + for(unsigned int i = 0; i < m_params.Size(); ++i) + { + for(int ch = 0; ch < NUM_CHANNELS; ++ch) + { + m_gaussian[i].mu[ch] = 0; + m_gaussian[i].var[ch] = 0; + } + } + + m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); +} + +void WrenGA::InitModel(const RgbImage& data) +{ + int pos = 0; + + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + for(int ch = 0; ch < NUM_CHANNELS; ++ch) + { + m_gaussian[pos].mu[ch] = data(r,c,ch); + m_gaussian[pos].var[ch] = m_variance; + } + + pos++; + } + } +} + +void WrenGA::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) +{ + int pos = 0; + + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + // perform conditional updating only if we are passed the learning phase + if(update_mask(r,c) == BACKGROUND || frame_num < m_params.LearningFrames()) + { + float dR = m_gaussian[pos].mu[0] - data(r,c,0); + float dG = m_gaussian[pos].mu[1] - data(r,c,1); + float dB = m_gaussian[pos].mu[2] - data(r,c,2); + + float dist = (dR*dR + dG*dG + dB*dB); + + m_gaussian[pos].mu[0] -= m_params.Alpha()*(dR); + m_gaussian[pos].mu[1] -= m_params.Alpha()*(dG); + m_gaussian[pos].mu[2] -= m_params.Alpha()*(dB); + + float sigmanew = m_gaussian[pos].var[0] + m_params.Alpha()*(dist-m_gaussian[pos].var[0]); + m_gaussian[pos].var[0] = sigmanew < 4 ? 4 : sigmanew > 5*m_variance ? 5*m_variance : sigmanew; + + m_background(r, c, 0) = (unsigned char)(m_gaussian[pos].mu[0] + 0.5); + m_background(r, c, 1) = (unsigned char)(m_gaussian[pos].mu[1] + 0.5); + m_background(r, c, 2) = (unsigned char)(m_gaussian[pos].mu[2] + 0.5); + } + + pos++; + } + } +} + +void WrenGA::SubtractPixel(int r, int c, const RgbPixel& pixel, + unsigned char& low_threshold, + unsigned char& high_threshold) +{ + unsigned int pos = r*m_params.Width()+c; + + // calculate distance between model and pixel + float mu[NUM_CHANNELS]; + float var[1]; + float delta[NUM_CHANNELS]; + float dist = 0; + for(int ch = 0; ch < NUM_CHANNELS; ++ch) + { + mu[ch] = m_gaussian[pos].mu[ch]; + var[0] = m_gaussian[pos].var[0]; + delta[ch] = mu[ch] - pixel(ch); + dist += delta[ch]*delta[ch]; + } + + // calculate the squared distance and see if pixel fits the B/G model + low_threshold = BACKGROUND; + high_threshold = BACKGROUND; + + if(dist > m_params.LowThreshold()*var[0]) + low_threshold = FOREGROUND; + if(dist > m_params.HighThreshold()*var[0]) + high_threshold = FOREGROUND; +} + +/////////////////////////////////////////////////////////////////////////////// +//Input: +// data - a pointer to the data of a RGB image of the same size +//Output: +// output - a pointer to the data of a gray value image of the same size +// (the memory should already be reserved) +// values: 255-foreground, 125-shadow, 0-background +/////////////////////////////////////////////////////////////////////////////// +void WrenGA::Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask) +{ + unsigned char low_threshold, high_threshold; + + // update each pixel of the image + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + SubtractPixel(r, c, data(r,c), low_threshold, high_threshold); + low_threshold_mask(r,c) = low_threshold; + high_threshold_mask(r,c) = high_threshold; + } + } +} + diff --git a/package_bgs/dp/WrenGA.h b/package_bgs/dp/WrenGA.h new file mode 100644 index 0000000000000000000000000000000000000000..d623e20557fc795dc04ea45478e6030094b16008 --- /dev/null +++ b/package_bgs/dp/WrenGA.h @@ -0,0 +1,120 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* WrenGA.hpp +* +* Purpose: Implementation of the running Gaussian average background +* subtraction algorithm described in: +* "Pfinder: real-time tracking of the human body" +* by C. Wren et al (1997) +* +* Author: Donovan Parks, September 2007 +* +* Please note that this is not an implementation of Pfinder. It implements +* a simple background subtraction algorithm where each pixel is represented +* by a single Gaussian and update using a simple weighting function. + +Example: +Algorithms::BackgroundSubtraction::WrenParams params; +params.SetFrameSize(width, height); +params.LowThreshold() = 3.5f*3.5f; +params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing +params.Alpha() = 0.005f; +params.LearningFrames() = 30; + +Algorithms::BackgroundSubtraction::WrenGA bgs; +bgs.Initalize(params); +******************************************************************************/ + +#ifndef WREN_GA_H +#define WREN_GA_H + +#include "Bgs.h" + +namespace Algorithms +{ + namespace BackgroundSubtraction + { + // --- Parameters used by the Mean BGS algorithm --- + class WrenParams : public BgsParams + { + public: + float &LowThreshold() { return m_low_threshold; } + float &HighThreshold() { return m_high_threshold; } + + float &Alpha() { return m_alpha; } + int &LearningFrames() { return m_learning_frames; } + + private: + // The threshold indicates the number of variances (not standard deviations) away + // from the mean before a pixel is considered to be from the foreground. + float m_low_threshold; + float m_high_threshold; + + float m_alpha; + int m_learning_frames; + }; + + + // --- Mean BGS algorithm --- + class WrenGA : public Bgs + { + private: + struct GAUSSIAN + { + float mu[NUM_CHANNELS]; + float var[NUM_CHANNELS]; + }; + + public: + WrenGA(); + ~WrenGA(); + + void Initalize(const BgsParams& param); + + void InitModel(const RgbImage& data); + void Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask); + void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); + + RgbImage* Background() { return &m_background; } + + private: + void SubtractPixel(int r, int c, const RgbPixel& pixel, + unsigned char& lowThreshold, unsigned char& highThreshold); + + WrenParams m_params; + + // Initial variance for the newly generated components. + float m_variance; + + // dynamic array for the mixture of Gaussians + GAUSSIAN* m_gaussian; + + RgbImage m_background; + }; + }; +}; + +#endif + + + + + + diff --git a/package_bgs/dp/ZivkovicAGMM.cpp b/package_bgs/dp/ZivkovicAGMM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c260132d8b2b85b5669d237ddc6080274b999adb --- /dev/null +++ b/package_bgs/dp/ZivkovicAGMM.cpp @@ -0,0 +1,411 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* ZivkovicAGMM.cpp +* +* Purpose: Implementation of the Gaussian mixture model (GMM) background +* subtraction algorithm developed by Z. Zivkovic. +* +* Author: Donovan Parks, September 2007 +* +* This code is based on code by Z. Zivkovic's. I have changed it from a pure +* C implementation to a cleaner (IMHO) C++ implementation. It is based on the +* following papers: +* +* "Improved adaptive Gausian mixture model for background subtraction" +* Z.Zivkovic +* International Conference Pattern Recognition, UK, August, 2004 +* +* +* "Efficient Adaptive Density Estimapion per Image Pixel for the +* Task of Background Subtraction" +* Z.Zivkovic, F. van der Heijden +* Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006. +* +* Zivkovic's code can be obtained at: www.zoranz.net +******************************************************************************/ + +#include "ZivkovicAGMM.h" + +using namespace Algorithms::BackgroundSubtraction; + +ZivkovicAGMM::ZivkovicAGMM() +{ + m_modes = NULL; + m_modes_per_pixel = NULL; +} + +ZivkovicAGMM::~ZivkovicAGMM() +{ + if(m_modes != NULL) + delete[] m_modes; + + if(m_modes_per_pixel != NULL) + delete[] m_modes_per_pixel; +} + +void ZivkovicAGMM::Initalize(const BgsParams& param) +{ + m_params = (ZivkovicParams&)param; + + m_num_bands = 3; //always 3 - not implemented for other values! + m_bg_threshold = 0.75f; //1-cf from the paper + m_variance = 36.0f; // variance for the new mode + m_complexity_prior = 0.05f; // complexity reduction prior constant + + // GMM for each pixel + m_modes = new GMM[m_params.Size()*m_params.MaxModes()]; + + // used modes per pixel + m_modes_per_pixel = new unsigned char[m_params.Size()]; + + m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); +} + +void ZivkovicAGMM::InitModel(const RgbImage& data) +{ + for(unsigned int i = 0; i < m_params.Size(); ++i) + { + m_modes_per_pixel[i] = 0; + } + + for(unsigned int i = 0; i < m_params.Size()*m_params.MaxModes(); ++i) + { + m_modes[i].weight = 0; + m_modes[i].sigma = 0; + m_modes[i].muR = 0; + m_modes[i].muG = 0; + m_modes[i].muB = 0; + } +} + +void ZivkovicAGMM::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) +{ + // it doesn't make sense to have conditional updates in the GMM framework +} + +void ZivkovicAGMM::SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char* pModesUsed, + unsigned char& low_threshold, unsigned char& high_threshold) +{ + //calculate distances to the modes (+ sort???) + //here we need to go in descending order!!! + long pos; + bool bFitsPDF=0; + bool bBackgroundLow=false; + bool bBackgroundHigh=false; + + float fOneMinAlpha = 1-m_params.Alpha(); + + float prune = -m_params.Alpha()*m_complexity_prior; + + int nModes =* pModesUsed; + float totalWeight = 0.0f; + + // calculate number of Gaussians to include in the background model + int backgroundGaussians = 0; + double sum = 0.0; + for(int i = 0; i < nModes; ++i) + { + if(sum < m_bg_threshold) + { + backgroundGaussians++; + sum += m_modes[posPixel+i].weight; + } + else + { + break; + } + } + + // update all distributions and check for match with current pixel + for (int iModes = 0; iModes < nModes; iModes++) + { + pos=posPixel+iModes; + float weight = m_modes[pos].weight; + + //fit not found yet + if (!bFitsPDF) + { + //check if it belongs to some of the modes + //calculate distance + float var = m_modes[pos].sigma; + float muR = m_modes[pos].muR; + float muG = m_modes[pos].muG; + float muB = m_modes[pos].muB; + + float dR=muR - pixel(0); + float dG=muG - pixel(1); + float dB=muB - pixel(2); + + // calculate the squared distance + float dist = (dR*dR + dG*dG + dB*dB); + + if(dist < m_params.HighThreshold()*var && iModes < backgroundGaussians) + bBackgroundHigh = true; + + //check fit + if (dist < m_params.LowThreshold()*var) + { + ///// + //belongs to the mode + bFitsPDF = true; + + // check if this Gaussian is part of the background model + if(iModes < backgroundGaussians) + bBackgroundLow = true; + + //update distribution + float k = m_params.Alpha()/weight; + weight = fOneMinAlpha*weight+prune; + weight += m_params.Alpha(); + m_modes[pos].weight = weight; + m_modes[pos].muR = muR - k*(dR); + m_modes[pos].muG = muG - k*(dG); + m_modes[pos].muB = muB - k*(dB); + + //limit update speed for cov matrice + //not needed + //k=k>20*m_m_params.Alpha()?20*m_m_params.Alpha():k; + //float sigmanew = var + k*((0.33*(dR*dR+dG*dG+dB*dB))-var); + //float sigmanew = var + k*((dR*dR+dG*dG+dB*dB)-var); + //float sigmanew = var + k*((0.33*dist)-var); + float sigmanew = var + k*(dist-var); + + //limit the variance + m_modes[pos].sigma = sigmanew < 4 ? 4 : sigmanew > 5*m_variance ? 5*m_variance : sigmanew; + + // Sort weights so they are in desending order. Note that only the weight for this + // mode will increase and that the weight for all modes that were previously larger than + // this one have already been modified and will not be modified again. Thus, we just need to + // the correct position of this mode in the already sorted list. + + // Zivkovic implementation has been modified for clarity, but the results are equivalent + /* + for (int iLocal = iModes;iLocal>0;iLocal--) + { + long posLocal=posPixel + iLocal; + if (weight < (m_modes[posLocal-1].weight)) + { + break; + } + else + { + //swap + GMM temp = m_modes[posLocal]; + m_modes[posLocal] = m_modes[posLocal-1]; + m_modes[posLocal-1] = temp; + } + } + */ + + for (int iLocal = iModes; iLocal > 0; iLocal--) + { + long posLocal = posPixel + iLocal; + if (m_modes[posLocal].weight > m_modes[posLocal-1].weight) + { + //swap + GMM temp = m_modes[posLocal]; + m_modes[posLocal] = m_modes[posLocal-1]; + m_modes[posLocal-1] = temp; + } + else + { + break; + } + } + } + else + { + weight = fOneMinAlpha*weight+prune; + //check prune + if (weight < -prune) + { + weight=0.0; + nModes--; + } + m_modes[pos].weight = weight; + } + //check if it fits the current mode (2.5 sigma) + /////// + } + //fit not found yet + ///// + else + { + weight = fOneMinAlpha*weight + prune; + //check prune + if (weight < -prune) + { + weight=0.0; + nModes--; + } + m_modes[pos].weight = weight; + } + totalWeight += weight; + } + + //renormalize weights so they sum to 1 + for (int iLocal = 0; iLocal < nModes; iLocal++) + { + m_modes[posPixel+ iLocal].weight = m_modes[posPixel+ iLocal].weight/totalWeight; + } + + //make new mode if needed and exit + if (!bFitsPDF) + { + if (nModes == m_params.MaxModes()) + { + //replace the weakest + } + else + { + nModes++; + } + pos = posPixel + nModes-1; + + if (nModes==1) + m_modes[pos].weight=1; + else + m_modes[pos].weight=m_params.Alpha(); + + // Zivkovic implementation changes as this will not result in the + // weights adding to 1 + /* + int iLocal; + for (iLocal = 0; iLocal < m_params.MaxModes()odes-1; iLocal++) + { + m_modes[posPixel+ iLocal].weight *= fOneMinAlpha; + } + */ + + // Revised implementation: + //renormalize weights + int iLocal; + float sum = 0.0; + for (iLocal = 0; iLocal < nModes; iLocal++) + { + sum += m_modes[posPixel+ iLocal].weight; + } + + float invSum = 1.0f/sum; + for (iLocal = 0; iLocal < nModes; iLocal++) + { + m_modes[posPixel+ iLocal].weight *= invSum; + } + + m_modes[pos].muR=pixel(0); + m_modes[pos].muG=pixel(1); + m_modes[pos].muB=pixel(2); + m_modes[pos].sigma=m_variance; + + // Zivkovic implementation to sort GMM so they are sorted in descending order according to their weight. + // It has been revised for clarity, but the results are equivalent + /* + for (iLocal = m_params.MaxModes()odes-1; iLocal > 0; iLocal--) + { + long posLocal = posPixel + iLocal; + if (m_params.Alpha() < (m_modes[posLocal-1].weight)) + { + break; + } + else + { + //swap + GMM temp = m_modes[posLocal]; + m_modes[posLocal] = m_modes[posLocal-1]; + m_modes[posLocal-1] = temp; + } + } + */ + + // sort GMM so they are sorted in descending order according to their weight + for (iLocal = nModes-1; iLocal > 0; iLocal--) + { + long posLocal = posPixel + iLocal; + if (m_modes[posLocal].weight > m_modes[posLocal-1].weight) + { + //swap + GMM temp = m_modes[posLocal]; + m_modes[posLocal] = m_modes[posLocal-1]; + m_modes[posLocal-1] = temp; + } + else + { + break; + } + } + } + + //set the number of modes + *pModesUsed=nModes; + + if(bBackgroundLow) + { + low_threshold = BACKGROUND; + } + else + { + low_threshold = FOREGROUND; + } + + if(bBackgroundHigh) + { + high_threshold = BACKGROUND; + } + else + { + high_threshold = FOREGROUND; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +//Input: +// data - a pointer to the data of a RGB image of the same size +//Output: +// output - a pointer to the data of a gray value image of the same size +// (the memory should already be reserved) +// values: 255-foreground, 125-shadow, 0-background +/////////////////////////////////////////////////////////////////////////////// +void ZivkovicAGMM::Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask) +{ + unsigned char low_threshold, high_threshold; + + // update each pixel of the image + long posPixel; + unsigned char* pUsedModes=m_modes_per_pixel; + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + //update model+ background subtract + posPixel=(r*m_params.Width()+c)*m_params.MaxModes(); + SubtractPixel(posPixel, data(r,c), pUsedModes, low_threshold, high_threshold); + low_threshold_mask(r,c) = low_threshold; + high_threshold_mask(r,c) = high_threshold; + + m_background(r,c,0) = (unsigned char)m_modes[posPixel].muR; + m_background(r,c,1) = (unsigned char)m_modes[posPixel].muG; + m_background(r,c,2) = (unsigned char)m_modes[posPixel].muB; + + pUsedModes++; + } + } +} + diff --git a/package_bgs/dp/ZivkovicAGMM.h b/package_bgs/dp/ZivkovicAGMM.h new file mode 100644 index 0000000000000000000000000000000000000000..c58fac424f2f6b83f51e676cc30d14038a67cd44 --- /dev/null +++ b/package_bgs/dp/ZivkovicAGMM.h @@ -0,0 +1,160 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* ZivkovicAGMM.hpp +* +* Purpose: Implementation of the Gaussian mixture model (GMM) background +* subtraction algorithm developed by Z. Zivkovic. +* +* Author: Donovan Parks, September 2007 +* +* This code is based on code by Z. Zivkovic's. I have changed it from a pure +* C implementation to a cleaner (IMHO) C++ implementation. It is based on the +* following papers: +* +* "Improved adaptive Gausian mixture model for background subtraction" +* Z.Zivkovic +* International Conference Pattern Recognition, UK, August, 2004 +* +* +* "Efficient Adaptive Density Estimapion per Image Pixel for the +* Task of Background Subtraction" +* Z.Zivkovic, F. van der Heijden +* Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006. +* +* Zivkovic's code can be obtained at: www.zoranz.net + +Example: +Algorithms::BackgroundSubtraction::ZivkovicParams params; +params.SetFrameSize(width, height); +params.LowThreshold() = 5.0f*5.0f; +params.HighThreshold() = 2*params.LowThreshold(); // Note: high threshold is used by post-processing +params.Alpha() = 0.001f; +params.MaxModes() = 3; + +Algorithms::BackgroundSubtraction::ZivkovicAGMM bgs; +bgs.Initalize(params); +******************************************************************************/ + +#ifndef ZIVKOVIC_AGMM_H +#define ZIVKOVIC_AGMM_H + +#include "Bgs.h" + +namespace Algorithms +{ + namespace BackgroundSubtraction + { + // --- User adjustable parameters used by the Grimson GMM BGS algorithm --- + class ZivkovicParams : public BgsParams + { + public: + float &LowThreshold() { return m_low_threshold; } + float &HighThreshold() { return m_high_threshold; } + + float &Alpha() { return m_alpha; } + int &MaxModes() { return m_max_modes; } + + private: + // Threshold on the squared dist. to decide when a sample is close to an existing + // components. If it is not close to any a new component will be generated. + // Smaller threshold values lead to more generated components and higher threshold values + // lead to a small number of components but they can grow too large. + // + // It is usual easiest to think of these thresholds as being the number of variances (not standard deviations) + // away from the mean of a pixel before it is considered to be from the foreground. + float m_low_threshold; + float m_high_threshold; + + // alpha - speed of update - if the time interval you want to average over is T + // set alpha=1/T. + float m_alpha; + + // Maximum number of modes (Gaussian components) that will be used per pixel + int m_max_modes; + }; + + // --- Zivkovic AGMM BGS algorithm --- + class ZivkovicAGMM : public Bgs + { + private: + struct GMM + { + float sigma; + float muR; + float muG; + float muB; + float weight; + }; + + public: + ZivkovicAGMM(); + ~ZivkovicAGMM(); + + void Initalize(const BgsParams& param); + + void InitModel(const RgbImage& data); + void Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask); + void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); + + RgbImage* Background() { return &m_background; } + + private: + void SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char* pModesUsed, + unsigned char& lowThreshold, unsigned char& highThreshold); + + // User adjustable parameters + ZivkovicParams m_params; + + // Threshold when the component becomes significant enough to be included into + // the background model. It is the TB = 1-cf from the paper. So I use cf=0.1 => TB=0.9 + // For alpha=0.001 it means that the mode should exist for approximately 105 frames before + // it is considered foreground + float m_bg_threshold; //1-cf from the paper + + // Initial variance for the newly generated components. + // It will will influence the speed of adaptation. A good guess should be made. + // A simple way is to estimate the typical standard deviation from the images. + float m_variance; + + // This is related to the number of samples needed to accept that a component + // actually exists. + float m_complexity_prior; + + //data + int m_num_bands; //only RGB now ==3 + + // dynamic array for the mixture of Gaussians + GMM* m_modes; + + RgbImage m_background; + + //number of Gaussian components per pixel + unsigned char* m_modes_per_pixel; + }; + }; +}; + +#endif + + + + + + diff --git a/package_bgs/jmo/BGS.h b/package_bgs/jmo/BGS.h new file mode 100644 index 0000000000000000000000000000000000000000..1fc0e444187b0ff059aaab597310c7c137f0799f --- /dev/null +++ b/package_bgs/jmo/BGS.h @@ -0,0 +1,216 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#if !defined(_BGS_H_) +#define _BGS_H_ + +#include <cv.h> + + +// TODO check these defines are not used (or not redundant with real params) +#define MAX_LBP_MODE_NUM 5 + +#define ROBUST_COLOR_OFFSET 6.0f + +#define LOW_INITIAL_MODE_WEIGHT 0.01f + +#define MODE_UPDATING_LEARN_RATE 0.01f +#define WEIGHT_UPDATING_LEARN_RATE 0.01f + +#define COLOR_MAX_MIN_OFFSET 5 + +#define BACKGROUND_MODEL_PERCENT 0.6f + +#define PATTERN_COLOR_DIST_BACKGROUND_THRESHOLD 0.2f + +#define PATTERN_DIST_SMOOTH_NEIG_HALF_SIZE 6 +#define PATTERN_DIST_CONV_GAUSSIAN_SIGMA 2.5f + +#define ROBUST_SHADOW_RATE 0.6f +#define ROBUST_HIGHLIGHT_RATE 1.20f + +#define BINARY_PATTERM_ELEM(c1, c2, offset) \ + ((float)(c2)-(float)(c1)+offset>0) + +/* +#define BINARY_PATTERM_ELEM(c1, c2, offset) \ +( fabsf((float)(c2)-(float)(c1)) <= offset ? 1 : (float)(c2)-(float)(c1) >=0 ) +*/ + +#ifndef PI +#define PI 3.141592653589793f +#endif + +/************************************************************************/ +/* some data structures for multi-level LBP (local binary pattern) */ +/* texture feature for background subtraction */ +/************************************************************************/ +typedef struct _LBP +{ + float* bg_pattern; /* the average local binary pattern of background mode */ + float* bg_intensity; /* the average color intensity of background mode */ + float* max_intensity; /* the maximal color intensity of background mode */ + float* min_intensity; /* the minimal color intensity of background mode */ + float weight; /* the weight of background mode, i.e. probability that the background mode belongs to background */ + float max_weight; /* the maximal weight of background mode */ + int bg_layer_num; /* the background layer number of background mode */ + unsigned long first_time; /* the first time of background mode appearing */ + unsigned long last_time; /* the last time of background model appearing */ + int freq; /* the appearing frequency */ + //long mnrl; /* maximum negative run-length */ + unsigned long layer_time; /* the first time of background mode becoming a background layer */ +} +LBPStruct; + +typedef struct _PixelLBP +{ + LBPStruct* LBPs; /* the background modes */ + unsigned short* lbp_idxes; /* the indices of background modes */ + unsigned int cur_bg_layer_no; + unsigned int num; /* the total number of background modes */ + unsigned int bg_num; /* the number of the first background modes for foreground detection */ + unsigned char* cur_intensity; /* the color intensity of current pixel */ + float* cur_pattern; /* the local binary pattern of current pixel */ + float matched_mode_first_time; /* the index of currently matched pixel mode */ +} +PixelLBPStruct; + +/*********************************************************************************/ +/* should replace the above structure using class in the future (not finished) */ +/*********************************************************************************/ + +class BG_PIXEL_MODE +{ +public: + float* bg_lbp_pattern; /* the average local binary pattern of background mode */ + float* bg_intensity; /* the average color intensity of background mode */ + float* max_intensity; /* the maximal color intensity of background mode */ + float* min_intensity; /* the minimal color intensity of background mode */ + float weight; /* the weight of background mode, i.e. probability that the background mode belongs to background */ + float max_weight; /* the maximal weight of background mode */ + int bg_layer_num; /* the background layer number of background mode */ + + int lbp_pattern_length; + int color_channel; + + BG_PIXEL_MODE(int _lbp_pattern_length, int _color_channel=3) { + lbp_pattern_length = _lbp_pattern_length; + color_channel = _color_channel; + + bg_lbp_pattern = new float[lbp_pattern_length]; + bg_intensity = new float[color_channel]; + max_intensity = new float[color_channel]; + min_intensity = new float[color_channel]; + }; + + virtual ~BG_PIXEL_MODE() { + delete [] bg_lbp_pattern; + delete [] bg_intensity; + delete [] max_intensity; + delete [] min_intensity; + }; +}; + +class BG_PIXEL_PATTERN +{ +public: + BG_PIXEL_MODE** pixel_MODEs; /* the background modes */ + unsigned short* lbp_pattern_idxes; /* the indices of background modes */ + unsigned int cur_bg_layer_no; + unsigned int num; /* the total number of background modes */ + unsigned int bg_num; /* the number of the first background modes for foreground detection */ + unsigned char* cur_intensity; /* the color intensity of current pixel */ + float* cur_lbp_pattern; /* the local binary pattern of current pixel */ + + int lbp_pattern_length; + int color_channel; + int pixel_mode_num; + + BG_PIXEL_PATTERN(int _pixel_mode_num, int _lbp_pattern_length, int _color_channel=3) { + pixel_mode_num = _pixel_mode_num; + lbp_pattern_length = _lbp_pattern_length; + color_channel = _color_channel; + + pixel_MODEs = new BG_PIXEL_MODE*[pixel_mode_num]; + + for ( int i = 0 ; i < pixel_mode_num ; i++ ) { + pixel_MODEs[i] = new BG_PIXEL_MODE(_lbp_pattern_length, _color_channel); + } + + lbp_pattern_idxes = new unsigned short[pixel_mode_num]; + cur_intensity = new unsigned char[color_channel]; + cur_lbp_pattern = new float[lbp_pattern_length]; + }; + + virtual ~BG_PIXEL_PATTERN() { + delete [] lbp_pattern_idxes; + delete [] cur_intensity; + delete [] cur_lbp_pattern; + + for ( int i = 0 ; i < pixel_mode_num ; i++ ) + delete pixel_MODEs[i]; + delete [] pixel_MODEs; + }; +}; + +class IMAGE_BG_MODEL +{ + int pixel_length; + + BG_PIXEL_PATTERN** pixel_PATTERNs; + + IMAGE_BG_MODEL(int _pixel_length, int _pixel_mode_num, int _lbp_pattern_length, int _color_channel=3) { + pixel_length = _pixel_length; + + pixel_PATTERNs = new BG_PIXEL_PATTERN*[pixel_length]; + for ( int i = 0 ; i < pixel_length ; i++ ) + pixel_PATTERNs[i] = new BG_PIXEL_PATTERN(_pixel_mode_num, _lbp_pattern_length, _color_channel); + } + virtual ~IMAGE_BG_MODEL() { + for ( int i = 0 ; i < pixel_length ; i++ ) + delete pixel_PATTERNs[i]; + delete [] pixel_PATTERNs; + } +}; + + +#endif // !defined(_BGS_H_) diff --git a/package_bgs/jmo/BackgroundSubtractionAPI.h b/package_bgs/jmo/BackgroundSubtractionAPI.h new file mode 100644 index 0000000000000000000000000000000000000000..7d01d692588e338fad43927761b11e4718d276d6 --- /dev/null +++ b/package_bgs/jmo/BackgroundSubtractionAPI.h @@ -0,0 +1,158 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +////////////////////////////////////////////////////////////////////// +// +// BackgroundSubtractionAPI.h: +// interface for the BackgroundSubtractionAPI class. +// +// A background subtraction algorithm takes as input +// an RGB image and provide as ouput a Binary mask +// with a value of 0 for points belonging to the +// background, and non zero for points belonging +// to the foreground. +// +// +// +// To add: +// - a function indicating the valid input and ouput +// images +// e.g. RGB image (default) or greylevel image for the input +// char image for the output +// +////////////////////////////////////////////////////////////////////// + + +#if !defined(_BACKGROUND_SUBTRACTION_API_H_) +#define _BACKGROUND_SUBTRACTION_API_H_ + +#include "cv.h" + +class CBackgroundSubtractionAPI +{ +public: + //CBackgroundSubtractionAPI(){}; + //virtual ~CBackgroundSubtractionAPI(){}; + + //------------------------------------------------------------- + // TO CALL AT INITIALISATION: DEFINES THE SIZE OF THE INPUT IMAGES + // NORMALLY, UNNECESSARY IF A CONFIGURATION FILE IS LOADED + void Init(int width,int height); + + //------------------------------------------------------------- + // PROVIDE A MASK TO DEFINE THE SET OF POINTS WHERE BACKGROUND + // SUBTRACTION DOES NOT NEED TO BE PERFORMED + // + // mode is useful to specify if the points to remove from + // processing are in addition to the ones potentially + // removed according to the configuration file, + // or if they are the only ones to be removed + // + // mode=0 : provided points need to be removed + // in addition to those already removed + // mode=1 : the provided points are the only one to remove + // from processing + // Note: maskImage(li,co)=0 indicate the points to remove + // from background processing + void SetValidPointMask(IplImage* maskImage, int mode); + + //------------------------------------------------------------- + // + // set the frame rate, to adjust the update parameters + // to the actual frame rate. + // Can be called only once at initialisation, + // but in online cases, can be used to indicate + // the time interval during the last processed frame + // + // frameDuration is in millisecond + void SetFrameRate(float frameDuration); + + //------------------------------------------------------------- + // PROVIDE A POINTER TO THE INPUT IMAGE + // -> INDICATE WHERE THE NEW IMAGE TO PROCESS IS STORED + // + // Here assumes that the input image will contain RGB images. + // The memory of this image is handled by the caller. + // + // The return value indicate whether the actual Background + // Subtraction algorithm handles RGB images (1) or not (0). + // + int SetRGBInputImage(IplImage * inputImage); + + //------------------------------------------------------------- + // PROVIDE A POINTER TO THE RESULT IMAGE + // INDICATE WHERE THE BACKGROUND RESULT NEED TO BE STORED + // + // The return value is 1 if correct image format is provided, + // otherwise the return value is 0. + // e.g. fg_mask_img = cvCreateImage(imgSize, IPL_DEPTH_8U, 1); + int SetForegroundMaskImage(IplImage *fg_mask_img); + + // The return value is 1 if the function is implemented + // with correct format, otherwise the return value is 0 + // e.g. fg_prob_img = cvCreateImage(imgSize, IPL_DEPTH_32F, 1); + int SetForegroundProbImage(IplImage *fg_prob_img); + + //------------------------------------------------------------- + // This function should be called each time a new image is + // available in the input image. + // + // The return value is 1 if everything goes well, + // otherwise the return value is 0. + // + int Process(); + + //------------------------------------------------------------- + // this function should save parameters and information of the model + // (e.g. after a training of the model, or in such a way + // that the model can be reload to process the next frame + // type of save: + void Save(char *bg_model_fn); + + //------------------------------------------------------------- + // this function should load the parameters necessary + // for the processing of the background subtraction or + // load background model information + void Load(char *bg_model_fn); +}; + +#endif // !defined(_BACKGROUND_SUBTRACTION_API_H_) diff --git a/package_bgs/jmo/BlobExtraction.cpp b/package_bgs/jmo/BlobExtraction.cpp new file mode 100644 index 0000000000000000000000000000000000000000..04560b92cf21a43a315825de24cc7451d6d1c559 --- /dev/null +++ b/package_bgs/jmo/BlobExtraction.cpp @@ -0,0 +1,1490 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +//***********************************************************// +//* Blob analysis package 8 August 2003 *// +//* Version 1.0 *// +//* Input: IplImage* binary image *// +//* Output: attributes of each connected region *// +//* Author: Dave Grossman *// +//* Modifications: Francesc Pinyol and Ricard Borras *// +//* Email: dgrossman@cdr.stanford.edu *// +//* Email: fpinyol@cvc.uab.es rborras@cvc.uab.es *// +//* Acknowledgement: the algorithm has been around > 20 yrs *// +//***********************************************************// + +//! Indica si la connectivitat es a 8 (si es desactiva es a 4) +#define B_CONNECTIVITAT_8 + +//! si la imatge �s c�clica verticalment (els blobs que toquen +//! les vores superior i inferior no es consideren externs) +#define IMATGE_CICLICA_VERTICAL 1 +//! si la imatge �s c�clica horitzontalment (els blobs que toquen +//! les vores dreta i esquerra no es consideren externs) +#define IMATGE_CICLICA_HORITZONTAL 0 + +#define PERIMETRE_DIAGONAL (1.41421356237310 - 2) +#define SQRT2 1.41421356237310 +// color dels p�xels de la m�scara per ser exteriors +#define PIXEL_EXTERIOR 0 + + +#include "BlobResult.h" +#include "BlobExtraction.h" + +namespace Blob +{ + +/** +- FUNCI�: BlobAnalysis +- FUNCIONALITAT: Extreu els blobs d'una imatge d'un sol canal +- PAR�METRES: +- inputImage: Imatge d'entrada. Ha de ser d'un sol canal +- threshold: Nivell de gris per considerar un pixel blanc o negre +- maskImage: Imatge de m�scara fora de la cual no es calculen els blobs. A m�s, +els blobs que toquen els pixels de la m�scara a 0, s�n considerats +externs +- borderColor: Color del marc de la imatge (0=black or 1=white) +- findmoments: calcula els moments dels blobs o no +- RegionData: on es desar� el resultat +- RESULTAT: +- retorna true si tot ha anat b�, false si no. Deixa el resultat a blobs. +- RESTRICCIONS: +- La imatge d'entrada ha de ser d'un sol canal +- AUTOR: dgrossman@cdr.stanford.edu +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +- fpinyol@cvc.uab.es, rborras@cvc.uab.es: adaptaci� a les OpenCV +*/ +bool BlobAnalysis( IplImage* inputImage, + uchar threshold, + IplImage* maskImage, + bool borderColor, + bool findmoments, + blob_vector &RegionData ) +{ + // dimensions of input image taking in account the ROI + int Cols, Rows, startCol, startRow; + + if( inputImage->roi ) + { + CvRect imageRoi = cvGetImageROI( inputImage ); + startCol = imageRoi.x; + startRow = imageRoi.y; + Cols = imageRoi.width; + Rows = imageRoi.height; + } + else + { + startCol = 0; + startRow = 0; + Cols = inputImage->width; + Rows = inputImage->height; + } + + int Trans = Cols; // MAX trans in any row + char* pMask = NULL; + char* pImage; + + // Convert image array into transition array. In each row + // the transition array tells which columns have a color change + int iCol,iRow,iTran, Tran; // Data for a given run + bool ThisCell, LastCell; // Contents (colors (0 or 1)) within this row + int TransitionOffset = 0; // Performance booster to avoid multiplication + + // row 0 and row Rows+1 represent the border + int i; + int *Transition; // Transition Matrix + + int nombre_pixels_mascara = 0; + //! Imatge amb el perimetre extern de cada pixel + IplImage *imatgePerimetreExtern; + + // input images must have only 1-channel and be an image + if( !CV_IS_IMAGE( inputImage ) || (inputImage->nChannels != 1) ) + { + return false; + } + if( maskImage != NULL ) + { + // input image and mask are a valid image? + if( !CV_IS_IMAGE( inputImage ) || !CV_IS_IMAGE( maskImage )) + return false; + + // comprova que la m�scara tingui les mateixes dimensions que la imatge + if( inputImage->width != maskImage->width || inputImage->height != maskImage->height ) + { + return false; + } + + // comprova que la m�scara sigui una imatge d'un sol canal (grayscale) + if( maskImage->nChannels != 1 ) + { + return false; + } + + } + + // Initialize Transition array + Transition=new int[(Rows + 2)*(Cols + 2)]; + memset(Transition,0,(Rows + 2) * (Cols + 2)*sizeof(int)); + Transition[0] = Transition[(Rows + 1) * (Cols + 2)] = Cols + 2; + + // Start at the beginning of the image (startCol, startRow) + pImage = inputImage->imageData + startCol - 1 + startRow * inputImage->widthStep; + + /* + Paral�lelitzaci� del c�lcul de la matriu de transicions + Fem que cada iteraci� del for el faci un thread o l'altre ( tenim 2 possibles threads ) + */ + if(maskImage == NULL) + { + imatgePerimetreExtern = NULL; + + //Fill Transition array + for(iRow = 1; iRow < Rows + 1; iRow++) // Choose a row of Bordered image + { + TransitionOffset = iRow*(Cols + 2); //per a que sigui paral�litzable + iTran = 0; // Index into Transition array + Tran = 0; // No transitions at row start + LastCell = borderColor; + + for(iCol = 0; iCol < Cols + 2; iCol++) // Scan that row of Bordered image + { + if(iCol == 0 || iCol == Cols+1) + ThisCell = borderColor; + else + ThisCell = ((unsigned char) *(pImage)) > threshold; + + if(ThisCell != LastCell) + { + Transition[TransitionOffset + iTran] = Tran; // Save completed Tran + iTran++; // Prepare new index + LastCell = ThisCell; // With this color + } + + Tran++; // Tran continues + pImage++; + } + + Transition[TransitionOffset + iTran] = Tran; // Save completed run + if ( (TransitionOffset + iTran + 1) < (Rows + 1)*(Cols + 2) ) + { + Transition[TransitionOffset + iTran + 1] = -1; + } + //jump to next row (beginning from (startCol, startRow)) + pImage = inputImage->imageData - 1 + startCol + (iRow+startRow)*inputImage->widthStep; + } + } + else + { + //maskImage not NULL: Cal rec�rrer la m�scara tamb� per calcular la matriu de transicions + + char perimeter; + char *pPerimetre; + + // creem la imatge que contindr� el perimetre extern de cada pixel + imatgePerimetreExtern = cvCreateImage( cvSize(maskImage->width, maskImage->height), IPL_DEPTH_8U, 1); + cvSetZero( imatgePerimetreExtern ); + + pMask = maskImage->imageData - 1; + + //Fill Transition array + for(iRow = 1; iRow < Rows + 1; iRow++) // Choose a row of Bordered image + { + TransitionOffset = iRow*(Cols + 2); + iTran = 0; // Index into Transition array + Tran = 0; // No transitions at row start + LastCell = borderColor; + + pPerimetre = imatgePerimetreExtern->imageData + (iRow - 1) * imatgePerimetreExtern->widthStep; + //pMask = maskImage->imageData + (iRow-1) * maskImage->widthStep; + + for(iCol = 0; iCol < Cols + 2; iCol++) // Scan that row of Bordered image + { + if(iCol == 0 || iCol == Cols+1 || ((unsigned char) *pMask) == PIXEL_EXTERIOR) + ThisCell = borderColor; + else + ThisCell = ((unsigned char) *(pImage)) > threshold; + + if(ThisCell != LastCell) + { + Transition[TransitionOffset + iTran] = Tran; // Save completed Tran + iTran++; // Prepare new index + LastCell = ThisCell; // With this color + } + + /*//////////////////////////////////////////////////////////////////////// + Calcul de la imatge amb els pixels externs + ////////////////////////////////////////////////////////////////////////*/ + // pels pixels externs no cal calcular res pq no hi accedir-hem + if( (iCol > 0) && (iCol < Cols) ) + { + if( *pMask == PIXEL_EXTERIOR ) + { + *pPerimetre = 0; + } + else + { + perimeter = 0; + + // pixels al nord de l'actual + if(iRow>1) + { + if( *(pMask - maskImage->widthStep ) == PIXEL_EXTERIOR) perimeter++; + } + + // pixels a l'est i oest de l'actual + if( iRow < imatgePerimetreExtern->height ) + { + if( (iCol>0) && (*(pMask-1) == PIXEL_EXTERIOR) ) perimeter++; + + if( ( iCol < imatgePerimetreExtern->width - 1) && (*(pMask+1) == PIXEL_EXTERIOR) ) perimeter++; + } + + // pixels al sud de l'actual + if( iRow < imatgePerimetreExtern->height - 1) + { + if( (*(pMask+maskImage->widthStep) == PIXEL_EXTERIOR) ) perimeter++; + } + + *pPerimetre = perimeter; + } + } + + Tran++; // Tran continues + pImage++; + pMask++; + pPerimetre++; + } + Transition[TransitionOffset + iTran] = Tran; // Save completed run + + if ( (TransitionOffset + iTran + 1) < (Rows + 1)*(Cols + 2) ) + { + Transition[TransitionOffset + iTran + 1] = -1; + } + + + //jump to next row (beginning from (startCol, startRow)) + pImage = inputImage->imageData - 1 + startCol + (iRow+startRow)*inputImage->widthStep; + //the mask should be the same size as image Roi, so don't take into account the offset + pMask = maskImage->imageData - 1 + iRow*maskImage->widthStep; + } + } + + // Process transition code depending on Last row and This row + // + // Last ---++++++--+++++++++++++++-----+++++++++++++++++++-----++++++-------+++--- + // This -----+++-----++++----+++++++++----+++++++---++------------------++++++++-- + // + // There are various possibilities: + // + // Case 1 2 3 4 5 6 7 8 + // Last |xxx |xxxxoo |xxxxxxx|xxxxxxx|ooxxxxx|ooxxx |ooxxxxx| xxx| + // This | yyy| yyy| yyyy | yyyyy|yyyyyyy|yyyyyyy|yyyy |yyyy | + // Here o is optional + // + // Here are the primitive tests to distinguish these 6 cases: + // A) Last end < This start - 1 OR NOT Note: -1 + // B) This end < Last start OR NOT + // C) Last start < This start OR NOT + // D) This end < Last end OR NOT + // E) This end = Last end OR NOT + // + // Here is how to use these tests to determine the case: + // Case 1 = A [=> NOT B AND C AND NOT D AND NOT E] + // Case 2 = C AND NOT D AND NOT E [AND NOT A AND NOT B] + // Case 3 = C AND D [=> NOT E] [AND NOT A AND NOT B] + // Case 4 = C AND NOT D AND E [AND NOT A AND NOT B] + // Case 5 = NOT C AND E [=> NOT D] [AND NOT A AND NOT B] + // Case 6 = NOT C AND NOT D AND NOT E [AND NOT A AND NOT B] + // Case 7 = NOT C AND D [=> NOT E] [AND NOT A AND NOT B] + // Case 8 = B [=> NOT A AND NOT C AND D AND NOT E] + // + // In cases 2,3,4,5,6,7 the following additional test is needed: + // Match) This color = Last color OR NOT + // + // In cases 5,6,7 the following additional test is needed: + // Known) This region was already matched OR NOT + // + // Here are the main tests and actions: + // Case 1: LastIndex++; + // Case 2: if(Match) {y = x;} + // LastIndex++; + // Case 3: if(Match) {y = x;} + // else {y = new} + // ThisIndex++; + // Case 4: if(Match) {y = x;} + // else {y = new} + // LastIndex++; + // ThisIndex++; + // Case 5: if(Match AND NOT Known) {y = x} + // else if(Match AND Known) {Subsume(x,y)} + // LastIndex++;ThisIndex++ + // Case 6: if(Match AND NOT Known) {y = x} + // else if(Match AND Known) {Subsume(x,y)} + // LastIndex++; + // Case 7: if(Match AND NOT Known) {y = x} + // else if(Match AND Known) {Subsume(x,y)} + // ThisIndex++; + // Case 8: ThisIndex++; + + int *SubsumedRegion = NULL; + + double ThisParent; // These data can change when the line is current + double ThisArea; + double ThisPerimeter; + double ThisSumX = 0; + double ThisSumY = 0; + double ThisSumXX = 0; + double ThisSumYY = 0; + double ThisSumXY = 0; + double ThisMinX; + double ThisMaxX; + double ThisMinY; + double ThisMaxY; + double LastPerimeter; // This is the only data for retroactive change + double ThisExternPerimeter; + + int HighRegionNum = 0; + //int RegionNum = 0; + int ErrorFlag = 0; + + int LastRow, ThisRow; // Row number + int LastStart, ThisStart; // Starting column of run + int LastEnd, ThisEnd; // Ending column of run + int LastColor, ThisColor; // Color of run + + int LastIndex, ThisIndex; // Which run are we up to + int LastIndexCount, ThisIndexCount; // Out of these runs + int LastRegionNum, ThisRegionNum; // Which assignment + int *LastRegion; // Row assignment of region number + int *ThisRegion; // Row assignment of region number + + int LastOffset = -(Trans + 2); // For performance to avoid multiplication + int ThisOffset = 0; // For performance to avoid multiplication + int ComputeData; + + CvPoint actualedge; + uchar imagevalue; + bool CandidatExterior = false; + + // apuntadors als blobs de la regi� actual i last + CBlob *regionDataThisRegion, *regionDataLastRegion; + + LastRegion=new int[Cols+2]; + ThisRegion=new int[Cols+2]; + + for(i = 0; i < Cols + 2; i++) // Initialize result arrays + { + LastRegion[i] = -1; + ThisRegion[i] = -1; + } + + //create the external blob + RegionData.push_back( new CBlob() ); + SubsumedRegion = NewSubsume(SubsumedRegion,0); + RegionData[0]->parent = -1; + RegionData[0]->area = (double) Transition[0]; + RegionData[0]->perimeter = (double) (2 + 2 * Transition[0]); + + ThisIndexCount = 1; + ThisRegion[0] = 0; // Border region + + // beginning of the image + // en cada linia, pimage apunta al primer pixel de la fila + pImage = inputImage->imageData - 1 + startCol + startRow * inputImage->widthStep; + //the mask should be the same size as image Roi, so don't take into account the offset + if(maskImage!=NULL) pMask = maskImage->imageData - 1; + + char *pImageAux, *pMaskAux = NULL; + + // Loop over all rows + for(ThisRow = 1; ThisRow < Rows + 2; ThisRow++) + { + //cout << "========= THIS ROW = " << ThisRow << endl; // for debugging + ThisOffset += Trans + 2; + ThisIndex = 0; + LastOffset += Trans + 2;; + LastRow = ThisRow - 1; + LastIndexCount = ThisIndexCount; + LastIndex = 0; + + int EndLast = 0; + int EndThis = 0; + + for(int j = 0; j < Trans + 2; j++) + { + int Index = ThisOffset + j; + int TranVal = Transition[Index]; + if(TranVal > 0) ThisIndexCount = j + 1; // stop at highest + + if(ThisRegion[j] == -1) { EndLast = 1; } + if(TranVal < 0) { EndThis = 1; } + + if(EndLast > 0 && EndThis > 0) { break; } + + LastRegion[j] = ThisRegion[j]; + ThisRegion[j] = -1; // Flag indicates region is not initialized + } + + int MaxIndexCount = LastIndexCount; + if(ThisIndexCount > MaxIndexCount) MaxIndexCount = ThisIndexCount; + + // Main loop over runs within Last and This rows + while (LastIndex < LastIndexCount && ThisIndex < ThisIndexCount) + { + ComputeData = 0; + + if(LastIndex == 0) LastStart = 0; + else LastStart = Transition[LastOffset + LastIndex - 1]; + LastEnd = Transition[LastOffset + LastIndex] - 1; + LastColor = LastIndex - 2 * (LastIndex / 2); + LastRegionNum = LastRegion[LastIndex]; + + regionDataLastRegion = RegionData[LastRegionNum]; + + + if(ThisIndex == 0) ThisStart = 0; + else ThisStart = Transition[ThisOffset + ThisIndex - 1]; + ThisEnd = Transition[ThisOffset + ThisIndex] - 1; + ThisColor = ThisIndex - 2 * (ThisIndex / 2); + ThisRegionNum = ThisRegion[ThisIndex]; + + if( ThisRegionNum >= 0 ) + regionDataThisRegion = RegionData[ThisRegionNum]; + else + regionDataThisRegion = NULL; + + + // blobs externs + CandidatExterior = false; + if( +#if !IMATGE_CICLICA_VERTICAL + ThisRow == 1 || ThisRow == Rows || +#endif +#if !IMATGE_CICLICA_HORITZONTAL + ThisStart <= 1 || ThisEnd >= Cols || +#endif + GetExternPerimeter( ThisStart, ThisEnd, ThisRow, inputImage->width, inputImage->height, imatgePerimetreExtern ) + ) + { + CandidatExterior = true; + } + + int TestA = (LastEnd < ThisStart - 1); // initially false + int TestB = (ThisEnd < LastStart); // initially false + int TestC = (LastStart < ThisStart); // initially false + int TestD = (ThisEnd < LastEnd); + int TestE = (ThisEnd == LastEnd); + + int TestMatch = (ThisColor == LastColor); // initially true + int TestKnown = (ThisRegion[ThisIndex] >= 0); // initially false + + int Case = 0; + if(TestA) Case = 1; + else if(TestB) Case = 8; + else if(TestC) + { + if(TestD) Case = 3; + else if(!TestE) Case = 2; + else Case = 4; + } + else + { + if(TestE) Case = 5; + else if(TestD) Case = 7; + else Case = 6; + } + + // Initialize common variables + ThisArea = (float) 0.0; + + if(findmoments) + { + ThisSumX = ThisSumY = (float) 0.0; + ThisSumXX = ThisSumYY = ThisSumXY = (float) 0.0; + } + ThisMinX = ThisMinY = (float) 1000000.0; + ThisMaxX = ThisMaxY = (float) -1.0; + + LastPerimeter = ThisPerimeter = (float) 0.0; + ThisParent = (float) -1; + ThisExternPerimeter = 0.0; + + // Determine necessary action and take it + switch (Case) + { + case 1: //|xxx | + //| yyy| + + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + LastIndex++; + + //afegim la cantonada a LastRegion + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges,&actualedge); + + //afegim la cantonada a ThisRegion + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + + break; + + + case 2: //|xxxxoo | + //| yyy| + + if(TestMatch) // Same color + { + ThisRegionNum = LastRegionNum; + regionDataThisRegion = regionDataLastRegion; + + ThisArea = ThisEnd - ThisStart + 1; + LastPerimeter = LastEnd - ThisStart + 1; // to subtract + ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter + + PERIMETRE_DIAGONAL*2; + + if( CandidatExterior ) + { + ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern ); + ThisExternPerimeter += PERIMETRE_DIAGONAL*2; + } + ComputeData = 1; + } + + //afegim la cantonada a ThisRegion + if(ThisRegionNum!=-1) + { + // afegim dos vertexs si s�n diferents, nom�s + if(ThisStart - 1 != ThisEnd) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + } + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + } + //afegim la cantonada a ThisRegion + if(LastRegionNum!=-1 && LastRegionNum != ThisRegionNum ) + { + // afegim dos vertexs si s�n diferents, nom�s + if(ThisStart - 1 != ThisEnd) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges,&actualedge); + } + } + + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + LastIndex++; + break; + + + case 3: //|xxxxxxx| + //| yyyy | + + if(TestMatch) // Same color + { + ThisRegionNum = LastRegionNum; + regionDataThisRegion = regionDataLastRegion; + + ThisArea = ThisEnd - ThisStart + 1; + LastPerimeter = ThisArea; // to subtract + ThisPerimeter = 2 + ThisArea + PERIMETRE_DIAGONAL*2; + if( CandidatExterior ) + { + ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern ); + + ThisExternPerimeter += PERIMETRE_DIAGONAL * 2; + } + } + else // Different color => New region + { + ThisParent = LastRegionNum; + ThisRegionNum = ++HighRegionNum; + ThisArea = ThisEnd - ThisStart + 1; + ThisPerimeter = 2 + 2 * ThisArea; + RegionData.push_back( new CBlob() ); + regionDataThisRegion = RegionData.back(); + + SubsumedRegion = NewSubsume(SubsumedRegion,HighRegionNum); + if( CandidatExterior ) + ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern ); + + } + + if(ThisRegionNum!=-1) + { + //afegim la cantonada a la regio + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + //afegim la cantonada a la regio + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + } + // si hem creat un nou blob, afegim tb a l'anterior + if(!TestMatch && LastRegionNum!=-1 && LastRegionNum != ThisRegionNum ) + { + //afegim la cantonada a la regio + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges,&actualedge); + //afegim la cantonada a la regio + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges,&actualedge); + } + + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + ComputeData = 1; + ThisIndex++; + break; + + + case 4: //|xxxxxxx| + //| yyyyy| + + if(TestMatch) // Same color + { + ThisRegionNum = LastRegionNum; + regionDataThisRegion = regionDataLastRegion; + ThisArea = ThisEnd - ThisStart + 1; + LastPerimeter = ThisArea; // to subtract + ThisPerimeter = 2 + ThisArea + PERIMETRE_DIAGONAL; + if( CandidatExterior ) + { + ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern ); + + ThisExternPerimeter += PERIMETRE_DIAGONAL; + } + } + else // Different color => New region + { + ThisParent = LastRegionNum; + ThisRegionNum = ++HighRegionNum; + ThisArea = ThisEnd - ThisStart + 1; + ThisPerimeter = 2 + 2 * ThisArea; + RegionData.push_back( new CBlob() ); + regionDataThisRegion = RegionData.back(); + SubsumedRegion = NewSubsume(SubsumedRegion,HighRegionNum); + if( CandidatExterior ) + ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern ); + + } + + if(ThisRegionNum!=-1) + { + //afegim la cantonada a la regio + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + } + // si hem creat un nou blob, afegim tb a l'anterior + if(!TestMatch && LastRegionNum!=-1 && LastRegionNum != ThisRegionNum ) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges,&actualedge); + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges,&actualedge); + } + + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + ComputeData = 1; + +#ifdef B_CONNECTIVITAT_8 + if( TestMatch ) + { + LastIndex++; + ThisIndex++; + } + else + { + LastIndex++; + } +#else + LastIndex++; + ThisIndex++; +#endif + break; + + + case 5: //|ooxxxxx| + //|yyyyyyy| + + if(!TestMatch && !TestKnown) // Different color and unknown => new region + { + ThisParent = LastRegionNum; + ThisRegionNum = ++HighRegionNum; + ThisArea = ThisEnd - ThisStart + 1; + ThisPerimeter = 2 + 2 * ThisArea; + RegionData.push_back( new CBlob() ); + regionDataThisRegion = RegionData.back(); + SubsumedRegion = NewSubsume(SubsumedRegion,HighRegionNum); + if( CandidatExterior ) + ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern ); + + } + else if(TestMatch && !TestKnown) // Same color and unknown + { + ThisRegionNum = LastRegionNum; + regionDataThisRegion = regionDataLastRegion; + ThisArea = ThisEnd - ThisStart + 1; + LastPerimeter = LastEnd - LastStart + 1; // to subtract + ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter + + PERIMETRE_DIAGONAL * (LastStart != ThisStart); + if( CandidatExterior ) + { + ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern ); + + + ThisExternPerimeter += PERIMETRE_DIAGONAL * (LastStart != ThisStart); + } + ComputeData = 1; + } + else if(TestMatch && TestKnown) // Same color and known + { + LastPerimeter = LastEnd - LastStart + 1; // to subtract + //ThisPerimeter = - LastPerimeter; + ThisPerimeter = - 2 * LastPerimeter + + PERIMETRE_DIAGONAL * (LastStart != ThisStart); + + if(ThisRegionNum > LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, + findmoments, ThisRegionNum, LastRegionNum ); + for(int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if(ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum; + if(LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum; + } + ThisRegionNum = LastRegionNum; + } + else if(ThisRegionNum < LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, + findmoments, LastRegionNum, ThisRegionNum ); + + for(int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if(ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum; + if(LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum; + } + LastRegionNum = ThisRegionNum; + } + } + + + if(ThisRegionNum!=-1) + { + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + + if( ThisStart - 1 != LastEnd ) + { + //afegim la cantonada a la regio + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + } + } + // si hem creat un nou blob, afegim tb a l'anterior + if(!TestMatch && LastRegionNum!=-1 && LastRegionNum != ThisRegionNum ) + { + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges,&actualedge); + } + + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + +#ifdef B_CONNECTIVITAT_8 + if( TestMatch ) + { + LastIndex++; + ThisIndex++; + } + else + { + LastIndex++; + } +#else + LastIndex++; + ThisIndex++; +#endif + break; + + + case 6: //|ooxxx | + //|yyyyyyy| + + if(TestMatch && !TestKnown) + { + ThisRegionNum = LastRegionNum; + regionDataThisRegion = regionDataLastRegion; + ThisArea = ThisEnd - ThisStart + 1; + LastPerimeter = LastEnd - LastStart + 1; // to subtract + ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter + + PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart!=LastStart); + if( CandidatExterior ) + { + ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern ); + + + ThisExternPerimeter += PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart!=LastStart); + } + ComputeData = 1; + } + else if(TestMatch && TestKnown) + { + LastPerimeter = LastEnd - LastStart + 1; // to subtract + //ThisPerimeter = - LastPerimeter; + ThisPerimeter = - 2 * LastPerimeter + + PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart!=LastStart); + + if(ThisRegionNum > LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, + findmoments, ThisRegionNum, LastRegionNum ); + for(int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if(ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum; + if(LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum; + } + ThisRegionNum = LastRegionNum; + } + else if(ThisRegionNum < LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, + findmoments, LastRegionNum, ThisRegionNum ); + for(int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if(ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum; + if(LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum; + } + LastRegionNum = ThisRegionNum; + } + } + + + if(ThisRegionNum!=-1) + { + //afegim la cantonada a la regio + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + if( ThisStart - 1 != LastEnd ) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + } + } + // si hem creat un nou blob, afegim tb a l'anterior + if(!TestMatch && LastRegionNum!=-1 && LastRegionNum != ThisRegionNum ) + { + //afegim la cantonada a la regio + if( ThisStart - 1 != LastEnd ) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + } + } + + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + LastIndex++; + break; + + + case 7: //|ooxxxxx| + //|yyyy | + + if(!TestMatch && !TestKnown) // Different color and unknown => new region + { + ThisParent = LastRegionNum; + ThisRegionNum = ++HighRegionNum; + ThisArea = ThisEnd - ThisStart + 1; + ThisPerimeter = 2 + 2 * ThisArea; + RegionData.push_back( new CBlob() ); + regionDataThisRegion = RegionData.back(); + SubsumedRegion = NewSubsume(SubsumedRegion,HighRegionNum); + if( CandidatExterior ) + ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern ); + + } + else if(TestMatch && !TestKnown) + { + ThisRegionNum = LastRegionNum; + regionDataThisRegion = regionDataLastRegion; + ThisArea = ThisEnd - ThisStart + 1; + ThisPerimeter = 2 + ThisArea; + LastPerimeter = ThisEnd - LastStart + 1; + ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter + + PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart!=LastStart); + if( CandidatExterior ) + { + ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern ); + + ThisExternPerimeter += PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart!=LastStart); + } + ComputeData = 1; + } + else if(TestMatch && TestKnown) + { + LastPerimeter = ThisEnd - LastStart + 1; // to subtract + //ThisPerimeter = - LastPerimeter; + ThisPerimeter = - 2 * LastPerimeter + + PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart!=LastStart); + + if(ThisRegionNum > LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, + findmoments, ThisRegionNum, LastRegionNum ); + for(int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if(ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum; + if(LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum; + } + ThisRegionNum = LastRegionNum; + } + else if(ThisRegionNum < LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, + findmoments, LastRegionNum, ThisRegionNum ); + for(int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if(ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum; + if(LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum; + } + LastRegionNum = ThisRegionNum; + } + } + + if(ThisRegionNum!=-1) + { + //afegim la cantonada a la regio + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + if( ThisStart - 1 != LastEnd ) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + } + } + // si hem creat un nou blob, afegim tb a l'anterior + if(!TestMatch && LastRegionNum!=-1 && LastRegionNum != ThisRegionNum ) + { + //afegim la cantonada a la regio + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges,&actualedge); + if( ThisStart - 1 != LastEnd ) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + } + } + + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + ThisIndex++; + break; + + case 8: //| xxx| + //|yyyy | + +#ifdef B_CONNECTIVITAT_8 + // fusionem blobs + if( TestMatch ) + { + if(ThisRegionNum > LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, + findmoments, ThisRegionNum, LastRegionNum ); + for(int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if(ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum; + if(LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum; + } + ThisRegionNum = LastRegionNum; + } + else if(ThisRegionNum < LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, + findmoments, LastRegionNum, ThisRegionNum ); + for(int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if(ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum; + if(LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum; + } + LastRegionNum = ThisRegionNum; + } + + regionDataThisRegion->perimeter = regionDataThisRegion->perimeter + PERIMETRE_DIAGONAL*2; + } +#endif + + if(ThisRegionNum!=-1) + { + //afegim la cantonada a la regio + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges,&actualedge); + } +#ifdef B_CONNECTIVITAT_8 + // si hem creat un nou blob, afegim tb a l'anterior + if(!TestMatch && LastRegionNum!=-1 && LastRegionNum != ThisRegionNum ) + { +#endif + //afegim la cantonada a la regio + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges,&actualedge); +#ifdef B_CONNECTIVITAT_8 + } +#endif + + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + ThisIndex++; +#ifdef B_CONNECTIVITAT_8 + LastIndex--; +#endif + break; + + default: + ErrorFlag = -1; + } // end switch case + + // calculate the blob moments and mean gray level of the current blob (ThisRegionNum) + if(ComputeData > 0) + { + // compute blob moments if necessary + if(findmoments) + { + float ImageRow = (float) (ThisRow - 1); + + for(int k = ThisStart; k <= ThisEnd; k++) + { + ThisSumX += (float) (k - 1); + ThisSumXX += (float) (k - 1) * (k - 1); + } + + ThisSumXY = ThisSumX * ImageRow; + ThisSumY = ThisArea * ImageRow; + ThisSumYY = ThisSumY * ImageRow; + + } + + // compute the mean gray level and its std deviation + if(ThisRow <= Rows ) + { + pImageAux = pImage + ThisStart; + if(maskImage!=NULL) pMaskAux = pMask + ThisStart; + for(int k = ThisStart; k <= ThisEnd; k++) + { + if((k>0) && (k <= Cols)) + { + if( maskImage!= NULL) + { + // nom�s es t� en compte el valor del p�xel de la + // imatge que queda dins de la m�scara + // (de pas, comptem el nombre de p�xels de la m�scara) + if( ((unsigned char) *pMaskAux) != PIXEL_EXTERIOR ) + { + imagevalue = (unsigned char) (*pImageAux); + regionDataThisRegion->mean+=imagevalue; + regionDataThisRegion->stddev+=imagevalue*imagevalue; + } + else + { + nombre_pixels_mascara++; + } + } + else + { + imagevalue = (unsigned char) (*pImageAux); + regionDataThisRegion->mean+=imagevalue; + regionDataThisRegion->stddev+=imagevalue*imagevalue; + + } + } + pImageAux++; + if(maskImage!=NULL) pMaskAux++; + } + } + + // compute the min and max values of X and Y + if(ThisStart - 1 < (int) ThisMinX) ThisMinX = (float) (ThisStart - 1); + if(ThisMinX < (float) 0.0) ThisMinX = (float) 0.0; + if(ThisEnd > (int) ThisMaxX) ThisMaxX = (float) ThisEnd; + + if(ThisRow - 1 < ThisMinY) ThisMinY = ThisRow - 1; + if(ThisMinY < (float) 0.0) ThisMinY = (float) 0.0; + if(ThisRow > ThisMaxY) ThisMaxY = ThisRow; + } + + // put the current results into RegionData + if(ThisRegionNum >= 0) + { + if(ThisParent >= 0) { regionDataThisRegion->parent = (int) ThisParent; } + regionDataThisRegion->etiqueta = ThisRegionNum; + regionDataThisRegion->area += ThisArea; + regionDataThisRegion->perimeter += ThisPerimeter; + regionDataThisRegion->externPerimeter += ThisExternPerimeter; + + if(ComputeData > 0) + { + if(findmoments) + { + regionDataThisRegion->sumx += ThisSumX; + regionDataThisRegion->sumy += ThisSumY; + regionDataThisRegion->sumxx += ThisSumXX; + regionDataThisRegion->sumyy += ThisSumYY; + regionDataThisRegion->sumxy += ThisSumXY; + } + regionDataThisRegion->perimeter -= LastPerimeter; + regionDataThisRegion->minx=MIN(regionDataThisRegion->minx,ThisMinX); + regionDataThisRegion->maxx=MAX(regionDataThisRegion->maxx,ThisMaxX); + regionDataThisRegion->miny=MIN(regionDataThisRegion->miny,ThisMinY); + regionDataThisRegion->maxy=MAX(regionDataThisRegion->maxy,ThisMaxY); + } + // blobs externs + if( CandidatExterior ) + { + regionDataThisRegion->exterior = true; + } + + } + } // end Main loop + + if(ErrorFlag != 0) return false; + // ens situem al primer pixel de la seguent fila + pImage = inputImage->imageData - 1 + startCol + (ThisRow+startRow) * inputImage->widthStep; + + if(maskImage!=NULL) + pMask = maskImage->imageData - 1 + ThisRow * maskImage->widthStep; + } // end Loop over all rows + + // eliminem l'�rea del marc + // i tamb� els p�xels de la m�scara + // ATENCIO: PERFER: el fet de restar el nombre_pixels_mascara del + // blob 0 nom�s ser� cert si la m�scara t� contacte amb el marc. + // Si no, s'haur� de trobar quin �s el blob que cont� m�s p�xels del + // compte. + RegionData[0]->area -= ( Rows + 1 + Cols + 1 )*2 + nombre_pixels_mascara; + + // eliminem el per�metre de m�s: + // - sense marc: 2m+2n (per�metre extern) + // - amb marc: 2(m+2)+2(n+2) = 2m+2n + 8 + // (segurament no �s del tot acurat) + // (i amb les m�scares encara menys...) + RegionData[0]->perimeter -= 8.0; + + // Condense the list + blob_vector::iterator itNew, itOld, iti; + CBlob *blobActual; + + itNew = RegionData.begin(); + itOld = RegionData.begin(); + int iNew = 0; + for(int iOld = 0; iOld <= HighRegionNum; iOld++, itOld++) + { + if(SubsumedRegion[iOld] < 1) // This number not subsumed + { + // Move data from old region number to new region number + //*RegionData[iNew] = *RegionData[iOld]; + **itNew = **itOld; + + // Update and parent pointer if necessary + iti = RegionData.begin(); + for(int i = 0; i <= HighRegionNum; i++) + { + //if(RegionData[i]->parent == iOld) { RegionData[i]->parent = iNew; } + if((*iti)->parent == iOld) { (*iti)->parent = iNew; } + + iti++; + } + iNew++; + itNew++; + } + } + + + HighRegionNum = iNew - 1; // Update where the data ends + RegionData[HighRegionNum]->parent = -1; // and set end of array flag + + + if(findmoments) + { + iti = RegionData.begin(); + // Normalize summation fields into moments + for(ThisRegionNum = 0; ThisRegionNum <= HighRegionNum; ThisRegionNum++, iti++) + { + blobActual = *iti; + + // Get averages + blobActual->sumx /= blobActual->area; + blobActual->sumy /= blobActual->area; + blobActual->sumxx /= blobActual->area; + blobActual->sumyy /= blobActual->area; + blobActual->sumxy /= blobActual->area; + + // Create moments + blobActual->sumxx -= blobActual->sumx * blobActual->sumx; + blobActual->sumyy -= blobActual->sumy * blobActual->sumy; + blobActual->sumxy -= blobActual->sumx * blobActual->sumy; + if(blobActual->sumxy > -1.0E-14 && blobActual->sumxy < 1.0E-14) + { + blobActual->sumxy = (float) 0.0; // Eliminate roundoff error + } + + } + } + + //Get the real mean and std deviation + iti = RegionData.begin(); + for(ThisRegionNum = 0; ThisRegionNum <= HighRegionNum; ThisRegionNum++, iti++) + { + blobActual = *iti; + if(blobActual->area > 1) + { + blobActual->stddev = + sqrt( + ( + blobActual->stddev * blobActual->area - + blobActual->mean * blobActual->mean + )/ + (blobActual->area*(blobActual->area-1)) + ); + } + else + blobActual->stddev=0; + + if(blobActual->area > 0) + blobActual->mean/=blobActual->area; + else + blobActual->mean = 0; + + } + // eliminem els blobs subsumats + blob_vector::iterator itBlobs = RegionData.begin() + HighRegionNum + 1; + while( itBlobs != RegionData.end() ) + { + delete *itBlobs; + //RegionData.erase( itBlobs ); + itBlobs++; + } + RegionData.erase( RegionData.begin() + HighRegionNum + 1, RegionData.end() ); + + //free(RegionData); + free(SubsumedRegion); + delete Transition; + delete ThisRegion; + delete LastRegion; + + if( imatgePerimetreExtern ) cvReleaseImage(&imatgePerimetreExtern); + + return true; +} + + +int *NewSubsume(int *subsumed, int index_subsume) +{ + if( index_subsume == 0 ) + { + subsumed = (int*)malloc(sizeof(int)); + } + else + { + subsumed = (int*)realloc(subsumed,(index_subsume+1)*sizeof(int)); + } + subsumed[index_subsume]=0; + return subsumed; +} + +/** +Fusiona dos blobs i afegeix el blob les caracter�stiques del blob RegionData[HiNum] +al blob RegionData[LoNum]. Al final allibera el blob de RegionData[HiNum] +*/ +void Subsume(blob_vector &RegionData, + int HighRegionNum, + int* SubsumedRegion, + CBlob* blobHi, + CBlob* blobLo, + bool findmoments, + int HiNum, + int LoNum) +{ + // cout << "\nSubsuming " << HiNum << " into " << LoNum << endl; // for debugging + + int i; + + blobLo->minx=MIN(blobHi->minx,blobLo->minx); + blobLo->miny=MIN(blobHi->miny,blobLo->miny); + blobLo->maxx=MAX(blobHi->maxx,blobLo->maxx); + blobLo->maxy=MAX(blobHi->maxy,blobLo->maxy); + blobLo->area+=blobHi->area; + blobLo->perimeter+=blobHi->perimeter; + blobLo->externPerimeter += blobHi->externPerimeter; + blobLo->exterior = blobLo->exterior || blobHi->exterior; + blobLo->mean += blobHi->mean; + blobLo->stddev += blobHi->stddev; + + if( findmoments ) + { + blobLo->sumx+=blobHi->sumx; + blobLo->sumy+=blobHi->sumy; + blobLo->sumxx+=blobHi->sumxx; + blobLo->sumyy+=blobHi->sumyy; + blobLo->sumxy+=blobHi->sumxy; + } + // Make sure no region still has subsumed region as parent + blob_vector::iterator it = (RegionData.begin() + HiNum + 1); + + for(i = HiNum + 1; i <= HighRegionNum; i++, it++) + { + if((*it)->parent == (float) HiNum) { (*it)->parent = LoNum; } + } + + // Mark dead region number for future compression + SubsumedRegion[HiNum] = 1; + // marquem el blob com a lliure + blobHi->etiqueta=-1; + + // Atenci�!!!! abans d'eliminar els edges + // s'han de traspassar del blob HiNum al blob LoNum + blobHi->CopyEdges( *blobLo ); + blobHi->ClearEdges(); +} + +/** +- FUNCI�: GetExternPerimeter +- FUNCIONALITAT: Retorna el perimetre extern d'una run lenght +- PAR�METRES: +- start: columna d'inici del run +- end: columna final del run +- row: fila del run +- maskImage: m�scara pels pixels externs +- RESULTAT: +- quantitat de perimetre extern d'un run, suposant que �s un blob +d'una �nica fila d'al�ada +- RESTRICCIONS: +- AUTOR: +- DATA DE CREACI�: 2006/02/27 +- MODIFICACI�: Data. Autor. Descripci�. +*/ +double GetExternPerimeter( int start, int end, int row, int width, int height, IplImage *imatgePerimetreExtern ) +{ + double perimeter = 0.0f; + char *pPerimetre; + + + // comprovem les dimensions de la imatge + perimeter += ( start <= 0 ) + ( end >= width - 1 ); + if(row <= 1 ) perimeter+= start - end; + if(row >= height - 1 ) perimeter+= start - end; + + + // comprovem els pixels que toquen a la m�scara (si s'escau) + if( imatgePerimetreExtern != NULL ) + { + if( row <= 0 || row >= height ) return perimeter; + + if( start < 0 ) start = 1; + if( end >= width ) end = width - 2; + + pPerimetre = imatgePerimetreExtern->imageData + (row - 1) * imatgePerimetreExtern->widthStep + start; + for (int x = start - 1; x <= end; x++ ) + { + perimeter += *pPerimetre; + pPerimetre++; + } + } + + return perimeter; +} + +} diff --git a/package_bgs/jmo/BlobExtraction.h b/package_bgs/jmo/BlobExtraction.h new file mode 100644 index 0000000000000000000000000000000000000000..157ee58a5552594aa5d4ccc6e8e77ac322c7eaf2 --- /dev/null +++ b/package_bgs/jmo/BlobExtraction.h @@ -0,0 +1,76 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +//***********************************************************// +//* Blob analysis package 8 August 2003 *// +//* Version 1.0 *// +//* Input: IplImage* binary image *// +//* Output: attributes of each connected region *// +//* Author: Dave Grossman *// +//* Email: dgrossman@cdr.stanford.edu *// +//* Acknowledgement: the algorithm has been around > 20 yrs *// +//***********************************************************// + + +#if !defined(_CLASSE_BLOBEXTRACTION_INCLUDED) +#define _CLASSE_BLOBEXTRACTION_INCLUDED + +namespace Blob +{ + +//! Extreu els blobs d'una imatge +bool BlobAnalysis(IplImage* inputImage, uchar threshold, IplImage* maskImage, + bool borderColor, bool findmoments, blob_vector &RegionData ); + + +// FUNCIONS AUXILIARS + +//! Fusiona dos blobs +void Subsume(blob_vector &RegionData, int, int*, CBlob*, CBlob*, bool, int, int ); +//! Reallocata el vector auxiliar de blobs subsumats +int *NewSubsume(int *SubSumedRegion, int elems_inbuffer); +//! Retorna el perimetre extern d'una run lenght +double GetExternPerimeter( int start, int end, int row, int width, int height, IplImage *maskImage ); +} + +#endif //_CLASSE_BLOBEXTRACTION_INCLUDED + diff --git a/package_bgs/jmo/BlobLibraryConfiguration.h b/package_bgs/jmo/BlobLibraryConfiguration.h new file mode 100644 index 0000000000000000000000000000000000000000..48119f7c7c679a3a7d89a16104a2779e4296ed33 --- /dev/null +++ b/package_bgs/jmo/BlobLibraryConfiguration.h @@ -0,0 +1,62 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/************************************************************************ +BlobLibraryConfiguration.h + +FUNCIONALITAT: Configuraci� del comportament global de la llibreria +AUTOR: Inspecta S.L. +MODIFICACIONS (Modificaci�, Autor, Data): + +FUNCTIONALITY: Global configuration of the library +AUTHOR: Inspecta S.L. +MODIFICATIONS (Modification, Author, Date): + +**************************************************************************/ + +//! Indica si es volen fer servir les MatrixCV o no +//! Use/Not use the MatrixCV class +//#define MATRIXCV_ACTIU + +// Uses/not use the blob object factory +//#define BLOB_OBJECT_FACTORY + diff --git a/package_bgs/jmo/BlobResult.cpp b/package_bgs/jmo/BlobResult.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fbd312a6db31297e5ca8df233ab17db69fc96e5c --- /dev/null +++ b/package_bgs/jmo/BlobResult.cpp @@ -0,0 +1,920 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/************************************************************************ +BlobResult.cpp + +FUNCIONALITAT: Implementaci� de la classe CBlobResult +AUTOR: Inspecta S.L. +MODIFICACIONS (Modificaci�, Autor, Data): + +**************************************************************************/ + +#include <limits.h> +#include <stdio.h> +#include <functional> +#include <algorithm> +#include "BlobResult.h" +#include "BlobExtraction.h" +#ifdef _DEBUG +#include <afx.h> //suport per a CStrings +#include <afxwin.h> //suport per a AfxMessageBox +#endif + +/************************************************************************** +Constructors / Destructors +**************************************************************************/ + +namespace Blob +{ + +/** +- FUNCI�: CBlobResult +- FUNCIONALITAT: Constructor estandard. +- PAR�METRES: +- RESULTAT: +- Crea un CBlobResult sense cap blob +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 20-07-2004. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: CBlobResult +- FUNCTIONALITY: Standard constructor +- PARAMETERS: +- RESULT: +- creates an empty set of blobs +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +CBlobResult::CBlobResult() +{ + m_blobs = blob_vector(); +} + +/** +- FUNCI�: CBlobResult +- FUNCIONALITAT: Constructor a partir d'una imatge. Inicialitza la seq��ncia de blobs +amb els blobs resultants de l'an�lisi de blobs de la imatge. +- PAR�METRES: +- source: imatge d'on s'extreuran els blobs +- mask: m�scara a aplicar. Nom�s es calcularan els blobs on la m�scara sigui +diferent de 0. Els blobs que toquin a un pixel 0 de la m�scara seran +considerats exteriors. +- threshold: llindar que s'aplicar� a la imatge source abans de calcular els blobs +- findmoments: indica si s'han de calcular els moments de cada blob +- RESULTAT: +- objecte CBlobResult amb els blobs de la imatge source +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: CBlob +- FUNCTIONALITY: Constructor from an image. Fills an object with all the blobs in +the image +- PARAMETERS: +- source: image to extract the blobs from +- mask: optional mask to apply. The blobs will be extracted where the mask is +not 0. All the neighbouring blobs where the mask is 0 will be extern blobs +- threshold: threshold level to apply to the image before computing blobs +- findmoments: true to calculate the blob moments (slower) +- RESULT: +- object with all the blobs in the image. It throws an EXCEPCIO_CALCUL_BLOBS +if some error appears in the BlobAnalysis function +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +CBlobResult::CBlobResult(IplImage *source, IplImage *mask, int threshold, bool findmoments) +{ + bool success; + + try + { + // cridem la funci� amb el marc a true=1=blanc (aix� no unir� els blobs externs) + success = BlobAnalysis(source,(uchar)threshold,mask,true,findmoments, m_blobs ); + } + catch(...) + { + success = false; + } + + if( !success ) throw EXCEPCIO_CALCUL_BLOBS; +} + +/** +- FUNCI�: CBlobResult +- FUNCIONALITAT: Constructor de c�pia. Inicialitza la seq��ncia de blobs +amb els blobs del par�metre. +- PAR�METRES: +- source: objecte que es copiar� +- RESULTAT: +- objecte CBlobResult amb els blobs de l'objecte source +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: CBlobResult +- FUNCTIONALITY: Copy constructor +- PARAMETERS: +- source: object to copy +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +CBlobResult::CBlobResult( const CBlobResult &source ) +{ + m_blobs = blob_vector( source.GetNumBlobs() ); + + // creem el nou a partir del passat com a par�metre + m_blobs = blob_vector( source.GetNumBlobs() ); + // copiem els blobs de l'origen a l'actual + blob_vector::const_iterator pBlobsSrc = source.m_blobs.begin(); + blob_vector::iterator pBlobsDst = m_blobs.begin(); + + while( pBlobsSrc != source.m_blobs.end() ) + { + // no podem cridar a l'operador = ja que blob_vector �s un + // vector de CBlob*. Per tant, creem un blob nou a partir del + // blob original + *pBlobsDst = new CBlob(**pBlobsSrc); + pBlobsSrc++; + pBlobsDst++; + } +} + + + +/** +- FUNCI�: ~CBlobResult +- FUNCIONALITAT: Destructor estandard. +- PAR�METRES: +- RESULTAT: +- Allibera la mem�ria reservada de cadascun dels blobs de la classe +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: ~CBlobResult +- FUNCTIONALITY: Destructor +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +CBlobResult::~CBlobResult() +{ + ClearBlobs(); +} + +/************************************************************************** +Operadors / Operators +**************************************************************************/ + + +/** +- FUNCI�: operador = +- FUNCIONALITAT: Assigna un objecte source a l'actual +- PAR�METRES: +- source: objecte a assignar +- RESULTAT: +- Substitueix els blobs actuals per els de l'objecte source +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: Assigment operator +- FUNCTIONALITY: +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +CBlobResult& CBlobResult::operator=(const CBlobResult& source) +{ + // si ja s�n el mateix, no cal fer res + if (this != &source) + { + // alliberem el conjunt de blobs antic + for( int i = 0; i < GetNumBlobs(); i++ ) + { + delete m_blobs[i]; + } + m_blobs.clear(); + // creem el nou a partir del passat com a par�metre + m_blobs = blob_vector( source.GetNumBlobs() ); + // copiem els blobs de l'origen a l'actual + blob_vector::const_iterator pBlobsSrc = source.m_blobs.begin(); + blob_vector::iterator pBlobsDst = m_blobs.begin(); + + while( pBlobsSrc != source.m_blobs.end() ) + { + // no podem cridar a l'operador = ja que blob_vector �s un + // vector de CBlob*. Per tant, creem un blob nou a partir del + // blob original + *pBlobsDst = new CBlob(**pBlobsSrc); + pBlobsSrc++; + pBlobsDst++; + } + } + return *this; +} + + +/** +- FUNCI�: operador + +- FUNCIONALITAT: Concatena els blobs de dos CBlobResult +- PAR�METRES: +- source: d'on s'agafaran els blobs afegits a l'actual +- RESULTAT: +- retorna un nou CBlobResult amb els dos CBlobResult concatenats +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- NOTA: per la implementaci�, els blobs del par�metre es posen en ordre invers +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: + operator +- FUNCTIONALITY: Joins the blobs in source with the current ones +- PARAMETERS: +- source: object to copy the blobs +- RESULT: +- object with the actual blobs and the source blobs +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +CBlobResult CBlobResult::operator+( const CBlobResult& source ) +{ + //creem el resultat a partir dels blobs actuals + CBlobResult resultat( *this ); + + // reservem mem�ria per als nous blobs + resultat.m_blobs.resize( resultat.GetNumBlobs() + source.GetNumBlobs() ); + + // declarem els iterador per rec�rrer els blobs d'origen i desti + blob_vector::const_iterator pBlobsSrc = source.m_blobs.begin(); + blob_vector::iterator pBlobsDst = resultat.m_blobs.end(); + + // insertem els blobs de l'origen a l'actual + while( pBlobsSrc != source.m_blobs.end() ) + { + pBlobsDst--; + *pBlobsDst = new CBlob(**pBlobsSrc); + pBlobsSrc++; + } + + return resultat; +} + +/************************************************************************** +Operacions / Operations +**************************************************************************/ + +/** +- FUNCI�: AddBlob +- FUNCIONALITAT: Afegeix un blob al conjunt +- PAR�METRES: +- blob: blob a afegir +- RESULTAT: +- modifica el conjunt de blobs actual +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 2006/03/01 +- MODIFICACI�: Data. Autor. Descripci�. +*/ +void CBlobResult::AddBlob( CBlob *blob ) +{ + if( blob != NULL ) + m_blobs.push_back( new CBlob( blob ) ); +} + + +#ifdef MATRIXCV_ACTIU + +/** +- FUNCI�: GetResult +- FUNCIONALITAT: Calcula el resultat especificat sobre tots els blobs de la classe +- PAR�METRES: +- evaluador: Qualsevol objecte derivat de COperadorBlob +- RESULTAT: +- Retorna un array de double's amb el resultat per cada blob +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: GetResult +- FUNCTIONALITY: Computes the function evaluador on all the blobs of the class +and returns a vector with the result +- PARAMETERS: +- evaluador: function to apply to each blob (any object derived from the +COperadorBlob class ) +- RESULT: +- vector with all the results in the same order as the blobs +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double_vector CBlobResult::GetResult( funcio_calculBlob *evaluador ) const +{ + if( GetNumBlobs() <= 0 ) + { + return double_vector(); + } + + // definim el resultat + double_vector result = double_vector( GetNumBlobs() ); + // i iteradors sobre els blobs i el resultat + double_vector::iterator itResult = result.GetIterator(); + blob_vector::const_iterator itBlobs = m_blobs.begin(); + + // avaluem la funci� en tots els blobs + while( itBlobs != m_blobs.end() ) + { + *itResult = (*evaluador)(**itBlobs); + itBlobs++; + itResult++; + } + return result; +} +#endif + +/** +- FUNCI�: GetSTLResult +- FUNCIONALITAT: Calcula el resultat especificat sobre tots els blobs de la classe +- PAR�METRES: +- evaluador: Qualsevol objecte derivat de COperadorBlob +- RESULTAT: +- Retorna un array de double's STL amb el resultat per cada blob +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: GetResult +- FUNCTIONALITY: Computes the function evaluador on all the blobs of the class +and returns a vector with the result +- PARAMETERS: +- evaluador: function to apply to each blob (any object derived from the +COperadorBlob class ) +- RESULT: +- vector with all the results in the same order as the blobs +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double_stl_vector CBlobResult::GetSTLResult( funcio_calculBlob *evaluador ) const +{ + if( GetNumBlobs() <= 0 ) + { + return double_stl_vector(); + } + + // definim el resultat + double_stl_vector result = double_stl_vector( GetNumBlobs() ); + // i iteradors sobre els blobs i el resultat + double_stl_vector::iterator itResult = result.begin(); + blob_vector::const_iterator itBlobs = m_blobs.begin(); + + // avaluem la funci� en tots els blobs + while( itBlobs != m_blobs.end() ) + { + *itResult = (*evaluador)(**itBlobs); + itBlobs++; + itResult++; + } + return result; +} + +/** +- FUNCI�: GetNumber +- FUNCIONALITAT: Calcula el resultat especificat sobre un �nic blob de la classe +- PAR�METRES: +- evaluador: Qualsevol objecte derivat de COperadorBlob +- indexblob: n�mero de blob del que volem calcular el resultat. +- RESULTAT: +- Retorna un double amb el resultat +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: GetNumber +- FUNCTIONALITY: Computes the function evaluador on a blob of the class +- PARAMETERS: +- indexBlob: index of the blob to compute the function +- evaluador: function to apply to each blob (any object derived from the +COperadorBlob class ) +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobResult::GetNumber( int indexBlob, funcio_calculBlob *evaluador ) const +{ + if( indexBlob < 0 || indexBlob >= GetNumBlobs() ) + RaiseError( EXCEPTION_BLOB_OUT_OF_BOUNDS ); + return (*evaluador)( *m_blobs[indexBlob] ); +} + +/////////////////////////// FILTRAT DE BLOBS //////////////////////////////////// + +/** +- FUNCI�: Filter +- FUNCIONALITAT: Filtra els blobs de la classe i deixa el resultat amb nom�s +els blobs que han passat el filtre. +El filtrat es basa en especificar condicions sobre un resultat dels blobs +i seleccionar (o excloure) aquells blobs que no compleixen una determinada +condicio +- PAR�METRES: +- dst: variable per deixar els blobs filtrats +- filterAction: acci� de filtrat. Incloure els blobs trobats (B_INCLUDE), +o excloure els blobs trobats (B_EXCLUDE) +- evaluador: Funci� per evaluar els blobs (qualsevol objecte derivat de COperadorBlob +- Condition: tipus de condici� que ha de superar la mesura (FilterType) +sobre cada blob per a ser considerat. +B_EQUAL,B_NOT_EQUAL,B_GREATER,B_LESS,B_GREATER_OR_EQUAL, +B_LESS_OR_EQUAL,B_INSIDE,B_OUTSIDE +- LowLimit: valor num�ric per a la comparaci� (Condition) de la mesura (FilterType) +- HighLimit: valor num�ric per a la comparaci� (Condition) de la mesura (FilterType) +(nom�s t� sentit per a aquelles condicions que tenen dos valors +(B_INSIDE, per exemple). +- RESULTAT: +- Deixa els blobs resultants del filtrat a destination +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: Filter +- FUNCTIONALITY: Get some blobs from the class based on conditions on measures +of the blobs. +- PARAMETERS: +- dst: where to store the selected blobs +- filterAction: B_INCLUDE: include the blobs which pass the filter in the result +B_EXCLUDE: exclude the blobs which pass the filter in the result +- evaluador: Object to evaluate the blob +- Condition: How to decide if the result returned by evaluador on each blob +is included or not. It can be: +B_EQUAL,B_NOT_EQUAL,B_GREATER,B_LESS,B_GREATER_OR_EQUAL, +B_LESS_OR_EQUAL,B_INSIDE,B_OUTSIDE +- LowLimit: numerical value to evaluate the Condition on evaluador(blob) +- HighLimit: numerical value to evaluate the Condition on evaluador(blob). +Only useful for B_INSIDE and B_OUTSIDE +- RESULT: +- It returns on dst the blobs that accomplish (B_INCLUDE) or discards (B_EXCLUDE) +the Condition on the result returned by evaluador on each blob +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +void CBlobResult::Filter(CBlobResult &dst, + int filterAction, + funcio_calculBlob *evaluador, + int condition, + double lowLimit, double highLimit /*=0*/) + +{ + int i, numBlobs; + bool resultavaluacio; + double_stl_vector avaluacioBlobs; + double_stl_vector::iterator itavaluacioBlobs; + + if( GetNumBlobs() <= 0 ) return; + if( !evaluador ) return; + //avaluem els blobs amb la funci� pertinent + avaluacioBlobs = GetSTLResult(evaluador); + itavaluacioBlobs = avaluacioBlobs.begin(); + numBlobs = GetNumBlobs(); + switch(condition) + { + case B_EQUAL: + for(i=0;i<numBlobs;i++, itavaluacioBlobs++) + { + resultavaluacio= *itavaluacioBlobs == lowLimit; + if( ( resultavaluacio && filterAction == B_INCLUDE ) || + ( !resultavaluacio && filterAction == B_EXCLUDE )) + { + dst.m_blobs.push_back( new CBlob( GetBlob( i ) )); + } + } + break; + case B_NOT_EQUAL: + for(i=0;i<numBlobs;i++, itavaluacioBlobs++) + { + resultavaluacio = *itavaluacioBlobs != lowLimit; + if( ( resultavaluacio && filterAction == B_INCLUDE ) || + ( !resultavaluacio && filterAction == B_EXCLUDE )) + { + dst.m_blobs.push_back( new CBlob( GetBlob( i ) )); + } + } + break; + case B_GREATER: + for(i=0;i<numBlobs;i++, itavaluacioBlobs++) + { + resultavaluacio= *itavaluacioBlobs > lowLimit; + if( ( resultavaluacio && filterAction == B_INCLUDE ) || + ( !resultavaluacio && filterAction == B_EXCLUDE )) + { + dst.m_blobs.push_back( new CBlob( GetBlob( i ) )); + } + } + break; + case B_LESS: + for(i=0;i<numBlobs;i++, itavaluacioBlobs++) + { + resultavaluacio= *itavaluacioBlobs < lowLimit; + if( ( resultavaluacio && filterAction == B_INCLUDE ) || + ( !resultavaluacio && filterAction == B_EXCLUDE )) + { + dst.m_blobs.push_back( new CBlob( GetBlob( i ) )); + } + } + break; + case B_GREATER_OR_EQUAL: + for(i=0;i<numBlobs;i++, itavaluacioBlobs++) + { + resultavaluacio= *itavaluacioBlobs>= lowLimit; + if( ( resultavaluacio && filterAction == B_INCLUDE ) || + ( !resultavaluacio && filterAction == B_EXCLUDE )) + { + dst.m_blobs.push_back( new CBlob( GetBlob( i ) )); + } + } + break; + case B_LESS_OR_EQUAL: + for(i=0;i<numBlobs;i++, itavaluacioBlobs++) + { + resultavaluacio= *itavaluacioBlobs <= lowLimit; + if( ( resultavaluacio && filterAction == B_INCLUDE ) || + ( !resultavaluacio && filterAction == B_EXCLUDE )) + { + dst.m_blobs.push_back( new CBlob( GetBlob( i ) )); + } + } + break; + case B_INSIDE: + for(i=0;i<numBlobs;i++, itavaluacioBlobs++) + { + resultavaluacio=( *itavaluacioBlobs >= lowLimit) && ( *itavaluacioBlobs <= highLimit); + if( ( resultavaluacio && filterAction == B_INCLUDE ) || + ( !resultavaluacio && filterAction == B_EXCLUDE )) + { + dst.m_blobs.push_back( new CBlob( GetBlob( i ) )); + } + } + break; + case B_OUTSIDE: + for(i=0;i<numBlobs;i++, itavaluacioBlobs++) + { + resultavaluacio=( *itavaluacioBlobs < lowLimit) || ( *itavaluacioBlobs > highLimit); + if( ( resultavaluacio && filterAction == B_INCLUDE ) || + ( !resultavaluacio && filterAction == B_EXCLUDE )) + { + dst.m_blobs.push_back( new CBlob( GetBlob( i ) )); + } + } + break; + } + + + // en cas de voler filtrar un CBlobResult i deixar-ho en el mateix CBlobResult + // ( operacio inline ) + if( &dst == this ) + { + // esborrem els primers blobs ( que s�n els originals ) + // ja que els tindrem replicats al final si passen el filtre + blob_vector::iterator itBlobs = m_blobs.begin(); + for( int i = 0; i < numBlobs; i++ ) + { + delete *itBlobs; + itBlobs++; + } + m_blobs.erase( m_blobs.begin(), itBlobs ); + } +} + + +/** +- FUNCI�: GetBlob +- FUNCIONALITAT: Retorna un blob si aquest existeix (index != -1) +- PAR�METRES: +- indexblob: index del blob a retornar +- RESULTAT: +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/* +- FUNCTION: GetBlob +- FUNCTIONALITY: Gets the n-th blob (without ordering the blobs) +- PARAMETERS: +- indexblob: index in the blob array +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +CBlob CBlobResult::GetBlob(int indexblob) const +{ + if( indexblob < 0 || indexblob >= GetNumBlobs() ) + RaiseError( EXCEPTION_BLOB_OUT_OF_BOUNDS ); + + return *m_blobs[indexblob]; +} +CBlob *CBlobResult::GetBlob(int indexblob) +{ + if( indexblob < 0 || indexblob >= GetNumBlobs() ) + RaiseError( EXCEPTION_BLOB_OUT_OF_BOUNDS ); + + return m_blobs[indexblob]; +} + +/** +- FUNCI�: GetNthBlob +- FUNCIONALITAT: Retorna l'en�ssim blob segons un determinat criteri +- PAR�METRES: +- criteri: criteri per ordenar els blobs (objectes derivats de COperadorBlob) +- nBlob: index del blob a retornar +- dst: on es retorna el resultat +- RESULTAT: +- retorna el blob nBlob a dst ordenant els blobs de la classe segons el criteri +en ordre DESCENDENT. Per exemple, per obtenir el blob major: +GetNthBlob( CBlobGetArea(), 0, blobMajor ); +GetNthBlob( CBlobGetArea(), 1, blobMajor ); (segon blob m�s gran) +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/* +- FUNCTION: GetNthBlob +- FUNCTIONALITY: Gets the n-th blob ordering first the blobs with some criteria +- PARAMETERS: +- criteri: criteria to order the blob array +- nBlob: index of the returned blob in the ordered blob array +- dst: where to store the result +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +void CBlobResult::GetNthBlob( funcio_calculBlob *criteri, int nBlob, CBlob &dst ) const +{ + // verifiquem que no estem accedint fora el vector de blobs + if( nBlob < 0 || nBlob >= GetNumBlobs() ) + { + //RaiseError( EXCEPTION_BLOB_OUT_OF_BOUNDS ); + dst = CBlob(); + return; + } + + double_stl_vector avaluacioBlobs, avaluacioBlobsOrdenat; + double valorEnessim; + + //avaluem els blobs amb la funci� pertinent + avaluacioBlobs = GetSTLResult(criteri); + + avaluacioBlobsOrdenat = double_stl_vector( GetNumBlobs() ); + + // obtenim els nBlob primers resultats (en ordre descendent) + std::partial_sort_copy( avaluacioBlobs.begin(), + avaluacioBlobs.end(), + avaluacioBlobsOrdenat.begin(), + avaluacioBlobsOrdenat.end(), + std::greater<double>() ); + + valorEnessim = avaluacioBlobsOrdenat[nBlob]; + + // busquem el primer blob que t� el valor n-ssim + double_stl_vector::const_iterator itAvaluacio = avaluacioBlobs.begin(); + + bool trobatBlob = false; + int indexBlob = 0; + while( itAvaluacio != avaluacioBlobs.end() && !trobatBlob ) + { + if( *itAvaluacio == valorEnessim ) + { + trobatBlob = true; + dst = CBlob( GetBlob(indexBlob)); + } + itAvaluacio++; + indexBlob++; + } +} + +/** +- FUNCI�: ClearBlobs +- FUNCIONALITAT: Elimina tots els blobs de l'objecte +- PAR�METRES: +- RESULTAT: +- Allibera tota la mem�ria dels blobs +- RESTRICCIONS: +- AUTOR: Ricard Borr�s Navarra +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/* +- FUNCTION: ClearBlobs +- FUNCTIONALITY: Clears all the blobs from the object and releases all its memory +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +void CBlobResult::ClearBlobs() +{ + /*for( int i = 0; i < GetNumBlobs(); i++ ) + { + delete m_blobs[i]; + }*/ + blob_vector::iterator itBlobs = m_blobs.begin(); + while( itBlobs != m_blobs.end() ) + { + delete *itBlobs; + itBlobs++; + } + + m_blobs.clear(); +} + +/** +- FUNCI�: RaiseError +- FUNCIONALITAT: Funci� per a notificar errors al l'usuari (en debug) i llen�a +les excepcions +- PAR�METRES: +- errorCode: codi d'error +- RESULTAT: +- Ensenya un missatge a l'usuari (en debug) i llen�a una excepci� +- RESTRICCIONS: +- AUTOR: Ricard Borr�s Navarra +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/* +- FUNCTION: RaiseError +- FUNCTIONALITY: Error handling function +- PARAMETERS: +- errorCode: reason of the error +- RESULT: +- in _DEBUG version, shows a message box with the error. In release is silent. +In both cases throws an exception with the error. +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +void CBlobResult::RaiseError(const int errorCode) const +{ + // estem en mode debug? +#ifdef _DEBUG + CString msg, format = "Error en CBlobResult: %s"; + + switch (errorCode) + { + case EXCEPTION_BLOB_OUT_OF_BOUNDS: + msg.Format(format, "Intentant accedir a un blob no existent"); + break; + default: + msg.Format(format, "Codi d'error desconegut"); + break; + } + + AfxMessageBox(msg); + +#endif + throw errorCode; +} + + + +/************************************************************************** +Auxiliars / Auxiliary functions +**************************************************************************/ + + +/** +- FUNCI�: PrintBlobs +- FUNCIONALITAT: Escriu els par�metres (�rea, per�metre, exterior, mitjana) +de tots els blobs a un fitxer. +- PAR�METRES: +- nom_fitxer: path complet del fitxer amb el resultat +- RESULTAT: +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/* +- FUNCTION: PrintBlobs +- FUNCTIONALITY: Prints some blob features in an ASCII file +- PARAMETERS: +- nom_fitxer: full path + filename to generate +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +void CBlobResult::PrintBlobs( char *nom_fitxer ) const +{ + double_stl_vector area, /*perimetre,*/ exterior, mitjana, compacitat, longitud, + externPerimeter, perimetreConvex, perimetre; + int i; + FILE *fitxer_sortida; + + area = GetSTLResult( CBlobGetArea()); + perimetre = GetSTLResult( CBlobGetPerimeter()); + exterior = GetSTLResult( CBlobGetExterior()); + mitjana = GetSTLResult( CBlobGetMean()); + compacitat = GetSTLResult(CBlobGetCompactness()); + longitud = GetSTLResult( CBlobGetLength()); + externPerimeter = GetSTLResult( CBlobGetExternPerimeter()); + perimetreConvex = GetSTLResult( CBlobGetHullPerimeter()); + + fitxer_sortida = fopen( nom_fitxer, "w" ); + + for(i=0; i<GetNumBlobs(); i++) + { + fprintf( fitxer_sortida, "blob %d ->\t a=%7.0f\t p=%8.2f (%8.2f extern)\t pconvex=%8.2f\t ext=%.0f\t m=%7.2f\t c=%3.2f\t l=%8.2f\n", + i, area[i], perimetre[i], externPerimeter[i], perimetreConvex[i], exterior[i], mitjana[i], compacitat[i], longitud[i] ); + } + fclose( fitxer_sortida ); + +} + +} diff --git a/package_bgs/jmo/BlobResult.h b/package_bgs/jmo/BlobResult.h new file mode 100644 index 0000000000000000000000000000000000000000..ef1fd40af258a83ee7eaba4786e3b750f936b276 --- /dev/null +++ b/package_bgs/jmo/BlobResult.h @@ -0,0 +1,213 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- + * Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/************************************************************************ + BlobResult.h + +FUNCIONALITAT: Definici� de la classe CBlobResult +AUTOR: Inspecta S.L. +MODIFICACIONS (Modificaci�, Autor, Data): + +FUNCTIONALITY: Definition of the CBlobResult class +AUTHOR: Inspecta S.L. +MODIFICATIONS (Modification, Author, Date): + +**************************************************************************/ + + +#if !defined(_CLASSE_BLOBRESULT_INCLUDED) +#define _CLASSE_BLOBRESULT_INCLUDED + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "BlobLibraryConfiguration.h" +#include <math.h> +#include "cxcore.h" + +#ifdef MATRIXCV_ACTIU + #include "matrixCV.h" +#else + // llibreria STL + #include "vector" + //! Vector de doubles + typedef std::vector<double> double_stl_vector; +#endif + +#include <vector> // vectors de la STL +#include <functional> +#include "blob.h" + +/************************************************************************** + Filtres / Filters +**************************************************************************/ + +//! accions que es poden fer amb els filtres +//! Actions performed by a filter (include or exclude blobs) +#define B_INCLUDE 1L +#define B_EXCLUDE 2L + +//! condicions sobre els filtres +//! Conditions to apply the filters +#define B_EQUAL 3L +#define B_NOT_EQUAL 4L +#define B_GREATER 5L +#define B_LESS 6L +#define B_GREATER_OR_EQUAL 7L +#define B_LESS_OR_EQUAL 8L +#define B_INSIDE 9L +#define B_OUTSIDE 10L + + +/************************************************************************** + Excepcions / Exceptions +**************************************************************************/ + +//! Excepcions llen�ades per les funcions: +#define EXCEPTION_BLOB_OUT_OF_BOUNDS 1000 +#define EXCEPCIO_CALCUL_BLOBS 1001 + +namespace Blob +{ + +//! definici� de que es un vector de blobs +typedef std::vector<CBlob*> blob_vector; + +/** + Classe que cont� un conjunt de blobs i permet extreure'n propietats + o filtrar-los segons determinats criteris. + Class to calculate the blobs of an image and calculate some properties + on them. Also, the class provides functions to filter the blobs using + some criteria. +*/ +class CBlobResult +{ +public: + + //! constructor estandard, crea un conjunt buit de blobs + //! Standard constructor, it creates an empty set of blobs + CBlobResult(); + //! constructor a partir d'una imatge + //! Image constructor, it creates an object with the blobs of the image + CBlobResult(IplImage *source, IplImage *mask, int threshold, bool findmoments); + //! constructor de c�pia + //! Copy constructor + CBlobResult( const CBlobResult &source ); + //! Destructor + virtual ~CBlobResult(); + + //! operador = per a fer assignacions entre CBlobResult + //! Assigment operator + CBlobResult& operator=(const CBlobResult& source); + //! operador + per concatenar dos CBlobResult + //! Addition operator to concatenate two sets of blobs + CBlobResult operator+( const CBlobResult& source ); + + //! Afegeix un blob al conjunt + //! Adds a blob to the set of blobs + void AddBlob( CBlob *blob ); + +#ifdef MATRIXCV_ACTIU + //! Calcula un valor sobre tots els blobs de la classe retornant una MatrixCV + //! Computes some property on all the blobs of the class + double_vector GetResult( funcio_calculBlob *evaluador ) const; +#endif + //! Calcula un valor sobre tots els blobs de la classe retornant un std::vector<double> + //! Computes some property on all the blobs of the class + double_stl_vector GetSTLResult( funcio_calculBlob *evaluador ) const; + + //! Calcula un valor sobre un blob de la classe + //! Computes some property on one blob of the class + double GetNumber( int indexblob, funcio_calculBlob *evaluador ) const; + + //! Retorna aquells blobs que compleixen les condicions del filtre en el destination + //! Filters the blobs of the class using some property + void Filter(CBlobResult &dst, + int filterAction, funcio_calculBlob *evaluador, + int condition, double lowLimit, double highLimit = 0 ); + + //! Retorna l'en�ssim blob segons un determinat criteri + //! Sorts the blobs of the class acording to some criteria and returns the n-th blob + void GetNthBlob( funcio_calculBlob *criteri, int nBlob, CBlob &dst ) const; + + //! Retorna el blob en�ssim + //! Gets the n-th blob of the class ( without sorting ) + CBlob GetBlob(int indexblob) const; + CBlob *GetBlob(int indexblob); + + //! Elimina tots els blobs de l'objecte + //! Clears all the blobs of the class + void ClearBlobs(); + + //! Escriu els blobs a un fitxer + //! Prints some features of all the blobs in a file + void PrintBlobs( char *nom_fitxer ) const; + + +//Metodes GET/SET + + //! Retorna el total de blobs + //! Gets the total number of blobs + int GetNumBlobs() const + { + return(m_blobs.size()); + } + + +private: + + //! Funci� per gestionar els errors + //! Function to manage the errors + void RaiseError(const int errorCode) const; + +protected: + + //! Vector amb els blobs + //! Vector with all the blobs + blob_vector m_blobs; +}; + +} + +#endif // !defined(_CLASSE_BLOBRESULT_INCLUDED) + diff --git a/package_bgs/jmo/CMultiLayerBGS.cpp b/package_bgs/jmo/CMultiLayerBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..134a24cf4963393c1dbe3b25910fdcfd591a22dc --- /dev/null +++ b/package_bgs/jmo/CMultiLayerBGS.cpp @@ -0,0 +1,2128 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// BackgroundSubtraction.cpp: implementation of the CMultiLayerBGS class. +// +////////////////////////////////////////////////////////////////////// + +#include "CMultiLayerBGS.h" + +#include <ctime> // clock +#include <cstdlib> // C standard library +#include <cstdio> // C I/O (for sscanf) +#include <cstring> // string manipulation +#include <fstream> // file I/O +#include <cmath> // math includes +#include <iostream> // I/O streams + +using namespace Blob; + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CMultiLayerBGS::CMultiLayerBGS() { + m_nMaxLBPModeNum = MAX_LBP_MODE_NUM; + + m_fModeUpdatingLearnRate = MODE_UPDATING_LEARN_RATE; + m_f1_ModeUpdatingLearnRate = 1.0f - m_fModeUpdatingLearnRate; + + m_fWeightUpdatingLearnRate = WEIGHT_UPDATING_LEARN_RATE; + m_f1_WeightUpdatingLearnRate = 1.0f - m_fWeightUpdatingLearnRate; + + m_fRobustColorOffset = ROBUST_COLOR_OFFSET; + + m_fLowInitialModeWeight = LOW_INITIAL_MODE_WEIGHT; + + m_fPatternColorDistBgThreshold = PATTERN_COLOR_DIST_BACKGROUND_THRESHOLD; + m_fPatternColorDistBgUpdatedThreshold = PATTERN_COLOR_DIST_BACKGROUND_THRESHOLD; + + m_fBackgroundModelPercent = BACKGROUND_MODEL_PERCENT; + + m_nPatternDistSmoothNeigHalfSize = PATTERN_DIST_SMOOTH_NEIG_HALF_SIZE; + m_fPatternDistConvGaussianSigma = PATTERN_DIST_CONV_GAUSSIAN_SIGMA; + + m_fRobustShadowRate = ROBUST_SHADOW_RATE; + m_fRobustHighlightRate = ROBUST_HIGHLIGHT_RATE; + + m_nCurImgFrameIdx = 0; + + m_pBkMaskImg = NULL; + + m_bUsedColorLBP = false; + m_bUsedGradImage = false; + + m_fMinLBPBinaryProb = 0.1f; + m_f1_MinLBPBinaryProb = 1.0f - m_fMinLBPBinaryProb; + + m_pOrgImg = m_pFgImg = m_pBgImg = m_pFgMaskImg = m_pBgDistImg = m_pEdgeImg = NULL; + m_ppOrgLBPImgs = NULL; + + m_disableLearning = false; + m_fSigmaS = 3.0f; + m_fSigmaR = 0.1f; + + m_fTextureWeight = 0.5f; + m_fColorWeight = 1.0f - m_fTextureWeight; + + m_fWeightUpdatingConstant = 5.0f; + + m_fReliableBackgroundModeWeight = 0.9f; + + //m_fMinBgLayerWeight = m_fLowInitialModeWeight/50.0f; + m_fMinBgLayerWeight = 0.0001f; + //m_fMinBgLayerWeight = 0.88f; + + m_fMinNoisedAngle = 3.0f / 180.0f * PI; + m_fMinNoisedAngleSine = sinf(m_fMinNoisedAngle); + + m_fFrameDuration = 1.0f / 25.0f; /* 25 frames per second */ + + m_fModeUpdatingLearnRatePerSecond = 0.2f; + m_fWeightUpdatingLearnRatePerSecond = 0.2f; + + m_pROI = NULL; +} + +CMultiLayerBGS::~CMultiLayerBGS() { + int img_length = m_cvImgSize.height * m_cvImgSize.width; + PixelLBPStruct* PLBP = m_pPixelLBPs; + for (int yx = 0; yx < img_length; yx++) { + delete (*PLBP).cur_intensity; + delete (*PLBP).cur_pattern; + delete (*PLBP).lbp_idxes; + for (int a = 0; a < m_nMaxLBPModeNum; a++) { + delete (*PLBP).LBPs[a].bg_intensity; + delete (*PLBP).LBPs[a].max_intensity; + delete (*PLBP).LBPs[a].min_intensity; + delete (*PLBP).LBPs[a].bg_pattern; + } + delete (*PLBP).LBPs; + PLBP++; + } + delete m_pPixelLBPs; + + /* release memories */ + if (m_pFgImg != NULL) + cvReleaseImage(&m_pFgImg); + if (m_pBgImg != NULL) + cvReleaseImage(&m_pBgImg); + if (m_pBgDistImg != NULL) + cvReleaseImage(&m_pBgDistImg); + if (m_ppOrgLBPImgs != NULL) { + int a; + for (a = 0; a < m_nLBPImgNum; a++) + cvReleaseImage(&m_ppOrgLBPImgs[a]); + delete [] m_ppOrgLBPImgs; + } + if (m_pEdgeImg) + cvReleaseImage(&m_pEdgeImg); +} + +void CMultiLayerBGS::ResetAllParameters() { + m_f1_ModeUpdatingLearnRate = 1.0f - m_fModeUpdatingLearnRate; + m_f1_WeightUpdatingLearnRate = 1.0f - m_fWeightUpdatingLearnRate; + m_f1_MinLBPBinaryProb = 1.0f - m_fMinLBPBinaryProb; + + m_fColorWeight = 1.0f - m_fTextureWeight; + + m_fMinNoisedAngleSine = sinf(m_fMinNoisedAngle); + + //m_fMinBgLayerWeight = m_fLowInitialModeWeight/50.0f; + m_fMinBgLayerWeight = 0.0001f; + + m_cLBP.m_fRobustWhiteNoise = m_fRobustColorOffset; +} + +void CMultiLayerBGS::MergeImages(int num, ...) { + if (num < 1 || num > 9) { + printf("Error: the number %d of merging images.\n", num); + exit(0); + } + + int nCols = 0, nRows = 0; + switch (num) { + case 1: nCols = nRows = 1; + break; + case 2: nCols = 1; + nRows = 2; + break; + case 3: + case 4: nCols = 2; + nRows = 2; + break; + case 5: + case 6: nCols = 3; + nRows = 2; + break; + case 7: + case 8: + case 9: nCols = 3; + nRows = 3; + break; + } + + int a, b; + + IplImage** ppIplImg = new IplImage*[num + 1]; + + va_list arg_ptr; + va_start(arg_ptr, num); + for (a = 0; a < num + 1; a++) + ppIplImg[a] = va_arg(arg_ptr, IplImage*); + va_end(arg_ptr); + + CvRect imgROIRect; + CvSize imgSize = cvGetSize(ppIplImg[0]); + if (ppIplImg[num] == NULL) { // for the output video + ppIplImg[num] = cvCreateImage(cvSize(imgSize.width*nCols, imgSize.height * nRows), IPL_DEPTH_8U, ppIplImg[0]->nChannels); + } + + int img_idx = 0; + for (a = 0; a < nRows; a++) + for (b = 0; b < nCols; b++) { + if (img_idx >= num) + break; + + imgROIRect = cvRect(b * imgSize.width, a * imgSize.height, imgSize.width, imgSize.height); + + cvSetImageROI(ppIplImg[num], imgROIRect); + cvCopyImage(ppIplImg[img_idx++], ppIplImg[num]); + cvResetImageROI(ppIplImg[num]); + } + + delete [] ppIplImg; +} + +void CMultiLayerBGS::Update_MAX_MIN_Intensity(unsigned char *cur_intensity, float *max_intensity, float *min_intensity) { + int a; + float curI; + for (a = 0; a < m_nChannel; a++) { + curI = (float) cur_intensity[a]; + + min_intensity[a] = MIN(curI, min_intensity[a]); + max_intensity[a] = MAX(curI, max_intensity[a]); + } +} + +void CMultiLayerBGS::UpdateBgPixelColor(unsigned char *cur_intensity, float* bg_intensity) { + int a; + for (a = 0; a < m_nChannel; a++) + bg_intensity[a] = m_f1_ModeUpdatingLearnRate * bg_intensity[a] + m_fModeUpdatingLearnRate * (float) cur_intensity[a]; +} + +void CMultiLayerBGS::UpdateBgPixelPattern(float *cur_pattern, float *bg_pattern) { + int a; + for (a = 0; a < m_nLBPLength; a++) + bg_pattern[a] = m_f1_ModeUpdatingLearnRate * bg_pattern[a] + m_fModeUpdatingLearnRate * cur_pattern[a]; +} + +/* sort everything inbetween `low' <-> `high' */ +void CMultiLayerBGS::QuickSort(float *pData, unsigned short *pIdxes, long low, long high, bool bAscent) { + long i = low; + long j = high; + float y = 0; + int idx = 0; + + /* compare value */ + float z = pData[(low + high) / 2]; + + /* partition */ + do { + if (bAscent) { + /* find member above ... */ + while (pData[i] < z) i++; + + /* find element below ... */ + while (pData[j] > z) j--; + } else { + /* find member below ... */ + while (pData[i] > z) i++; + + /* find element above ... */ + while (pData[j] < z) j--; + } + + if (i <= j) { + /* swap two elements */ + y = pData[i]; + pData[i] = pData[j]; + pData[j] = y; + + idx = pIdxes[i]; + pIdxes[i] = pIdxes[j]; + pIdxes[j] = idx; + + i++; + j--; + } + } while (i <= j); + + /* recurse */ + if (low < j) + QuickSort(pData, pIdxes, low, j, bAscent); + + if (i < high) + QuickSort(pData, pIdxes, i, high, bAscent); +} + +float CMultiLayerBGS::DistLBP(LBPStruct *LBP1, LBPStruct *LBP2) { + int a; + + float pattern_dist = 0; + for (a = 0; a < m_nLBPLength; a++) { + pattern_dist = fabsf(LBP1->bg_pattern[a] - LBP1->bg_pattern[a]); + } + pattern_dist /= (float) m_nLBPLength; + + float color_dist = 0; + for (a = 0; a < m_nChannel; a++) { + color_dist += fabsf((float) LBP1->bg_intensity[a]-(float) LBP2->bg_intensity[a]); + } + color_dist /= 3.0f * 125.0f; + + //return MAX(pattern_dist, color_dist); + return color_dist; +} + +void CMultiLayerBGS::SetNewImage(IplImage *new_img, CvRect *roi) { + m_pOrgImg = new_img; + m_pROI = roi; + if (roi && (roi->width <= 0 || roi->height <= 0)) + return; + + if (roi) { + cvSetImageROI(m_pOrgImg, *roi); + for (int a = 0; a < m_nLBPImgNum; a++) + cvSetImageROI(m_ppOrgLBPImgs[a], *roi); + } + + switch (m_nLBPImgNum) { + case 1: + cvCvtColor(m_pOrgImg, m_ppOrgLBPImgs[0], CV_BGR2GRAY); + break; + case 2: + cvCvtColor(m_pOrgImg, m_ppOrgLBPImgs[0], CV_BGR2GRAY); + ComputeGradientImage(m_ppOrgLBPImgs[0], m_ppOrgLBPImgs[1], false); + break; + case 3: + cvSplit(m_pOrgImg, m_ppOrgLBPImgs[0], m_ppOrgLBPImgs[1], m_ppOrgLBPImgs[2], NULL); + break; + case 4: + cvSplit(m_pOrgImg, m_ppOrgLBPImgs[0], m_ppOrgLBPImgs[1], m_ppOrgLBPImgs[2], NULL); + ComputeGradientImage(m_ppOrgLBPImgs[0], m_ppOrgLBPImgs[3], false); + break; + } + + if (roi) { + cvResetImageROI(m_pOrgImg); + for (int a = 0; a < m_nLBPImgNum; a++) + cvResetImageROI(m_ppOrgLBPImgs[a]); + } + m_cLBP.SetNewImages(m_ppOrgLBPImgs); + + m_nCurImgFrameIdx++; +} + +void CMultiLayerBGS::SetBkMaskImage(IplImage *mask_img) { + if (m_pBkMaskImg == NULL) { + m_pBkMaskImg = cvCreateImage(cvGetSize(mask_img), mask_img->depth, mask_img->nChannels); + } + cvCopyImage(mask_img, m_pBkMaskImg); +} + +void CMultiLayerBGS::BackgroundSubtractionProcess() { + CvRect *roi = m_pROI; + + if (roi && (roi->width <= 0 || roi->height <= 0)) + return; + LBPStruct* LBPs; + unsigned int bg_num; + float* cur_pattern; + unsigned char* cur_intensity; + int a, b; + unsigned int lbp_num; + unsigned short* lbp_idxes; + unsigned short cur_lbp_idx; + bool bBackgroundUpdating; + + PixelLBPStruct *PLBP = m_pPixelLBPs; + + bool bFirstFrame = (PLBP[0].num == 0); + float best_match_bg_dist, bg_pattern_dist, bg_color_dist, bg_pattern_color_dist; + + // compute the local binary pattern + if (m_fTextureWeight > 0) + m_cLBP.ComputeLBP(PLBP, roi); + + LBPStruct* curLBP; + + int data_length; + + if (roi) + data_length = roi->width * roi->height; + else + data_length = m_cvImgSize.width * m_cvImgSize.height; + + int best_match_idx; + + COpencvDataConversion<uchar, uchar> ODC1; + if (roi) { + cvSetImageROI(m_pBkMaskImg, *roi); + cvSetImageROI(m_pOrgImg, *roi); + } + uchar *_mask = ODC1.GetImageData(m_pBkMaskImg); + uchar *_org_intensity = ODC1.GetImageData(m_pOrgImg); + + if (roi) { + cvResetImageROI(m_pBkMaskImg); + cvResetImageROI(m_pOrgImg); + } + + COpencvDataConversion<float, float> ODC2; + float *_bg_dist = new float[data_length]; + + uchar *mask = _mask; + uchar *org_intensity = _org_intensity; + float *bg_dist = _bg_dist; + + bool removed_modes[10]; + + // scanning the point via first x-axis and then y-axis + int x, y; + + for (y = 0; y < (roi ? roi->height : m_cvImgSize.height); y++) { + if (roi) + PLBP = m_pPixelLBPs + (roi->y + y) * m_cvImgSize.width + roi->x; + else + PLBP = m_pPixelLBPs + y * m_cvImgSize.width; + + for (x = 0; x < (roi ? roi->width : m_cvImgSize.width); x++) { + // check whether the current pixel is the pixel to be modeled + if (*mask++ == 0) { + PLBP++; + *bg_dist++ = 0.0f; //m_fPatternColorDistBgThreshold*1.01f; + org_intensity += m_nChannel; + continue; + } + + // removing the background layers + if (!m_disableLearning) { + RemoveBackgroundLayers(PLBP); + } + + // check whether the current image is the first image + bFirstFrame = ((*PLBP).num == 0); + + // get lbp information + lbp_num = (*PLBP).num; + LBPs = (*PLBP).LBPs; + lbp_idxes = (*PLBP).lbp_idxes; + + (*PLBP).cur_bg_layer_no = 0; + + // set the current pixel's intensity + cur_intensity = (*PLBP).cur_intensity; + for (a = 0; a < m_nChannel; a++) + cur_intensity[a] = *org_intensity++; + + // get the current lbp pattern + cur_pattern = (*PLBP).cur_pattern; + + // first check whether the pixel is background or foreground and then update the background pattern model + if (lbp_num == 0) { // empty pattern list + curLBP = (&(LBPs[0])); + for (a = 0; a < m_nLBPLength; a++) { + curLBP->bg_pattern[a] = (float) cur_pattern[a]; + } + + curLBP->bg_layer_num = 0; + curLBP->weight = m_fLowInitialModeWeight; + curLBP->max_weight = m_fLowInitialModeWeight; + + curLBP->first_time = m_nCurImgFrameIdx; + curLBP->last_time = m_nCurImgFrameIdx; + curLBP->freq = 1; + + (*PLBP).matched_mode_first_time = (float) m_nCurImgFrameIdx; + + for (a = 0; a < m_nChannel; a++) { + curLBP->bg_intensity[a] = (float) cur_intensity[a]; + curLBP->min_intensity[a] = (float) cur_intensity[a]; + curLBP->max_intensity[a] = (float) cur_intensity[a]; + } + + lbp_idxes[0] = 0; + + lbp_num++; + (*PLBP).num = 1; + (*PLBP).bg_num = 1; + + PLBP++; + *bg_dist++ = 0.0f; + continue; + } else { // not empty pattern list + /* + // remove the background layers + // end of removing the background layer + */ + + best_match_idx = -1; + best_match_bg_dist = 999.0f; + + // find the best match + for (a = 0; a < (int) lbp_num; a++) { + // get the current index for lbp pattern + cur_lbp_idx = lbp_idxes[a]; + + // get the current LBP pointer + curLBP = &(LBPs[cur_lbp_idx]); + + // compute the background probability based on lbp pattern + bg_pattern_dist = 0.0f; + if (m_fTextureWeight > 0) + bg_pattern_dist = CalPatternBgDist(cur_pattern, curLBP->bg_pattern); + + // compute the color invariant probability based on RGB color + bg_color_dist = 0.0f; + if (m_fColorWeight > 0) + bg_color_dist = CalColorBgDist(cur_intensity, curLBP->bg_intensity, curLBP->max_intensity, curLBP->min_intensity); + + // compute the joint background probability + //bg_pattern_color_dist = sqrtf(bg_color_dist*bg_pattern_dist); + + //UpdatePatternColorDistWeights(cur_pattern, curLBP->bg_pattern); + + bg_pattern_color_dist = m_fColorWeight * bg_color_dist + m_fTextureWeight*bg_pattern_dist; + //bg_pattern_color_dist = MAX(bg_color_dist, bg_pattern_dist); + + //bg_pattern_color_dist = 1.0f - (1.0f-bg_color_dist)*(1.0-bg_pattern_dist); + //bg_pattern_color_dist = bg_pattern_dist; + + //bg_pattern_color_dist = bg_color_dist; + + if (bg_pattern_color_dist < best_match_bg_dist) { + best_match_bg_dist = bg_pattern_color_dist; + best_match_idx = a; + } + } + + bg_num = (*PLBP).bg_num; + + // check + bBackgroundUpdating = ((best_match_bg_dist < m_fPatternColorDistBgUpdatedThreshold)); + + // reset the weight of the mode + if (best_match_idx >= (int) bg_num && LBPs[lbp_idxes[best_match_idx]].max_weight < m_fReliableBackgroundModeWeight) // found not in the background models + best_match_bg_dist = MAX(best_match_bg_dist, m_fPatternColorDistBgThreshold * 2.5f); + + *bg_dist = best_match_bg_dist; + } + if (m_disableLearning) { + // no creation or update when learning is disabled + } else if (!bBackgroundUpdating) { // no match + + for (a = 0; a < (int) lbp_num; a++) { // decrease the weights + curLBP = &(LBPs[lbp_idxes[a]]); + curLBP->weight *= (1.0f - m_fWeightUpdatingLearnRate / (1.0f + m_fWeightUpdatingConstant * curLBP->max_weight)); + } + + if ((int) lbp_num < m_nMaxLBPModeNum) { // add a new pattern + // find the pattern index for addition + int add_lbp_idx = 0; + bool bFound; + for (a = 0; a < m_nMaxLBPModeNum; a++) { + bFound = true; + for (b = 0; b < (int) lbp_num; b++) + bFound &= (a != lbp_idxes[b]); + if (bFound) { + add_lbp_idx = a; + break; + } + } + curLBP = &(LBPs[add_lbp_idx]); + + curLBP->first_time = m_nCurImgFrameIdx; + curLBP->last_time = m_nCurImgFrameIdx; + curLBP->freq = 1; + curLBP->layer_time = -1; + + (*PLBP).matched_mode_first_time = (float) m_nCurImgFrameIdx; + + for (a = 0; a < m_nLBPLength; a++) { + curLBP->bg_pattern[a] = (float) cur_pattern[a]; + } + + curLBP->bg_layer_num = 0; + curLBP->weight = m_fLowInitialModeWeight; + curLBP->max_weight = m_fLowInitialModeWeight; + + for (a = 0; a < m_nChannel; a++) { + curLBP->bg_intensity[a] = (float) cur_intensity[a]; + curLBP->min_intensity[a] = (float) cur_intensity[a]; + curLBP->max_intensity[a] = (float) cur_intensity[a]; + } + + lbp_idxes[lbp_num] = add_lbp_idx; + + lbp_num++; + (*PLBP).num = lbp_num; + } else { // replacing the pattern with the minimal weight + // find the replaced pattern index + /* + int rep_pattern_idx = -1; + for ( a = m_nLBPLength-1 ; a >= 0 ; a-- ) { + if ( LBPs[lbp_idxes[a]].bg_layer_num == 0 ) + rep_pattern_idx = lbp_idxes[a]; + } + if ( rep_pattern_idx < 0 ) { + rep_pattern_idx = lbp_idxes[m_nMaxLBPModeNum-1]; + for ( a = 0 ; a < m_nLBPLength ; a++ ) { + if ( LBPs[lbp_idxes[a]].bg_layer_num > LBPs[rep_pattern_idx].bg_layer_num ) + LBPs[lbp_idxes[a]].bg_layer_num--; + } + } + */ + int rep_pattern_idx = lbp_idxes[m_nMaxLBPModeNum - 1]; + + curLBP = &(LBPs[rep_pattern_idx]); + + curLBP->first_time = m_nCurImgFrameIdx; + curLBP->last_time = m_nCurImgFrameIdx; + curLBP->freq = 1; + curLBP->layer_time = -1; + + (*PLBP).matched_mode_first_time = (float) m_nCurImgFrameIdx; + + for (a = 0; a < m_nLBPLength; a++) { + curLBP->bg_pattern[a] = (float) cur_pattern[a]; + } + + curLBP->bg_layer_num = 0; + curLBP->weight = m_fLowInitialModeWeight; + curLBP->max_weight = m_fLowInitialModeWeight; + + for (a = 0; a < m_nChannel; a++) { + curLBP->bg_intensity[a] = (float) cur_intensity[a]; + curLBP->min_intensity[a] = (float) cur_intensity[a]; + curLBP->max_intensity[a] = (float) cur_intensity[a]; + } + } + } else { // find match + // updating the background pattern model + cur_lbp_idx = lbp_idxes[best_match_idx]; + curLBP = &(LBPs[cur_lbp_idx]); + + curLBP->first_time = MAX(MIN(curLBP->first_time, m_nCurImgFrameIdx), 0); + (*PLBP).matched_mode_first_time = curLBP->first_time; + + curLBP->last_time = m_nCurImgFrameIdx; + curLBP->freq++; + + if (m_fColorWeight > 0) { + // update the color information + UpdateBgPixelColor(cur_intensity, curLBP->bg_intensity); + // update the MAX and MIN color intensity + Update_MAX_MIN_Intensity(cur_intensity, curLBP->max_intensity, curLBP->min_intensity); + } + + // update the texture information + if (m_fTextureWeight > 0) + UpdateBgPixelPattern(cur_pattern, curLBP->bg_pattern); + + + // increase the weight of the best matched mode + float increasing_weight_factor = m_fWeightUpdatingLearnRate * (1.0f + m_fWeightUpdatingConstant * curLBP->max_weight); + curLBP->weight = (1.0f - increasing_weight_factor) * curLBP->weight + increasing_weight_factor; //*expf(-best_match_dist/m_fPatternColorDistBgThreshold); + + // update the maximal weight for the best matched mode + curLBP->max_weight = MAX(curLBP->weight, curLBP->max_weight); + + // calculate the number of background layer + if (curLBP->bg_layer_num > 0) { + bool removed_bg_layers = false; + if (curLBP->weight > curLBP->max_weight * 0.2f) { + for (a = 0; a < (int) lbp_num; a++) { + removed_modes[a] = false; + if (LBPs[lbp_idxes[a]].bg_layer_num > curLBP->bg_layer_num && + LBPs[lbp_idxes[a]].weight < LBPs[lbp_idxes[a]].max_weight * 0.9f) { /* remove layers */ + //LBPs[lbp_idxes[a]].bg_layer_num = 0; + removed_modes[a] = true; + removed_bg_layers = true; + } + } + } + + if (removed_bg_layers) { + RemoveBackgroundLayers(PLBP, removed_modes); + lbp_num = (*PLBP).num; + } + } else if (curLBP->max_weight > m_fReliableBackgroundModeWeight && curLBP->bg_layer_num == 0) { + int max_bg_layer_num = LBPs[lbp_idxes[0]].bg_layer_num; + for (a = 1; a < (int) lbp_num; a++) + max_bg_layer_num = MAX(max_bg_layer_num, LBPs[lbp_idxes[a]].bg_layer_num); + curLBP->bg_layer_num = max_bg_layer_num + 1; + curLBP->layer_time = m_nCurImgFrameIdx; + } + + (*PLBP).cur_bg_layer_no = curLBP->bg_layer_num; + + // decrease the weights of non-best matched modes + for (a = 0; a < (int) lbp_num; a++) { + if (a != best_match_idx) { + curLBP = &(LBPs[lbp_idxes[a]]); + curLBP->weight *= (1.0f - m_fWeightUpdatingLearnRate / (1.0f + m_fWeightUpdatingConstant * curLBP->max_weight)); + } + } + } + + // sort the list of modes based on the weights of modes + if ((int) lbp_num > 1 && !m_disableLearning) { + float weights[100], tot_weights = 0; + for (a = 0; a < (int) lbp_num; a++) { + weights[a] = LBPs[lbp_idxes[a]].weight; + tot_weights += weights[a]; + } + + // sort weights in the descent order + QuickSort(weights, lbp_idxes, 0, (int) lbp_num - 1, false); + + // calculate the first potential background modes number, bg_num + float threshold_weight = m_fBackgroundModelPercent*tot_weights; + tot_weights = 0; + for (a = 0; a < (int) lbp_num; a++) { + tot_weights += LBPs[lbp_idxes[a]].weight; + if (tot_weights > threshold_weight) { + bg_num = a + 1; + break; + } + } + (*PLBP).bg_num = bg_num; + } + + PLBP++; + bg_dist++; + } + } + + if (bFirstFrame) { // check whether it is the first frame for background modeling + if (m_pFgMaskImg) + cvSetZero(m_pFgMaskImg); + cvSetZero(m_pBgDistImg); + } else { + // set the image data + if (roi) { + cvSetZero(m_pBgDistImg); + cvSetImageROI(m_pBgDistImg, *roi); + } + ODC2.SetImageData(m_pBgDistImg, _bg_dist); + + // do gaussian smooth + if (m_nPatternDistSmoothNeigHalfSize >= 0) + cvSmooth(m_pBgDistImg, m_pBgDistImg, CV_GAUSSIAN, (2 * m_nPatternDistSmoothNeigHalfSize + 1), (2 * m_nPatternDistSmoothNeigHalfSize + 1), m_fPatternDistConvGaussianSigma); + + if (roi) + cvResetImageROI(m_pBgDistImg); +#ifdef LINUX_BILATERAL_FILTER + // do cross bilateral filter + fprintf(stderr, "%f %f\n", m_fSigmaS, m_fSigmaR); + if (m_fSigmaS > 0 && m_fSigmaR > 0) { + GetFloatEdgeImage(m_ppOrgLBPImgs[0], m_pEdgeImg); + //ComputeGradientImage(m_ppOrgLBPImgs[0], m_pEdgeImg, true); + m_cCrossBF.SetNewImages(m_pBgDistImg, m_pEdgeImg); + m_cCrossBF.FastCrossBF(); + m_cCrossBF.GetFilteredImage(m_pBgDistImg); + } +#endif + + // get the foreground mask by thresholding + if (m_pFgMaskImg) + cvThreshold(m_pBgDistImg, m_pFgMaskImg, m_fPatternColorDistBgThreshold, 255, CV_THRESH_BINARY); + + // get the foreground probability image (uchar) + if (m_pFgProbImg) + GetForegroundProbabilityImage(m_pFgProbImg); + + // do post-processing + //Postprocessing(); + } + + // release memories + delete [] _mask; + delete [] _bg_dist; + delete [] _org_intensity; +} + +void CMultiLayerBGS::GetBackgroundImage(IplImage *bk_img) { + IplImage *bg_img = m_pBgImg; + uchar *c1; + float *c2; + int bg_img_idx; + int channel; + int img_length = m_cvImgSize.height * m_cvImgSize.width; + int yx; + + COpencvDataConversion<uchar, uchar> ODC; + uchar *org_data = ODC.GetImageData(bg_img); + c1 = org_data; + + //c1 = (uchar*)(bg_img->imageData); + + PixelLBPStruct* PLBP = m_pPixelLBPs; + + for (yx = 0; yx < img_length; yx++) { + // the newest background image + bg_img_idx = (*PLBP).lbp_idxes[0]; + if ((*PLBP).num == 0) { + for (channel = 0; channel < m_nChannel; channel++) + *c1++ = 0; + } else { + c2 = (*PLBP).LBPs[bg_img_idx].bg_intensity; + for (channel = 0; channel < m_nChannel; channel++) + *c1++ = cvRound(*c2++); + } + PLBP++; + } + + ODC.SetImageData(bg_img, org_data); + delete [] org_data; + + cvCopyImage(m_pBgImg, bk_img); +} + +void CMultiLayerBGS::GetForegroundImage(IplImage *fg_img, CvScalar bg_color) { + if (m_pROI && (m_pROI->width <= 0 || m_pROI->height <= 0)) + return; + IplImage* org_img; + IplImage* fg_mask_img; // the Mat pointer of the foreground mask matrices at different levels + + org_img = m_pOrgImg; + fg_mask_img = m_pFgMaskImg; + + cvSet(fg_img, bg_color); + if (m_pROI) { + cvSetImageROI(org_img, *m_pROI); + cvSetImageROI(fg_img, *m_pROI); + cvSetImageROI(fg_mask_img, *m_pROI); + cvCopy(org_img, fg_img, fg_mask_img); + cvResetImageROI(org_img); + cvResetImageROI(fg_img); + cvResetImageROI(fg_mask_img); + } else + cvCopy(org_img, fg_img, fg_mask_img); +} + +void CMultiLayerBGS::GetForegroundMaskImage(IplImage *fg_mask_img) { + if (m_pROI && (m_pROI->width <= 0 || m_pROI->height <= 0)) + return; + + //cvCopyImage(m_pFgMaskImg, fg_mask_img); + if (m_pROI) { + cvSetImageROI(m_pFgMaskImg, *m_pROI); + cvSetImageROI(fg_mask_img, *m_pROI); + cvThreshold(m_pFgMaskImg, fg_mask_img, 0, 255, CV_THRESH_BINARY); + cvResetImageROI(m_pFgMaskImg); + cvResetImageROI(fg_mask_img); + } else + cvThreshold(m_pFgMaskImg, fg_mask_img, 0, 255, CV_THRESH_BINARY); +} + +void CMultiLayerBGS::GetForegroundMaskMap(CvMat *fg_mask_mat) { + COpencvDataConversion<uchar, uchar> ODC; + ODC.ConvertData(m_pFgMaskImg, fg_mask_mat); +} + +void CMultiLayerBGS::GetCurrentBackgroundDistMap(CvMat *bk_dist_map) { + cvCopy(m_pBgDistImg, bk_dist_map); +} + +void CMultiLayerBGS::Initialization(IplImage *first_img, int lbp_level_num, float *radiuses, int *neig_pt_nums) { + int a; + + m_nLBPLength = 0; + m_nLBPLevelNum = lbp_level_num; + for (a = 0; a < lbp_level_num; a++) { + m_nLBPLength += neig_pt_nums[a]; + m_pLBPRadiuses[a] = radiuses[a]; + m_pLBPMeigPointNums[a] = neig_pt_nums[a]; + } + + m_pFgImg = NULL; + m_pFgMaskImg = NULL; + m_pBgDistImg = NULL; + m_pOrgImg = NULL; + m_pBgImg = NULL; + m_ppOrgLBPImgs = NULL; + m_pFgProbImg = NULL; + + m_cvImgSize = cvGetSize(first_img); + + m_nChannel = first_img->nChannels; + + m_pOrgImg = first_img; + + if (m_bUsedColorLBP && m_bUsedGradImage) + m_nLBPImgNum = 4; + else if (m_bUsedColorLBP && !m_bUsedGradImage) + m_nLBPImgNum = 3; + else if (!m_bUsedColorLBP && m_bUsedGradImage) + m_nLBPImgNum = 2; + else + m_nLBPImgNum = 1; + + m_nLBPLength *= m_nLBPImgNum; + + m_ppOrgLBPImgs = new IplImage*[m_nLBPImgNum]; + for (a = 0; a < m_nLBPImgNum; a++) + m_ppOrgLBPImgs[a] = cvCreateImage(m_cvImgSize, IPL_DEPTH_8U, 1); + + m_pBgImg = cvCreateImage(m_cvImgSize, IPL_DEPTH_8U, m_nChannel); + m_pFgImg = cvCreateImage(m_cvImgSize, IPL_DEPTH_8U, m_nChannel); + m_pEdgeImg = cvCreateImage(m_cvImgSize, IPL_DEPTH_32F, 1); + m_pBgDistImg = cvCreateImage(m_cvImgSize, IPL_DEPTH_32F, 1); + + ResetAllParameters(); + + int img_length = m_cvImgSize.height * m_cvImgSize.width; + m_pPixelLBPs = new PixelLBPStruct[img_length]; + PixelLBPStruct* PLBP = m_pPixelLBPs; + int yx; + for (yx = 0; yx < img_length; yx++) { + (*PLBP).cur_intensity = new unsigned char[m_nChannel]; + (*PLBP).cur_pattern = new float[m_nLBPLength]; + (*PLBP).LBPs = new LBPStruct[m_nMaxLBPModeNum]; + (*PLBP).lbp_idxes = new unsigned short[m_nMaxLBPModeNum]; + (*PLBP).lbp_idxes[0] = 0; + (*PLBP).num = 0; + (*PLBP).cur_bg_layer_no = 0; + (*PLBP).matched_mode_first_time = 0; + for (a = 0; a < m_nMaxLBPModeNum; a++) { + (*PLBP).LBPs[a].bg_intensity = new float[m_nChannel]; + (*PLBP).LBPs[a].max_intensity = new float[m_nChannel]; + (*PLBP).LBPs[a].min_intensity = new float[m_nChannel]; + (*PLBP).LBPs[a].bg_pattern = new float[m_nLBPLength]; + (*PLBP).LBPs[a].first_time = -1; + (*PLBP).LBPs[a].last_time = -1; + (*PLBP).LBPs[a].freq = -1; + (*PLBP).LBPs[a].layer_time = -1; + } + PLBP++; + } + + m_pBkMaskImg = cvCreateImage(m_cvImgSize, IPL_DEPTH_8U, 1); + cvSet(m_pBkMaskImg, cvScalar(1)); + + m_cLBP.Initialization(m_ppOrgLBPImgs, m_nLBPImgNum, lbp_level_num, radiuses, neig_pt_nums, m_fRobustColorOffset); + +#ifdef LINUX_BILATERAL_FILTER + if (m_fSigmaS > 0 && m_fSigmaR > 0) + m_cCrossBF.Initialization(m_pBgDistImg, m_pBgDistImg, m_fSigmaS, m_fSigmaR); +#endif +} + +float CMultiLayerBGS::CalPatternBgDist(float *cur_pattern, float *bg_pattern) { + float bg_hamming_dist = 0; + int a; + for (a = 0; a < m_nLBPLength; a++) + bg_hamming_dist += fabsf(cur_pattern[a] - bg_pattern[a]) > m_f1_MinLBPBinaryProb; + + bg_hamming_dist /= (float) m_nLBPLength; + + return bg_hamming_dist; +} + +float CMultiLayerBGS::CalColorBgDist(uchar *cur_intensity, float *bg_intensity, float *max_intensity, float *min_intensity) { + float noised_angle, range_dist, bg_color_dist; + + range_dist = CalColorRangeDist(cur_intensity, bg_intensity, max_intensity, min_intensity, m_fRobustShadowRate, m_fRobustHighlightRate); + + if (range_dist == 1.0f) + bg_color_dist = range_dist; + else { + noised_angle = CalVectorsNoisedAngle(bg_intensity, cur_intensity, MAX(m_fRobustColorOffset, 5.0f), m_nChannel); + bg_color_dist = (1.0f - expf(-100.0f * noised_angle * noised_angle)); + } + + //float bg_color_dist = (expf(-100.0f*noised_angle*noised_angle)*(1.0f-range_dist); + + //float bg_color_dist = 0.5f*(1.0f-expf(-noised_angle*noised_angle/0.005f)) + 0.5f*range_dist; + //float bg_color_dist = MAX((float)(noised_angle>0.08f), range_dist); + + return bg_color_dist; +} + +void CMultiLayerBGS::ComputeGradientImage(IplImage *src, IplImage *dst, bool bIsFloat) { + if (src->nChannels != 1 || dst->nChannels != 1) { + printf("Input images error for computing gradient images!"); + exit(1); + } + + int a; + + IplImage* _dX = cvCreateImage(cvGetSize(dst), IPL_DEPTH_16S, 1); + IplImage* _dY = cvCreateImage(cvGetSize(dst), IPL_DEPTH_16S, 1); + + int aperture_size = 3; + + cvSobel(src, _dX, 1, 0, aperture_size); + cvSobel(src, _dY, 0, 1, aperture_size); + + COpencvDataConversion<short, short> ODC1; + COpencvDataConversion<uchar, uchar> ODC2; + COpencvDataConversion<float, float> ODC3; + + short* dX_data = ODC1.GetImageData(_dX); + short* dY_data = ODC1.GetImageData(_dY); + + uchar* dst_u_data = NULL; + float* dst_f_data = NULL; + + if (bIsFloat) + dst_f_data = ODC3.GetImageData(dst); + else + dst_u_data = ODC2.GetImageData(dst); + + short* dX = dX_data; + short* dY = dY_data; + uchar *uSrc = dst_u_data; + float *fSrc = dst_f_data; + + /* + short* dX = (short*)(_dX->imageData); + short* dY = (short*)(_dY->imageData); + uchar *dSrc = (uchar*)(dst->imageData); + */ + + int length; + if (src->roi) + length = dst->width * dst->height; + else + length = dst->roi->width * dst->roi->height; + /* + int x, y; + uchar *x_u_data; + float *x_f_data; + */ + + if (bIsFloat) { + for (a = 0; a < length; a++) { + *fSrc = cvSqrt((float) ((*dX)*(*dX)+(*dY)*(*dY)) / (32.0f * 255.0f)); + fSrc++; + dX++; + dY++; + } + ODC3.SetImageData(dst, dst_f_data); + delete [] dst_f_data; + } else { + for (a = 0; a < length; a++) { + *uSrc = cvRound(cvSqrt((float) ((*dX)*(*dX)+(*dY)*(*dY)) / 32.0f)); + uSrc++; + dX++; + dY++; + } + ODC2.SetImageData(dst, dst_u_data); + delete [] dst_u_data; + } + + delete [] dX_data; + delete [] dY_data; + + cvReleaseImage(&_dX); + cvReleaseImage(&_dY); +} + +float CMultiLayerBGS::CalVectorsNoisedAngle(float *bg_color, unsigned char *noised_color, float offset, int length) { + float org_angle = CalVectorsAngle(bg_color, noised_color, length); + float norm_color = 0, elem, noised_angle; + int a; + for (a = 0; a < length; a++) { + elem = bg_color[a]; + norm_color += elem*elem; + } + norm_color = sqrtf(norm_color); + if (norm_color == 0) + noised_angle = PI; + else { + float sin_angle = offset / norm_color; + if (sin_angle < m_fMinNoisedAngleSine) + noised_angle = m_fMinNoisedAngle; + else + //noised_angle = ( sin_angle >= 1 ? PI : asinf(sin_angle)); + noised_angle = (sin_angle >= 1 ? PI : sin_angle); + } + + float angle = org_angle - noised_angle; + if (angle < 0) + angle = 0; + return angle; + + /* + float org_angle = CalVectorsAngle(bg_color, noised_color, length); + float max_norm_color, bg_norm_color = 0, noised_norm_color = 0, elem, noised_angle; + int a; + for ( a = 0 ; a < length ; a++ ) { + elem = bg_color[a]; + bg_norm_color += elem*elem; + elem = (float)noised_color[a]; + noised_norm_color += elem*elem; + } + max_norm_color = MIN(bg_norm_color, noised_norm_color); + max_norm_color = sqrtf(max_norm_color); + if ( max_norm_color == 0 ) + noised_angle = PI; + else { + float sin_angle = offset/max_norm_color; + noised_angle = ( sin_angle >= 1 ? PI : asinf(sin_angle)); + } + + float angle = org_angle-noised_angle; + if ( angle < 0 ) + angle = 0; + return angle; + */ +} + +float CMultiLayerBGS::CalVectorsAngle(float *c1, unsigned char *c2, int length) { + float angle; + float dot2, norm1, norm2, elem1, elem2; + + dot2 = norm1 = norm2 = 0; + + int a; + for (a = 0; a < length; a++) { + elem1 = (float) (c1[a]); + elem2 = (float) (c2[a]); + dot2 += elem1*elem2; + norm1 += elem1*elem1; + norm2 += elem2*elem2; + } + + //angle = (norm1*norm2==0 ? 0 : acosf(dot2/sqrtf(norm1*norm2))); + //angle = (norm1 * norm2 == 0 ? 0 : sqrtf(fmax(1.0f - dot2 * dot2 / (norm1 * norm2), 0.f))); + angle = (norm1 * norm2 == 0 ? 0 : sqrtf(std::max(1.0f - dot2 * dot2 / (norm1 * norm2), 0.f))); + + return angle; +} + +float CMultiLayerBGS::CalColorRangeDist(unsigned char *cur_intensity, float *bg_intensity, float *max_intensity, float *min_intensity, float shadow_rate, float highlight_rate) { + float dist = 0.0f, minI, maxI, bgI, curI; + int channel; + //float cdist; + + + for (channel = 0; channel < m_nChannel; channel++) { + bgI = bg_intensity[channel]; + + /* + minI = MIN(MIN(min_intensity[channel], bgI*shadow_rate), min_intensity[channel]-15.0f); + maxI = MAX(MAX(max_intensity[channel], bgI*highlight_rate), max_intensity[channel]+15.0f); + */ + + minI = MIN(min_intensity[channel], bgI * shadow_rate - 5.0f); + maxI = MAX(max_intensity[channel], bgI * highlight_rate + 5.0f); + + /* + if ( rand()/((double)RAND_MAX+1) > 0.999 ) { + char msg[200]; + sprintf(msg, "%d\t%d\n", min_intensity[channel]<bgI*shadow_rate-5.0f ? 1 : 0, max_intensity[channel]>bgI*highlight_rate+5.0f ? 1 : 0); + ExportLogMessage(msg); + } + */ + + /* + minI = max_intensity[channel]*shadow_rate; + maxI = MAX(bg_intensity[channel]+m_fRobustColorOffset, MIN(max_intensity[channel]*highlight_rate,min_intensity[channel]/shadow_rate)); + */ + + curI = (float) (cur_intensity[channel]); + + /* + //cdist = fabsf(bgI-curI)/512.0f; + if ( curI > bgI ) + cdist = fabsf(bgI-curI)/256.0f; + else + cdist = fabsf(bgI-curI)/512.0f; + + if ( curI > maxI ) + //cdist += (curI-maxI)/(2.0f*(255.0f-maxI))*0.5f; + cdist += (1.0f-expf(10.0f*(maxI-curI)/MAX(255.0f-maxI,10.0f)))*0.5f; + else if ( curI < minI ) + //cdist += (minI-curI)/(2.0f*minI)*0.5f; + cdist += (1.0f-expf(10.0f*(curI-minI)/MAX(minI,10.0f)))*0.5f; + //dist += cdist; + if ( cdist > dist ) + dist = cdist; + */ + + if (curI > maxI || curI < minI) { + dist = 1.0f; + break; + } + } + //dist = powf(dist, 1.0f/(float)m_nChannel); + //dist /= (float)m_nChannel; + + return dist; +} + +void CMultiLayerBGS::GetLayeredBackgroundImage(int layered_no, IplImage *layered_bg_img, CvScalar empty_color) { + PixelLBPStruct *PLBP = m_pPixelLBPs; + LBPStruct* LBPs; + unsigned short* lbp_idxes; + + int a, b, c; + int img_length = m_pOrgImg->width * m_pOrgImg->height; + + cvSet(layered_bg_img, empty_color); + + COpencvDataConversion<uchar, uchar> ODC; + + uchar *bg_img_data = ODC.GetImageData(layered_bg_img); + uchar *_bg_img_data = bg_img_data; + float *cur_bg_intensity; + int lbp_num; + + for (a = 0; a < img_length; a++) { + // get lbp information + LBPs = (*PLBP).LBPs; + lbp_idxes = (*PLBP).lbp_idxes; + lbp_num = (int) ((*PLBP).num); + bool found = false; + for (b = 0; b < lbp_num; b++) { + if (LBPs[lbp_idxes[b]].bg_layer_num == layered_no) { + cur_bg_intensity = LBPs[lbp_idxes[b]].bg_intensity; + for (c = 0; c < m_pOrgImg->nChannels; c++) + *_bg_img_data++ = (uchar) * cur_bg_intensity++; + found = true; + break; + } + } + if (!found) + _bg_img_data += m_pOrgImg->nChannels; + + PLBP++; + } + + ODC.SetImageData(layered_bg_img, bg_img_data); + + delete [] bg_img_data; +} + +void CMultiLayerBGS::GetBgLayerNoImage(IplImage *bg_layer_no_img, CvScalar *layer_colors, int layer_num) { + if (layer_num != 0 && layer_num != m_nMaxLBPModeNum) { + printf("Must be set in %d layers in function GetBgLayerNoImage!\n", m_nMaxLBPModeNum); + exit(1); + } + + CvScalar *bg_layer_colors; + int bg_layer_color_num = layer_num; + if (bg_layer_color_num == 0) + bg_layer_color_num = m_nMaxLBPModeNum; + bg_layer_colors = new CvScalar[bg_layer_color_num]; + if (layer_colors) { + for (int l = 0; l < layer_num; l++) + bg_layer_colors[l] = layer_colors[l]; + } else { + int rgb[3]; + rgb[0] = rgb[1] = rgb[2] = 0; + int rgb_idx = 0; + for (int l = 0; l < bg_layer_color_num; l++) { + bg_layer_colors[l] = CV_RGB(rgb[0], rgb[1], rgb[2]); + rgb[rgb_idx] += 200; + rgb[rgb_idx] %= 255; + rgb_idx++; + rgb_idx %= 3; + } + } + + int img_length = m_pOrgImg->width * m_pOrgImg->height; + uchar *bg_layer_data = new uchar[img_length * bg_layer_no_img->nChannels]; + uchar *_bg_layer_data = bg_layer_data; + + PixelLBPStruct *PLBP = m_pPixelLBPs; + unsigned int cur_bg_layer_no; + + for (int a = 0; a < img_length; a++) { + cur_bg_layer_no = (*PLBP).cur_bg_layer_no; + for (int b = 0; b < bg_layer_no_img->nChannels; b++) { + *_bg_layer_data++ = (uchar) (bg_layer_colors[cur_bg_layer_no].val[b]); + } + PLBP++; + } + + COpencvDataConversion<uchar, uchar> ODC; + ODC.SetImageData(bg_layer_no_img, bg_layer_data); + + delete [] bg_layer_data; + delete [] bg_layer_colors; +} + +void CMultiLayerBGS::GetCurrentLayeredBackgroundImage(int layered_no, IplImage *layered_bg_img, IplImage *layered_fg_img, CvScalar layered_bg_bk_color, CvScalar layered_fg_color, + int smooth_win, float smooth_sigma, float below_layer_noise, float above_layer_noise, int min_blob_size) { + PixelLBPStruct *PLBP = m_pPixelLBPs; + LBPStruct* LBPs; + unsigned short* lbp_idxes; + + int a; + int img_length = m_pOrgImg->width * m_pOrgImg->height; + + float *bg_layer_mask = new float[img_length]; + float *_bg_layer_mask = bg_layer_mask; + + for (a = 0; a < img_length; a++) { + // get lbp information + LBPs = (*PLBP).LBPs; + lbp_idxes = (*PLBP).lbp_idxes; + *_bg_layer_mask++ = (float) (*PLBP).cur_bg_layer_no; + PLBP++; + } + + COpencvDataConversion<float, float> ODC; + IplImage* bg_layer_float_mask_img = cvCreateImage(cvGetSize(m_pOrgImg), IPL_DEPTH_32F, 1); + IplImage* bg_layer_low_mask_img = cvCreateImage(cvGetSize(m_pOrgImg), IPL_DEPTH_8U, 1); + IplImage* bg_layer_high_mask_img = cvCreateImage(cvGetSize(m_pOrgImg), IPL_DEPTH_8U, 1); + IplImage* bg_layer_mask_img = cvCreateImage(cvGetSize(m_pOrgImg), IPL_DEPTH_8U, 1); + + ODC.SetImageData(bg_layer_float_mask_img, bg_layer_mask); + + /* method 1 using smooth */ + /* + cvSmooth(bg_layer_float_mask_img, bg_layer_float_mask_img, CV_GAUSSIAN, smooth_win, smooth_win, smooth_sigma); + + cvThreshold(bg_layer_float_mask_img, bg_layer_low_mask_img, (float)layered_no-below_layer_noise, 1, CV_THRESH_BINARY); + cvThreshold(bg_layer_float_mask_img, bg_layer_high_mask_img, (float)layered_no+above_layer_noise, 1, CV_THRESH_BINARY_INV); + + cvAnd(bg_layer_low_mask_img, bg_layer_high_mask_img, bg_layer_mask_img); + */ + + /* method 2 using dilate, erode, blob removing */ + cvSmooth(bg_layer_float_mask_img, bg_layer_float_mask_img, CV_GAUSSIAN, smooth_win, smooth_win, smooth_sigma); + + cvThreshold(bg_layer_float_mask_img, bg_layer_low_mask_img, (float) layered_no - below_layer_noise, 1, CV_THRESH_BINARY); + cvThreshold(bg_layer_float_mask_img, bg_layer_high_mask_img, (float) layered_no + above_layer_noise, 1, CV_THRESH_BINARY_INV); + cvAnd(bg_layer_low_mask_img, bg_layer_high_mask_img, bg_layer_mask_img); + + cvDilate(bg_layer_mask_img, bg_layer_mask_img, 0, 2); + cvErode(bg_layer_mask_img, bg_layer_mask_img, 0, 2); + + //cvMorphologyEx(bg_layer_mask_img, bg_layer_mask_img, NULL, 0, CV_MOP_OPEN|CV_MOP_CLOSE, 1); + + // Extract the blobs using a threshold of 100 in the image + CBlobResult blobs = CBlobResult(bg_layer_mask_img, NULL, 0, true); + // discard the blobs with less area than 100 pixels + // ( the criteria to filter can be any class derived from COperadorBlob ) + blobs.Filter(blobs, B_INCLUDE, CBlobGetArea(), B_GREATER, min_blob_size); + + CBlob filtered_blob; + cvSetZero(bg_layer_mask_img); + for (a = 0; a < blobs.GetNumBlobs(); a++) { + filtered_blob = blobs.GetBlob(a); + filtered_blob.FillBlob(bg_layer_mask_img, cvScalar(1)); + } + blobs.GetNthBlob(CBlobGetArea(), 0, filtered_blob); + filtered_blob.FillBlob(bg_layer_mask_img, cvScalar(0)); + + + cvSet(layered_bg_img, layered_bg_bk_color); + cvCopy(m_pBgImg, layered_bg_img, bg_layer_mask_img); + + if (layered_fg_img) { + cvCopy(m_pOrgImg, layered_fg_img); + cvSet(layered_fg_img, layered_fg_color, bg_layer_mask_img); + } + + cvReleaseImage(&bg_layer_float_mask_img); + cvReleaseImage(&bg_layer_low_mask_img); + cvReleaseImage(&bg_layer_high_mask_img); + cvReleaseImage(&bg_layer_mask_img); + delete [] bg_layer_mask; +} + +void CMultiLayerBGS::GetColoredBgMultiLayeredImage(IplImage *bg_multi_layer_img, CvScalar *layer_colors) { + cvCopyImage(m_pOrgImg, bg_multi_layer_img); + + COpencvDataConversion<uchar, uchar> ODC; + + uchar *bg_ml_imgD = ODC.GetImageData(bg_multi_layer_img); + uchar *fg_maskD = ODC.GetImageData(m_pFgMaskImg); + + uchar *_bg_ml_imgD = bg_ml_imgD; + uchar *_fg_maskD = fg_maskD; + + PixelLBPStruct *PLBP = m_pPixelLBPs; + LBPStruct* LBPs; + unsigned short* lbp_idxes; + unsigned int lbp_num; + int bg_layer_num; + + int a, c; + int img_length = m_pOrgImg->width * m_pOrgImg->height; + int channels = m_pOrgImg->nChannels; + bool bLayeredBg; + + for (a = 0; a < img_length; a++) { + // get lbp information + lbp_num = (*PLBP).num; + LBPs = (*PLBP).LBPs; + lbp_idxes = (*PLBP).lbp_idxes; + bLayeredBg = false; + + if ((*_fg_maskD == 0)) { + bg_layer_num = LBPs[lbp_idxes[0]].bg_layer_num; + int first_layer_idx = 0; + for (c = 0; c < (int) lbp_num; c++) { + if (LBPs[lbp_idxes[c]].bg_layer_num == 1) { + first_layer_idx = c; + break; + } + } + if (bg_layer_num > 1 && DistLBP(&(LBPs[lbp_idxes[0]]), &(LBPs[first_layer_idx])) > 0.1f) { + for (c = 0; c < channels; c++) + *_bg_ml_imgD++ = (uchar) (layer_colors[bg_layer_num].val[c]); + bLayeredBg = true; + } + + if (!bLayeredBg) + _bg_ml_imgD += channels; + } else { + _bg_ml_imgD += channels; + } + + PLBP++; + _fg_maskD++; + } + + ODC.SetImageData(bg_multi_layer_img, bg_ml_imgD); + + delete [] fg_maskD; + delete [] bg_ml_imgD; +} + +void CMultiLayerBGS::GetForegroundProbabilityImage(IplImage *fg_dist_img) { + COpencvDataConversion<float, float> ODC1; + COpencvDataConversion<uchar, uchar> ODC2; + + float *_fg_distD = ODC1.GetImageData(m_pBgDistImg); + uchar *_fg_progI = ODC2.GetImageData(fg_dist_img); + float *fg_distD = _fg_distD; + uchar *fg_progI = _fg_progI; + + /* + float *fg_distD = (float*)(m_pBgDistImg->imageData); + uchar *fg_progI = (uchar*)(fg_dist_img->imageData); + */ + + int channels = fg_dist_img->nChannels; + + int a, b; + int img_length = fg_dist_img->width * fg_dist_img->height; + uchar temp; + for (a = 0; a < img_length; a++) { + temp = cvRound(255.0f * ((*fg_distD++))); + for (b = 0; b < channels; b++) + *fg_progI++ = temp; + } + + ODC2.SetImageData(fg_dist_img, _fg_progI); + + delete [] _fg_distD; + delete [] _fg_progI; +} + +void CMultiLayerBGS::RemoveBackgroundLayers(PixelLBPStruct *PLBP, bool *removed_modes) { + int a, b; + int lbp_num = PLBP->num; + + /* + if ( lbp_num < m_nMaxLBPModeNum ) + return; + */ + + /* testing */ + + unsigned short* lbp_idxes = PLBP->lbp_idxes; + if (!removed_modes) { + int removed_bg_layer_num = 0; + for (a = 0; a < lbp_num; a++) { + if (PLBP->LBPs[lbp_idxes[a]].bg_layer_num && PLBP->LBPs[lbp_idxes[a]].weight < m_fMinBgLayerWeight) { // should be removed + removed_bg_layer_num = PLBP->LBPs[lbp_idxes[a]].bg_layer_num; + lbp_num--; + for (b = a; b < lbp_num; b++) + lbp_idxes[b] = lbp_idxes[b + 1]; + break; + } + } + if (removed_bg_layer_num) { + for (a = 0; a < lbp_num; a++) { + if (PLBP->LBPs[lbp_idxes[a]].bg_layer_num > removed_bg_layer_num) + PLBP->LBPs[lbp_idxes[a]].bg_layer_num--; + } + } + } else { + int removed_bg_layer_nums[10]; + int removed_layer_num = 0; + for (a = 0; a < lbp_num; a++) { + if (removed_modes[a] && PLBP->LBPs[lbp_idxes[a]].bg_layer_num) { // should be removed + removed_bg_layer_nums[removed_layer_num++] = PLBP->LBPs[lbp_idxes[a]].bg_layer_num; + } + } + + for (a = 0; a < lbp_num; a++) { + if (removed_modes[a]) { // should be removed + lbp_num--; + for (b = a; b < lbp_num; b++) + lbp_idxes[b] = lbp_idxes[b + 1]; + } + } + + for (a = 0; a < lbp_num; a++) { + for (b = 0; b < removed_layer_num; b++) { + if (PLBP->LBPs[lbp_idxes[a]].bg_layer_num > removed_bg_layer_nums[b]) + PLBP->LBPs[lbp_idxes[a]].bg_layer_num--; + } + } + } + + // sort the list of modes based on the weights of modes + if (lbp_num != (int) PLBP->num) { + float weights[100], tot_weights = 0; + for (a = 0; a < (int) lbp_num; a++) { + weights[a] = PLBP->LBPs[lbp_idxes[a]].weight; + tot_weights += weights[a]; + } + + // sort weights in the descent order + QuickSort(weights, lbp_idxes, 0, (int) lbp_num - 1, false); + + // calculate the first potential background modes number, bg_num + float threshold_weight = m_fBackgroundModelPercent*tot_weights; + int bg_num = 0; + tot_weights = 0; + for (a = 0; a < (int) lbp_num; a++) { + tot_weights += PLBP->LBPs[lbp_idxes[a]].weight; + if (tot_weights > threshold_weight) { + bg_num = a + 1; + break; + } + } + (*PLBP).bg_num = bg_num; + + } + + PLBP->num = lbp_num; + + float bg_layer_data[10]; + unsigned short bg_layer_idxes[10]; + int bg_layer_num; + int tot_bg_layer_num = 0; + for (a = 0; a < lbp_num; a++) { + bg_layer_num = PLBP->LBPs[lbp_idxes[a]].bg_layer_num; + if (bg_layer_num) { + bg_layer_data[tot_bg_layer_num] = (float) bg_layer_num; + bg_layer_idxes[tot_bg_layer_num++] = lbp_idxes[a]; + } + } + if (tot_bg_layer_num == 1) { + PLBP->LBPs[bg_layer_idxes[0]].bg_layer_num = 1; + } else if (tot_bg_layer_num) { + // sort weights in the descent order + QuickSort(bg_layer_data, bg_layer_idxes, 0, tot_bg_layer_num - 1, true); + for (a = 0; a < tot_bg_layer_num; a++) + PLBP->LBPs[bg_layer_idxes[a]].bg_layer_num = a + 1; + } + + /* + int max_bg_layer_num = 0; + for ( a = 0 ; a < lbp_num ; a++ ) + max_bg_layer_num = MAX(max_bg_layer_num, PLBP->LBPs[lbp_idxes[a]].bg_layer_num); + if ( max_bg_layer_num >= 2 ) { + bool find_first_layer = false; + for ( a = 0 ; a < lbp_num ; a++ ) { + max_bg_layer_num = MAX(max_bg_layer_num, PLBP->LBPs[lbp_idxes[a]].bg_layer_num); + if ( PLBP->LBPs[lbp_idxes[a]].bg_layer_num == 1 ) { + find_first_layer = true; + break; + } + } + if ( !find_first_layer ) { + printf("\n===============================================\n"); + printf(" have second layer, no first layer \n"); + printf("\n===============================================\n"); + exit(1); + } + } + */ +} + +void CMultiLayerBGS::Postprocessing() { + // post-processing for background subtraction results + cvDilate(m_pFgMaskImg, m_pFgMaskImg, 0, 2); + cvErode(m_pFgMaskImg, m_pFgMaskImg, 0, 2); + + /** Example of extracting and filtering the blobs of an image */ + + // object that will contain blobs of inputImage + CBlobResult blobs; + + IplImage *inputImage = m_pFgMaskImg; + + // Extract the blobs using a threshold of 100 in the image + blobs = CBlobResult(inputImage, NULL, 0, true); + + // create a file with some of the extracted features + //blobs.PrintBlobs( ".\\blobs.txt" ); + + // discard the blobs with less area than 100 pixels + // ( the criteria to filter can be any class derived from COperadorBlob ) + blobs.Filter(blobs, B_INCLUDE, CBlobGetArea(), B_GREATER, 100); + + // create a file with filtered results + //blobs.PrintBlobs( ".\\filteredBlobs.txt" ); + + // build an output image equal to the input but with 3 channels (to draw the coloured blobs) + IplImage *outputImage; + outputImage = cvCreateImage(cvSize(inputImage->width, inputImage->height), IPL_DEPTH_8U, 1); + cvSet(outputImage, cvScalar(0)); + + // plot the selected blobs in a output image + CBlob filtered_blob; + //cvSet(outputImage, CV_RGB(0,0,255)); + int a; + for (a = 0; a < blobs.GetNumBlobs(); a++) { + filtered_blob = blobs.GetBlob(a); + filtered_blob.FillBlob(outputImage, cvScalar(255)); + } + blobs.GetNthBlob(CBlobGetArea(), 0, filtered_blob); + filtered_blob.FillBlob(outputImage, cvScalar(0)); + + /* + char *win_name = "blob filtered image"; + cvNamedWindow(win_name); + cvShowImage(win_name, outputImage); + cvWaitKey(3); + */ + + cvReleaseImage(&outputImage); +} + +void CMultiLayerBGS::GetFloatEdgeImage(IplImage *src, IplImage *dst) { + if (src->nChannels > 1) { + printf("Error: the input source image must be single-channel image!\n"); + exit(1); + } + if (dst->depth != IPL_DEPTH_32F) { + printf("Error: the output edge image must be float image ranging in [0,1]!\n"); + exit(1); + } + + uchar *src_x_data; + float *dst_x_data; + + int x, y; + for (y = 0; y < dst->height; y++) { + src_x_data = (uchar*) (src->imageData + src->widthStep * y); + dst_x_data = (float*) (dst->imageData + dst->widthStep * y); + for (x = 0; x < dst->width; x++) { + *dst_x_data++ = (float) (*src_x_data++) / 255.0f; + } + } +} + +void CMultiLayerBGS::ExportLogMessage(char *msg) { + const char *log_fn = "log_message.txt"; + ofstream fout(log_fn, ios::app); + if (fout.fail()) { + printf("Error opening log output file %s.\n", log_fn); + fout.close(); + exit(0); + } + + fout << msg; + fout.close(); +} + +void CMultiLayerBGS::UpdatePatternColorDistWeights(float *cur_pattern, float *bg_pattern) { + return; + + int cur_true_num = 0, cur_false_num = 0, bg_true_num = 0, bg_false_num = 0; + int a; + + for (a = 0; a < m_nLBPLength; a++) { + cur_true_num += (cur_pattern[a] > 0.5f ? 1 : 0); + cur_false_num += (cur_pattern[a] < 0.5f ? 0 : 1); + bg_true_num += (bg_pattern[a] > 0.5f); + bg_false_num += (bg_pattern[a] < 0.5f); + } + m_fTextureWeight = expf(-(fabsf(cur_true_num - cur_false_num) + fabsf(bg_true_num - bg_false_num) + 0.8f) / (float) m_nLBPLength); + m_fTextureWeight = MAX(MIN(m_fTextureWeight, 0.5f), 0.1f); + m_fColorWeight = 1.0f - m_fTextureWeight; +} + +void CMultiLayerBGS::Save(const char *bg_model_fn) { + Save(bg_model_fn, 2); +} + +void CMultiLayerBGS::Save(const char *bg_model_fn, int save_type) { + FILE * fout = fopen(bg_model_fn, "w"); + if (!fout) { + printf("Error opening background model output file %s.\n", bg_model_fn); + fclose(fout); + //exit(0); + return; + } + + int i, j; + if (save_type == 0) { /* save the background model information */ + fprintf(fout, "FILE_TYPE: MODEL_INFO\n\n"); + + fprintf(fout, "MAX_MODEL_NUM: %5d\n", m_nMaxLBPModeNum); + fprintf(fout, "LBP_LENGTH: %5d\n", m_nLBPLength); + fprintf(fout, "CHANNELS_NUM: %5d\n", m_nChannel); + fprintf(fout, "IMAGE_SIZE: %5d %5d\n\n", m_cvImgSize.width, m_cvImgSize.height); + + fprintf(fout, "MODEL_INFO_PIXEL_BY_PIXEL:\n"); + + int img_length = m_cvImgSize.height * m_cvImgSize.width; + PixelLBPStruct* PLBP = m_pPixelLBPs; + + for (int yx = 0; yx < img_length; yx++) { + fprintf(fout, "%3d %3d %3d", (*PLBP).num, (*PLBP).bg_num, (*PLBP).cur_bg_layer_no); + for (i = 0; i < (int) (*PLBP).num; i++) + fprintf(fout, " %3d", (*PLBP).lbp_idxes[i]); + for (i = 0; i < (int) (*PLBP).num; i++) { + int li = (*PLBP).lbp_idxes[i]; + for (j = 0; j < m_nChannel; j++) { + fprintf(fout, " %7.1f %7.1f %7.1f", (*PLBP).LBPs[li].bg_intensity[j], + (*PLBP).LBPs[li].max_intensity[j], (*PLBP).LBPs[li].min_intensity[j]); + } + for (j = 0; j < m_nLBPLength; j++) + fprintf(fout, " %7.3f", (*PLBP).LBPs[li].bg_pattern[j]); + fprintf(fout, " %10.5f", (*PLBP).LBPs[li].weight); + fprintf(fout, " %10.5f", (*PLBP).LBPs[li].max_weight); + fprintf(fout, " %3d", (*PLBP).LBPs[li].bg_layer_num); + fprintf(fout, " %20lu", (*PLBP).LBPs[li].first_time); + fprintf(fout, " %20lu", (*PLBP).LBPs[li].last_time); + fprintf(fout, " %8d", (*PLBP).LBPs[li].freq); + } + fprintf(fout, "\n"); + PLBP++; + } + } else if (save_type == 1) { /* save current parameters for background subtraction */ + fprintf(fout, "FILE_TYPE: MODEL_PARAS\n\n"); + + fprintf(fout, "MAX_MODEL_NUM: %5d\n", m_nMaxLBPModeNum); + fprintf(fout, "FRAME_DURATION: %f\n", m_fFrameDuration); + fprintf(fout, "MODEL_UPDATING_LEARN_RATE: %f\n", m_fModeUpdatingLearnRate); + fprintf(fout, "WEIGHT_UPDATING_LEARN_RATE: %f\n", m_fWeightUpdatingLearnRate); + fprintf(fout, "WEIGHT_UPDATING_CONSTANT: %f\n", m_fWeightUpdatingConstant); + fprintf(fout, "LOW_INITIAL_MODE_WEIGHT: %f\n", m_fLowInitialModeWeight); + fprintf(fout, "RELIABLE_BACKGROUND_MODE_WEIGHT: %f\n", m_fReliableBackgroundModeWeight); + fprintf(fout, "ROBUST_COLOR_OFFSET: %f\n", m_fRobustColorOffset); + fprintf(fout, "BACKGROUND_MODEL_PERCENT: %f\n", m_fBackgroundModelPercent); + fprintf(fout, "ROBUST_SHADOW_RATE: %f\n", m_fRobustShadowRate); + fprintf(fout, "ROBUST_HIGHLIGHT_RATE: %f\n", m_fRobustHighlightRate); + fprintf(fout, "PATTERN_COLOR_DIST_BACKGROUND_THRESHOLD: %f\n", m_fPatternColorDistBgThreshold); + fprintf(fout, "PATTERN_COLOR_DIST_BACKGROUND_UPDATED_THRESHOLD: %f\n", m_fPatternColorDistBgUpdatedThreshold); + fprintf(fout, "MIN_BACKGROUND_LAYER_WEIGHT: %f\n", m_fMinBgLayerWeight); + fprintf(fout, "PATTERN_DIST_SMOOTH_NEIG_HALF_SIZE: %d\n", m_nPatternDistSmoothNeigHalfSize); + fprintf(fout, "PATTERN_DIST_CONV_GAUSSIAN_SIGMA: %f\n", m_fPatternDistConvGaussianSigma); + fprintf(fout, "TEXTURE_WEIGHT: %f\n", m_fTextureWeight); + fprintf(fout, "MIN_NOISED_ANGLE: %f\n", m_fMinNoisedAngle); + fprintf(fout, "MIN_NOISED_ANGLE_SINE: %f\n", m_fMinNoisedAngleSine); + fprintf(fout, "BILATERAL_SIGMA_S: %f\n", m_fSigmaS); + fprintf(fout, "BILATERAL_SIGMA_R: %f\n", m_fSigmaR); + fprintf(fout, "LBP_LENGTH: %5d\n", m_nLBPLength); + fprintf(fout, "LBP_LEVEL_NUM: %5d\n", m_nLBPLevelNum); + fprintf(fout, "LBP_RADIUSES: "); + for (i = 0; i < m_nLBPLevelNum; i++) + fprintf(fout, "%10.5f", m_pLBPRadiuses[i]); + fprintf(fout, "\nLBP_NEIG_POINT_NUMS: "); + for (i = 0; i < m_nLBPLevelNum; i++) + fprintf(fout, "%6d", m_pLBPMeigPointNums[i]); + } else if (save_type == 2) { /* save the background model information and parameters */ + fprintf(fout, "FILE_TYPE: MODEL_PARAS_INFO\n\n"); + + fprintf(fout, "MAX_MODEL_NUM: %5d\n", m_nMaxLBPModeNum); + fprintf(fout, "FRAME_DURATION: %f\n", m_fFrameDuration); + fprintf(fout, "MODEL_UPDATING_LEARN_RATE: %f\n", m_fModeUpdatingLearnRate); + fprintf(fout, "WEIGHT_UPDATING_LEARN_RATE: %f\n", m_fWeightUpdatingLearnRate); + fprintf(fout, "WEIGHT_UPDATING_CONSTANT: %f\n", m_fWeightUpdatingConstant); + fprintf(fout, "LOW_INITIAL_MODE_WEIGHT: %f\n", m_fLowInitialModeWeight); + fprintf(fout, "RELIABLE_BACKGROUND_MODE_WEIGHT: %f\n", m_fReliableBackgroundModeWeight); + fprintf(fout, "ROBUST_COLOR_OFFSET: %f\n", m_fRobustColorOffset); + fprintf(fout, "BACKGROUND_MODEL_PERCENT: %f\n", m_fBackgroundModelPercent); + fprintf(fout, "ROBUST_SHADOW_RATE: %f\n", m_fRobustShadowRate); + fprintf(fout, "ROBUST_HIGHLIGHT_RATE: %f\n", m_fRobustHighlightRate); + fprintf(fout, "PATTERN_COLOR_DIST_BACKGROUND_THRESHOLD: %f\n", m_fPatternColorDistBgThreshold); + fprintf(fout, "PATTERN_COLOR_DIST_BACKGROUND_UPDATED_THRESHOLD: %f\n", m_fPatternColorDistBgUpdatedThreshold); + fprintf(fout, "MIN_BACKGROUND_LAYER_WEIGHT: %f\n", m_fMinBgLayerWeight); + fprintf(fout, "PATTERN_DIST_SMOOTH_NEIG_HALF_SIZE: %d\n", m_nPatternDistSmoothNeigHalfSize); + fprintf(fout, "PATTERN_DIST_CONV_GAUSSIAN_SIGMA: %f\n", m_fPatternDistConvGaussianSigma); + fprintf(fout, "TEXTURE_WEIGHT: %f\n", m_fTextureWeight); + fprintf(fout, "MIN_NOISED_ANGLE: %f\n", m_fMinNoisedAngle); + fprintf(fout, "MIN_NOISED_ANGLE_SINE: %f\n", m_fMinNoisedAngleSine); + fprintf(fout, "BILATERAL_SIGMA_S: %f\n", m_fSigmaS); + fprintf(fout, "BILATERAL_SIGMA_R: %f\n", m_fSigmaR); + fprintf(fout, "LBP_LENGTH: %5d\n", m_nLBPLength); + fprintf(fout, "LBP_LEVEL_NUM: %5d\n", m_nLBPLevelNum); + fprintf(fout, "LBP_RADIUSES: "); + for (i = 0; i < m_nLBPLevelNum; i++) + fprintf(fout, "%10.5f", m_pLBPRadiuses[i]); + fprintf(fout, "\nLBP_NEIG_POINT_NUMS: "); + for (i = 0; i < m_nLBPLevelNum; i++) + fprintf(fout, "%6d", m_pLBPMeigPointNums[i]); + + fprintf(fout, "\nMAX_MODEL_NUM: %5d\n", m_nMaxLBPModeNum); + fprintf(fout, "LBP_LENGTH: %5d\n", m_nLBPLength); + fprintf(fout, "CHANNELS_NUM: %5d\n", m_nChannel); + fprintf(fout, "IMAGE_SIZE: %5d %5d\n\n", m_cvImgSize.width, m_cvImgSize.height); + + fprintf(fout, "MODEL_INFO_PIXEL_BY_PIXEL:\n"); + + int img_length = m_cvImgSize.height * m_cvImgSize.width; + PixelLBPStruct* PLBP = m_pPixelLBPs; + + for (int yx = 0; yx < img_length; yx++) { + fprintf(fout, "%3d %3d %3d", (*PLBP).num, (*PLBP).bg_num, (*PLBP).cur_bg_layer_no); + for (i = 0; i < (int) (*PLBP).num; i++) + fprintf(fout, " %3d", (*PLBP).lbp_idxes[i]); + for (i = 0; i < (int) (*PLBP).num; i++) { + int li = (*PLBP).lbp_idxes[i]; + for (j = 0; j < m_nChannel; j++) { + fprintf(fout, " %7.1f %7.1f %7.1f", (*PLBP).LBPs[li].bg_intensity[j], + (*PLBP).LBPs[li].max_intensity[j], (*PLBP).LBPs[li].min_intensity[j]); + } + for (j = 0; j < m_nLBPLength; j++) + fprintf(fout, " %7.3f", (*PLBP).LBPs[li].bg_pattern[j]); + fprintf(fout, " %10.5f", (*PLBP).LBPs[li].weight); + fprintf(fout, " %10.5f", (*PLBP).LBPs[li].max_weight); + fprintf(fout, " %3d", (*PLBP).LBPs[li].bg_layer_num); + fprintf(fout, " %20lu", (*PLBP).LBPs[li].first_time); + fprintf(fout, " %20lu", (*PLBP).LBPs[li].last_time); + fprintf(fout, " %8d", (*PLBP).LBPs[li].freq); + } + fprintf(fout, "\n"); + PLBP++; + } + } else { /* wrong save type */ + printf("Please input correct save type: 0 - model_info 1 - model_paras 2 - model_paras_info\n"); + fclose(fout); + exit(0); + } + + fclose(fout); +} + +bool CMultiLayerBGS::Load(const char *bg_model_fn) { + ifstream fin(bg_model_fn, ios::in); + if (fin.fail()) { + printf("Error opening background model file %s.\n", bg_model_fn); + fin.close(); + return false; + } + + char para_name[1024], model_type[1024]; + + fin >> para_name >> model_type; + + int i, j; + CvSize img_size; + int img_length = m_cvImgSize.width * m_cvImgSize.height; + int max_lbp_mode_num = m_nMaxLBPModeNum; + + if (!strcmp(model_type, "MODEL_INFO")) { + fin >> para_name >> m_nMaxLBPModeNum; + fin >> para_name >> m_nLBPLength; + fin >> para_name >> m_nChannel; + fin >> para_name >> img_size.width >> img_size.height; + + if (m_cvImgSize.width != img_size.width || m_cvImgSize.height != img_size.height) { + printf("Image size is not matched!\n"); + return false; + } + + if (max_lbp_mode_num != m_nMaxLBPModeNum) { + PixelLBPStruct* PLBP = m_pPixelLBPs; + for (int yx = 0; yx < img_length; yx++) { + delete [] (*PLBP).LBPs; + delete [] (*PLBP).lbp_idxes; + (*PLBP).LBPs = new LBPStruct[m_nMaxLBPModeNum]; + (*PLBP).lbp_idxes = new unsigned short[m_nMaxLBPModeNum]; + } + } + + fin >> para_name; + + int img_length = m_cvImgSize.height * m_cvImgSize.width; + PixelLBPStruct* PLBP = m_pPixelLBPs; + + for (int yx = 0; yx < img_length; yx++) { + fin >> (*PLBP).num >> (*PLBP).bg_num >> (*PLBP).cur_bg_layer_no; + for (i = 0; i < (int) (*PLBP).num; i++) + fin >> (*PLBP).lbp_idxes[i]; + for (i = 0; i < (int) (*PLBP).num; i++) { + int li = (*PLBP).lbp_idxes[i]; + for (j = 0; j < m_nChannel; j++) { + fin >> (*PLBP).LBPs[li].bg_intensity[j] >> + (*PLBP).LBPs[li].max_intensity[j] >> (*PLBP).LBPs[li].min_intensity[j]; + } + for (j = 0; j < m_nLBPLength; j++) + fin >> (*PLBP).LBPs[li].bg_pattern[j]; + fin >> (*PLBP).LBPs[li].weight >> (*PLBP).LBPs[li].max_weight >> (*PLBP).LBPs[li].bg_layer_num + >> (*PLBP).LBPs[li].first_time >> (*PLBP).LBPs[li].last_time >> (*PLBP).LBPs[li].freq; + } + PLBP++; + } + } else if (!strcmp(model_type, "MODEL_PARAS")) { + fin >> para_name >> m_nMaxLBPModeNum; + fin >> para_name >> m_fFrameDuration; + fin >> para_name >> m_fModeUpdatingLearnRate; + fin >> para_name >> m_fWeightUpdatingLearnRate; + fin >> para_name >> m_fWeightUpdatingConstant; + fin >> para_name >> m_fLowInitialModeWeight; + fin >> para_name >> m_fReliableBackgroundModeWeight; + fin >> para_name >> m_fRobustColorOffset; + fin >> para_name >> m_fBackgroundModelPercent; + fin >> para_name >> m_fRobustShadowRate; + fin >> para_name >> m_fRobustHighlightRate; + fin >> para_name >> m_fPatternColorDistBgThreshold; + fin >> para_name >> m_fPatternColorDistBgUpdatedThreshold; + fin >> para_name >> m_fMinBgLayerWeight; + fin >> para_name >> m_nPatternDistSmoothNeigHalfSize; + fin >> para_name >> m_fPatternDistConvGaussianSigma; + fin >> para_name >> m_fTextureWeight; + fin >> para_name >> m_fMinNoisedAngle; + fin >> para_name >> m_fMinNoisedAngleSine; + fin >> para_name >> m_fSigmaS; + fin >> para_name >> m_fSigmaR; + fin >> para_name >> m_nLBPLength; + fin >> para_name >> m_nLBPLevelNum; + fin >> para_name; + for (i = 0; i < m_nLBPLevelNum; i++) + fin >> m_pLBPRadiuses[i]; + fin >> para_name; + for (i = 0; i < m_nLBPLevelNum; i++) + fin >> m_pLBPMeigPointNums[i]; + } else if (!strcmp(model_type, "MODEL_PARAS_INFO")) { + fin >> para_name >> m_nMaxLBPModeNum; + fin >> para_name >> m_fFrameDuration; + fin >> para_name >> m_fModeUpdatingLearnRate; + fin >> para_name >> m_fWeightUpdatingLearnRate; + fin >> para_name >> m_fWeightUpdatingConstant; + fin >> para_name >> m_fLowInitialModeWeight; + fin >> para_name >> m_fReliableBackgroundModeWeight; + fin >> para_name >> m_fRobustColorOffset; + fin >> para_name >> m_fBackgroundModelPercent; + fin >> para_name >> m_fRobustShadowRate; + fin >> para_name >> m_fRobustHighlightRate; + fin >> para_name >> m_fPatternColorDistBgThreshold; + fin >> para_name >> m_fPatternColorDistBgUpdatedThreshold; + fin >> para_name >> m_fMinBgLayerWeight; + fin >> para_name >> m_nPatternDistSmoothNeigHalfSize; + fin >> para_name >> m_fPatternDistConvGaussianSigma; + fin >> para_name >> m_fTextureWeight; + fin >> para_name >> m_fMinNoisedAngle; + fin >> para_name >> m_fMinNoisedAngleSine; + fin >> para_name >> m_fSigmaS; + fin >> para_name >> m_fSigmaR; + fin >> para_name >> m_nLBPLength; + fin >> para_name >> m_nLBPLevelNum; + fin >> para_name; + for (i = 0; i < m_nLBPLevelNum; i++) + fin >> m_pLBPRadiuses[i]; + fin >> para_name; + for (i = 0; i < m_nLBPLevelNum; i++) + fin >> m_pLBPMeigPointNums[i]; + + fin >> para_name >> m_nMaxLBPModeNum; + fin >> para_name >> m_nLBPLength; + fin >> para_name >> m_nChannel; + fin >> para_name >> img_size.width >> img_size.height; + + if (m_cvImgSize.width != img_size.width || m_cvImgSize.height != img_size.height) { + printf("Image size is not matched!\n"); + return false; + } + + if (max_lbp_mode_num != m_nMaxLBPModeNum) { + PixelLBPStruct* PLBP = m_pPixelLBPs; + for (int yx = 0; yx < img_length; yx++) { + delete [] (*PLBP).LBPs; + delete [] (*PLBP).lbp_idxes; + (*PLBP).LBPs = new LBPStruct[m_nMaxLBPModeNum]; + (*PLBP).lbp_idxes = new unsigned short[m_nMaxLBPModeNum]; + } + } + + fin >> para_name; + + int img_length = m_cvImgSize.height * m_cvImgSize.width; + PixelLBPStruct* PLBP = m_pPixelLBPs; + + for (int yx = 0; yx < img_length; yx++) { + fin >> (*PLBP).num >> (*PLBP).bg_num >> (*PLBP).cur_bg_layer_no; + for (i = 0; i < (int) (*PLBP).num; i++) + fin >> (*PLBP).lbp_idxes[i]; + for (i = 0; i < (int) (*PLBP).num; i++) { + int li = (*PLBP).lbp_idxes[i]; + for (j = 0; j < m_nChannel; j++) { + fin >> (*PLBP).LBPs[li].bg_intensity[j] >> + (*PLBP).LBPs[li].max_intensity[j] >> (*PLBP).LBPs[li].min_intensity[j]; + } + for (j = 0; j < m_nLBPLength; j++) + fin >> (*PLBP).LBPs[li].bg_pattern[j]; + fin >> (*PLBP).LBPs[li].weight >> (*PLBP).LBPs[li].max_weight >> (*PLBP).LBPs[li].bg_layer_num + >> (*PLBP).LBPs[li].first_time >> (*PLBP).LBPs[li].last_time >> (*PLBP).LBPs[li].freq; + } + PLBP++; + } + } else { + printf("Not correct model save type!\n"); + fin.close(); + exit(0); + } + + fin.close(); + + ResetAllParameters(); + + return true; +} + +void CMultiLayerBGS::SetValidPointMask(IplImage *maskImage, int mode) { + if (mode == 1) + SetBkMaskImage(maskImage); + else + cvAnd(m_pBkMaskImg, maskImage, m_pBkMaskImg); +} + +void CMultiLayerBGS::SetFrameRate(float frameDuration) { + m_fModeUpdatingLearnRate = m_fModeUpdatingLearnRatePerSecond*frameDuration; + m_fWeightUpdatingLearnRate = m_fWeightUpdatingLearnRatePerSecond*frameDuration; + + m_fFrameDuration = frameDuration; + + m_f1_ModeUpdatingLearnRate = 1.0f - m_fModeUpdatingLearnRate; + m_f1_WeightUpdatingLearnRate = 1.0f - m_fWeightUpdatingLearnRate; +} + +void CMultiLayerBGS::Init(int width, int height) { + IplImage* first_img = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3); + int lbp_level_num = 1; + float radiuses[] = {2.0f}; + int neig_pt_nums[] = {6}; + Initialization(first_img, lbp_level_num, radiuses, neig_pt_nums); + cvReleaseImage(&first_img); +} + +int CMultiLayerBGS::SetRGBInputImage(IplImage *inputImage, CvRect *roi) { + if (!inputImage) { + printf("Please allocate the IplImage memory!\n"); + return 0; + } + if (inputImage->width != m_cvImgSize.width || + inputImage->height != m_cvImgSize.height || + inputImage->depth != IPL_DEPTH_8U || + inputImage->nChannels != 3) { + printf("Please provide the correct IplImage pointer, \ne.g. inputImage = cvCreateImage(imgSize, IPL_DEPTH_8U, 3);\n"); + return 0; + } + SetNewImage(inputImage, roi); + return 1; +} + +void CMultiLayerBGS::SetParameters(int max_lbp_mode_num, float mode_updating_learn_rate_per_second, float weight_updating_learn_rate_per_second, float low_init_mode_weight) { + m_nMaxLBPModeNum = max_lbp_mode_num; + m_fModeUpdatingLearnRate = mode_updating_learn_rate_per_second*m_fFrameDuration; + m_fWeightUpdatingLearnRate = weight_updating_learn_rate_per_second*m_fFrameDuration; + m_fLowInitialModeWeight = low_init_mode_weight; + + m_fModeUpdatingLearnRatePerSecond = mode_updating_learn_rate_per_second; + m_fWeightUpdatingLearnRatePerSecond = weight_updating_learn_rate_per_second; + + m_f1_ModeUpdatingLearnRate = 1.0f - m_fModeUpdatingLearnRate; + m_f1_WeightUpdatingLearnRate = 1.0f - m_fWeightUpdatingLearnRate; +} + +int CMultiLayerBGS::Process() { + BackgroundSubtractionProcess(); + return 1; +} + +int CMultiLayerBGS::SetForegroundMaskImage(IplImage* fg_mask_img) { + if (!fg_mask_img) { + printf("Please allocate the IplImage memory!\n"); + return 0; + } + if (fg_mask_img->width != m_cvImgSize.width || + fg_mask_img->height != m_cvImgSize.height || + fg_mask_img->depth != IPL_DEPTH_8U || + fg_mask_img->nChannels != 1) { + printf("Please provide the correct IplImage pointer, \ne.g. fg_mask_img = cvCreateImage(imgSize, IPL_DEPTH_8U, 1);\n"); + return 0; + } + + m_pFgMaskImg = fg_mask_img; + + return 1; +} + +int CMultiLayerBGS::SetForegroundProbImage(IplImage* fg_prob_img) { + if (!fg_prob_img) { + printf("Please allocate the IplImage memory!\n"); + return 0; + } + if (fg_prob_img->width != m_cvImgSize.width || + fg_prob_img->height != m_cvImgSize.height || + fg_prob_img->depth != IPL_DEPTH_8U) { + printf("Please provide the correct IplImage pointer, \ne.g. fg_prob_img = cvCreateImage(imgSize, IPL_DEPTH_8U, 1);\n"); + return 0; + } + + m_pFgProbImg = fg_prob_img; + + return 1; +} + +void CMultiLayerBGS::SetCurrentFrameNumber(unsigned long cur_frame_no) { + m_nCurImgFrameIdx = cur_frame_no; +} diff --git a/package_bgs/jmo/CMultiLayerBGS.h b/package_bgs/jmo/CMultiLayerBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..1eda55b1a60f59a506d3beb8a7394d8dee309bc6 --- /dev/null +++ b/package_bgs/jmo/CMultiLayerBGS.h @@ -0,0 +1,313 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// BackgroundSubtraction.h: interface for the CBackgroundSubtraction class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(_MULTI_LAYER_BGS_H_) +#define _MULTI_LAYER_BGS_H_ + +/* +Since the used fast cross bilateral filter codes can not be compiled under Windows, +we don't use the bilateral filter to remove the noise in the foreground detection +step. If you compile it under Linux, please uncomment it. +*/ +//#define LINUX_BILATERAL_FILTER + +#include "LocalBinaryPattern.h" +#include "BGS.h" +#include <stdio.h> +#include <stdarg.h> +#include "BlobResult.h" +#include "OpenCvDataConversion.h" + +#include "BackgroundSubtractionAPI.h" + +#ifdef LINUX_BILATERAL_FILTER +#include "CrossBilateralFilter.h" // cross bilateral filter +#endif + +#include <ctime> // clock +#include <cstdlib> // C standard library +#include <cstdio> // C I/O (for sscanf) +#include <cstring> // string manipulation +#include <fstream> // file I/O +#include <cmath> // math includes +#include <iostream> // I/O streams + +using namespace std; // make std:: accessible + +class CMultiLayerBGS : public CBackgroundSubtractionAPI +{ +public: + //------------------------------------------------------------- + // TO CALL AT INITIALISATION: DEFINES THE SIZE OF THE INPUT IMAGES + // NORMALLY, UNNECESSARY IF A CONFIGURATION FILE IS LOADED + void Init(int width,int height); + + //------------------------------------------------------------- + // PROVIDE A MASK TO DEFINE THE SET OF POINTS WHERE BACKGROUND + // SUBTRACTION DOES NOT NEED TO BE PERFORMED + // + // mode is useful to specify if the points to remove from + // processing are in addition to the ones potentially + // removed according to the configuration file, + // or if they are the only ones to be removed + // + // mode=0 : provided points need to be removed + // in addition to those already removed + // mode=1 : the provided points are the only one to remove + // from processing + // Note: maskImage(li,co)=0 indicate the points to remove + // from background processing + void SetValidPointMask(IplImage* maskImage, int mode); + + //------------------------------------------------------------- + // + // set the frame rate, to adjust the update parameters + // to the actual frame rate. + // Can be called only once at initialisation, + // but in online cases, can be used to indicate + // the time interval during the last processed frame + // + // frameDuration is in millisecond + void SetFrameRate(float frameDuration); + + //------------------------------------------------------------- + // + // set some main parameters for background model learning. + // in general, we can set large updating rates for background + // model learning and set small updating rates in foreground + // detection + void SetParameters(int max_lbp_mode_num, // maximal LBP mode number + float mode_updating_learn_rate_per_second, // background mode updating learning rate per second + float weight_updating_learn_rate_per_second, // mode's weight updating learning rate per second + float low_init_mode_weight); // the low initial mode weight + + //------------------------------------------------------------- + // PROVIDE A POINTER TO THE INPUT IMAGE + // -> INDICATE WHERE THE NEW IMAGE TO PROCESS IS STORED + // + // Here assumes that the input image will contain RGB images. + // The memory of this image is handled by the caller. + // + // The return value indicate whether the actual Background + // Subtraction algorithm handles RGB images (1) or not (0). + // + int SetRGBInputImage(IplImage * inputImage, CvRect *roi=NULL); + + //------------------------------------------------------------- + // PROVIDE A POINTER TO THE RESULT IMAGE + // INDICATE WHERE THE BACKGROUND RESULT NEED TO BE STORED + // + int SetForegroundMaskImage(IplImage* fg_mask_img); + int SetForegroundProbImage(IplImage* fg_prob_img); + + //------------------------------------------------------------- + // This function should be called each time a new image is + // available in the input image. + // + // The return value is 0 if everything goes well, a non-zero value + // otherwise. + // + int Process(); + + //------------------------------------------------------------- + // this function should save parameters and information of the model + // (e.g. after a training of the model, or in such a way + // that the model can be reload to process the next frame + // type of save: + // 0 - background model information (pixel by pixel) + // 1 - background model parameters + // 2 - both background information (pixel by pixel) and parameters + void Save(const char *bg_model_fn, int save_type); + void Save(const char* bg_model_fn); + + //------------------------------------------------------------- + // this function should load the parameters necessary + // for the processing of the background subtraction or + // load background model information + bool Load(const char *bg_model_fn); + + + void SetCurrentFrameNumber(unsigned long cur_frame_no); + + void GetForegroundMaskImage(IplImage *fg_mask_img); + void GetForegroundImage(IplImage *fg_img, CvScalar bg_color=CV_RGB(0,255,0)); + void GetBackgroundImage(IplImage *bk_img); + void GetForegroundProbabilityImage(IplImage* fg_prob_img); + + void GetBgLayerNoImage(IplImage *bg_layer_no_img, CvScalar* layer_colors=NULL, int layer_num=0); + void GetLayeredBackgroundImage(int layered_no, IplImage *layered_bg_img, CvScalar empty_color=CV_RGB(0,0,0)); + void GetCurrentLayeredBackgroundImage(int layered_no, IplImage *layered_bg_img, IplImage *layered_fg_img=NULL, + CvScalar layered_bg_bk_color=CV_RGB(0,0,0), CvScalar layered_fg_color=CV_RGB(255,0,0), + int smooth_win=13, float smooth_sigma=3.0f, float below_layer_noise=0.5f, float above_layer_noise=0.3f, int min_blob_size=50); + float DistLBP(LBPStruct *LBP1, LBPStruct *LBP2); + void GetColoredBgMultiLayeredImage(IplImage *bg_multi_layer_img, CvScalar *layer_colors); + void UpdatePatternColorDistWeights(float *cur_pattern, float *bg_pattern); + void ExportLogMessage(char* msg); + void Postprocessing(); + void GetFloatEdgeImage(IplImage *src, IplImage *dst); + void RemoveBackgroundLayers(PixelLBPStruct *PLBP, bool *removed_modes=NULL); + float CalColorRangeDist(unsigned char *cur_intensity, float *bg_intensity, float *max_intensity, + float *min_intensity, float shadow_rate, float highlight_rate); + float CalVectorsAngle(float *c1, unsigned char *c2, int length); + float CalVectorsNoisedAngle(float *bg_color, unsigned char *noised_color, float offset, int length); + void ComputeGradientImage(IplImage *src, IplImage *dst, bool bIsFloat); + float CalColorBgDist(uchar *cur_intensity, float *bg_intensity, float *max_intensity, float *min_intensity); + float CalPatternBgDist(float *cur_pattern, float *bg_pattern); + + void GetForegroundMaskMap(CvMat *fg_mask_mat); + void Initialization(IplImage *first_img, int lbp_level_num, float *radiuses, int *neig_pt_nums); + void GetCurrentBackgroundDistMap(CvMat *bk_dist_map); + void BackgroundSubtractionProcess(); + void SetBkMaskImage(IplImage *mask_img); + void SetNewImage(IplImage *new_img, CvRect *roi=NULL); + + void ResetAllParameters(); + void QuickSort(float *pData, unsigned short *pIdxes, long low, long high, bool bAscent); + void UpdateBgPixelPattern(float *cur_pattern, float *bg_bg_pattern); + void UpdateBgPixelColor(unsigned char* cur_intensity, float* bg_intensity); + void Update_MAX_MIN_Intensity(unsigned char *cur_intensity, float *max_intensity, float *min_intensity); + void MergeImages(int num, ...); + + int m_nChannel; /* most of opencv functions support 1,2,3 or 4 channels, for the input images */ + + PixelLBPStruct* m_pPixelLBPs; /* the LBP texture patterns for each image */ + int m_nMaxLBPModeNum; /* the maximal number for the used LBP pattern models */ + float m_fModeUpdatingLearnRate; /* the background mode learning rate */ + float m_fWeightUpdatingLearnRate; /* the background mode weight updating rate */ + float m_f1_ModeUpdatingLearnRate; /* 1 - background_mode_learning_rate */ + float m_f1_WeightUpdatingLearnRate; /* 1 - background_mode_weight_updating_rate */ + float m_fRobustColorOffset; /* the intensity offset robust to noise */ + float m_fLowInitialModeWeight; /* the lowest weight of initial background mode */ + int m_nLBPLength; /* the length of texture LBP operator */ + float m_fPatternColorDistBgThreshold; /* the threshold value used to classify background and foreground */ + float m_fPatternColorDistBgUpdatedThreshold; /* the threshold value used to update the background modeling */ + float m_fMinBgLayerWeight; /* the minimal weight to remove background layers */ + + int m_nPatternDistSmoothNeigHalfSize; /* the neighboring half size of gaussian window to remove the noise + on the distance map */ + float m_fPatternDistConvGaussianSigma; /* the gaussian sigma used to remove the noise on the distance map */ + + float m_fBackgroundModelPercent; /* the background mode percent, the first several background modes + with high mode weights should be regarded as reliable background modes */ + + float m_fRobustShadowRate; /* the minimal shadow rate, [0.4, 0.7] */ + float m_fRobustHighlightRate; /* the maximal highlight rate, [1.1, 1.4] */ + + int m_nLBPImgNum; /* the number of images used for texture LBP feature */ + + float m_fMinLBPBinaryProb; /* the minimal LBP binary probability */ + float m_f1_MinLBPBinaryProb; /* 1 - minimal_LBP_binary_probability */ + + CvSize m_cvImgSize; /* the image size (width, height) */ + + unsigned long m_nCurImgFrameIdx; /* the frame index of current image */ + + bool m_bUsedGradImage; /* the boolean variable signaling whether the gradient image is used + or not for computing LBP operator */ + + bool m_bUsedColorLBP; /* true - multi-channel color image for LBP operator, + false - gray-scale image for LBP operator */ + + CLocalBinaryPattern m_cLBP; /* the class instant for computing LBP (local binary pattern) texture feature */ + + IplImage* m_pBkMaskImg; /* the mask image corresponding to the input image, + i.e. all the masked pixels should be processed */ + + IplImage* m_pOrgImg; /* the original image */ + IplImage** m_ppOrgLBPImgs; /* the multi-layer images used for LBP feature extraction */ + IplImage* m_pFgImg; /* the foreground image */ + IplImage* m_pBgImg; /* the background image */ + IplImage* m_pFgMaskImg; /* the foreground mask image */ + IplImage* m_pBgDistImg; /* the background distance image (float) */ + IplImage* m_pEdgeImg; /* the edge image used for cross bilateral filter */ + IplImage* m_pFgProbImg; /* the foreground probability image (uchar) */ + + IplImage* m_pFirstAppearingTimeMap; + +#ifdef LINUX_BILATERAL_FILTER + CCrossBilateralFilter m_cCrossBF; /* the class instant for cross bilateral filter + which should be used to remove noise on the distance map */ +#endif + + bool m_disableLearning; + float m_fSigmaS; /* sigma in the spatial domain for cross bilateral filter */ + float m_fSigmaR; /* sigma in the normalized intensity domain for cross bilateral filter */ + + float m_fTextureWeight; /* the weight value of texture LBP feature + for background modeling & foreground detection */ + + float m_fColorWeight; /* the weight value of color invariant feature + for background modeling & foreground detection */ + + float m_fWeightUpdatingConstant; /* the constant ( >= 1 ) for 'hysteries' weight updating scheme + (increase when matched, decrease when un-matched */ + + float m_fReliableBackgroundModeWeight; /* the weight value for background mode + which should be regarded as a reliable background mode, + which is useful for multi-layer scheme */ + + float m_fMinNoisedAngle; /* the minimal angle value between the background color + and the noised observed color */ + + float m_fMinNoisedAngleSine; /* the minimal angle sine value between the background color + and the noised observed color */ + + float m_fFrameDuration; /* frame duration */ + + float m_fModeUpdatingLearnRatePerSecond; + float m_fWeightUpdatingLearnRatePerSecond; + + int m_nLBPLevelNum; + float m_pLBPRadiuses[10]; + int m_pLBPMeigPointNums[10]; + + CvRect* m_pROI; + CMultiLayerBGS(); + virtual ~CMultiLayerBGS(); +}; + +#endif // !defined(_MULTI_LAYER_BGS_H_) + diff --git a/package_bgs/jmo/LocalBinaryPattern.cpp b/package_bgs/jmo/LocalBinaryPattern.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45528aaec04fa71a1a347ab7ca03373e95cd5360 --- /dev/null +++ b/package_bgs/jmo/LocalBinaryPattern.cpp @@ -0,0 +1,314 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// LocalBinaryPattern.cpp: implementation of the CLocalBinaryPattern class. +// +////////////////////////////////////////////////////////////////////// + +#include "LocalBinaryPattern.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CLocalBinaryPattern::CLocalBinaryPattern() +{ + m_ppOrgImgs = NULL; + m_pRadiuses = NULL; + m_fRobustWhiteNoise = 3.0f; + m_pNeigPointsNums = NULL; + m_pXYShifts = NULL; + m_pShiftedImg = NULL; +} + +CLocalBinaryPattern::~CLocalBinaryPattern() +{ + FreeMemories(); +} + +void CLocalBinaryPattern::Initialization(IplImage **first_imgs, int imgs_num, int level_num, float *radius, int *neig_pt_num, float robust_white_noise, int type) +{ + + m_nImgsNum = imgs_num; + + m_nLBPLevelNum = level_num; + + m_pRadiuses = new float[m_nLBPLevelNum]; + m_pNeigPointsNums= new int[m_nLBPLevelNum]; + m_ppOrgImgs = first_imgs; + + int a, b; + for ( a = 0 ; a < m_nImgsNum ; a++ ) { + m_cvImgSize = cvGetSize(first_imgs[a]); + + if ( first_imgs[a]->nChannels > 1 ) { + printf("Input image channel must be 1!"); + exit(1); + } + } + + int tot_neig_pts_num = 0; + for ( a = 0 ; a < m_nLBPLevelNum ; a++ ) { + m_pRadiuses[a] = radius[a]; + m_pNeigPointsNums[a] = neig_pt_num[a]; + tot_neig_pts_num += neig_pt_num[a]; + if ( m_pNeigPointsNums[a] % 2 != 0 ) { + printf("Even number must be given for points number for LBP!\n"); + exit(1); + } + } + + m_pShiftedImg = cvCloneImage(m_ppOrgImgs[0]); + + m_pXYShifts = new CvPoint[tot_neig_pts_num]; + + m_nMaxShift.x = 0; + m_nMaxShift.y = 0; + int shift_idx = 0; + for ( a = 0 ; a < m_nLBPLevelNum ; a++ ) + for ( b = 0 ; b < m_pNeigPointsNums[a] ; b++ ) { + // compute the offset of neig point + CalNeigPixelOffset(m_pRadiuses[a], m_pNeigPointsNums[a], b, m_pXYShifts[shift_idx].x, m_pXYShifts[shift_idx].y); + m_nMaxShift.x = MAX(m_nMaxShift.x, m_pXYShifts[shift_idx].x); + m_nMaxShift.y = MAX(m_nMaxShift.y, m_pXYShifts[shift_idx].y); + shift_idx++; + } + + m_fRobustWhiteNoise = robust_white_noise; +} + +void CLocalBinaryPattern::SetNewImages(IplImage **new_imgs) +{ + m_ppOrgImgs = new_imgs; +} + +void CLocalBinaryPattern::ComputeLBP(PixelLBPStruct *PLBP, CvRect *roi) +{ + float *dif_pattern; + float *_dif_pattern; + PixelLBPStruct *_PLBP; + int data_length; + float *cur_pattern; + + // allocate memories + if ( roi ) + data_length = roi->height*roi->width; + else + data_length = m_cvImgSize.width*m_cvImgSize.height; + + dif_pattern = new float[data_length]; + + int img_idx, pt_idx, yx, level; + int pattern_idx = 0; + for ( img_idx = 0 ; img_idx < m_nImgsNum ; img_idx++ ) { + for ( level = 0 ; level < m_nLBPLevelNum ; level++ ) { + for ( pt_idx = 0 ; pt_idx < m_pNeigPointsNums[level] ; pt_idx++ ) { + + // computing the shifted image + CalShiftedImage(m_ppOrgImgs[img_idx], m_pXYShifts[pattern_idx].x, m_pXYShifts[pattern_idx].y, m_pShiftedImg, roi); + + // computing the different binary images + CalImageDifferenceMap(m_ppOrgImgs[img_idx], m_pShiftedImg, dif_pattern, roi); + + // set the binary values + _PLBP = PLBP; + _dif_pattern = dif_pattern; + + if ( roi ) { + int x, y; + for ( y = 0 ; y < roi->height ; y++ ) { + _PLBP = PLBP + (y+roi->y)*m_cvImgSize.width + roi->x; + for ( x = 0 ; x < roi->width ; x++ ) { + cur_pattern = (*_PLBP++).cur_pattern; + cur_pattern[pattern_idx] = *_dif_pattern++; + } + } + } + else { + for ( yx = 0 ; yx < data_length ; yx++ ) { + cur_pattern = (*_PLBP++).cur_pattern; + cur_pattern[pattern_idx] = *_dif_pattern++; + } + } + + pattern_idx++; + + } + } + } + + // release memories + delete [] dif_pattern; + + //delete [] shifted_dif_pattern; + //cvReleaseImage(&shifted_img); + //cvReleaseImage(&pattern_img); +} + +void CLocalBinaryPattern::FreeMemories() +{ + if ( m_pRadiuses != NULL ) + delete [] m_pRadiuses; + if ( m_pNeigPointsNums != NULL ) + delete [] m_pNeigPointsNums; + if ( m_pXYShifts ) + delete [] m_pXYShifts; + if ( m_pShiftedImg ) + cvReleaseImage(&m_pShiftedImg); + + m_pXYShifts = NULL; + m_pRadiuses = NULL; + m_pNeigPointsNums = NULL; + m_pShiftedImg = NULL; +} + +void CLocalBinaryPattern::SetShiftedMeshGrid(CvSize img_size, float offset_x, float offset_y, CvMat *grid_map_x, CvMat *grid_map_y) +{ + float *gX = (float*)(grid_map_x->data.ptr); + float *gY = (float*)(grid_map_y->data.ptr); + + int x, y; + for ( y = 0 ; y < img_size.height ; y++ ) { + for ( x = 0 ; x < img_size.width ; x++ ) { + *gX++ = (float)x + offset_x; + *gY++ = (float)y + offset_y; + } + } +} + +void CLocalBinaryPattern::CalShiftedImage(IplImage *src, int offset_x, int offset_y, IplImage *dst, CvRect *roi) +{ + CvRect src_roi, dst_roi; + int roi_width, roi_height; + + if ( roi ) { + src_roi.x = MAX(offset_x+roi->x, 0); + src_roi.y = MAX(offset_y+roi->y, 0); + + dst_roi.x = MAX(-(offset_x+roi->x), roi->x); + dst_roi.y = MAX(-(offset_y+roi->y), roi->y); + + roi_width = MIN(MIN(roi->width+(int)fabsf((float)offset_x), src->width-src_roi.x), dst->width-dst_roi.x); + roi_height = MIN(MIN(roi->height+(int)fabsf((float)offset_y), src->height-src_roi.y), dst->height-dst_roi.y); + + src_roi.width = roi_width; + src_roi.height = roi_height; + + dst_roi.width = roi_width; + dst_roi.height = roi_height; + } + else { + roi_width = src->width-(int)fabsf((float)offset_x); + roi_height = src->height-(int)fabsf((float)offset_y); + + src_roi.x = MAX(offset_x, 0); + src_roi.y = MAX(offset_y, 0); + src_roi.width = roi_width; + src_roi.height = roi_height; + + dst_roi.x = MAX(-offset_x, 0); + dst_roi.y = MAX(-offset_y, 0); + dst_roi.width = roi_width; + dst_roi.height = roi_height; + } + + cvSet(dst,cvScalar(0)); + + if ( roi_width <= 0 || roi_height <= 0 ) + return; + + cvSetImageROI(src, src_roi); + cvSetImageROI(dst, dst_roi); + cvCopy(src, dst); + cvResetImageROI(src); + cvResetImageROI(dst); +} + +void CLocalBinaryPattern::CalNeigPixelOffset(float radius, int tot_neig_pts_num, int neig_pt_idx, int &offset_x, int &offset_y) +{ + float angle = (float)neig_pt_idx/(float)tot_neig_pts_num*2.0f*PI; + offset_x = cvRound(radius*cosf(angle)); + offset_y = cvRound(-radius*sinf(angle)); +} + +void CLocalBinaryPattern::CalImageDifferenceMap(IplImage *cent_img, IplImage *neig_img, float *pattern, CvRect *roi) +{ + COpencvDataConversion<uchar, uchar> ODC; + + if ( roi ) { + cvSetImageROI(cent_img, *roi); + cvSetImageROI(neig_img, *roi); + } + + uchar *_centI = ODC.GetImageData(cent_img); + uchar *_neigI = ODC.GetImageData(neig_img); + uchar *centI = _centI; + uchar *neigI = _neigI; + + float *tmp_pattern = pattern; + + int xy; + int length; + + if ( roi ) + length = roi->height*roi->width; + else + length = cent_img->height*cent_img->width; + + for ( xy = 0 ; xy < length ; xy++ ) { + *tmp_pattern = (float)BINARY_PATTERM_ELEM(*neigI, *centI, m_fRobustWhiteNoise); + tmp_pattern++; + centI++; + neigI++; + } + + if ( roi ) { + cvResetImageROI(cent_img); + cvResetImageROI(neig_img); + } + + // release memories + delete [] _centI; + delete [] _neigI; + +} + diff --git a/package_bgs/jmo/LocalBinaryPattern.h b/package_bgs/jmo/LocalBinaryPattern.h new file mode 100644 index 0000000000000000000000000000000000000000..307d94fb316dbefc73439342e4d5d560f64b84cf --- /dev/null +++ b/package_bgs/jmo/LocalBinaryPattern.h @@ -0,0 +1,103 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// LocalBinaryPattern.h: interface for the CLocalBinaryPattern class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(_LOCAL_BINARY_PATTERN_H_) +#define _LOCAL_BINARY_PATTERN_H_ + +#include <cv.h> +#include "BGS.h" + + +/************************************************************************/ +/* two types of computing the LBP operators but currently GENERAL_LBP */ +/* has been implemented. */ +/************************************************************************/ +#define GENERAL_LBP 0 +#define SYMMETRIC_LBP 1 + +#include <cstdio> // C I/O (for sscanf) +#include "OpenCvDataConversion.h" + + +class CLocalBinaryPattern +{ +public: + void CalImageDifferenceMap(IplImage *cent_img, IplImage *neig_img, float *pattern, CvRect *roi=NULL); + void CalNeigPixelOffset(float radius, int tot_neig_pts_num, int neig_pt_idx, int &offset_x, int &offset_y); + void CalShiftedImage(IplImage *src, int offset_x, int offset_y, IplImage *dst, CvRect *roi=NULL); + void FreeMemories(); + void ComputeLBP(PixelLBPStruct *PLBP, CvRect *roi=NULL); + void SetNewImages(IplImage **new_imgs); + + IplImage** m_ppOrgImgs; /* the original images used for computing the LBP operators */ + + void Initialization(IplImage **first_imgs, int imgs_num, + int level_num, float *radius, int *neig_pt_num, + float robust_white_noise = 3.0f, int type = GENERAL_LBP); + + CLocalBinaryPattern(); + virtual ~CLocalBinaryPattern(); + + float m_fRobustWhiteNoise; /* the robust noise value for computing the LBP operator in each channel */ + +private: + void SetShiftedMeshGrid(CvSize img_size, float offset_x, float offset_y, CvMat *grid_map_x, CvMat *grid_map_y); + + float* m_pRadiuses; /* the circle radiuses for the LBP operator */ + int m_nLBPType; /* the type of computing LBP operator */ + int* m_pNeigPointsNums; /* the numbers of neighboring pixels on multi-level circles */ + int m_nImgsNum; /* the number of multi-channel image */ + int m_nLBPLevelNum; /* the number of multi-level LBP operator */ + CvSize m_cvImgSize; /* the image size (width, height) */ + + CvPoint* m_pXYShifts; + CvPoint m_nMaxShift; + + IplImage* m_pShiftedImg; +}; + +#endif // !defined(_LOCAL_BINARY_PATTERN_H_) + diff --git a/package_bgs/jmo/MultiLayerBGS.cpp b/package_bgs/jmo/MultiLayerBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc0a6e88a318d017f34ac4cc46cac7931b8fc2b5 --- /dev/null +++ b/package_bgs/jmo/MultiLayerBGS.cpp @@ -0,0 +1,332 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "MultiLayerBGS.h" + +MultiLayerBGS::MultiLayerBGS() : firstTime(true), showOutput(true), + bg_model_preload(""), saveModel(false), disableLearning(false), disableDetectMode(true), loadDefaultParams(true), + detectAfter(0), frameNumber(0) +{ + std::cout << "MultiLayerBGS()" << std::endl; +} + +MultiLayerBGS::~MultiLayerBGS() +{ + finish(); + std::cout << "~MultiLayerBGS()" << std::endl; +} + +void MultiLayerBGS::setStatus(Status _status) +{ + status = _status; +} + +void MultiLayerBGS::finish(void) +{ + if(bg_model_preload.empty()) + { + bg_model_preload = "./models/MultiLayerBGSModel.yml"; + saveConfig(); + } + + if(status == MLBGS_LEARN && saveModel == true) + { + std::cout << "MultiLayerBGS saving background model: " << bg_model_preload << std::endl; + BGS->Save(bg_model_preload.c_str()); + } + + cvReleaseImage(&fg_img); + cvReleaseImage(&bg_img); + cvReleaseImage(&fg_prob_img); + cvReleaseImage(&fg_mask_img); + cvReleaseImage(&fg_prob_img3); + cvReleaseImage(&merged_img); + + delete BGS; +} + +void MultiLayerBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + CvSize img_size = cvSize(cvCeil((double) img_input.size().width), cvCeil((double) img_input.size().height)); + + if(firstTime) + { + if(disableDetectMode) + status = MLBGS_LEARN; + + if(status == MLBGS_LEARN) + std::cout << "MultiLayerBGS in LEARN mode" << std::endl; + + if(status == MLBGS_DETECT) + std::cout << "MultiLayerBGS in DETECT mode" << std::endl; + + org_img = new IplImage(img_input); + + fg_img = cvCreateImage(img_size, org_img->depth, org_img->nChannels); + bg_img = cvCreateImage(img_size, org_img->depth, org_img->nChannels); + fg_prob_img = cvCreateImage(img_size, org_img->depth, 1); + fg_mask_img = cvCreateImage(img_size, org_img->depth, 1); + fg_prob_img3 = cvCreateImage(img_size, org_img->depth, org_img->nChannels); + merged_img = cvCreateImage(cvSize(img_size.width * 2, img_size.height * 2), org_img->depth, org_img->nChannels); + + BGS = new CMultiLayerBGS(); + BGS->Init(img_size.width, img_size.height); + BGS->SetForegroundMaskImage(fg_mask_img); + BGS->SetForegroundProbImage(fg_prob_img); + + if(bg_model_preload.empty() == false) + { + std::cout << "MultiLayerBGS loading background model: " << bg_model_preload << std::endl; + BGS->Load(bg_model_preload.c_str()); + } + + if(status == MLBGS_DETECT) + { + BGS->m_disableLearning = disableLearning; + + if(disableLearning) + std::cout << "MultiLayerBGS disabled learning in DETECT mode" << std::endl; + else + std::cout << "MultiLayerBGS enabled learning in DETECT mode" << std::endl; + } + + if(loadDefaultParams) + { + std::cout << "MultiLayerBGS loading default params" << std::endl; + + max_mode_num = 5; + weight_updating_constant = 5.0; + texture_weight = 0.5; + bg_mode_percent = 0.6; + pattern_neig_half_size = 4; + pattern_neig_gaus_sigma = 3.0; + bg_prob_threshold = 0.2; + bg_prob_updating_threshold = 0.2; + robust_LBP_constant = 3; + min_noised_angle = 10.0 / 180.0 * PI; //0,01768 + shadow_rate = 0.6; + highlight_rate = 1.2; + bilater_filter_sigma_s = 3.0; + bilater_filter_sigma_r = 0.1; + } + else + std::cout << "MultiLayerBGS loading config params" << std::endl; + + BGS->m_nMaxLBPModeNum = max_mode_num; + BGS->m_fWeightUpdatingConstant = weight_updating_constant; + BGS->m_fTextureWeight = texture_weight; + BGS->m_fBackgroundModelPercent = bg_mode_percent; + BGS->m_nPatternDistSmoothNeigHalfSize = pattern_neig_half_size; + BGS->m_fPatternDistConvGaussianSigma = pattern_neig_gaus_sigma; + BGS->m_fPatternColorDistBgThreshold = bg_prob_threshold; + BGS->m_fPatternColorDistBgUpdatedThreshold = bg_prob_updating_threshold; + BGS->m_fRobustColorOffset = robust_LBP_constant; + BGS->m_fMinNoisedAngle = min_noised_angle; + BGS->m_fRobustShadowRate = shadow_rate; + BGS->m_fRobustHighlightRate = highlight_rate; + BGS->m_fSigmaS = bilater_filter_sigma_s; + BGS->m_fSigmaR = bilater_filter_sigma_r; + + if(loadDefaultParams) + { + //frame_duration = 1.0 / 30.0; + //frame_duration = 1.0 / 25.0; + frame_duration = 1.0 / 10.0; + } + + BGS->SetFrameRate(frame_duration); + + if(status == MLBGS_LEARN) + { + if(loadDefaultParams) + { + mode_learn_rate_per_second = 0.5; + weight_learn_rate_per_second = 0.5; + init_mode_weight = 0.05; + } + else + { + mode_learn_rate_per_second = learn_mode_learn_rate_per_second; + weight_learn_rate_per_second = learn_weight_learn_rate_per_second; + init_mode_weight = learn_init_mode_weight; + } + } + + if(status == MLBGS_DETECT) + { + if(loadDefaultParams) + { + mode_learn_rate_per_second = 0.01; + weight_learn_rate_per_second = 0.01; + init_mode_weight = 0.001; + } + else + { + mode_learn_rate_per_second = detect_mode_learn_rate_per_second; + weight_learn_rate_per_second = detect_weight_learn_rate_per_second; + init_mode_weight = detect_init_mode_weight; + } + } + + BGS->SetParameters(max_mode_num, mode_learn_rate_per_second, weight_learn_rate_per_second, init_mode_weight); + + saveConfig(); + + delete org_img; + } + + //IplImage* inputImage = new IplImage(img_input); + //IplImage* img = cvCreateImage(img_size, IPL_DEPTH_8U, 3); + //cvCopy(inputImage, img); + //delete inputImage; + + if(detectAfter > 0 && detectAfter == frameNumber) + { + std::cout << "MultiLayerBGS in DETECT mode" << std::endl; + + status = MLBGS_DETECT; + + mode_learn_rate_per_second = 0.01; + weight_learn_rate_per_second = 0.01; + init_mode_weight = 0.001; + + BGS->SetParameters(max_mode_num, mode_learn_rate_per_second, weight_learn_rate_per_second, init_mode_weight); + + BGS->m_disableLearning = disableLearning; + + if(disableLearning) + std::cout << "MultiLayerBGS disabled learning in DETECT mode" << std::endl; + else + std::cout << "MultiLayerBGS enabled learning in DETECT mode" << std::endl; + } + + IplImage* img = new IplImage(img_input); + + BGS->SetRGBInputImage(img); + BGS->Process(); + + BGS->GetBackgroundImage(bg_img); + BGS->GetForegroundImage(fg_img); + BGS->GetForegroundProbabilityImage(fg_prob_img3); + BGS->GetForegroundMaskImage(fg_mask_img); + BGS->MergeImages(4, img, bg_img, fg_prob_img3, fg_img, merged_img); + + img_merged = cv::Mat(merged_img); + img_foreground = cv::Mat(fg_mask_img); + img_background = cv::Mat(bg_img); + + if(showOutput) + { + cv::imshow("MLBGS Layers", img_merged); + cv::imshow("MLBGS FG Mask", img_foreground); + } + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + delete img; + //cvReleaseImage(&img); + + firstTime = false; + frameNumber++; +} + +void MultiLayerBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/MultiLayerBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteString(fs, "preloadModel", bg_model_preload.c_str()); + cvWriteInt(fs, "saveModel", saveModel); + cvWriteInt(fs, "detectAfter", detectAfter); + cvWriteInt(fs, "disableDetectMode", disableDetectMode); + cvWriteInt(fs, "disableLearningInDetecMode", disableLearning); + cvWriteInt(fs, "loadDefaultParams", loadDefaultParams); + + cvWriteInt(fs, "max_mode_num", max_mode_num); + cvWriteReal(fs, "weight_updating_constant", weight_updating_constant); + cvWriteReal(fs, "texture_weight", texture_weight); + cvWriteReal(fs, "bg_mode_percent", bg_mode_percent); + cvWriteInt(fs, "pattern_neig_half_size", pattern_neig_half_size); + cvWriteReal(fs, "pattern_neig_gaus_sigma", pattern_neig_gaus_sigma); + cvWriteReal(fs, "bg_prob_threshold", bg_prob_threshold); + cvWriteReal(fs, "bg_prob_updating_threshold", bg_prob_updating_threshold); + cvWriteInt(fs, "robust_LBP_constant", robust_LBP_constant); + cvWriteReal(fs, "min_noised_angle", min_noised_angle); + cvWriteReal(fs, "shadow_rate", shadow_rate); + cvWriteReal(fs, "highlight_rate", highlight_rate); + cvWriteReal(fs, "bilater_filter_sigma_s", bilater_filter_sigma_s); + cvWriteReal(fs, "bilater_filter_sigma_r", bilater_filter_sigma_r); + + cvWriteReal(fs, "frame_duration", frame_duration); + + cvWriteReal(fs, "learn_mode_learn_rate_per_second", learn_mode_learn_rate_per_second); + cvWriteReal(fs, "learn_weight_learn_rate_per_second", learn_weight_learn_rate_per_second); + cvWriteReal(fs, "learn_init_mode_weight", learn_init_mode_weight); + + cvWriteReal(fs, "detect_mode_learn_rate_per_second", detect_mode_learn_rate_per_second); + cvWriteReal(fs, "detect_weight_learn_rate_per_second", detect_weight_learn_rate_per_second); + cvWriteReal(fs, "detect_init_mode_weight", detect_init_mode_weight); + + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void MultiLayerBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/MultiLayerBGS.xml", 0, CV_STORAGE_READ); + + bg_model_preload = cvReadStringByName(fs, 0, "preloadModel", ""); + saveModel = cvReadIntByName(fs, 0, "saveModel", false); + detectAfter = cvReadIntByName(fs, 0, "detectAfter", 0); + disableDetectMode = cvReadIntByName(fs, 0, "disableDetectMode", true); + disableLearning = cvReadIntByName(fs, 0, "disableLearningInDetecMode", false); + loadDefaultParams = cvReadIntByName(fs, 0, "loadDefaultParams", true); + + max_mode_num = cvReadIntByName(fs, 0, "max_mode_num", 5); + weight_updating_constant = cvReadRealByName(fs, 0, "weight_updating_constant", 5.0); + texture_weight = cvReadRealByName(fs, 0, "texture_weight", 0.5); + bg_mode_percent = cvReadRealByName(fs, 0, "bg_mode_percent", 0.6); + pattern_neig_half_size = cvReadIntByName(fs, 0, "pattern_neig_half_size", 4); + pattern_neig_gaus_sigma = cvReadRealByName(fs, 0, "pattern_neig_gaus_sigma", 3.0); + bg_prob_threshold = cvReadRealByName(fs, 0, "bg_prob_threshold", 0.2); + bg_prob_updating_threshold = cvReadRealByName(fs, 0, "bg_prob_updating_threshold", 0.2); + robust_LBP_constant = cvReadIntByName(fs, 0, "robust_LBP_constant", 3); + min_noised_angle = cvReadRealByName(fs, 0, "min_noised_angle", 0.01768); + shadow_rate = cvReadRealByName(fs, 0, "shadow_rate", 0.6); + highlight_rate = cvReadRealByName(fs, 0, "highlight_rate", 1.2); + bilater_filter_sigma_s = cvReadRealByName(fs, 0, "bilater_filter_sigma_s", 3.0); + bilater_filter_sigma_r = cvReadRealByName(fs, 0, "bilater_filter_sigma_r", 0.1); + + frame_duration = cvReadRealByName(fs, 0, "frame_duration", 0.1); + + learn_mode_learn_rate_per_second = cvReadRealByName(fs, 0, "learn_mode_learn_rate_per_second", 0.5); + learn_weight_learn_rate_per_second = cvReadRealByName(fs, 0, "learn_weight_learn_rate_per_second", 0.5); + learn_init_mode_weight = cvReadRealByName(fs, 0, "learn_init_mode_weight", 0.05); + + detect_mode_learn_rate_per_second = cvReadRealByName(fs, 0, "detect_mode_learn_rate_per_second", 0.01); + detect_weight_learn_rate_per_second = cvReadRealByName(fs, 0, "detect_weight_learn_rate_per_second", 0.01); + detect_init_mode_weight = cvReadRealByName(fs, 0, "detect_init_mode_weight", 0.001); + + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/jmo/MultiLayerBGS.h b/package_bgs/jmo/MultiLayerBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..671a788c76e277f9b2b020e04a7413e6b3ace0ff --- /dev/null +++ b/package_bgs/jmo/MultiLayerBGS.h @@ -0,0 +1,101 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "CMultiLayerBGS.h" + +class MultiLayerBGS : public IBGS +{ +public: + enum Status + { + MLBGS_NONE = -1, + MLBGS_LEARN = 0, + MLBGS_DETECT = 1 + }; + +private: + bool firstTime; + long long frameNumber; + cv::Mat img_foreground; + cv::Mat img_merged; + cv::Mat img_background; + bool showOutput; + bool saveModel; + bool disableDetectMode; + bool disableLearning; + int detectAfter; + CMultiLayerBGS* BGS; + Status status; + IplImage* img; + IplImage* org_img; + IplImage* fg_img; + IplImage* bg_img; + IplImage* fg_prob_img; + IplImage* fg_mask_img; + IplImage* fg_prob_img3; + IplImage* merged_img; + std::string bg_model_preload; + + bool loadDefaultParams; + + int max_mode_num; + float weight_updating_constant; + float texture_weight; + float bg_mode_percent; + int pattern_neig_half_size; + float pattern_neig_gaus_sigma; + float bg_prob_threshold; + float bg_prob_updating_threshold; + int robust_LBP_constant; + float min_noised_angle; + float shadow_rate; + float highlight_rate; + float bilater_filter_sigma_s; + float bilater_filter_sigma_r; + + float frame_duration; + + float mode_learn_rate_per_second; + float weight_learn_rate_per_second; + float init_mode_weight; + + float learn_mode_learn_rate_per_second; + float learn_weight_learn_rate_per_second; + float learn_init_mode_weight; + + float detect_mode_learn_rate_per_second; + float detect_weight_learn_rate_per_second; + float detect_init_mode_weight; + +public: + MultiLayerBGS(); + ~MultiLayerBGS(); + + void setStatus(Status status); + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void finish(void); + void saveConfig(); + void loadConfig(); +}; \ No newline at end of file diff --git a/package_bgs/jmo/OpenCvDataConversion.h b/package_bgs/jmo/OpenCvDataConversion.h new file mode 100644 index 0000000000000000000000000000000000000000..22d6ec90302e09760e4af3c944d4abee5631b166 --- /dev/null +++ b/package_bgs/jmo/OpenCvDataConversion.h @@ -0,0 +1,224 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// OpencvDataConversion.h: interface for the COpencvDataConversion class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(_OPENCV_DATA_CONVERSION_H_) +#define _OPENCV_DATA_CONVERSION_H_ + +#include <cv.h> +#include <stdio.h> + +template <class TI, class TM> /* class TI - the type of image data, class TM - the type of matrix data */ +class COpencvDataConversion +{ +public: + + /* get the image data */ + TI * GetImageData(IplImage *img) + { + if ( !img->roi ) { /* no ROI used, i.e. the whole image */ + int y; //, x; + TI* img_data = new TI[img->width*img->height*img->nChannels]; + TI* temp = img_data; + TI* x_data; + + for ( y = 0 ; y < img->height ; y++ ) { + x_data = (TI*)(img->imageData + img->widthStep*y); + int row_length = img->width*img->nChannels; + memcpy(temp, x_data, sizeof(TI)*row_length); + temp += row_length; + /* + for ( x = 0 ; x < img->width*img->nChannels ; x++ ) + *temp++ = *x_data++; + */ + } + + return img_data; + } + else { /* get image data only in ROI */ + int y;//, x; + TI* img_data = new TI[img->roi->width*img->roi->height*img->nChannels]; + TI* temp = img_data; + TI* x_data; + for ( y = img->roi->yOffset ; y < img->roi->yOffset+img->roi->height ; y++ ) { + x_data = (TI*)(img->imageData + img->widthStep*y + img->roi->xOffset*sizeof(TI)*img->nChannels); + int row_length = img->roi->width*img->nChannels; + memcpy(temp, x_data, sizeof(TI)*row_length); + temp += row_length; + /* + for ( x = 0 ; x < img->roi->width*img->nChannels ; x++ ) + *temp++ = *x_data++; + */ + } + return img_data; + } + }; + + /* set the image data */ + void SetImageData(IplImage *img, TI *img_data) + { + if ( !img->roi ) { /* no ROI used, i.e. the whole image */ + int y;//, x; + TI* temp = img_data; + TI* x_data; + for ( y = 0 ; y < img->height ; y++ ) { + x_data = (TI*)(img->imageData + img->widthStep*y); + int row_length = img->width*img->nChannels; + memcpy(x_data, temp, sizeof(TI)*row_length); + temp += row_length; + /* + for ( x = 0 ; x < img->width*img->nChannels ; x++ ) + *x_data++ = *temp++; + */ + } + } + else { /* set image data only in ROI */ + int y;//, x; + TI* temp = img_data; + TI* x_data; + for ( y = img->roi->yOffset ; y < img->roi->yOffset+img->roi->height ; y++ ) { + x_data = (TI*)(img->imageData + img->widthStep*y + img->roi->xOffset*sizeof(TI)*img->nChannels); + int row_length = img->roi->width*img->nChannels; + memcpy(x_data, temp, sizeof(TI)*row_length); + temp += row_length; + /* + for ( x = 0 ; x < img->roi->width*img->nChannels ; x++ ) + *x_data++ = *temp++; + */ + } + } + } + + /* get the matrix data */ + TM * GetMatData(CvMat *mat) + { + TM* mat_data = new TM[mat->width*mat->height]; + memcpy(mat_data, mat->data.ptr, sizeof(TM)*mat->width*mat->height); + return mat_data; + + /* + int y, x; + TM* mat_data = new TM[mat->width*mat->height]; + TM* temp = mat_data; + TM* x_data; + for ( y = 0 ; y < mat->height ; y++ ) { + x_data = (TM*)(mat->data.ptr + mat->step*y); + for ( x = 0 ; x < mat->width ; x++ ) + *temp++ = *x_data++; + } + return mat_data; + */ + }; + + /* set the matrix data */ + void SetMatData(CvMat *mat, TM *mat_data) + { + memcpy(mat->data.ptr, mat_data, sizeof(TM)*mat->width*mat->height); + + /* + int y, x; + TM* temp = mat_data; + TM* x_data; + for ( y = 0 ; y < mat->height ; y++ ) { + x_data = (TM*)(mat->data.ptr + mat->step*y); + for ( x = 0 ; x < mat->width ; x++ ) + *x_data++ = *temp++; + } + */ + } + + /* convert the image data to the matrix data */ + void ConvertData(IplImage *img_src, CvMat *mat_dst) + { + if ( img_src->nChannels > 1 ) { + printf("Must be one-channel image for ConvertImageData!\n"); + exit(1); + } + + TI* _img_data = GetImageData(img_src); + TM* _mat_data = new TM[img_src->width*img_src->height]; + + TI* img_data = _img_data; + TM* mat_data = _mat_data; + int i; + for ( i = 0 ; i < img_src->width*img_src->height ; i++ ) + *mat_data++ = (TM)(*img_data++); + + SetMatData(mat_dst, _mat_data); + + delete [] _img_data; + delete [] _mat_data; + } + + /* convert the matrix data to the image data */ + void ConvertData(CvMat *mat_src, IplImage *img_dst) + { + if ( img_dst->nChannels > 1 ) { + printf("Must be one-channel image for ConvertImageData!\n"); + exit(1); + } + + TM* _mat_data = GetMatData(mat_src); + TI* _img_data = new TI[mat_src->width*mat_src->height]; + + TM* mat_data = _mat_data; + TI* img_data = _img_data; + + int i; + for ( i = 0 ; i < mat_src->width*mat_src->height ; i++ ) + *img_data++ = (TI)(*mat_data++); + + SetImageData(img_dst, _img_data); + + delete [] _img_data; + delete [] _mat_data; + } + + COpencvDataConversion() {}; + virtual ~COpencvDataConversion() {}; +}; + +#endif // !defined(_OPENCV_DATA_CONVERSION_H_) + diff --git a/package_bgs/jmo/blob.cpp b/package_bgs/jmo/blob.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2b99b2a52ef9a61366659ed9322deca3317a9440 --- /dev/null +++ b/package_bgs/jmo/blob.cpp @@ -0,0 +1,1149 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/************************************************************************ +Blob.cpp + +- FUNCIONALITAT: Implementaci� de la classe CBlob +- AUTOR: Inspecta S.L. +MODIFICACIONS (Modificaci�, Autor, Data): + + +FUNCTIONALITY: Implementation of the CBlob class and some helper classes to perform +some calculations on it +AUTHOR: Inspecta S.L. +MODIFICATIONS (Modification, Author, Date): + +**************************************************************************/ + + +#include <limits.h> +#include "blob.h" +#include "cv.h" + +namespace Blob +{ + +/** +- FUNCI�: CBlob +- FUNCIONALITAT: Constructor est�ndard +- PAR�METRES: +- RESULTAT: +- inicialitzaci� de totes les variables internes i de l'storage i la sequencia +per a les cantonades del blob +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: CBlob +- FUNCTIONALITY: Standard constructor +- PARAMETERS: +- RESULT: +- memory allocation for the blob edges and initialization of member variables +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +CBlob::CBlob() +{ + etiqueta = -1; // Flag indicates null region + exterior = 0; + area = 0.0f; + perimeter = 0.0f; + parent = -1; + minx = LONG_MAX; + maxx = 0; + miny = LONG_MAX; + maxy = 0; + sumx = 0; + sumy = 0; + sumxx = 0; + sumyy = 0; + sumxy = 0; + mean = 0; + stddev = 0; + externPerimeter = 0; + + m_storage = cvCreateMemStorage(0); + edges = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, + sizeof(CvContour), + sizeof(CvPoint),m_storage); +} + +/** +- FUNCI�: CBlob +- FUNCIONALITAT: Constructor de c�pia +- PAR�METRES: +- RESULTAT: +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: CBlob +- FUNCTIONALITY: Copy constructor +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +CBlob::CBlob( const CBlob &src ) +{ + // copiem les propietats del blob origen a l'actual + etiqueta = src.etiqueta; + exterior = src.exterior; + area = src.Area(); + perimeter = src.Perimeter(); + parent = src.parent; + minx = src.minx; + maxx = src.maxx; + miny = src.miny; + maxy = src.maxy; + sumx = src.sumx; + sumy = src.sumy; + sumxx = src.sumxx; + sumyy = src.sumyy; + sumxy = src.sumxy; + mean = src.mean; + stddev = src.stddev; + externPerimeter = src.externPerimeter; + + // copiem els edges del blob origen a l'actual + CvSeqReader reader; + CvSeqWriter writer; + CvPoint edgeactual; + + // creem una sequencia buida per als edges + m_storage = cvCreateMemStorage(0); + edges = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, + sizeof(CvContour), + sizeof(CvPoint),m_storage); + + cvStartReadSeq( src.Edges(), &reader); + cvStartAppendToSeq( edges, &writer ); + + for( int i=0; i< src.Edges()->total; i++) + { + CV_READ_SEQ_ELEM( edgeactual ,reader); + CV_WRITE_SEQ_ELEM( edgeactual , writer ); + } + + cvEndWriteSeq( &writer ); +} +CBlob::CBlob( const CBlob *src ) +{ + // copiem les propietats del blob origen a l'actual + etiqueta = src->etiqueta; + exterior = src->exterior; + area = src->Area(); + perimeter = src->Perimeter(); + parent = src->parent; + minx = src->minx; + maxx = src->maxx; + miny = src->miny; + maxy = src->maxy; + sumx = src->sumx; + sumy = src->sumy; + sumxx = src->sumxx; + sumyy = src->sumyy; + sumxy = src->sumxy; + mean = src->mean; + stddev = src->stddev; + externPerimeter = src->externPerimeter; + + // copiem els edges del blob origen a l'actual + CvSeqReader reader; + CvSeqWriter writer; + CvPoint edgeactual; + + // creem una sequencia buida per als edges + m_storage = cvCreateMemStorage(0); + edges = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, + sizeof(CvContour), + sizeof(CvPoint),m_storage); + + cvStartReadSeq( src->Edges(), &reader); + cvStartAppendToSeq( edges, &writer ); + + for( int i=0; i< src->Edges()->total; i++) + { + CV_READ_SEQ_ELEM( edgeactual ,reader); + CV_WRITE_SEQ_ELEM( edgeactual , writer ); + } + + cvEndWriteSeq( &writer ); +} + +/** +- FUNCI�: ~CBlob +- FUNCIONALITAT: Destructor est�ndard +- PAR�METRES: +- RESULTAT: +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: CBlob +- FUNCTIONALITY: Standard destructor +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +CBlob::~CBlob() +{ + // Eliminar v�rtexs del blob + cvClearSeq(edges); + // i la zona de mem�ria on s�n + cvReleaseMemStorage( &m_storage ); +} + +/** +- FUNCI�: operator= +- FUNCIONALITAT: Operador d'assignaci� +- PAR�METRES: +- src: blob a assignar a l'actual +- RESULTAT: +- Substitueix el blob actual per el src +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: Assigment operator +- FUNCTIONALITY: Assigns a blob to the current +- PARAMETERS: +- src: blob to assign +- RESULT: +- the current blob is replaced by the src blob +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +CBlob& CBlob::operator=(const CBlob &src ) +{ + // si ja s�n el mateix, no cal fer res + if (this != &src) + { + // Eliminar v�rtexs del blob + cvClearSeq(edges); + // i la zona de mem�ria on s�n + cvReleaseMemStorage( &m_storage ); + + // creem una sequencia buida per als edges + m_storage = cvCreateMemStorage(0); + edges = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, + sizeof(CvContour), + sizeof(CvPoint),m_storage); + + // copiem les propietats del blob origen a l'actual + etiqueta = src.etiqueta; + exterior = src.exterior; + area = src.Area(); + perimeter = src.Perimeter(); + parent = src.parent; + minx = src.minx; + maxx = src.maxx; + miny = src.miny; + maxy = src.maxy; + sumx = src.sumx; + sumy = src.sumy; + sumxx = src.sumxx; + sumyy = src.sumyy; + sumxy = src.sumxy; + mean = src.mean; + stddev = src.stddev; + externPerimeter = src.externPerimeter; + + // copiem els edges del blob origen a l'actual + CvSeqReader reader; + CvSeqWriter writer; + CvPoint edgeactual; + + cvStartReadSeq( src.Edges(), &reader); + cvStartAppendToSeq( edges, &writer ); + + for( int i=0; i< src.Edges()->total; i++) + { + CV_READ_SEQ_ELEM( edgeactual ,reader); + CV_WRITE_SEQ_ELEM( edgeactual , writer ); + } + + cvEndWriteSeq( &writer ); + } + return *this; +} + +/** +- FUNCI�: FillBlob +- FUNCIONALITAT: Pinta l'interior d'un blob amb el color especificat +- PAR�METRES: +- imatge: imatge on es vol pintar el el blob +- color: color amb que es vol pintar el blob +- RESULTAT: +- retorna la imatge d'entrada amb el blob pintat +- RESTRICCIONS: +- AUTOR: +- Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: FillBlob +- FUNCTIONALITY: +- Fills the blob with a specified colour +- PARAMETERS: +- imatge: where to paint +- color: colour to paint the blob +- RESULT: +- modifies input image and returns the seed point used to fill the blob +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +void CBlob::FillBlob( IplImage *imatge, CvScalar color, int offsetX /*=0*/, int offsetY /*=0*/) const +{ + + //verifiquem que existeixi el blob i que tingui cantonades + if( edges == NULL || edges->total == 0 ) return; + + CvPoint edgeactual, pt1, pt2; + CvSeqReader reader; + vectorPunts vectorEdges = vectorPunts( edges->total ); + vectorPunts::iterator itEdges, itEdgesSeguent; + bool dinsBlob; + int yActual; + + // passem els punts del blob a un vector de punts de les STL + cvStartReadSeq( edges, &reader); + itEdges = vectorEdges.begin(); + while( itEdges != vectorEdges.end() ) + { + CV_READ_SEQ_ELEM( edgeactual ,reader); + *itEdges = edgeactual; + itEdges++; + } + // ordenem el vector per les Y's i les X's d'esquerra a dreta + std::sort( vectorEdges.begin(), vectorEdges.end(), comparaCvPoint() ); + + // recorrem el vector ordenat i fem linies entre punts consecutius + itEdges = vectorEdges.begin(); + itEdgesSeguent = vectorEdges.begin() + 1; + dinsBlob = true; + while( itEdges != (vectorEdges.end() - 1)) + { + yActual = (*itEdges).y; + + if( ( (*itEdges).x != (*itEdgesSeguent).x ) && + ( (*itEdgesSeguent).y == yActual ) + ) + { + if( dinsBlob ) + { + pt1 = *itEdges; + pt1.x += offsetX; + pt1.y += offsetY; + + pt2 = *itEdgesSeguent; + pt2.x += offsetX; + pt2.y += offsetY; + + cvLine( imatge, pt1, pt2, color ); + } + dinsBlob =! dinsBlob; + } + itEdges++; + itEdgesSeguent++; + if( (*itEdges).y != yActual ) dinsBlob = true; + } + vectorEdges.clear(); +} + +/** +- FUNCI�: CopyEdges +- FUNCIONALITAT: Afegeix els v�rtexs del blob al blob destination +- PAR�METRES: +- destination: blob al que volem afegir els v�rtexs +- RESULTAT: +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: CopyEdges +- FUNCTIONALITY: Adds the blob edges to destination +- PARAMETERS: +- destination: where to add the edges +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +void CBlob::CopyEdges( CBlob &destination ) const +{ + CvSeqReader reader; + CvSeqWriter writer; + CvPoint edgeactual; + + cvStartReadSeq( edges, &reader); + cvStartAppendToSeq( destination.Edges(), &writer ); + + for( int i=0; i<edges->total; i++) + { + CV_READ_SEQ_ELEM( edgeactual ,reader); + CV_WRITE_SEQ_ELEM( edgeactual , writer ); + } + + cvEndWriteSeq( &writer ); +} + +/** +- FUNCI�: ClearEdges +- FUNCIONALITAT: Elimina els v�rtexs del blob +- PAR�METRES: +- RESULTAT: +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: ClearEdges +- FUNCTIONALITY: Delete current blob edges +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +void CBlob::ClearEdges() +{ + // Eliminar v�rtexs del blob eliminat + cvClearSeq( edges ); +} + +/** +- FUNCI�: GetConvexHull +- FUNCIONALITAT: Retorna el poligon convex del blob +- PAR�METRES: +- dst: sequencia on desarem el resultat (no ha d'estar inicialitzada) +- RESULTAT: +- true si tot ha anat b� +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: GetConvexHull +- FUNCTIONALITY: Calculates the convex hull polygon of the blob +- PARAMETERS: +- dst: where to store the result +- RESULT: +- true if no error ocurred +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +bool CBlob::GetConvexHull( CvSeq **dst ) const +{ + if( edges != NULL && edges->total > 0) + { + *dst = cvConvexHull2( edges, 0, CV_CLOCKWISE, 0 ); + return true; + } + return false; +} + +/** +- FUNCI�: GetEllipse +- FUNCIONALITAT: Retorna l'ellipse que s'ajusta millor a les cantonades del blob +- PAR�METRES: +- RESULTAT: +- estructura amb l'ellipse +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 25-05-2005. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: GetEllipse +- FUNCTIONALITY: Calculates the ellipse that best fits the edges of the blob +- PARAMETERS: +- RESULT: +- CvBox2D struct with the calculated ellipse +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +CvBox2D CBlob::GetEllipse() const +{ + CvBox2D elipse; + // necessitem 6 punts per calcular l'elipse + if( edges != NULL && edges->total > 6) + { + elipse = cvFitEllipse2( edges ); + } + else + { + elipse.center.x = 0.0; + elipse.center.y = 0.0; + elipse.size.width = 0.0; + elipse.size.height = 0.0; + elipse.angle = 0.0; + } + return elipse; +} + + + +/*************************************************************************** +Implementaci� de les classes per al c�lcul de caracter�stiques sobre el blob + +Implementation of the helper classes to perform operations on blobs +**************************************************************************/ + +/** +- FUNCI�: Moment +- FUNCIONALITAT: Calcula el moment pq del blob +- RESULTAT: +- retorna el moment pq especificat o 0 si el moment no est� implementat +- RESTRICCIONS: +- Implementats els moments pq: 00, 01, 10, 20, 02 +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 20-07-2004. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: Moment +- FUNCTIONALITY: Calculates the pq moment of the blob +- PARAMETERS: +- RESULT: +- returns the pq moment or 0 if the moment it is not implemented +- RESTRICTIONS: +- Currently, only implemented the 00, 01, 10, 20, 02 pq moments +- AUTHOR: Ricard Borr�s +- CREATION DATE: 20-07-2004. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetMoment::operator()(const CBlob &blob) const +{ + //Moment 00 + if((m_p==0) && (m_q==0)) + return blob.Area(); + + //Moment 10 + if((m_p==1) && (m_q==0)) + return blob.SumX(); + + //Moment 01 + if((m_p==0) && (m_q==1)) + return blob.SumY(); + + //Moment 20 + if((m_p==2) && (m_q==0)) + return blob.SumXX(); + + //Moment 02 + if((m_p==0) && (m_q==2)) + return blob.SumYY(); + + return 0; +} + +/** +- FUNCI�: HullPerimeter +- FUNCIONALITAT: Calcula la longitud del perimetre convex del blob. +Fa servir la funci� d'OpenCV cvConvexHull2 per a +calcular el perimetre convex. + +- PAR�METRES: +- RESULTAT: +- retorna la longitud del per�metre convex del blob. Si el blob no t� coordenades +associades retorna el per�metre normal del blob. +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 20-07-2004. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: CBlobGetHullPerimeter +- FUNCTIONALITY: Calculates the convex hull perimeter of the blob +- PARAMETERS: +- RESULT: +- returns the convex hull perimeter of the blob or the perimeter if the +blob edges could not be retrieved +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetHullPerimeter::operator()(const CBlob &blob) const +{ + if(blob.Edges() != NULL && blob.Edges()->total > 0) + { + CvSeq *hull = cvConvexHull2( blob.Edges(), 0, CV_CLOCKWISE, 1 ); + return fabs(cvArcLength(hull,CV_WHOLE_SEQ,1)); + } + return blob.Perimeter(); +} + +double CBlobGetHullArea::operator()(const CBlob &blob) const +{ + if(blob.Edges() != NULL && blob.Edges()->total > 0) + { + CvSeq *hull = cvConvexHull2( blob.Edges(), 0, CV_CLOCKWISE, 1 ); + return fabs(cvContourArea(hull)); + } + return blob.Perimeter(); +} + +/** +- FUNCI�: MinX_at_MinY +- FUNCIONALITAT: Calcula el valor MinX a MinY. +- PAR�METRES: +- blob: blob del que volem calcular el valor +- RESULTAT: +- retorna la X minima en la Y minima. +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 20-07-2004. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: CBlobGetMinXatMinY +- FUNCTIONALITY: Calculates the minimum X on the minimum Y +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetMinXatMinY::operator()(const CBlob &blob) const +{ + double MinX_at_MinY = LONG_MAX; + + CvSeqReader reader; + CvPoint edgeactual; + + cvStartReadSeq(blob.Edges(),&reader); + + for(int j=0;j<blob.Edges()->total;j++) + { + CV_READ_SEQ_ELEM(edgeactual,reader); + if( (edgeactual.y == blob.MinY()) && (edgeactual.x < MinX_at_MinY) ) + { + MinX_at_MinY = edgeactual.x; + } + } + + return MinX_at_MinY; +} + +/** +- FUNCI�: MinY_at_MaxX +- FUNCIONALITAT: Calcula el valor MinX a MaxX. +- PAR�METRES: +- blob: blob del que volem calcular el valor +- RESULTAT: +- retorna la Y minima en la X maxima. +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 20-07-2004. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: CBlobGetMinXatMinY +- FUNCTIONALITY: Calculates the minimum Y on the maximum X +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetMinYatMaxX::operator()(const CBlob &blob) const +{ + double MinY_at_MaxX = LONG_MAX; + + CvSeqReader reader; + CvPoint edgeactual; + + cvStartReadSeq(blob.Edges(),&reader); + + for(int j=0;j<blob.Edges()->total;j++) + { + CV_READ_SEQ_ELEM(edgeactual,reader); + if( (edgeactual.x == blob.MaxX()) && (edgeactual.y < MinY_at_MaxX) ) + { + MinY_at_MaxX = edgeactual.y; + } + } + + return MinY_at_MaxX; +} + +/** +- FUNCI�: MaxX_at_MaxY +- FUNCIONALITAT: Calcula el valor MaxX a MaxY. +- PAR�METRES: +- blob: blob del que volem calcular el valor +- RESULTAT: +- retorna la X maxima en la Y maxima. +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 20-07-2004. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: CBlobGetMaxXatMaxY +- FUNCTIONALITY: Calculates the maximum X on the maximum Y +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetMaxXatMaxY::operator()(const CBlob &blob) const +{ + double MaxX_at_MaxY = LONG_MIN; + + CvSeqReader reader; + CvPoint edgeactual; + + cvStartReadSeq(blob.Edges(),&reader); + + for(int j=0;j<blob.Edges()->total;j++) + { + CV_READ_SEQ_ELEM(edgeactual,reader); + if( (edgeactual.y == blob.MaxY()) && (edgeactual.x > MaxX_at_MaxY) ) + { + MaxX_at_MaxY = edgeactual.x; + } + } + + return MaxX_at_MaxY; +} + +/** +- FUNCI�: MaxY_at_MinX +- FUNCIONALITAT: Calcula el valor MaxY a MinX. +- PAR�METRES: +- blob: blob del que volem calcular el valor +- RESULTAT: +- retorna la Y maxima en la X minima. +- RESTRICCIONS: +- AUTOR: Ricard Borr�s +- DATA DE CREACI�: 20-07-2004. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: CBlobGetMaxYatMinX +- FUNCTIONALITY: Calculates the maximum Y on the minimum X +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetMaxYatMinX::operator()(const CBlob &blob) const +{ + double MaxY_at_MinX = LONG_MIN; + + CvSeqReader reader; + CvPoint edgeactual; + + cvStartReadSeq(blob.Edges(),&reader); + + for(int j=0;j<blob.Edges()->total;j++) + { + CV_READ_SEQ_ELEM(edgeactual,reader); + if( (edgeactual.x == blob.MinY()) && (edgeactual.y > MaxY_at_MinX) ) + { + MaxY_at_MinX = edgeactual.y; + } + } + + return MaxY_at_MinX; +} + +/** +Retorna l'elongaci� del blob (longitud/amplada) +*/ +/** +- FUNCTION: CBlobGetElongation +- FUNCTIONALITY: Calculates the elongation of the blob ( length/breadth ) +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- See below to see how the lenght and the breadth are aproximated +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetElongation::operator()(const CBlob &blob) const +{ + double ampladaC,longitudC,amplada,longitud; + + ampladaC=(double) (blob.Perimeter()+sqrt(pow(blob.Perimeter(),2)-16*blob.Area()))/4; + if(ampladaC<=0.0) return 0; + longitudC=(double) blob.Area()/ampladaC; + + longitud=MAX( longitudC , ampladaC ); + amplada=MIN( longitudC , ampladaC ); + + return (double) longitud/amplada; +} + +/** +Retorna la compacitat del blob +*/ +/** +- FUNCTION: CBlobGetCompactness +- FUNCTIONALITY: Calculates the compactness of the blob +( maximum for circle shaped blobs, minimum for the rest) +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetCompactness::operator()(const CBlob &blob) const +{ + if( blob.Area() != 0.0 ) + return (double) pow(blob.Perimeter(),2)/(4*CV_PI*blob.Area()); + else + return 0.0; +} + +/** +Retorna la rugositat del blob +*/ +/** +- FUNCTION: CBlobGetRoughness +- FUNCTIONALITY: Calculates the roughness of the blob +( ratio between perimeter and convex hull perimeter) +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetRoughness::operator()(const CBlob &blob) const +{ + CBlobGetHullPerimeter getHullPerimeter = CBlobGetHullPerimeter(); + + double hullPerimeter = getHullPerimeter(blob); + + if( hullPerimeter != 0.0 ) + return blob.Perimeter() / hullPerimeter;//HullPerimeter(); + + return 0.0; +} + +/** +Retorna la longitud del blob +*/ +/** +- FUNCTION: CBlobGetLength +- FUNCTIONALITY: Calculates the lenght of the blob (the biggest axis of the blob) +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- The lenght is an aproximation to the real lenght +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetLength::operator()(const CBlob &blob) const +{ + double ampladaC,longitudC; + double tmp; + + tmp = blob.Perimeter()*blob.Perimeter() - 16*blob.Area(); + + if( tmp > 0.0 ) + ampladaC = (double) (blob.Perimeter()+sqrt(tmp))/4; + // error intr�nsec en els c�lculs de l'�rea i el per�metre + else + ampladaC = (double) (blob.Perimeter())/4; + + if(ampladaC<=0.0) return 0; + longitudC=(double) blob.Area()/ampladaC; + + return MAX( longitudC , ampladaC ); +} + +/** +Retorna l'amplada del blob +*/ +/** +- FUNCTION: CBlobGetBreadth +- FUNCTIONALITY: Calculates the breadth of the blob (the smallest axis of the blob) +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- The breadth is an aproximation to the real breadth +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetBreadth::operator()(const CBlob &blob) const +{ + double ampladaC,longitudC; + double tmp; + + tmp = blob.Perimeter()*blob.Perimeter() - 16*blob.Area(); + + if( tmp > 0.0 ) + ampladaC = (double) (blob.Perimeter()+sqrt(tmp))/4; + // error intr�nsec en els c�lculs de l'�rea i el per�metre + else + ampladaC = (double) (blob.Perimeter())/4; + + if(ampladaC<=0.0) return 0; + longitudC = (double) blob.Area()/ampladaC; + + return MIN( longitudC , ampladaC ); +} + +/** +Calcula la dist�ncia entre un punt i el centre del blob +*/ +/** +- FUNCTION: CBlobGetDistanceFromPoint +- FUNCTIONALITY: Calculates the euclidean distance between the blob center and +the point specified in the constructor +- PARAMETERS: +- RESULT: +- RESTRICTIONS: +- AUTHOR: Ricard Borr�s +- CREATION DATE: 25-05-2005. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetDistanceFromPoint::operator()(const CBlob &blob) const +{ + double xmitjana, ymitjana; + CBlobGetXCenter getXCenter; + CBlobGetYCenter getYCenter; + + xmitjana = m_x - getXCenter( blob ); + ymitjana = m_y - getYCenter( blob ); + + return sqrt((xmitjana*xmitjana)+(ymitjana*ymitjana)); +} + +/** +- FUNCI�: BlobGetXYInside +- FUNCIONALITAT: Calcula si un punt cau dins de la capsa rectangular +del blob +- RESULTAT: +- retorna 1 si hi est�; 0 si no +- RESTRICCIONS: +- AUTOR: Francesc Pinyol Margalef +- DATA DE CREACI�: 16-01-2006. +- MODIFICACI�: Data. Autor. Descripci�. +*/ +/** +- FUNCTION: BlobGetXYInside +- FUNCTIONALITY: Calculates whether a point is inside the +rectangular bounding box of a blob +- PARAMETERS: +- RESULT: +- returns 1 if it is inside; o if not +- RESTRICTIONS: +- AUTHOR: Francesc Pinyol Margalef +- CREATION DATE: 16-01-2006. +- MODIFICATION: Date. Author. Description. +*/ +double CBlobGetXYInside::operator()(const CBlob &blob) const +{ + if( blob.Edges() == NULL || blob.Edges()->total == 0 ) return 0.0; + + // passem els punts del blob a un vector de punts de les STL + CvSeqReader reader; + CBlob::vectorPunts vectorEdges; + CBlob::vectorPunts::iterator itEdges, itEdgesSeguent; + CvPoint edgeactual; + bool dinsBlob; + + // agafem tots els punts amb la mateixa y que l'actual + cvStartReadSeq( blob.Edges(), &reader); + + for( int i=0; i< blob.Edges()->total; i++) + { + CV_READ_SEQ_ELEM( edgeactual ,reader ); + if( edgeactual.y == m_p.y ) + vectorEdges.push_back( edgeactual ); + } + + if( vectorEdges.size() == 0 ) return 0.0; + + // ordenem el vector per les Y's i les X's d'esquerra a dreta + std::sort( vectorEdges.begin(), vectorEdges.end(), CBlob::comparaCvPoint() ); + + // recorrem el punts del blob de la mateixa fila que el punt d'entrada + // i mirem si la X del punt d'entrada est� entre dos coordenades "plenes" + // del blob + itEdges = vectorEdges.begin(); + itEdgesSeguent = vectorEdges.begin() + 1; + dinsBlob = true; + + while( itEdges != (vectorEdges.end() - 1) ) + { + if( (*itEdges).x <= m_p.x && (*itEdgesSeguent).x >= m_p.x && dinsBlob ) + { + vectorEdges.clear(); + return 1.0; + } + + itEdges++; + itEdgesSeguent++; + dinsBlob = !dinsBlob; + } + + vectorEdges.clear(); + return 0.0; +} + +#ifdef BLOB_OBJECT_FACTORY + +/** +- FUNCI�: RegistraTotsOperadors +- FUNCIONALITAT: Registrar tots els operadors definits a blob.h +- PAR�METRES: +- fabricaOperadorsBlob: f�brica on es registraran els operadors +- RESULTAT: +- Modifica l'objecte fabricaOperadorsBlob +- RESTRICCIONS: +- Nom�s es registraran els operadors de blob.h. Si se'n volen afegir, cal afegir-los amb +el m�tode Register de la f�brica. +- AUTOR: rborras +- DATA DE CREACI�: 2006/05/18 +- MODIFICACI�: Data. Autor. Descripci�. +*/ +void RegistraTotsOperadors( t_OperadorBlobFactory &fabricaOperadorsBlob ) +{ + // blob shape + fabricaOperadorsBlob.Register( CBlobGetArea().GetNom(), Type2Type<CBlobGetArea>()); + fabricaOperadorsBlob.Register( CBlobGetBreadth().GetNom(), Type2Type<CBlobGetBreadth>()); + fabricaOperadorsBlob.Register( CBlobGetCompactness().GetNom(), Type2Type<CBlobGetCompactness>()); + fabricaOperadorsBlob.Register( CBlobGetElongation().GetNom(), Type2Type<CBlobGetElongation>()); + fabricaOperadorsBlob.Register( CBlobGetExterior().GetNom(), Type2Type<CBlobGetExterior>()); + fabricaOperadorsBlob.Register( CBlobGetLength().GetNom(), Type2Type<CBlobGetLength>()); + fabricaOperadorsBlob.Register( CBlobGetPerimeter().GetNom(), Type2Type<CBlobGetPerimeter>()); + fabricaOperadorsBlob.Register( CBlobGetRoughness().GetNom(), Type2Type<CBlobGetRoughness>()); + + // extern pixels + fabricaOperadorsBlob.Register( CBlobGetExternPerimeterRatio().GetNom(), Type2Type<CBlobGetExternPerimeterRatio>()); + fabricaOperadorsBlob.Register( CBlobGetExternHullPerimeterRatio().GetNom(), Type2Type<CBlobGetExternHullPerimeterRatio>()); + fabricaOperadorsBlob.Register( CBlobGetExternPerimeter().GetNom(), Type2Type<CBlobGetExternPerimeter>()); + + + // hull + fabricaOperadorsBlob.Register( CBlobGetHullPerimeter().GetNom(), Type2Type<CBlobGetHullPerimeter>()); + fabricaOperadorsBlob.Register( CBlobGetHullArea().GetNom(), Type2Type<CBlobGetHullArea>()); + + + // elipse info + fabricaOperadorsBlob.Register( CBlobGetMajorAxisLength().GetNom(), Type2Type<CBlobGetMajorAxisLength>()); + fabricaOperadorsBlob.Register( CBlobGetMinorAxisLength().GetNom(), Type2Type<CBlobGetMinorAxisLength>()); + fabricaOperadorsBlob.Register( CBlobGetAxisRatio().GetNom(), Type2Type<CBlobGetAxisRatio>()); + fabricaOperadorsBlob.Register( CBlobGetOrientation().GetNom(), Type2Type<CBlobGetOrientation>()); + fabricaOperadorsBlob.Register( CBlobGetOrientationCos().GetNom(), Type2Type<CBlobGetOrientationCos>()); + fabricaOperadorsBlob.Register( CBlobGetAreaElipseRatio().GetNom(), Type2Type<CBlobGetAreaElipseRatio>()); + + // min an max + fabricaOperadorsBlob.Register( CBlobGetMaxX().GetNom(), Type2Type<CBlobGetMaxX>()); + fabricaOperadorsBlob.Register( CBlobGetMaxY().GetNom(), Type2Type<CBlobGetMaxY>()); + fabricaOperadorsBlob.Register( CBlobGetMinX().GetNom(), Type2Type<CBlobGetMinX>()); + fabricaOperadorsBlob.Register( CBlobGetMinY().GetNom(), Type2Type<CBlobGetMinY>()); + + fabricaOperadorsBlob.Register( CBlobGetMaxXatMaxY().GetNom(), Type2Type<CBlobGetMaxXatMaxY>()); + fabricaOperadorsBlob.Register( CBlobGetMaxYatMinX().GetNom(), Type2Type<CBlobGetMaxYatMinX>()); + fabricaOperadorsBlob.Register( CBlobGetMinXatMinY().GetNom(), Type2Type<CBlobGetMinXatMinY>()); + fabricaOperadorsBlob.Register( CBlobGetMinYatMaxX().GetNom(), Type2Type<CBlobGetMinYatMaxX>()); + + // grey level stats + fabricaOperadorsBlob.Register( CBlobGetMean().GetNom(), Type2Type<CBlobGetMean>()); + fabricaOperadorsBlob.Register( CBlobGetStdDev().GetNom(), Type2Type<CBlobGetStdDev>()); + + // coordinate info + fabricaOperadorsBlob.Register( CBlobGetXYInside().GetNom(), Type2Type<CBlobGetXYInside>()); + fabricaOperadorsBlob.Register( CBlobGetDiffY().GetNom(), Type2Type<CBlobGetDiffY>()); + fabricaOperadorsBlob.Register( CBlobGetDiffX().GetNom(), Type2Type<CBlobGetDiffX>()); + fabricaOperadorsBlob.Register( CBlobGetXCenter().GetNom(), Type2Type<CBlobGetXCenter>()); + fabricaOperadorsBlob.Register( CBlobGetYCenter().GetNom(), Type2Type<CBlobGetYCenter>()); + fabricaOperadorsBlob.Register( CBlobGetDistanceFromPoint().GetNom(), Type2Type<CBlobGetDistanceFromPoint>()); + + // moments + fabricaOperadorsBlob.Register( CBlobGetMoment().GetNom(), Type2Type<CBlobGetMoment>()); + +} + +#endif + +} + diff --git a/package_bgs/jmo/blob.h b/package_bgs/jmo/blob.h new file mode 100644 index 0000000000000000000000000000000000000000..463e81acd05d4d4ec02230ac53aca7796f6b531e --- /dev/null +++ b/package_bgs/jmo/blob.h @@ -0,0 +1,853 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* --- --- --- +* Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. The name of the author may not be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/************************************************************************ +Blob.h + +FUNCIONALITAT: Definici� de la classe CBlob +AUTOR: Inspecta S.L. +MODIFICACIONS (Modificaci�, Autor, Data): + +FUNCTIONALITY: Definition of the CBlob class and some helper classes to perform +some calculations on it +AUTHOR: Inspecta S.L. +MODIFICATIONS (Modification, Author, Date): + +**************************************************************************/ + +//! Disable warnings referred to 255 character truncation for the std:map +//#pragma warning( disable : 4786 ) + +#ifndef CBLOB_INSPECTA_INCLUDED +#define CBLOB_INSPECTA_INCLUDED + +#include "cxcore.h" +#include "BlobLibraryConfiguration.h" +#include <functional> +#include <vector> +#include <algorithm> + + +#ifdef BLOB_OBJECT_FACTORY +//! Object factory pattern implementation +#include "..\inspecta\DesignPatterns\ObjectFactory.h" +#endif + + +//! Factor de conversi� de graus a radians +#define DEGREE2RAD (CV_PI / 180.0) + +namespace Blob +{ + +/** +Classe que representa un blob, ent�s com un conjunt de pixels del +mateix color contigus en una imatge binaritzada. + +Class to represent a blob, a group of connected pixels in a binary image +*/ +class CBlob +{ +public: + //! Constructor est�ndard + //! Standard constructor + CBlob(); + //! Constructor de c�pia + //! Copy constructor + CBlob( const CBlob &src ); + CBlob( const CBlob *src ); + + //! Destructor est�ndard + //! Standard Destructor + ~CBlob(); + + //! Operador d'assignaci� + //! Assigment operator + CBlob& operator=(const CBlob &src ); + + //! Indica si el blob est� buit ( no t� cap info associada ) + //! Shows if the blob has associated information + bool IsEmpty() const + { + return (area == 0.0 && perimeter == 0.0 ); + }; + + //! Neteja les cantonades del blob + //! Clears the edges of the blob + void ClearEdges(); + //! Copia les cantonades del blob a un altre (les afegeix al dest�) + //! Adds the blob edges to another blob + void CopyEdges( CBlob &destination ) const; + //! Retorna el poligon convex del blob + //! Calculates the convex hull of the blob + bool GetConvexHull( CvSeq **dst ) const; + //! Calcula l'elipse que s'adapta als v�rtexs del blob + //! Fits an ellipse to the blob edges + CvBox2D GetEllipse() const; + + //! Pinta l'interior d'un blob d'un color determinat + //! Paints the blob in an image + void FillBlob( IplImage *imatge, CvScalar color, int offsetX = 0, int offsetY = 0 ) const; + + //! Funcions GET sobre els valors dels blobs + //! Get functions + + inline int Label() const { return etiqueta; } + inline int Parent() const { return parent; } + inline double Area() const { return area; } + inline double Perimeter() const { return perimeter; } + inline double ExternPerimeter() const { return externPerimeter; } + inline int Exterior() const { return exterior; } + inline double Mean() const { return mean; } + inline double StdDev() const { return stddev; } + inline double MinX() const { return minx; } + inline double MinY() const { return miny; } + inline double MaxX() const { return maxx; } + inline double MaxY() const { return maxy; } + inline CvSeq *Edges() const { return edges; } + inline double SumX() const { return sumx; } + inline double SumY() const { return sumy; } + inline double SumXX() const { return sumxx; } + inline double SumYY() const { return sumyy; } + inline double SumXY() const { return sumxy; } + + //! etiqueta del blob + //! label of the blob + int etiqueta; + //! flag per indicar si es exterior o no + //! true for extern blobs + int exterior; + //! area del blob + //! Blob area + double area; + //! perimetre del blob + //! Blob perimeter + double perimeter; + //! quantitat de perimetre del blob extern + //! amount of blob perimeter which is exterior + double externPerimeter; + //! etiqueta del blob pare + //! label of the parent blob + int parent; + //! moments + double sumx; + double sumy; + double sumxx; + double sumyy; + double sumxy; + //! Bounding rect + double minx; + double maxx; + double miny; + double maxy; + + //! mitjana + //! mean of the grey scale values of the blob pixels + double mean; + //! desviaci� standard + //! standard deviation of the grey scale values of the blob pixels + double stddev; + + //! �rea de mem�ria on es desaran els punts de contorn del blob + //! storage which contains the edges of the blob + CvMemStorage *m_storage; + //! Sequ�ncia de punts del contorn del blob + //! Sequence with the edges of the blob + CvSeq *edges; + + + //! Point datatype for plotting (FillBlob) + typedef std::vector<CvPoint> vectorPunts; + + //! Helper class to compare two CvPoints (for sorting in FillBlob) + struct comparaCvPoint : public std::binary_function<CvPoint, CvPoint, bool> + { + //! Definim que un punt �s menor com m�s amunt a la dreta estigui + bool operator()(CvPoint a, CvPoint b) + { + if( a.y == b.y ) + return a.x < b.x; + else + return a.y < b.y; + } + }; +}; + + + +/************************************************************************** +Definici� de les classes per a fer operacions sobre els blobs + +Helper classes to perform operations on blobs +**************************************************************************/ + + +//! Classe d'on derivarem totes les operacions sobre els blobs +//! Interface to derive all blob operations +class COperadorBlob +{ +public: + virtual ~COperadorBlob(){}; + + //! Aplica l'operaci� al blob + virtual double operator()(const CBlob &blob) const = 0; + //! Obt� el nom de l'operador + virtual const char *GetNom() const = 0; + + operator COperadorBlob*() const + { + return (COperadorBlob*)this; + } +}; + +typedef COperadorBlob funcio_calculBlob; + +#ifdef BLOB_OBJECT_FACTORY +/** +Funci� per comparar dos identificadors dins de la f�brica de COperadorBlobs +*/ +struct functorComparacioIdOperador +{ + bool operator()(const char* s1, const char* s2) const + { + return strcmp(s1, s2) < 0; + } +}; + +//! Definition of Object factory type for COperadorBlob objects +typedef ObjectFactory<COperadorBlob, const char *, functorComparacioIdOperador > t_OperadorBlobFactory; + +//! Funci� global per a registrar tots els operadors definits a blob.h +void RegistraTotsOperadors( t_OperadorBlobFactory &fabricaOperadorsBlob ); + +#endif + +//! Classe per calcular l'�rea d'un blob +//! Class to get the area of a blob +class CBlobGetArea : public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.Area(); + } + const char *GetNom() const + { + return "CBlobGetArea"; + } +}; + +//! Classe per calcular el perimetre d'un blob +//! Class to get the perimeter of a blob +class CBlobGetPerimeter: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.Perimeter(); + } + const char *GetNom() const + { + return "CBlobGetPerimeter"; + } +}; + +//! Classe que diu si un blob �s extern o no +//! Class to get the extern flag of a blob +class CBlobGetExterior: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.Exterior(); + } + const char *GetNom() const + { + return "CBlobGetExterior"; + } +}; + +//! Classe per calcular la mitjana de nivells de gris d'un blob +//! Class to get the mean grey level of a blob +class CBlobGetMean: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.Mean(); + } + const char *GetNom() const + { + return "CBlobGetMean"; + } +}; + +//! Classe per calcular la desviaci� est�ndard dels nivells de gris d'un blob +//! Class to get the standard deviation of the grey level values of a blob +class CBlobGetStdDev: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.StdDev(); + } + const char *GetNom() const + { + return "CBlobGetStdDev"; + } +}; + +//! Classe per calcular la compacitat d'un blob +//! Class to calculate the compactness of a blob +class CBlobGetCompactness: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetCompactness"; + } +}; + +//! Classe per calcular la longitud d'un blob +//! Class to calculate the length of a blob +class CBlobGetLength: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetLength"; + } +}; + +//! Classe per calcular l'amplada d'un blob +//! Class to calculate the breadth of a blob +class CBlobGetBreadth: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetBreadth"; + } +}; + +//! Classe per calcular la difer�ncia en X del blob +class CBlobGetDiffX: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.maxx - blob.minx; + } + const char *GetNom() const + { + return "CBlobGetDiffX"; + } +}; + +//! Classe per calcular la difer�ncia en X del blob +class CBlobGetDiffY: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.maxy - blob.miny; + } + const char *GetNom() const + { + return "CBlobGetDiffY"; + } +}; + +//! Classe per calcular el moment PQ del blob +//! Class to calculate the P,Q moment of a blob +class CBlobGetMoment: public COperadorBlob +{ +public: + //! Constructor est�ndard + //! Standard constructor (gets the 00 moment) + CBlobGetMoment() + { + m_p = m_q = 0; + } + //! Constructor: indiquem el moment p,q a calcular + //! Constructor: gets the PQ moment + CBlobGetMoment( int p, int q ) + { + m_p = p; + m_q = q; + }; + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetMoment"; + } + +private: + //! moment que volem calcular + int m_p, m_q; +}; + +//! Classe per calcular el perimetre del poligon convex d'un blob +//! Class to calculate the convex hull perimeter of a blob +class CBlobGetHullPerimeter: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetHullPerimeter"; + } +}; + +//! Classe per calcular l'�rea del poligon convex d'un blob +//! Class to calculate the convex hull area of a blob +class CBlobGetHullArea: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetHullArea"; + } +}; + +//! Classe per calcular la x minima en la y minima +//! Class to calculate the minimum x on the minimum y +class CBlobGetMinXatMinY: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetMinXatMinY"; + } +}; + +//! Classe per calcular la y minima en la x maxima +//! Class to calculate the minimum y on the maximum x +class CBlobGetMinYatMaxX: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetMinYatMaxX"; + } +}; + +//! Classe per calcular la x maxima en la y maxima +//! Class to calculate the maximum x on the maximum y +class CBlobGetMaxXatMaxY: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetMaxXatMaxY"; + } +}; + +//! Classe per calcular la y maxima en la x minima +//! Class to calculate the maximum y on the minimum y +class CBlobGetMaxYatMinX: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetMaxYatMinX"; + } +}; + +//! Classe per a calcular la x m�nima +//! Class to get the minimum x +class CBlobGetMinX: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.MinX(); + } + const char *GetNom() const + { + return "CBlobGetMinX"; + } +}; + +//! Classe per a calcular la x m�xima +//! Class to get the maximum x +class CBlobGetMaxX: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.MaxX(); + } + const char *GetNom() const + { + return "CBlobGetMaxX"; + } +}; + +//! Classe per a calcular la y m�nima +//! Class to get the minimum y +class CBlobGetMinY: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.MinY(); + } + const char *GetNom() const + { + return "CBlobGetMinY"; + } +}; + +//! Classe per a calcular la y m�xima +//! Class to get the maximum y +class CBlobGetMaxY: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.MaxY(); + } + const char *GetNom() const + { + return "CBlobGetMax"; + } +}; + + +//! Classe per calcular l'elongacio d'un blob +//! Class to calculate the elongation of the blob +class CBlobGetElongation: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetElongation"; + } +}; + +//! Classe per calcular la rugositat d'un blob +//! Class to calculate the roughness of the blob +class CBlobGetRoughness: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetRoughness"; + } +}; + +//! Classe per calcular la dist�ncia entre el centre del blob i un punt donat +//! Class to calculate the euclidean distance between the center of a blob and a given point +class CBlobGetDistanceFromPoint: public COperadorBlob +{ +public: + //! Standard constructor (distance to point 0,0) + CBlobGetDistanceFromPoint() + { + m_x = m_y = 0.0; + } + //! Constructor (distance to point x,y) + CBlobGetDistanceFromPoint( const double x, const double y ) + { + m_x = x; + m_y = y; + } + + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetDistanceFromPoint"; + } + +private: + // coordenades del punt on volem calcular la dist�ncia + double m_x, m_y; +}; + +//! Classe per calcular el nombre de pixels externs d'un blob +//! Class to get the number of extern pixels of a blob +class CBlobGetExternPerimeter: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.ExternPerimeter(); + } + const char *GetNom() const + { + return "CBlobGetExternPerimeter"; + } +}; + +//! Classe per calcular el ratio entre el perimetre i nombre pixels externs +//! valors propers a 0 indiquen que la majoria del blob �s intern +//! valors propers a 1 indiquen que la majoria del blob �s extern +//! Class to calculate the ratio between the perimeter and the number of extern pixels +class CBlobGetExternPerimeterRatio: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + if( blob.Perimeter() != 0 ) + return blob.ExternPerimeter() / blob.Perimeter(); + else + return blob.ExternPerimeter(); + } + const char *GetNom() const + { + return "CBlobGetExternPerimeterRatio"; + } +}; + +//! Classe per calcular el ratio entre el perimetre convex i nombre pixels externs +//! valors propers a 0 indiquen que la majoria del blob �s intern +//! valors propers a 1 indiquen que la majoria del blob �s extern +//! Class to calculate the ratio between the perimeter and the number of extern pixels +class CBlobGetExternHullPerimeterRatio: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + CBlobGetHullPerimeter getHullPerimeter; + double hullPerimeter; + + if( (hullPerimeter = getHullPerimeter( blob ) ) != 0 ) + return blob.ExternPerimeter() / hullPerimeter; + else + return blob.ExternPerimeter(); + } + const char *GetNom() const + { + return "CBlobGetExternHullPerimeterRatio"; + } +}; + +//! Classe per calcular el centre en el eix X d'un blob +//! Class to calculate the center in the X direction +class CBlobGetXCenter: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.MinX() + (( blob.MaxX() - blob.MinX() ) / 2.0); + } + const char *GetNom() const + { + return "CBlobGetXCenter"; + } +}; + +//! Classe per calcular el centre en el eix Y d'un blob +//! Class to calculate the center in the Y direction +class CBlobGetYCenter: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + return blob.MinY() + (( blob.MaxY() - blob.MinY() ) / 2.0); + } + const char *GetNom() const + { + return "CBlobGetYCenter"; + } +}; + +//! Classe per calcular la longitud de l'eix major d'un blob +//! Class to calculate the length of the major axis of the ellipse that fits the blob edges +class CBlobGetMajorAxisLength: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + CvBox2D elipse = blob.GetEllipse(); + + return elipse.size.width; + } + const char *GetNom() const + { + return "CBlobGetMajorAxisLength"; + } +}; + +//! Classe per calcular el ratio entre l'area de la elipse i la de la taca +//! Class +class CBlobGetAreaElipseRatio: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + if( blob.Area()==0.0 ) return 0.0; + + CvBox2D elipse = blob.GetEllipse(); + double ratioAreaElipseAreaTaca = ( (elipse.size.width/2.0) + * + (elipse.size.height/2.0) + *CV_PI + ) + / + blob.Area(); + + return ratioAreaElipseAreaTaca; + } + const char *GetNom() const + { + return "CBlobGetAreaElipseRatio"; + } +}; + +//! Classe per calcular la longitud de l'eix menor d'un blob +//! Class to calculate the length of the minor axis of the ellipse that fits the blob edges +class CBlobGetMinorAxisLength: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + CvBox2D elipse = blob.GetEllipse(); + + return elipse.size.height; + } + const char *GetNom() const + { + return "CBlobGetMinorAxisLength"; + } +}; + +//! Classe per calcular l'orientaci� de l'ellipse del blob en radians +//! Class to calculate the orientation of the ellipse that fits the blob edges in radians +class CBlobGetOrientation: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + CvBox2D elipse = blob.GetEllipse(); + + if( elipse.angle > 180.0 ) + return (( elipse.angle - 180.0 )* DEGREE2RAD); + else + return ( elipse.angle * DEGREE2RAD); + + } + const char *GetNom() const + { + return "CBlobGetOrientation"; + } +}; + +//! Classe per calcular el cosinus de l'orientaci� de l'ellipse del blob +//! Class to calculate the cosinus of the orientation of the ellipse that fits the blob edges +class CBlobGetOrientationCos: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + CBlobGetOrientation getOrientation; + return fabs( cos( getOrientation(blob) )); + } + const char *GetNom() const + { + return "CBlobGetOrientationCos"; + } +}; + + +//! Classe per calcular el ratio entre l'eix major i menor de la el�lipse +//! Class to calculate the ratio between both axes of the ellipse +class CBlobGetAxisRatio: public COperadorBlob +{ +public: + double operator()(const CBlob &blob) const + { + CvBox2D elipse = blob.GetEllipse(); + + return elipse.size.height / elipse.size.width; + } + const char *GetNom() const + { + return "CBlobGetAxisRatio"; + } +}; + + +//! Classe per calcular si un punt cau dins del blob +//! Class to calculate whether a point is inside a blob +class CBlobGetXYInside: public COperadorBlob +{ +public: + //! Constructor est�ndard + //! Standard constructor + CBlobGetXYInside() + { + m_p = cvPoint(0,0); + } + //! Constructor: indiquem el punt + //! Constructor: sets the point + CBlobGetXYInside( CvPoint p ) + { + m_p = p; + }; + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetXYInside"; + } + +private: + //! punt que considerem + //! point to be considered + CvPoint m_p; +}; + +} + +#endif //CBLOB_INSPECTA_INCLUDED + diff --git a/package_bgs/lb/BGModel.cpp b/package_bgs/lb/BGModel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c42bec73e34dfb1dd83dc319bbb8569281521a62 --- /dev/null +++ b/package_bgs/lb/BGModel.cpp @@ -0,0 +1,87 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments + BGModel.cpp + + Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "BGModel.h" + +namespace lb_library +{ + BGModel::BGModel(int width, int height): m_width(width), m_height(height) + { + m_SrcImage = cvCreateImage(cvSize(m_width,m_height), IPL_DEPTH_8U, 3); + m_BGImage = cvCreateImage(cvSize(m_width,m_height), IPL_DEPTH_8U, 3); + m_FGImage = cvCreateImage(cvSize(m_width,m_height), IPL_DEPTH_8U, 3); + + cvZero(m_SrcImage); + cvZero(m_BGImage); + cvZero(m_FGImage); + } + + BGModel::~BGModel() + { + if (m_SrcImage!=NULL) cvReleaseImage(&m_SrcImage); + if (m_BGImage!=NULL) cvReleaseImage(&m_BGImage); + if (m_FGImage!=NULL) cvReleaseImage(&m_FGImage); + } + + IplImage* BGModel::GetSrc() + { + return m_SrcImage; + } + + IplImage* BGModel::GetFG() + { + return m_FGImage; + } + + IplImage* BGModel::GetBG() + { + return m_BGImage; + } + + void BGModel::InitModel(IplImage* image) + { + cvCopy(image,m_SrcImage); + Init(); + return; + } + + void BGModel::UpdateModel(IplImage* image) + { + cvCopy(image,m_SrcImage); + Update(); + return; + } +} \ No newline at end of file diff --git a/package_bgs/lb/BGModel.h b/package_bgs/lb/BGModel.h new file mode 100644 index 0000000000000000000000000000000000000000..d53b52e7729e9ecdfbe326054c0bc2fff1fc1a86 --- /dev/null +++ b/package_bgs/lb/BGModel.h @@ -0,0 +1,78 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments + BGModel.h + + Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef BGMODEL_H +#define BGMODEL_H + +#include <cv.h> +#include <math.h> +#include <float.h> + +#include "Types.h" + +namespace lb_library +{ + class BGModel + { + public: + + BGModel(int width, int height); + virtual ~BGModel(); + + void InitModel(IplImage* image); + void UpdateModel(IplImage* image); + + virtual void setBGModelParameter(int id, int value) {}; + + virtual IplImage* GetSrc(); + virtual IplImage* GetFG(); + virtual IplImage* GetBG(); + + protected: + + IplImage* m_SrcImage; + IplImage* m_BGImage; + IplImage* m_FGImage; + + const int m_width; + const int m_height; + + virtual void Init() = 0; + virtual void Update() = 0; + }; +} + +#endif diff --git a/package_bgs/lb/BGModelFuzzyGauss.cpp b/package_bgs/lb/BGModelFuzzyGauss.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8706b1376e76f635eae77759b2ab59c63808d152 --- /dev/null +++ b/package_bgs/lb/BGModelFuzzyGauss.cpp @@ -0,0 +1,210 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments +BGModelFuzzyGauss.cpp + +Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "BGModelFuzzyGauss.h" + +namespace lb_library +{ + namespace FuzzyGaussian + { + BGModelFuzzyGauss::BGModelFuzzyGauss(int width, int height) : BGModel(width,height) + { + m_alphamax = ALPHAFUZZYGAUSS; + m_threshold = THRESHOLDFUZZYGAUSS * THRESHOLDFUZZYGAUSS; + m_threshBG = THRESHOLDBG; + m_noise = NOISEFUZZYGAUSS; + + m_pMu = new DBLRGB[m_width * m_height]; + m_pVar = new DBLRGB[m_width * m_height]; + + DBLRGB *pMu = m_pMu; + DBLRGB *pVar = m_pVar; + + for(int k = 0; k < (m_width * m_height); k++) + { + pMu->Red = 0.0; + pMu->Green = 0.0; + pMu->Blue = 0.0; + + pVar->Red = m_noise; + pVar->Green = m_noise; + pVar->Blue = m_noise; + + pMu++; + pVar++; + } + } + + BGModelFuzzyGauss::~BGModelFuzzyGauss() + { + delete [] m_pMu; + delete [] m_pVar; + } + + void BGModelFuzzyGauss::setBGModelParameter(int id, int value) + { + double dvalue = (double)value/255.0; + + switch(id) + { + case 0: + m_threshold = 100.0*dvalue*dvalue; + break; + + case 1: + m_threshBG = dvalue; + break; + + case 2: + m_alphamax = dvalue*dvalue*dvalue; + break; + + case 3: + m_noise = 100.0*dvalue; + break; + } + + return; + } + + void BGModelFuzzyGauss::Init() + { + DBLRGB *pMu = m_pMu; + DBLRGB *pVar = m_pVar; + + Image<BYTERGB> prgbSrc(m_SrcImage); + + for(int i = 0; i < m_height; i++) + { + for(int j = 0; j < m_width; j++) + { + pMu->Red = prgbSrc[i][j].Red; + pMu->Green = prgbSrc[i][j].Green; + pMu->Blue = prgbSrc[i][j].Blue; + + pVar->Red = m_noise; + pVar->Green = m_noise; + pVar->Blue = m_noise; + + pMu++; + pVar++; + } + } + + return; + } + + void BGModelFuzzyGauss::Update() + { + DBLRGB *pMu = m_pMu; + DBLRGB *pVar = m_pVar; + + Image<BYTERGB> prgbSrc(m_SrcImage); + Image<BYTERGB> prgbBG(m_BGImage); + Image<BYTERGB> prgbFG(m_FGImage); + + for(int i = 0; i < m_height; i++) + { + for(int j = 0; j < m_width; j++) + { + double srcR = (double) prgbSrc[i][j].Red; + double srcG = (double) prgbSrc[i][j].Green; + double srcB = (double) prgbSrc[i][j].Blue; + + // Fuzzy background subtraction (Mahalanobis distance) + + double dr = srcR - pMu->Red; + double dg = srcG - pMu->Green; + double db = srcB - pMu->Blue; + + double d2 = dr*dr/pVar->Red + dg*dg/pVar->Green + db*db/pVar->Blue; + + double fuzzyBG = 1.0; + + if(d2 < m_threshold) + fuzzyBG = d2/m_threshold; + + // Fuzzy running average + + double alpha = m_alphamax*exp(FUZZYEXP*fuzzyBG); + + if(dr*dr > DBL_MIN) + pMu->Red += alpha*dr; + + if(dg*dg > DBL_MIN) + pMu->Green += alpha*dg; + + if(db*db > DBL_MIN) + pMu->Blue += alpha*db; + + double d; + + d = (srcR - pMu->Red)*(srcR - pMu->Red) - pVar->Red; + if(d*d > DBL_MIN) + pVar->Red += alpha*d; + + d = (srcG - pMu->Green)*(srcG - pMu->Green) - pVar->Green; + if(d*d > DBL_MIN) + pVar->Green += alpha*d; + + d = (srcB - pMu->Blue)*(srcB - pMu->Blue) - pVar->Blue; + if(d*d > DBL_MIN) + pVar->Blue += alpha*d; + + pVar->Red = (std::max)(pVar->Red,m_noise); + pVar->Green = (std::max)(pVar->Green,m_noise); + pVar->Blue = (std::max)(pVar->Blue,m_noise); + + // Set foreground and background + + if(fuzzyBG >= m_threshBG) + prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 255; + else + prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 0; + + prgbBG[i][j].Red = (unsigned char)pMu->Red; + prgbBG[i][j].Green = (unsigned char)pMu->Green; + prgbBG[i][j].Blue = (unsigned char)pMu->Blue; + + pMu++; + pVar++; + } + } + + return; + } + } +} \ No newline at end of file diff --git a/package_bgs/lb/BGModelFuzzyGauss.h b/package_bgs/lb/BGModelFuzzyGauss.h new file mode 100644 index 0000000000000000000000000000000000000000..033eaf318d260cd8bc1c2c459cb3314886c36d8e --- /dev/null +++ b/package_bgs/lb/BGModelFuzzyGauss.h @@ -0,0 +1,75 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments +BGModelFuzzyGauss.h + +Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef BGMODELFUZZYGAUSS_H +#define BGMODELFUZZYGAUSS_H + +#include "BGModel.h" + +namespace lb_library +{ + namespace FuzzyGaussian + { + const float ALPHAFUZZYGAUSS = 0.02; + const float THRESHOLDFUZZYGAUSS = 3.5; + const float THRESHOLDBG = 0.5; + const float NOISEFUZZYGAUSS = 50.0; + const float FUZZYEXP = -5.0; + + class BGModelFuzzyGauss : public BGModel + { + public: + BGModelFuzzyGauss(int width, int height); + ~BGModelFuzzyGauss(); + + void setBGModelParameter(int id, int value); + + protected: + double m_alphamax; + double m_threshold; + double m_threshBG; + double m_noise; + + DBLRGB* m_pMu; + DBLRGB* m_pVar; + + void Init(); + void Update(); + }; + } +} + +#endif diff --git a/package_bgs/lb/BGModelFuzzySom.cpp b/package_bgs/lb/BGModelFuzzySom.cpp new file mode 100644 index 0000000000000000000000000000000000000000..189cb1aa3cd0faa138e70a197dafe6283eda0656 --- /dev/null +++ b/package_bgs/lb/BGModelFuzzySom.cpp @@ -0,0 +1,298 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments +BGModelFuzzySom.cpp + +Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "BGModelFuzzySom.h" + +namespace lb_library +{ + namespace FuzzyAdaptiveSOM + { + BGModelFuzzySom::BGModelFuzzySom(int width, int height) : BGModel(width,height) + { + m_offset = (KERNEL - 1)/2; + + if(SPAN_NEIGHBORS) + m_pad = 0; + else + m_pad = m_offset; + + // SOM models + + m_widthSOM = m_width*M + 2*m_offset + (m_width-1)*m_pad; + m_heightSOM = m_height*N + 2*m_offset + (m_height-1)*m_pad; + + m_ppSOM = new DBLRGB*[m_heightSOM]; + for(int n = 0; n < m_heightSOM; n++) + m_ppSOM[n] = new DBLRGB[m_widthSOM]; + + for(int j = 0; j < m_heightSOM; j++) + { + for(int i = 0; i < m_widthSOM; i++) + { + m_ppSOM[j][i].Red = 0.0; + m_ppSOM[j][i].Green = 0.0; + m_ppSOM[j][i].Blue = 0.0; + } + } + + // Create weights + + m_ppW = new double*[KERNEL]; + for(int n = 0; n < KERNEL; n++) + m_ppW[n] = new double[KERNEL]; + + // Construct Gaussian kernel using Pascal's triangle + + int cM; + int cN; + m_Wmax = DBL_MIN; + + cN = 1; + for(int j = 0; j < KERNEL; j++) + { + cM = 1; + + for(int i = 0; i < KERNEL; i++) + { + m_ppW[j][i] = cN*cM; + + if(m_ppW[j][i] > m_Wmax) + m_Wmax = m_ppW[j][i]; + + cM = cM * (KERNEL - 1 - i) / (i + 1); + } + + cN = cN * (KERNEL - 1 - j) / (j + 1); + } + + // Parameters + + m_epsilon1 = EPS1*EPS1; + m_epsilon2 = EPS2*EPS2; + + m_alpha1 = C1/m_Wmax; + m_alpha2 = C2/m_Wmax; + + m_K = 0; + m_TSteps = TRAINING_STEPS; + } + + BGModelFuzzySom::~BGModelFuzzySom() + { + for(int n = 0; n < m_heightSOM; n++) + delete [] m_ppSOM[n]; + + delete [] m_ppSOM; + + for(int n = 0; n < KERNEL; n++) + delete [] m_ppW[n]; + + delete [] m_ppW; + } + + void BGModelFuzzySom::setBGModelParameter(int id, int value) + { + double dvalue = (double)value/255.0; + + switch(id) + { + case 0: + m_epsilon2 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue; + break; + + case 1: + m_epsilon1 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue; + break; + + case 2: + m_alpha2 = dvalue*dvalue*dvalue/m_Wmax; + break; + + case 3: + m_alpha1 = dvalue*dvalue*dvalue/m_Wmax; + break; + + case 5: + m_TSteps = (int)(255.0*dvalue); + break; + } + + return; + } + + void BGModelFuzzySom::Init() + { + Image<BYTERGB> prgbSrc(m_SrcImage); + + for(int j = 0; j < m_height; j++) + { + int jj = m_offset + j*(N + m_pad); + + for(int i = 0; i < m_width; i++) + { + int ii = m_offset + i*(M + m_pad); + + for(int l = 0; l < N; l++) + { + for(int k = 0; k < M; k++) + { + m_ppSOM[jj+l][ii+k].Red = (double)prgbSrc[j][i].Red; + m_ppSOM[jj+l][ii+k].Green = (double)prgbSrc[j][i].Green; + m_ppSOM[jj+l][ii+k].Blue = (double)prgbSrc[j][i].Blue; + } + } + } + } + + m_K = 0; + + return; + } + + void BGModelFuzzySom::Update() + { + double alpha,a; + double epsilon; + + // calibration phase + if(m_K <= m_TSteps) + { + epsilon = m_epsilon1; + alpha = (m_alpha1 - m_K * (m_alpha1 - m_alpha2) / m_TSteps); + m_K++; + } + else // online phase + { + epsilon = m_epsilon2; + alpha = m_alpha2; + } + + Image<BYTERGB> prgbSrc(m_SrcImage); + Image<BYTERGB> prgbBG(m_BGImage); + Image<BYTERGB> prgbFG(m_FGImage); + + for(int j = 0; j < m_height; j++) + { + int jj = m_offset + j*(N + m_pad); + + for(int i = 0; i < m_width; i++) + { + int ii = m_offset + i*(M + m_pad); + + double srcR = (double)prgbSrc[j][i].Red; + double srcG = (double)prgbSrc[j][i].Green; + double srcB = (double)prgbSrc[j][i].Blue; + + // Find BMU + + double d2min = DBL_MAX; + int iiHit = ii; + int jjHit = jj; + + for(int l = 0; l < N; l++) + { + for(int k = 0; k < M; k++) + { + double dr = srcR - m_ppSOM[jj+l][ii+k].Red; + double dg = srcG - m_ppSOM[jj+l][ii+k].Green; + double db = srcB - m_ppSOM[jj+l][ii+k].Blue; + + double d2 = dr*dr + dg*dg + db*db; + + if(d2 < d2min) + { + d2min = d2; + iiHit = ii + k; + jjHit = jj + l; + } + } + } + + double fuzzyBG = 1.0; + + if(d2min < epsilon) + fuzzyBG = d2min/epsilon; + + // Update SOM + + double alphamax = alpha*exp(FUZZYEXP*fuzzyBG); + + for(int l = (jjHit - m_offset); l <= (jjHit + m_offset); l++) + { + for(int k = (iiHit - m_offset); k <= (iiHit + m_offset); k++) + { + a = alphamax * m_ppW[l - jjHit + m_offset][k - iiHit + m_offset]; + + // speed hack.. avoid very small increment values. abs() is sloooow. + + double d; + + d = srcR - m_ppSOM[l][k].Red; + if(d*d > DBL_MIN) + m_ppSOM[l][k].Red += a*d; + + d = srcG - m_ppSOM[l][k].Green; + if(d*d > DBL_MIN) + m_ppSOM[l][k].Green += a*d; + + d = srcB - m_ppSOM[l][k].Blue; + if(d*d > DBL_MIN) + m_ppSOM[l][k].Blue += a*d; + } + } + + if(fuzzyBG >= FUZZYTHRESH) + { + // Set foreground image + prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 255; + } + else + { + // Set background image + prgbBG[j][i].Red = m_ppSOM[jjHit][iiHit].Red; + prgbBG[j][i].Green = m_ppSOM[jjHit][iiHit].Green; + prgbBG[j][i].Blue = m_ppSOM[jjHit][iiHit].Blue; + + // Set foreground image + prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 0; + } + } + } + + return; + } + } +} \ No newline at end of file diff --git a/package_bgs/lb/BGModelFuzzySom.h b/package_bgs/lb/BGModelFuzzySom.h new file mode 100644 index 0000000000000000000000000000000000000000..2f21d3e01368863f6fa2188117b7527a520f82c4 --- /dev/null +++ b/package_bgs/lb/BGModelFuzzySom.h @@ -0,0 +1,95 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments +BGModelFuzzySom.h + +Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef BGMODELFUZZYSOM_H +#define BGMODELFUZZYSOM_H + +#include "BGModel.h" + +namespace lb_library +{ + namespace FuzzyAdaptiveSOM + { + // SOM parameters + + const int M = 3; // width SOM (per pixel) + const int N = 3; // height SOM (per pixel) + const int KERNEL = 3; // size Gaussian kernel + + const bool SPAN_NEIGHBORS = false; // true if update neighborhood spans different pixels // + const int TRAINING_STEPS = 100; // number of training steps + + const double EPS1 = 100.0; // model match distance during training + const double EPS2 = 20.0; // model match distance + const double C1 = 1.0; // learning rate during training + const double C2 = 0.05; // learning rate + + const double FUZZYEXP = -5.0; + const double FUZZYTHRESH = 0.8; + + class BGModelFuzzySom : public BGModel + { + public: + BGModelFuzzySom(int width, int height); + ~BGModelFuzzySom(); + + void setBGModelParameter(int id, int value); + + protected: + int m_widthSOM; + int m_heightSOM; + int m_offset; + int m_pad; + int m_K; + int m_TSteps; + + double m_Wmax; + + double m_epsilon1; + double m_epsilon2; + double m_alpha1; + double m_alpha2; + + DBLRGB** m_ppSOM; // SOM grid + double** m_ppW; // Weights + + void Init(); + void Update(); + }; + } +} + +#endif diff --git a/package_bgs/lb/BGModelGauss.cpp b/package_bgs/lb/BGModelGauss.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6892d13b9b9820f8f61734b2bde3d37ce8c74c5d --- /dev/null +++ b/package_bgs/lb/BGModelGauss.cpp @@ -0,0 +1,200 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments +BGModelGauss.cpp + +Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "BGModelGauss.h" + +namespace lb_library +{ + namespace SimpleGaussian + { + BGModelGauss::BGModelGauss(int width, int height) : BGModel(width,height) + { + m_alpha = ALPHAGAUSS; + m_threshold = THRESHGAUSS*THRESHGAUSS; + m_noise = NOISEGAUSS; + + m_pMu = new DBLRGB[m_width * m_height]; + m_pVar = new DBLRGB[m_width * m_height]; + + DBLRGB *pMu = m_pMu; + DBLRGB *pVar = m_pVar; + + for(int k = 0; k < (m_width * m_height); k++) + { + pMu->Red = 0.0; + pMu->Green = 0.0; + pMu->Blue = 0.0; + + pVar->Red = m_noise; + pVar->Green = m_noise; + pVar->Blue = m_noise; + + pMu++; + pVar++; + } + } + + BGModelGauss::~BGModelGauss() + { + delete [] m_pMu; + delete [] m_pVar; + } + + void BGModelGauss::setBGModelParameter(int id, int value) + { + double dvalue = (double)value/255.0; + + switch(id) + { + case 0: + m_threshold = 100.0*dvalue*dvalue; + break; + + case 1: + m_noise = 100.0*dvalue; + break; + + case 2: + m_alpha = dvalue*dvalue*dvalue; + break; + } + + return; + } + + void BGModelGauss::Init() + { + DBLRGB *pMu = m_pMu; + DBLRGB *pVar = m_pVar; + + Image<BYTERGB> prgbSrc(m_SrcImage); + + for(int i = 0; i < m_height; i++) + { + for(int j = 0; j < m_width; j++) + { + pMu->Red = prgbSrc[i][j].Red; + pMu->Green = prgbSrc[i][j].Green; + pMu->Blue = prgbSrc[i][j].Blue; + + pVar->Red = m_noise; + pVar->Green = m_noise; + pVar->Blue = m_noise; + + pMu++; + pVar++; + } + } + + return; + } + + void BGModelGauss::Update() + { + DBLRGB *pMu = m_pMu; + DBLRGB *pVar = m_pVar; + + Image<BYTERGB> prgbSrc(m_SrcImage); + Image<BYTERGB> prgbBG(m_BGImage); + Image<BYTERGB> prgbFG(m_FGImage); + + for(int i = 0; i < m_height; i++) + { + for(int j = 0; j < m_width; j++) + { + double srcR = (double) prgbSrc[i][j].Red; + double srcG = (double) prgbSrc[i][j].Green; + double srcB = (double) prgbSrc[i][j].Blue; + + // Mahalanobis distance + + double dr = srcR - pMu->Red; + double dg = srcG - pMu->Green; + double db = srcB - pMu->Blue; + + double d2 = dr*dr/pVar->Red + dg*dg/pVar->Green + db*db/pVar->Blue; + + // Classify + + if(d2 < m_threshold) + prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 0; + else + prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 255; + + // Update parameters + + if(dr*dr > DBL_MIN) + pMu->Red += m_alpha*dr; + + if(dg*dg > DBL_MIN) + pMu->Green += m_alpha*dg; + + if(db*db > DBL_MIN) + pMu->Blue += m_alpha*db; + + double d; + + d = (srcR - pMu->Red)*(srcR - pMu->Red) - pVar->Red; + if(d*d > DBL_MIN) + pVar->Red += m_alpha*d; + + d = (srcG - pMu->Green)*(srcG - pMu->Green) - pVar->Green; + if(d*d > DBL_MIN) + pVar->Green += m_alpha*d; + + d = (srcB - pMu->Blue)*(srcB - pMu->Blue) - pVar->Blue; + if(d*d > DBL_MIN) + pVar->Blue += m_alpha*d; + + pVar->Red = (std::min)(pVar->Red,m_noise); + pVar->Green = (std::min)(pVar->Green,m_noise); + pVar->Blue = (std::min)(pVar->Blue,m_noise); + + // Set background + + prgbBG[i][j].Red = (unsigned char)pMu->Red; + prgbBG[i][j].Green = (unsigned char)pMu->Green; + prgbBG[i][j].Blue = (unsigned char)pMu->Blue; + + pMu++; + pVar++; + } + } + + return; + } + } +} \ No newline at end of file diff --git a/package_bgs/lb/BGModelGauss.h b/package_bgs/lb/BGModelGauss.h new file mode 100644 index 0000000000000000000000000000000000000000..d36a716ab4094acd9ea431b6c834dc563ecfacae --- /dev/null +++ b/package_bgs/lb/BGModelGauss.h @@ -0,0 +1,73 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments +BGModelGauss.h + +Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef BGMODELGAUSS_H +#define BGMODELGAUSS_H + +#include "BGModel.h" + +namespace lb_library +{ + namespace SimpleGaussian + { + // Parameters + const double THRESHGAUSS = 2.5; // Threshold + const double ALPHAGAUSS = 0.0001; // Learning rate + const double NOISEGAUSS = 50.0; // Minimum variance (noise) + + class BGModelGauss : public BGModel + { + public: + BGModelGauss(int width, int height); + ~BGModelGauss(); + + void setBGModelParameter(int id, int value); + + protected: + double m_alpha; + double m_threshold; + double m_noise; + + DBLRGB* m_pMu; + DBLRGB* m_pVar; + + void Init(); + void Update(); + }; + } +} + +#endif diff --git a/package_bgs/lb/BGModelMog.cpp b/package_bgs/lb/BGModelMog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..df6986c90327115f7fb56d5066f976b7e1f8b3f7 --- /dev/null +++ b/package_bgs/lb/BGModelMog.cpp @@ -0,0 +1,309 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments +BGModelMog.cpp + +Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "BGModelMog.h" + +namespace lb_library +{ + namespace MixtureOfGaussians + { + BGModelMog::BGModelMog(int width, int height) : BGModel(width, height) + { + m_alpha = LEARNINGRATEMOG; + m_threshold = THRESHOLDMOG*THRESHOLDMOG; + m_noise = INITIALVARMOG; + + m_T = BGTHRESHOLDMOG; + + m_pMOG = new MOGDATA[NUMBERGAUSSIANS*m_width*m_height]; + m_pK = new int[m_width*m_height]; + + MOGDATA *pMOG = m_pMOG; + int *pK = m_pK; + + for(int i = 0; i < (m_width * m_height); i++) + { + for(int k = 0; k < NUMBERGAUSSIANS; k++) + { + pMOG->mu.Red = 0.0; + pMOG->mu.Green = 0.0; + pMOG->mu.Blue = 0.0; + + pMOG->var.Red = 0.0; + pMOG->var.Green = 0.0; + pMOG->var.Blue = 0.0; + + pMOG->w = 0.0; + pMOG->sortKey = 0.0; + + pMOG++; + } + + pK[i] = 0; + } + } + + BGModelMog::~BGModelMog() + { + delete [] m_pMOG; + delete [] m_pK; + } + + void BGModelMog::setBGModelParameter(int id, int value) + { + double dvalue = (double)value/255.0; + + switch(id) + { + case 0: + m_threshold = 100.0*dvalue*dvalue; + break; + + case 1: + m_T = dvalue; + break; + + case 2: + m_alpha = dvalue*dvalue*dvalue; + break; + + case 3: + m_noise = 100.0*dvalue;; + break; + } + + return; + } + + void BGModelMog::Init() + { + MOGDATA *pMOG = m_pMOG; + int *pK = m_pK; + + Image<BYTERGB> prgbSrc(m_SrcImage); + + int n = 0; + for(int i = 0; i < m_height; i++) + { + for(int j = 0; j < m_width; j++) + { + pMOG[0].mu.Red = prgbSrc[i][j].Red; + pMOG[0].mu.Green = prgbSrc[i][j].Green; + pMOG[0].mu.Blue = prgbSrc[i][j].Blue; + + pMOG[0].var.Red = m_noise; + pMOG[0].var.Green = m_noise; + pMOG[0].var.Blue = m_noise; + + pMOG[0].w = 1.0; + pMOG[0].sortKey = pMOG[0].w/sqrt(pMOG[0].var.Red+pMOG[0].var.Green+pMOG[0].var.Blue); + + pK[n] = 1; + n++; + + pMOG += NUMBERGAUSSIANS; + } + } + + return; + } + + void BGModelMog::Update() + { + int kBG; + + MOGDATA *pMOG = m_pMOG; + int *pK = m_pK; + + Image<BYTERGB> prgbSrc(m_SrcImage); + Image<BYTERGB> prgbBG(m_BGImage); + Image<BYTERGB> prgbFG(m_FGImage); + + int n = 0; + for(int i = 0; i < m_height; i++) + { + for(int j = 0; j < m_width; j++) + { + double srcR = (double) prgbSrc[i][j].Red; + double srcG = (double) prgbSrc[i][j].Green; + double srcB = (double) prgbSrc[i][j].Blue; + + // Find matching distribution + + int kHit = -1; + + for(int k = 0; k < pK[n]; k++) + { + // Mahalanobis distance + double dr = srcR - pMOG[k].mu.Red; + double dg = srcG - pMOG[k].mu.Green; + double db = srcB - pMOG[k].mu.Blue; + double d2 = dr*dr/pMOG[k].var.Red + dg*dg/pMOG[k].var.Green + db*db/pMOG[k].var.Blue; + + if(d2 < m_threshold) + { + kHit = k; + break; + } + } + + // Adjust parameters + + // matching distribution found + if(kHit != -1) + { + for(int k = 0; k < pK[n]; k++) + { + if(k == kHit) + { + pMOG[k].w = pMOG[k].w + m_alpha*(1.0f - pMOG[k].w); + + double d; + + d = srcR - pMOG[k].mu.Red; + if(d*d > DBL_MIN) + pMOG[k].mu.Red += m_alpha*d; + + d = srcG - pMOG[k].mu.Green; + if(d*d > DBL_MIN) + pMOG[k].mu.Green += m_alpha*d; + + d = srcB - pMOG[k].mu.Blue; + if(d*d > DBL_MIN) + pMOG[k].mu.Blue += m_alpha*d; + + d = (srcR - pMOG[k].mu.Red)*(srcR - pMOG[k].mu.Red) - pMOG[k].var.Red; + if(d*d > DBL_MIN) + pMOG[k].var.Red += m_alpha*d; + + d = (srcG - pMOG[k].mu.Green)*(srcG - pMOG[k].mu.Green) - pMOG[k].var.Green; + if(d*d > DBL_MIN) + pMOG[k].var.Green += m_alpha*d; + + d = (srcB - pMOG[k].mu.Blue)*(srcB - pMOG[k].mu.Blue) - pMOG[k].var.Blue; + if(d*d > DBL_MIN) + pMOG[k].var.Blue += m_alpha*d; + + pMOG[k].var.Red = (std::max)(pMOG[k].var.Red,m_noise); + pMOG[k].var.Green = (std::max)(pMOG[k].var.Green,m_noise); + pMOG[k].var.Blue = (std::max)(pMOG[k].var.Blue,m_noise); + } + else + pMOG[k].w = (1.0 - m_alpha)*pMOG[k].w; + } + } + // no match found... create new one + else + { + if(pK[n] < NUMBERGAUSSIANS) + pK[n]++; + + kHit = pK[n] - 1; + + if(pK[n] == 1) + pMOG[kHit].w = 1.0; + else + pMOG[kHit].w = LEARNINGRATEMOG; + + pMOG[kHit].mu.Red = srcR; + pMOG[kHit].mu.Green = srcG; + pMOG[kHit].mu.Blue = srcB; + + pMOG[kHit].var.Red = m_noise; + pMOG[kHit].var.Green = m_noise; + pMOG[kHit].var.Blue = m_noise; + } + + // Normalize weights + + double wsum = 0.0; + + for(int k = 0; k < pK[n]; k++) + wsum += pMOG[k].w; + + double wfactor = 1.0/wsum; + + for(int k = 0; k < pK[n]; k++) + { + pMOG[k].w *= wfactor; + pMOG[k].sortKey = pMOG[k].w/sqrt(pMOG[k].var.Red+pMOG[k].var.Green+pMOG[k].var.Blue); + } + + // Sort distributions + + for (int k = 0; k < kHit; k++) + { + if(pMOG[kHit].sortKey > pMOG[k].sortKey) + { + std::swap(pMOG[kHit],pMOG[k]); + break; + } + } + + // Determine background distributions + + wsum = 0.0; + + for(int k = 0; k < pK[n]; k++) + { + wsum += pMOG[k].w; + + if(wsum > m_T) + { + kBG = k; + break; + } + } + + if(kHit > kBG) + prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 255; + else + prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 0; + + prgbBG[i][j].Red = (unsigned char)pMOG[0].mu.Red; + prgbBG[i][j].Green = (unsigned char)pMOG[0].mu.Green; + prgbBG[i][j].Blue = (unsigned char)pMOG[0].mu.Blue; + + pMOG += NUMBERGAUSSIANS; + + n++; + } + } + + return; + } + } +} \ No newline at end of file diff --git a/package_bgs/lb/BGModelMog.h b/package_bgs/lb/BGModelMog.h new file mode 100644 index 0000000000000000000000000000000000000000..c4e51a4447722bb0d8719297e1bd2e6269a53968 --- /dev/null +++ b/package_bgs/lb/BGModelMog.h @@ -0,0 +1,83 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments +BGModelMog.h + +Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef BGMODELMOGRGB_H +#define BGMODELMOGRGB_H + +#include "BGModel.h" + +namespace lb_library +{ + namespace MixtureOfGaussians + { + const unsigned int NUMBERGAUSSIANS = 3; + const float LEARNINGRATEMOG = 0.001f; + const float THRESHOLDMOG = 2.5f; + const float BGTHRESHOLDMOG = 0.5f; + const float INITIALVARMOG = 50.0f; + + typedef struct tagMOGDATA + { + DBLRGB mu; + DBLRGB var; + double w; + double sortKey; + } MOGDATA; + + class BGModelMog : public BGModel + { + public: + BGModelMog(int width, int height); + ~BGModelMog(); + + void setBGModelParameter(int id, int value); + + protected: + double m_alpha; + double m_threshold; + double m_noise; + double m_T; + + MOGDATA* m_pMOG; + int* m_pK; // number of distributions per pixel + + void Init(); + void Update(); + }; + } +} + +#endif diff --git a/package_bgs/lb/BGModelSom.cpp b/package_bgs/lb/BGModelSom.cpp new file mode 100644 index 0000000000000000000000000000000000000000..256d4855f62d71aab321be466098f754fa7597ed --- /dev/null +++ b/package_bgs/lb/BGModelSom.cpp @@ -0,0 +1,291 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments +BGModelSom.cpp + +Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "BGModelSom.h" + +namespace lb_library +{ + namespace AdaptiveSOM + { + BGModelSom::BGModelSom(int width, int height) : BGModel(width,height) + { + m_offset = (KERNEL - 1)/2; + + if(SPAN_NEIGHBORS) + m_pad = 0; + else + m_pad = m_offset; + + // SOM models + + m_widthSOM = m_width*M + 2*m_offset + (m_width-1)*m_pad; + m_heightSOM = m_height*N + 2*m_offset + (m_height-1)*m_pad; + + m_ppSOM = new DBLRGB*[m_heightSOM]; + for(int n = 0; n < m_heightSOM; n++) + m_ppSOM[n] = new DBLRGB[m_widthSOM]; + + for(int j = 0; j < m_heightSOM; j++) + { + for(int i = 0; i < m_widthSOM; i++) + { + m_ppSOM[j][i].Red = 0.0; + m_ppSOM[j][i].Green = 0.0; + m_ppSOM[j][i].Blue = 0.0; + } + } + + // Create weights + + m_ppW = new double*[KERNEL]; + for(int n = 0; n < KERNEL; n++) + m_ppW[n] = new double[KERNEL]; + + // Construct Gaussian kernel using Pascal's triangle + + int cM; + int cN; + m_Wmax = DBL_MIN; + + cN = 1; + for(int j = 0; j < KERNEL; j++) + { + cM = 1; + + for(int i = 0; i < KERNEL; i++) + { + m_ppW[j][i] = cN*cM; + + if(m_ppW[j][i] > m_Wmax) + m_Wmax = m_ppW[j][i]; + + cM = cM * (KERNEL - 1 - i) / (i + 1); + } + + cN = cN * (KERNEL - 1 - j) / (j + 1); + } + + // Parameters + + m_epsilon1 = EPS1*EPS1; + m_epsilon2 = EPS2*EPS2; + + m_alpha1 = C1/m_Wmax; + m_alpha2 = C2/m_Wmax; + + m_K = 0; + m_TSteps = TRAINING_STEPS; + } + + BGModelSom::~BGModelSom() + { + for(int n = 0; n < m_heightSOM; n++) + delete [] m_ppSOM[n]; + + delete [] m_ppSOM; + + for(int n = 0; n < KERNEL; n++) + delete [] m_ppW[n]; + + delete [] m_ppW; + } + + void BGModelSom::setBGModelParameter(int id, int value) + { + double dvalue = (double)value/255.0; + + switch(id) + { + case 0: + m_epsilon2 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue; + break; + + case 1: + m_epsilon1 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue; + break; + + case 2: + m_alpha2 = dvalue*dvalue*dvalue/m_Wmax; + break; + + case 3: + m_alpha1 = dvalue*dvalue*dvalue/m_Wmax; + break; + + case 5: + m_TSteps = (int)(255.0*dvalue); + break; + } + + return; + } + + void BGModelSom::Init() + { + Image<BYTERGB> prgbSrc(m_SrcImage); + + for(int j = 0; j < m_height; j++) + { + int jj = m_offset + j*(N + m_pad); + + for(int i = 0; i < m_width; i++) + { + int ii = m_offset + i*(M + m_pad); + + for(int l = 0; l < N; l++) + { + for(int k = 0; k < M; k++) + { + m_ppSOM[jj+l][ii+k].Red = (double)prgbSrc[j][i].Red; + m_ppSOM[jj+l][ii+k].Green = (double)prgbSrc[j][i].Green; + m_ppSOM[jj+l][ii+k].Blue = (double)prgbSrc[j][i].Blue; + } + } + } + } + + m_K = 0; + + return; + } + + void BGModelSom::Update() + { + double alpha,a; + double epsilon; + + // calibration phase + if(m_K <= m_TSteps) + { + epsilon = m_epsilon1; + alpha = (m_alpha1-m_K*(m_alpha1-m_alpha2)/m_TSteps); + m_K++; + } + else // online phase + { + epsilon = m_epsilon2; + alpha = m_alpha2; + } + + Image<BYTERGB> prgbSrc(m_SrcImage); + Image<BYTERGB> prgbBG(m_BGImage); + Image<BYTERGB> prgbFG(m_FGImage); + + for(int j = 0; j < m_height; j++) + { + int jj = m_offset + j*(N + m_pad); + + for(int i = 0; i < m_width; i++) + { + int ii = m_offset + i*(M + m_pad); + + double srcR = (double)prgbSrc[j][i].Red; + double srcG = (double)prgbSrc[j][i].Green; + double srcB = (double)prgbSrc[j][i].Blue; + + // Find BMU + + double d2min = DBL_MAX; + int iiHit = ii; + int jjHit = jj; + + for(int l = 0; l < N; l++) + { + for(int k = 0; k < M; k++) + { + double dr = srcR - m_ppSOM[jj+l][ii+k].Red; + double dg = srcG - m_ppSOM[jj+l][ii+k].Green; + double db = srcB - m_ppSOM[jj+l][ii+k].Blue; + + double d2 = dr*dr + dg*dg + db*db; + + if(d2 < d2min) + { + d2min = d2; + iiHit = ii + k; + jjHit = jj + l; + } + } + } + + // Update SOM + + if(d2min <= epsilon) // matching model found + { + for(int l = (jjHit - m_offset); l <= (jjHit + m_offset); l++) + { + for(int k = (iiHit - m_offset); k <= (iiHit + m_offset); k++) + { + a = alpha*m_ppW[l-jjHit+m_offset][k-iiHit+m_offset]; + + // speed hack.. avoid very small increment values. abs() is sloooow. + + double d; + + d = srcR - m_ppSOM[l][k].Red; + if(d*d > DBL_MIN) + m_ppSOM[l][k].Red += a*d; + + d = srcG - m_ppSOM[l][k].Green; + if(d*d > DBL_MIN) + m_ppSOM[l][k].Green += a*d; + + d = srcB - m_ppSOM[l][k].Blue; + if(d*d > DBL_MIN) + m_ppSOM[l][k].Blue += a*d; + } + } + + // Set background image + prgbBG[j][i].Red = m_ppSOM[jjHit][iiHit].Red; + prgbBG[j][i].Green = m_ppSOM[jjHit][iiHit].Green; + prgbBG[j][i].Blue = m_ppSOM[jjHit][iiHit].Blue; + + // Set foreground image + prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 0; + } + else + { + // Set foreground image + prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 255; + } + } + } + + return; + } + } +} \ No newline at end of file diff --git a/package_bgs/lb/BGModelSom.h b/package_bgs/lb/BGModelSom.h new file mode 100644 index 0000000000000000000000000000000000000000..e95af11fffd5e97a18a040722d25c30cc9ec298d --- /dev/null +++ b/package_bgs/lb/BGModelSom.h @@ -0,0 +1,92 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments +BGModelSom.h + +Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef BGMODELSOM_H +#define BGMODELSOM_H + +#include "BGModel.h" + +namespace lb_library +{ + namespace AdaptiveSOM + { + // SOM parameters + + const int M = 3; // width SOM (per pixel) + const int N = 3; // height SOM (per pixel) + const int KERNEL = 3; // size Gaussian kernel + + const bool SPAN_NEIGHBORS = false; // true if update neighborhood spans different pixels // + const int TRAINING_STEPS = 100; // number of training steps + + const float EPS1 = 100.0; // model match distance during training + const float EPS2 = 20.0; // model match distance + const float C1 = 1.0; // learning rate during training + const float C2 = 0.05; // learning rate + + class BGModelSom : public BGModel + { + public: + BGModelSom(int width, int height); + ~BGModelSom(); + + void setBGModelParameter(int id, int value); + + protected: + int m_widthSOM; + int m_heightSOM; + int m_offset; + int m_pad; + int m_K; + int m_TSteps; + + double m_Wmax; + + double m_epsilon1; + double m_epsilon2; + double m_alpha1; + double m_alpha2; + + DBLRGB** m_ppSOM; // SOM grid + double** m_ppW; // Weights + + void Init(); + void Update(); + }; + } +} + +#endif diff --git a/package_bgs/lb/LBAdaptiveSOM.cpp b/package_bgs/lb/LBAdaptiveSOM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce06433cf8ab1547a053bbf0576f7c2c6026009f --- /dev/null +++ b/package_bgs/lb/LBAdaptiveSOM.cpp @@ -0,0 +1,109 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "LBAdaptiveSOM.h" + +LBAdaptiveSOM::LBAdaptiveSOM() : firstTime(true), showOutput(true), + sensitivity(75), trainingSensitivity(245), learningRate(62), trainingLearningRate(255), trainingSteps(55) +{ + std::cout << "LBAdaptiveSOM()" << std::endl; +} + +LBAdaptiveSOM::~LBAdaptiveSOM() +{ + delete m_pBGModel; + std::cout << "~LBAdaptiveSOM()" << std::endl; +} + +void LBAdaptiveSOM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + IplImage *frame = new IplImage(img_input); + + if(firstTime) + { + saveConfig(); + + int w = cvGetSize(frame).width; + int h = cvGetSize(frame).height; + + m_pBGModel = new BGModelSom(w,h); + m_pBGModel->InitModel(frame); + } + + m_pBGModel->setBGModelParameter(0,sensitivity); + m_pBGModel->setBGModelParameter(1,trainingSensitivity); + m_pBGModel->setBGModelParameter(2,learningRate); + m_pBGModel->setBGModelParameter(3,trainingLearningRate); + m_pBGModel->setBGModelParameter(5,trainingSteps); + + m_pBGModel->UpdateModel(frame); + + img_foreground = cv::Mat(m_pBGModel->GetFG()); + img_background = cv::Mat(m_pBGModel->GetBG()); + + if(showOutput) + { + cv::imshow("SOM Mask", img_foreground); + cv::imshow("SOM Model", img_background); + } + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + delete frame; + + firstTime = false; +} + +//void LBAdaptiveSOM::finish(void) +//{ +// delete m_pBGModel; +//} + +void LBAdaptiveSOM::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/LBAdaptiveSOM.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "sensitivity", sensitivity); + cvWriteInt(fs, "trainingSensitivity", trainingSensitivity); + cvWriteInt(fs, "learningRate", learningRate); + cvWriteInt(fs, "trainingLearningRate", trainingLearningRate); + cvWriteInt(fs, "trainingSteps", trainingSteps); + + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void LBAdaptiveSOM::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/LBAdaptiveSOM.xml", 0, CV_STORAGE_READ); + + sensitivity = cvReadIntByName(fs, 0, "sensitivity", 75); + trainingSensitivity = cvReadIntByName(fs, 0, "trainingSensitivity", 245); + learningRate = cvReadIntByName(fs, 0, "learningRate", 62); + trainingLearningRate = cvReadIntByName(fs, 0, "trainingLearningRate", 255); + trainingSteps = cvReadIntByName(fs, 0, "trainingSteps", 55); + + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/lb/LBAdaptiveSOM.h b/package_bgs/lb/LBAdaptiveSOM.h new file mode 100644 index 0000000000000000000000000000000000000000..6e001dfcffc35cfbc609d7f0bb4905f4b2008852 --- /dev/null +++ b/package_bgs/lb/LBAdaptiveSOM.h @@ -0,0 +1,56 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "BGModelSom.h" + +#include "../IBGS.h" + +using namespace lb_library; +using namespace lb_library::AdaptiveSOM; + +class LBAdaptiveSOM : public IBGS +{ +private: + bool firstTime; + bool showOutput; + + BGModel* m_pBGModel; + int sensitivity; + int trainingSensitivity; + int learningRate; + int trainingLearningRate; + int trainingSteps; + + cv::Mat img_foreground; + cv::Mat img_background; + +public: + LBAdaptiveSOM(); + ~LBAdaptiveSOM(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + //void finish(void); + +private: + void saveConfig(); + void loadConfig(); +}; \ No newline at end of file diff --git a/package_bgs/lb/LBFuzzyAdaptiveSOM.cpp b/package_bgs/lb/LBFuzzyAdaptiveSOM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ef856028cbd063cda01e3aaacef7c702c9713d4 --- /dev/null +++ b/package_bgs/lb/LBFuzzyAdaptiveSOM.cpp @@ -0,0 +1,109 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "LBFuzzyAdaptiveSOM.h" + +LBFuzzyAdaptiveSOM::LBFuzzyAdaptiveSOM() : firstTime(true), showOutput(true), + sensitivity(90), trainingSensitivity(240), learningRate(38), trainingLearningRate(255), trainingSteps(81) +{ + std::cout << "LBFuzzyAdaptiveSOM()" << std::endl; +} + +LBFuzzyAdaptiveSOM::~LBFuzzyAdaptiveSOM() +{ + delete m_pBGModel; + std::cout << "~LBFuzzyAdaptiveSOM()" << std::endl; +} + +void LBFuzzyAdaptiveSOM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + IplImage *frame = new IplImage(img_input); + + if(firstTime) + { + saveConfig(); + + int w = cvGetSize(frame).width; + int h = cvGetSize(frame).height; + + m_pBGModel = new BGModelFuzzySom(w,h); + m_pBGModel->InitModel(frame); + } + + m_pBGModel->setBGModelParameter(0,sensitivity); + m_pBGModel->setBGModelParameter(1,trainingSensitivity); + m_pBGModel->setBGModelParameter(2,learningRate); + m_pBGModel->setBGModelParameter(3,trainingLearningRate); + m_pBGModel->setBGModelParameter(5,trainingSteps); + + m_pBGModel->UpdateModel(frame); + + img_foreground = cv::Mat(m_pBGModel->GetFG()); + img_background = cv::Mat(m_pBGModel->GetBG()); + + if(showOutput) + { + cv::imshow("FSOM Mask", img_foreground); + cv::imshow("FSOM Model", img_background); + } + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + delete frame; + + firstTime = false; +} + +//void LBFuzzyAdaptiveSOM::finish(void) +//{ +// //delete m_pBGModel; +//} + +void LBFuzzyAdaptiveSOM::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/LBFuzzyAdaptiveSOM.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "sensitivity", sensitivity); + cvWriteInt(fs, "trainingSensitivity", trainingSensitivity); + cvWriteInt(fs, "learningRate", learningRate); + cvWriteInt(fs, "trainingLearningRate", trainingLearningRate); + cvWriteInt(fs, "trainingSteps", trainingSteps); + + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void LBFuzzyAdaptiveSOM::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/LBFuzzyAdaptiveSOM.xml", 0, CV_STORAGE_READ); + + sensitivity = cvReadIntByName(fs, 0, "sensitivity", 90); + trainingSensitivity = cvReadIntByName(fs, 0, "trainingSensitivity", 240); + learningRate = cvReadIntByName(fs, 0, "learningRate", 38); + trainingLearningRate = cvReadIntByName(fs, 0, "trainingLearningRate", 255); + trainingSteps = cvReadIntByName(fs, 0, "trainingSteps", 81); + + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/lb/LBFuzzyAdaptiveSOM.h b/package_bgs/lb/LBFuzzyAdaptiveSOM.h new file mode 100644 index 0000000000000000000000000000000000000000..76dec2f87bd4679c690c5bb0d3be2f9e0640ad83 --- /dev/null +++ b/package_bgs/lb/LBFuzzyAdaptiveSOM.h @@ -0,0 +1,56 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "BGModelFuzzySom.h" + +#include "../IBGS.h" + +using namespace lb_library; +using namespace lb_library::FuzzyAdaptiveSOM; + +class LBFuzzyAdaptiveSOM : public IBGS +{ +private: + bool firstTime; + bool showOutput; + + BGModel* m_pBGModel; + int sensitivity; + int trainingSensitivity; + int learningRate; + int trainingLearningRate; + int trainingSteps; + + cv::Mat img_foreground; + cv::Mat img_background; + +public: + LBFuzzyAdaptiveSOM(); + ~LBFuzzyAdaptiveSOM(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + //void finish(void); + +private: + void saveConfig(); + void loadConfig(); +}; \ No newline at end of file diff --git a/package_bgs/lb/LBFuzzyGaussian.cpp b/package_bgs/lb/LBFuzzyGaussian.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc257bf40325167efbe49220be9d08f9f745d4f7 --- /dev/null +++ b/package_bgs/lb/LBFuzzyGaussian.cpp @@ -0,0 +1,105 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "LBFuzzyGaussian.h" + +LBFuzzyGaussian::LBFuzzyGaussian() : firstTime(true), showOutput(true), sensitivity(72), bgThreshold(162), learningRate(49), noiseVariance(195) +{ + std::cout << "LBFuzzyGaussian()" << std::endl; +} + +LBFuzzyGaussian::~LBFuzzyGaussian() +{ + delete m_pBGModel; + std::cout << "~LBFuzzyGaussian()" << std::endl; +} + +void LBFuzzyGaussian::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + IplImage *frame = new IplImage(img_input); + + if(firstTime) + { + saveConfig(); + + int w = cvGetSize(frame).width; + int h = cvGetSize(frame).height; + + m_pBGModel = new BGModelFuzzyGauss(w,h); + m_pBGModel->InitModel(frame); + } + + m_pBGModel->setBGModelParameter(0,sensitivity); + m_pBGModel->setBGModelParameter(1,bgThreshold); + m_pBGModel->setBGModelParameter(2,learningRate); + m_pBGModel->setBGModelParameter(3,noiseVariance); + + m_pBGModel->UpdateModel(frame); + + img_foreground = cv::Mat(m_pBGModel->GetFG()); + img_background = cv::Mat(m_pBGModel->GetBG()); + + if(showOutput) + { + cv::imshow("FG Mask", img_foreground); + cv::imshow("FG Model", img_background); + } + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + delete frame; + + firstTime = false; +} + +//void LBFuzzyGaussian::finish(void) +//{ +// delete m_pBGModel; +//} + +void LBFuzzyGaussian::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/LBFuzzyGaussian.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "sensitivity", sensitivity); + cvWriteInt(fs, "bgThreshold", bgThreshold); + cvWriteInt(fs, "learningRate", learningRate); + cvWriteInt(fs, "noiseVariance", noiseVariance); + + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void LBFuzzyGaussian::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/LBFuzzyGaussian.xml", 0, CV_STORAGE_READ); + + sensitivity = cvReadIntByName(fs, 0, "sensitivity", 72); + bgThreshold = cvReadIntByName(fs, 0, "bgThreshold", 162); + learningRate = cvReadIntByName(fs, 0, "learningRate", 49); + noiseVariance = cvReadIntByName(fs, 0, "noiseVariance", 195); + + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/lb/LBFuzzyGaussian.h b/package_bgs/lb/LBFuzzyGaussian.h new file mode 100644 index 0000000000000000000000000000000000000000..408c4a51d50ad36c0df49e092ea3f36d51ee2b6d --- /dev/null +++ b/package_bgs/lb/LBFuzzyGaussian.h @@ -0,0 +1,55 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "BGModelFuzzyGauss.h" + +#include "../IBGS.h" + +using namespace lb_library; +using namespace lb_library::FuzzyGaussian; + +class LBFuzzyGaussian : public IBGS +{ +private: + bool firstTime; + bool showOutput; + + BGModel* m_pBGModel; + int sensitivity; + int bgThreshold; + int learningRate; + int noiseVariance; + + cv::Mat img_foreground; + cv::Mat img_background; + +public: + LBFuzzyGaussian(); + ~LBFuzzyGaussian(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + //void finish(void); + +private: + void saveConfig(); + void loadConfig(); +}; \ No newline at end of file diff --git a/package_bgs/lb/LBMixtureOfGaussians.cpp b/package_bgs/lb/LBMixtureOfGaussians.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d235c506601f7b2a57379810c3fcd9c4ee75779 --- /dev/null +++ b/package_bgs/lb/LBMixtureOfGaussians.cpp @@ -0,0 +1,105 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "LBMixtureOfGaussians.h" + +LBMixtureOfGaussians::LBMixtureOfGaussians() : firstTime(true), showOutput(true), sensitivity(81), bgThreshold(83), learningRate(59), noiseVariance(206) +{ + std::cout << "LBMixtureOfGaussians()" << std::endl; +} + +LBMixtureOfGaussians::~LBMixtureOfGaussians() +{ + delete m_pBGModel; + std::cout << "~LBMixtureOfGaussians()" << std::endl; +} + +void LBMixtureOfGaussians::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + IplImage *frame = new IplImage(img_input); + + if(firstTime) + { + saveConfig(); + + int w = cvGetSize(frame).width; + int h = cvGetSize(frame).height; + + m_pBGModel = new BGModelMog(w,h); + m_pBGModel->InitModel(frame); + } + + m_pBGModel->setBGModelParameter(0,sensitivity); + m_pBGModel->setBGModelParameter(1,bgThreshold); + m_pBGModel->setBGModelParameter(2,learningRate); + m_pBGModel->setBGModelParameter(3,noiseVariance); + + m_pBGModel->UpdateModel(frame); + + img_foreground = cv::Mat(m_pBGModel->GetFG()); + img_background = cv::Mat(m_pBGModel->GetBG()); + + if(showOutput) + { + cv::imshow("MOG Mask", img_foreground); + cv::imshow("MOG Model", img_background); + } + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + delete frame; + + firstTime = false; +} + +//void LBMixtureOfGaussians::finish(void) +//{ +// delete m_pBGModel; +//} + +void LBMixtureOfGaussians::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/LBMixtureOfGaussians.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "sensitivity", sensitivity); + cvWriteInt(fs, "bgThreshold", bgThreshold); + cvWriteInt(fs, "learningRate", learningRate); + cvWriteInt(fs, "noiseVariance", noiseVariance); + + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void LBMixtureOfGaussians::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/LBMixtureOfGaussians.xml", 0, CV_STORAGE_READ); + + sensitivity = cvReadIntByName(fs, 0, "sensitivity", 81); + bgThreshold = cvReadIntByName(fs, 0, "bgThreshold", 83); + learningRate = cvReadIntByName(fs, 0, "learningRate", 59); + noiseVariance = cvReadIntByName(fs, 0, "noiseVariance", 206); + + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/lb/LBMixtureOfGaussians.h b/package_bgs/lb/LBMixtureOfGaussians.h new file mode 100644 index 0000000000000000000000000000000000000000..71ab5f0c3f5309df637732dd563b26edc6c1f1af --- /dev/null +++ b/package_bgs/lb/LBMixtureOfGaussians.h @@ -0,0 +1,55 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "BGModelMog.h" + +#include "../IBGS.h" + +using namespace lb_library; +using namespace lb_library::MixtureOfGaussians; + +class LBMixtureOfGaussians : public IBGS +{ +private: + bool firstTime; + bool showOutput; + + BGModel* m_pBGModel; + int sensitivity; + int bgThreshold; + int learningRate; + int noiseVariance; + + cv::Mat img_foreground; + cv::Mat img_background; + +public: + LBMixtureOfGaussians(); + ~LBMixtureOfGaussians(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + //void finish(void); + +private: + void saveConfig(); + void loadConfig(); +}; \ No newline at end of file diff --git a/package_bgs/lb/LBSimpleGaussian.cpp b/package_bgs/lb/LBSimpleGaussian.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f0ce6fc69c6e572e1ebba8883af7706a117f91ad --- /dev/null +++ b/package_bgs/lb/LBSimpleGaussian.cpp @@ -0,0 +1,100 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "LBSimpleGaussian.h" + +LBSimpleGaussian::LBSimpleGaussian() : firstTime(true), showOutput(true), sensitivity(66), noiseVariance(162), learningRate(18) +{ + std::cout << "LBSimpleGaussian()" << std::endl; +} + +LBSimpleGaussian::~LBSimpleGaussian() +{ + delete m_pBGModel; + std::cout << "~LBSimpleGaussian()" << std::endl; +} + +void LBSimpleGaussian::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + IplImage *frame = new IplImage(img_input); + + if(firstTime) + { + saveConfig(); + + int w = cvGetSize(frame).width; + int h = cvGetSize(frame).height; + + m_pBGModel = new BGModelGauss(w,h); + m_pBGModel->InitModel(frame); + } + + m_pBGModel->setBGModelParameter(0,sensitivity); + m_pBGModel->setBGModelParameter(1,noiseVariance); + m_pBGModel->setBGModelParameter(2,learningRate); + + m_pBGModel->UpdateModel(frame); + + img_foreground = cv::Mat(m_pBGModel->GetFG()); + img_background = cv::Mat(m_pBGModel->GetBG()); + + if(showOutput) + { + cv::imshow("SG Mask", img_foreground); + cv::imshow("SG Model", img_background); + } + + img_foreground.copyTo(img_output); + img_background.copyTo(img_bgmodel); + + delete frame; + + firstTime = false; +} + +//void LBSimpleGaussian::finish(void) +//{ +// delete m_pBGModel; +//} + +void LBSimpleGaussian::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/LBSimpleGaussian.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "sensitivity", sensitivity); + cvWriteInt(fs, "noiseVariance", noiseVariance); + cvWriteInt(fs, "learningRate", learningRate); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void LBSimpleGaussian::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/LBSimpleGaussian.xml", 0, CV_STORAGE_READ); + + sensitivity = cvReadIntByName(fs, 0, "sensitivity", 66); + noiseVariance = cvReadIntByName(fs, 0, "noiseVariance", 162); + learningRate = cvReadIntByName(fs, 0, "learningRate", 18); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/lb/LBSimpleGaussian.h b/package_bgs/lb/LBSimpleGaussian.h new file mode 100644 index 0000000000000000000000000000000000000000..ea8317c49496fac4e60fb42595d075fcf75218cd --- /dev/null +++ b/package_bgs/lb/LBSimpleGaussian.h @@ -0,0 +1,54 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "BGModelGauss.h" + +#include "../IBGS.h" + +using namespace lb_library; +using namespace lb_library::SimpleGaussian; + +class LBSimpleGaussian : public IBGS +{ +private: + bool firstTime; + bool showOutput; + + BGModel* m_pBGModel; + int sensitivity; + int noiseVariance; + int learningRate; + + cv::Mat img_foreground; + cv::Mat img_background; + +public: + LBSimpleGaussian(); + ~LBSimpleGaussian(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + //void finish(void); + +private: + void saveConfig(); + void loadConfig(); +}; \ No newline at end of file diff --git a/package_bgs/lb/Types.h b/package_bgs/lb/Types.h new file mode 100644 index 0000000000000000000000000000000000000000..5b1fd5b358a7cf84c663a435a4c52e20530fefe5 --- /dev/null +++ b/package_bgs/lb/Types.h @@ -0,0 +1,99 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/* Scene 1.0.1 -- Background subtraction and object tracking for complex environments +Types.h + +Copyright (C) 2011 Laurence Bender <lbender@untref.edu.ar> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef TYPES_H +#define TYPES_H + +#include <cv.h> + +namespace lb_library +{ + template<class T> class Image + { + private: + IplImage* imgp; + + public: + Image(IplImage* img=0) {imgp=img;} + ~Image(){imgp=0;} + + void operator=(IplImage* img) {imgp=img;} + + inline T* operator[](const int rowIndx) + { + return ((T *)(imgp->imageData + rowIndx*imgp->widthStep)); + } + }; + + typedef struct{ + unsigned char b,g,r; + } RgbPixel; + + typedef struct{ + unsigned char Blue,Green,Red; + } BYTERGB; + + typedef struct{ + unsigned int Blue,Green,Red; + } INTRGB; + + typedef struct{ + float b,g,r; + }RgbPixelFloat; + + typedef struct{ + double Blue,Green,Red; + } DBLRGB; + + typedef Image<RgbPixel> RgbImage; + typedef Image<RgbPixelFloat> RgbImageFloat; + typedef Image<unsigned char> BwImage; + typedef Image<float> BwImageFloat; + + /* + IplImage* img = cvCreateImage(cvSize(640,480), IPL_DEPTH_32F, 3); + RgbImageFloat imgA(img); + for(int i = 0; i < m_height; i++) + for(int j = 0; j < m_width; j++) + imgA[i][j].b = 111; + imgA[i][j].g = 111; + imgA[i][j].r = 111; + */ +} + +//--------------------------------------------- + +#endif diff --git a/package_bgs/my/MyBGS.cpp b/package_bgs/my/MyBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..07b51cde46b07c5da9e0ad19951d35aca191f771 --- /dev/null +++ b/package_bgs/my/MyBGS.cpp @@ -0,0 +1,26 @@ +#include "MyBGS.h" + +MyBGS::MyBGS(){} +MyBGS::~MyBGS(){} + +void MyBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + if(img_previous.empty()) + img_input.copyTo(img_previous); + + cv::Mat img_foreground; + cv::absdiff(img_previous, img_input, img_foreground); + + if(img_foreground.channels() == 3) + cv::cvtColor(img_foreground, img_foreground, CV_BGR2GRAY); + + cv::threshold(img_foreground, img_foreground, 15, 255, cv::THRESH_BINARY); + + img_foreground.copyTo(img_output); + img_previous.copyTo(img_bgmodel); + + img_input.copyTo(img_previous); +} diff --git a/package_bgs/my/MyBGS.h b/package_bgs/my/MyBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..daba73043a777e7ba97b60f539b4eaa4871b7d38 --- /dev/null +++ b/package_bgs/my/MyBGS.h @@ -0,0 +1,22 @@ +#pragma once + +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" + +class MyBGS : public IBGS +{ +private: + cv::Mat img_previous; + +public: + MyBGS(); + ~MyBGS(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(){} + void loadConfig(){} +}; \ No newline at end of file diff --git a/package_bgs/pt/PBAS.cpp b/package_bgs/pt/PBAS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f74b829f19c59ca2a30b58a5236b686786f01f6 --- /dev/null +++ b/package_bgs/pt/PBAS.cpp @@ -0,0 +1,586 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "PBAS.h" + +PBAS::PBAS(void) : N(20), R_lower(18), Raute_min(2), T_lower(2), T_upper(200), R_scale(5), R_incdec(0.05), T_dec(0.05), T_inc(1) +{ + std::cout << "PBAS()" << std::endl; + + //feature vector + alpha = 7.0; + beta = 1.0; + formerMeanNorm = 0; + width = 0; + + //result image + foregroundValue = 255; + backgroundValue = 0; + + //length of random array + countOfRandomNumb = 1000; + + //the T(x_i) value needs initiation + T_init = R_lower; + + //check if something is moving in the picture + isMove = false; + + //for init, count number of runs + runs = 0; + newInitialization(); +} + +void PBAS::newInitialization() +{ + if(!randomN.empty()) + randomN.clear(); + + if(!randomX.empty()) + randomX.clear(); + + if(!randomY.empty()) + randomY.clear(); + + if(!randomMinDist.empty()) + randomMinDist.clear(); + + if(!randomT.empty()) + randomT.clear(); + + if(!randomTN.empty()) + randomTN.clear(); + + for(int l = 0; l < countOfRandomNumb; l++) + { + randomN.push_back((int)randomGenerator.uniform((int)0,(int)N)); + randomX.push_back((int)randomGenerator.uniform(-1, +2)); + randomY.push_back((int)randomGenerator.uniform(-1, +2)); + randomMinDist.push_back((int)randomGenerator.uniform((int)0, (int)N)); + randomT.push_back((int)randomGenerator.uniform((int)0, (int)T_upper)); + randomTN.push_back((int)randomGenerator.uniform((int)0, (int)T_upper)); + } +} + +PBAS::~PBAS(void) +{ + std::cout << "~PBAS()" << std::endl; + + randomN.clear(); + randomX.clear(); + randomY.clear(); + randomMinDist.clear(); + randomT.clear(); + randomTN.clear(); + + for(int k = 0; k < backgroundModel.size(); ++k) + { + if(chans == 1) + { + backgroundModel.at(k).at(0).release(); + backgroundModel.at(k).at(1).release(); + } + else + { + backgroundModel.at(k).at(0).release(); + backgroundModel.at(k).at(1).release(); + backgroundModel.at(k).at(2).release(); + + backgroundModel.at(k).at(3).release(); + backgroundModel.at(k).at(4).release(); + backgroundModel.at(k).at(5).release(); + } + } + + backgroundModel.clear(); + meanMinDist.release(); + + actualR.release(); + actualT.release(); + + sobelX.release(); + sobelY.release(); +} + +bool PBAS::process(cv::Mat* input, cv::Mat* output) +{ + if(width != input->cols) + { + width = input->cols; + chans = input->channels(); + height = input->rows; + + if(input->rows < 1 || input->cols < 1) + { + std::cout << "Error: Occurrence of to small (or empty?) image size in PBAS. STOPPING " << std::endl; + return false; + } + } + + //iniate the background model + init(input); + + resultMap = new cv::Mat(input->rows,input->cols,CV_8UC1); + + //calculate features + calculateFeatures(¤tFeatures, input); + + //set sumMagnitude to zero at beginning and then sum up in the loop + sumMagnitude = 0; + long glCounterFore = 0; + isMove = false; + + //Here starts the whole processing of each pixel of the image + // for each pixel + for (int j=0; j< resultMap->rows; ++j) + { + resultMap_Pt = resultMap->ptr<uchar>(j); + currentFeaturesM_Pt.clear(); + currentFeaturesC_Pt.clear(); + std::vector<float*> fT; + std::vector<uchar*> uT; + B_Mag_Pts.clear(); + B_Col_Pts.clear(); + + for(int z = 0; z < chans; ++z) + { + currentFeaturesM_Pt.push_back(currentFeatures.at(z).ptr<float>(j)); + currentFeaturesC_Pt.push_back(currentFeatures.at(z + chans).ptr<uchar>(j)); + + B_Mag_Pts.push_back(fT); + + B_Col_Pts.push_back(uT); + } + + meanMinDist_Pt = meanMinDist.ptr<float>(j); + actualR_Pt = actualR.ptr<float>(j); + actualT_Pt = actualT.ptr<float>(j); + + for(int k = 0; k < runs; ++k) + { + for(int z = 0; z < chans; ++z) + { + B_Mag_Pts.at(z).push_back(backgroundModel.at(k).at(z).ptr<float>(j)); + B_Col_Pts.at(z).push_back(backgroundModel.at(k).at(z+chans).ptr<uchar>(j)); + } + } + + for(int i = 0; i < resultMap->cols; ++i) + { + //Compare each pixel to in the worst runtime-case each background model + int count = 0; + int index = 0; + + double norm = 0.0; + double dist = 0.0; + double minDist = 1000.0; + int entry = randomGenerator.uniform(3, countOfRandomNumb-4); + + do + { + if(chans == 3) + { + norm = sqrt( + (((double)B_Mag_Pts.at(0).at(index)[i] - ((double)*currentFeaturesM_Pt.at(0)))*((double)B_Mag_Pts.at(0).at(index)[i] - ((double)*currentFeaturesM_Pt.at(0))))+ + (((double)B_Mag_Pts.at(1).at(index)[i] - ((double)*currentFeaturesM_Pt.at(1)))*((double)B_Mag_Pts.at(1).at(index)[i] - ((double)*currentFeaturesM_Pt.at(1))))+ + (((double)B_Mag_Pts.at(2).at(index)[i] - ((double)*currentFeaturesM_Pt.at(2)))*((double)B_Mag_Pts.at(2).at(index)[i] - ((double)*currentFeaturesM_Pt.at(2)))) + ); + + dist = sqrt( + (((double)B_Col_Pts.at(0).at(index)[i] - ((double)*currentFeaturesC_Pt.at(0)))*((double)B_Col_Pts.at(0).at(index)[i] - ((double)*currentFeaturesC_Pt.at(0))))+ + (((double)B_Col_Pts.at(1).at(index)[i] - ((double)*currentFeaturesC_Pt.at(1)))*((double)B_Col_Pts.at(1).at(index)[i] - ((double)*currentFeaturesC_Pt.at(1))))+ + (((double)B_Col_Pts.at(2).at(index)[i] - ((double)*currentFeaturesC_Pt.at(2)))*((double)B_Col_Pts.at(2).at(index)[i] - ((double)*currentFeaturesC_Pt.at(2)))) + ); + } + else + { + norm = abs((((double)B_Mag_Pts.at(0).at(index)[i] - + ((double)*currentFeaturesM_Pt.at(0)))*((double)B_Mag_Pts.at(0).at(index)[i] - ((double)*currentFeaturesM_Pt.at(0))))); + + dist = abs((((double)B_Col_Pts.at(0).at(index)[i] - + ((double)*currentFeaturesC_Pt.at(0)))*((double)B_Col_Pts.at(0).at(index)[i] - ((double)*currentFeaturesC_Pt.at(0)))) + ); + } + dist = ((double)alpha*(norm/formerMeanMag) + beta*dist); + + if((dist < *actualR_Pt)) + { + ++count; + if(minDist > dist) + minDist = dist; + } + else + { + sumMagnitude += (double)(norm); + ++glCounterFore; + } + ++index; + } + while((count< Raute_min) && (index < runs)); + + + //############################################# + //update backgroundmodel + // is BACKGROUND + if(count >= Raute_min) + { + *resultMap_Pt = 0; + double ratio = std::ceil((double)T_upper/(double)(*actualT_Pt)); + //in the first run every distance is zero, because there is no background model + //in the secont run, we have already one image as background model, hence a + // reasonable minDist could be found -> because of the partly 1/run changing in the running average, we set in the first try meanMinDist to the actual minDist value + if(runs < N && runs > 2) + { + *meanMinDist_Pt = ((((float)(runs-1)) * (*meanMinDist_Pt)) + (float)minDist) / ((float)runs); + } + else if(runs < N && runs == 2) + { + *meanMinDist_Pt = (float)minDist; + } + + //1. update model + if(runs == N) + { + //Update current pixel + //check if random numer is smaller than ratio + if(randomT.at(entry) < ratio) + { + // replace randomly chosen sample + int rand = randomN.at(entry+1); //randomGenerator.uniform((int)0,(int)N-1); + for(int z = 0; z < chans; ++z) + { + B_Mag_Pts.at(z).at(rand)[i] = (float)*currentFeaturesM_Pt.at(z); + B_Col_Pts.at(z).at(rand)[i] = (uchar)*currentFeaturesC_Pt.at(z); + + } + + *meanMinDist_Pt = ((((float)(N-1)) * (*meanMinDist_Pt)) + (float)minDist) / ((float)N); + } + + //Update neighboring pixel model + if(randomTN.at(entry) < ratio) + { + //choose neighboring pixel randomly + int xNeigh = randomX.at(entry)+i; + int yNeigh = randomY.at(entry)+j; + checkValid(&xNeigh, &yNeigh); + + // replace randomly chosen sample + int rand = randomN.at(entry-1); + for(int z = 0; z < chans; ++z) + { + (backgroundModel.at(rand)).at(z).at<float>(yNeigh,xNeigh) = currentFeatures.at(z).at<float>(yNeigh,xNeigh); + (backgroundModel.at(rand)).at(z + chans).at<uchar>(yNeigh,xNeigh) = currentFeatures.at(z+chans).at<uchar>(yNeigh,xNeigh); + } + } + } + } + else + { + // store pixel as foreground + *resultMap_Pt = 255; + + //there is some movement + isMove = true; + } + + //#######################//#######################//#######################//####################### + //control loops + //#######################//#######################//#######################//####################### + //update R + decisionThresholdRegulator(actualR_Pt,meanMinDist_Pt); + + //update T + learningRateRegulator(actualT_Pt, meanMinDist_Pt,resultMap_Pt); + + //#######################//#######################//#######################//####################### + //#######################//#######################//#######################//####################### + + //jump to next pixel + ++resultMap_Pt; + for(int z = 0; z < chans; ++z) + { + ++currentFeaturesM_Pt.at(z); + ++currentFeaturesC_Pt.at(z); + } + + ++meanMinDist_Pt; + ++actualR_Pt; + ++actualT_Pt; + } + } + + resultMap->copyTo(*output); + + //if there is no foreground -> no magnitudes fount + //-> initiate some low value to prevent diving through zero + double meanMag = sumMagnitude/(double)(glCounterFore + 1); //height*width); + + if(meanMag > 20) + formerMeanMag = meanMag; + else + formerMeanMag = 20; + + delete resultMap; + + for(int z = 0; z < chans; ++z) + { + currentFeatures.at(z+chans).release(); + currentFeatures.at(z).release(); + } + + return true; +} + +void PBAS::decisionThresholdRegulator(float* pt, float* meanDist) +{ + //update R + double tempR = *pt; + double newThresh = (*meanDist)*R_scale; + + if( tempR < newThresh) + { + tempR += tempR * R_incdec; + } + else + { + tempR -= tempR * R_incdec; + } + + if(tempR >= R_lower) + *pt = (float)tempR; + else + *pt = (float)R_lower; +} + +void PBAS::learningRateRegulator(float* pt, float* meanDist,uchar* isFore) +{ + //time update + double tempT = *pt; + + if((int)*isFore < 128) + { + tempT -= T_inc/(*meanDist+1.0); + } + else + { + tempT += T_dec/(*meanDist+1.0); + } + + if(tempT > T_lower && tempT < T_upper) + *pt = (float)tempT; +} + +void PBAS::checkValid(int *x, int *y) +{ + if(*x < 0) + { + *x = 0; + } + else if(*x >= width) + { + *x = width -1; + } + + if(*y < 0) + { + *y = 0; + } + else if(*y >= height) + { + *y = height - 1; + } +} + +void PBAS::init(cv::Mat* input) +{ + if(runs < N) + { + std::vector<cv::Mat> init; + calculateFeatures(&init, input); + backgroundModel.push_back(init); + + if(chans == 1) + { + init.at(0).release(); + init.at(1).release(); + } + else + { + init.at(0).release(); + init.at(1).release(); + init.at(2).release(); + init.at(3).release(); + init.at(4).release(); + init.at(5).release(); + } + + init.clear(); + + if(runs == 0) + { + meanMinDist.create(input->size(), CV_32FC1); + meanMinDist.zeros(input->rows, input->cols, CV_32FC1); + + actualR.create(input->rows, input->cols, CV_32FC1); + actualT.create(input->rows, input->cols, CV_32FC1); + + float* ptRs, *ptTs; //, *ptM; + for(int rows = 0; rows < actualR.rows; ++rows) + { + ptRs = actualR.ptr<float>(rows); + ptTs = actualT.ptr<float>(rows); + + for(int cols = 0; cols < actualR.cols; ++cols) + { + ptRs[cols] = (float)R_lower; + ptTs[cols] = (float)T_init; + } + } + } + + ++runs; + } +} + +void PBAS::calculateFeatures(std::vector<cv::Mat>* feature, cv::Mat* inputImage) +{ + if(!feature->empty()) + feature->clear(); + + cv::Mat mag[3], dir; + + if(inputImage->channels() == 3) + { + std::vector<cv::Mat> rgbChannels(3); + cv::split(*inputImage, rgbChannels); + + for(int l = 0; l < 3; ++l) + { + cv::Sobel(rgbChannels.at(l),sobelX,CV_32F,1,0, 3, 1, 0.0); + cv::Sobel(rgbChannels.at(l),sobelY,CV_32F,0,1, 3, 1, 0.0); + + // Compute the L2 norm and direction of the gradient + cv::cartToPolar(sobelX,sobelY,mag[l],dir, true); + feature->push_back(mag[l]); + sobelX.release(); + sobelY.release(); + } + + feature->push_back(rgbChannels.at(0)); + feature->push_back(rgbChannels.at(1)); + feature->push_back(rgbChannels.at(2)); + rgbChannels.at(0).release(); + rgbChannels.at(1).release(); + rgbChannels.at(2).release(); + } + else + { + cv::Sobel(*inputImage,sobelX,CV_32F,1,0, 3, 1, 0.0); + cv::Sobel(*inputImage,sobelY,CV_32F,0,1, 3, 1, 0.0); + + // Compute the L2 norm and direction of the gradient + cv::cartToPolar(sobelX,sobelY,mag[0],dir, true); + feature->push_back(mag[0]); + + cv::Mat temp; + inputImage->copyTo(temp); + feature->push_back(temp); + temp.release(); + } + + mag[0].release(); + mag[1].release(); + mag[2].release(); + dir.release(); +} + +void PBAS::setN(int temp) +{ + N = temp; + newInitialization(); +} + +void PBAS::setRaute_min(int temp) +{ + Raute_min = temp; +} + +void PBAS::setR_lower(double temp) +{ + R_lower = temp; +} + +void PBAS::setR_incdec(double temp) +{ + R_incdec = temp; +} + +void PBAS::setR_scale(double temp) +{ + R_scale = temp; +} + +void PBAS::setT_init(double temp) +{ + T_init = temp; +} + +void PBAS::setT_lower(double temp) +{ + T_lower = temp; +} + +void PBAS::setT_upper(double temp) +{ + T_upper = temp; + newInitialization(); +} + +void PBAS::setT_dec(double temp) +{ + T_dec = temp; +} + +void PBAS::setT_inc(double temp) +{ + T_inc = temp; +} + +void PBAS::setAlpha(double temp) +{ + alpha = temp; +} + +void PBAS::setBeta(double temp) +{ + beta = temp; +} + +bool PBAS::isMovement() +{ + return isMove; +} + +//cv::Mat* PBAS::getR1_xi() +//{ +// return &actualR; +//} +// +//cv::Mat* PBAS::getT_xi() +//{ +// return &actualT; +//} diff --git a/package_bgs/pt/PBAS.h b/package_bgs/pt/PBAS.h new file mode 100644 index 0000000000000000000000000000000000000000..917c525bfb2af10f849dc2d746b3a6c4db2c127c --- /dev/null +++ b/package_bgs/pt/PBAS.h @@ -0,0 +1,207 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +//Implementation of the PBAS from: +// +//M. Hofmann, P. Tiefenbacher, G. Rigoll +//"Background Segmentation with Feedback: The Pixel-Based Adaptive Segmenter", +//in proc of IEEE Workshop on Change Detection, 2012 +// +//Note: some changes, to improve the speed and memory requirements, were achieved in comparison to the +//described PBAS algorithm in the paper above. +// +//Example usage: +// //Somewhere during initalization: +// #include "PBAS.h" +// #include <opencv2/opencv.hpp> +// PBAS pbas; +// +// //you might want to change some parameters of the PBAS here... +// .... +// +// //repeat for each frame +// //make gaussian blur for reducing image noise +//cv::Mat bluredImage; +//cv::Mat pbastResult; +//cv::GaussianBlur(singleFrame, bluredImage, cv::Size(5,5), 1.5); +// +// //process image and receive segmentation in pbasResult +//pbas.process(&bluredImage, &pbasResult); +// +// //make medianBlur on the result to reduce "salt and pepper noise" +// //of the per pixel wise segmentation +//cv::medianBlur(pbasResult, pbasResult, 5); +// +// +// +//Author: P.Tiefenbacher, https://sites.google.com/site/pbassegmenter/ +//Technische Universit�t M�nchen, Germany +//Date: 22-May-2012, Version:0.1 +/////////// + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#pragma once +class PBAS +{ +public: + PBAS(void); + ~PBAS(void); + bool process(cv::Mat* input, cv::Mat* output); + + void setN(int); + void setRaute_min(int); + void setR_lower(double); + void setR_incdec(double); + void setR_scale(double); + void setT_init(double); + void setT_lower(double); + void setT_upper(double); + void setT_dec(double); + void setT_inc(double); + void setAlpha(double); + void setBeta(double); + + bool isMovement(); + + +private: + void calculateFeatures(std::vector<cv::Mat>* feature, cv::Mat* inputImage); + void checkValid(int *x, int *y); + void decisionThresholdRegulator(float* pt, float* meanDistArr); + void learningRateRegulator(float* pt, float* meanDist,uchar* isFore); + void init(cv::Mat*); + void newInitialization(); + + cv::Mat meanMinDist; + float* meanMinDist_Pt; + + + + int width, height; + int chans; + + //balance of feature pixel value to conture value + double alpha, beta; + //################################################################################## + + double formerMeanNorm; + + //define value of foreground/background pixels + int foregroundValue, backgroundValue; + + //################################################################################## + //random number parameters + + //random number generator + cv::RNG randomGenerator; + + //length of random array initialization + long countOfRandomNumb; + + //pre - initialize the randomNumbers for better performance + std::vector<int> randomN, randomMinDist, randomX, randomY, randomT, randomTN; + + //################################################################################### + + //check if something is moving + bool isMove; + + //for init, count number of runs + int runs; + + + cv::Mat* resultMap; + std::vector<cv::Mat> currentFeatures; + + std::vector<float*> currentFeaturesM_Pt; + std::vector<uchar*> currentFeaturesC_Pt; + uchar* resultMap_Pt; + + std::vector<std::vector<float*>>B_Mag_Pts; + std::vector<std::vector<uchar*>>B_Col_Pts; + + double sumMagnitude; + double formerMeanMag; + float formerDistanceBack; + + //#################################################################################### + //N - Number: Defining the size of the background-history-model + // number of samples per pixel + //size of background history B(x_i) + int N; + // background model + std::vector<std::vector<cv::Mat>> backgroundModel; + //#################################################################################### + //#################################################################################### + //R-Threshhold - Variables + //minimal Threshold for foreground and initial value of R(x_i) + // radius of the sphere -> lower border boundary + double R_lower; + + //factor which defines new threshold of R(x_i) together with meanMinDist(x_i) + // scale for the sphere threshhold to define pixel-based Thresholds + double R_scale; + + //decreasing / increasing factor of R(x_i) + // increasing/decreasing factor for the r-Threshold based on the result of rTreshScale * meanMinDistBackground + double R_incdec; + + cv::Mat actualR; + float* actualR_Pt; //new pixel-based r-threshhold -> pointer to arrays + //##################################################################################### + //#################################################################################### + //counter for minimal distance to background + // Defining the number of background-model-images, which have a lowerDIstance to the current Image than defined by the R-Thresholds, that are necessary + // to decide that this pixel is background + int Raute_min; + //##################################################################################### + //#################################################################################### + //initial value of inverse update factor T(x_i) + // Initialize the background-model update rate + double T_init; + + //increasing Factor of the update rate 1/T(x_i) + // scale that defines the increasing of the update rate of the background model, if the current pixel is background + //--> more frequently updates if pixel is background because, there shouln't be any change + double T_inc; + + //upper boundary of T(x_i) + // defining an upper value, that nrSubsampling can achieve, thus it doesn't reach to an infinite value, where actually no update is possible + // at all + double T_upper; + + //lower boundary of T(x_i) + // defining a minimum value for nrSubsampling --> base value 2.0 + double T_lower; + + //decreasing factor of the update rate 1/T(x_i) + // opposite scale to increasingRateScale, for decreasing the update rate of the background model, if the current pixel is foreground + //--> Thesis: Our Foreground is a real moving object -> thus the background-model is good, so don't update it + double T_dec; + + //holds update rate of current pixel + cv::Mat actualT; + float* actualT_Pt; + + //##################################################################################### + + + cv::Mat sobelX, sobelY; +}; + diff --git a/package_bgs/pt/PixelBasedAdaptiveSegmenter.cpp b/package_bgs/pt/PixelBasedAdaptiveSegmenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..10c8e8e1f87511906ef8b8bac25ecd82f01d186b --- /dev/null +++ b/package_bgs/pt/PixelBasedAdaptiveSegmenter.cpp @@ -0,0 +1,124 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "PixelBasedAdaptiveSegmenter.h" + +PixelBasedAdaptiveSegmenter::PixelBasedAdaptiveSegmenter() : firstTime(true), showOutput(true), enableInputBlur(true), enableOutputBlur(true), +alpha(7.0), beta(1.0), N(20), Raute_min(2), R_incdec(0.05), R_lower(18), +R_scale(5), T_dec(0.05), T_inc(1), T_init(18), T_lower(2), T_upper(200) +{ + std::cout << "PixelBasedAdaptiveSegmenter()" << std::endl; +} + +PixelBasedAdaptiveSegmenter::~PixelBasedAdaptiveSegmenter() +{ + std::cout << "~PixelBasedAdaptiveSegmenter()" << std::endl; +} + +void PixelBasedAdaptiveSegmenter::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + { + pbas.setAlpha(alpha); + pbas.setBeta(beta); + pbas.setN(N); + pbas.setRaute_min(Raute_min); + pbas.setR_incdec(R_incdec); + pbas.setR_lower(R_lower); + pbas.setR_scale(R_scale); + pbas.setT_dec(T_dec); + pbas.setT_inc(T_inc); + pbas.setT_init(T_init); + pbas.setT_lower(T_lower); + pbas.setT_upper(T_upper); + + saveConfig(); + } + + cv::Mat img_input_new; + if(enableInputBlur) + cv::GaussianBlur(img_input, img_input_new, cv::Size(5,5), 1.5); + else + img_input.copyTo(img_input_new); + + cv::Mat img_foreground; + pbas.process(&img_input_new, &img_foreground); + + if(enableOutputBlur) + cv::medianBlur(img_foreground, img_foreground, 5); + + if(showOutput) + cv::imshow("PBAS", img_foreground); + + img_foreground.copyTo(img_output); + + firstTime = false; +} + +void PixelBasedAdaptiveSegmenter::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/PixelBasedAdaptiveSegmenter.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "enableInputBlur", enableInputBlur); + cvWriteInt(fs, "enableOutputBlur", enableOutputBlur); + + cvWriteReal(fs, "alpha", alpha); + cvWriteReal(fs, "beta", beta); + cvWriteInt(fs, "N", N); + cvWriteInt(fs, "Raute_min", Raute_min); + cvWriteReal(fs, "R_incdec", R_incdec); + cvWriteInt(fs, "R_lower", R_lower); + cvWriteInt(fs, "R_scale", R_scale); + cvWriteReal(fs, "T_dec", T_dec); + cvWriteInt(fs, "T_inc", T_inc); + cvWriteInt(fs, "T_init", T_init); + cvWriteInt(fs, "T_lower", T_lower); + cvWriteInt(fs, "T_upper", T_upper); + + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void PixelBasedAdaptiveSegmenter::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/PixelBasedAdaptiveSegmenter.xml", 0, CV_STORAGE_READ); + + enableInputBlur = cvReadIntByName(fs, 0, "enableInputBlur", true); + enableOutputBlur = cvReadIntByName(fs, 0, "enableOutputBlur", true); + + alpha = cvReadRealByName(fs, 0, "alpha", 7.0); + beta = cvReadRealByName(fs, 0, "beta", 1.0); + N = cvReadIntByName(fs, 0, "N", 20); + Raute_min = cvReadIntByName(fs, 0, "Raute_min", 2); + R_incdec = cvReadRealByName(fs, 0, "R_incdec", 0.05); + R_lower = cvReadIntByName(fs, 0, "R_lower", 18); + R_scale = cvReadIntByName(fs, 0, "R_scale", 5); + T_dec = cvReadRealByName(fs, 0, "T_dec", 0.05); + T_inc = cvReadIntByName(fs, 0, "T_inc", 1); + T_init = cvReadIntByName(fs, 0, "T_init", 18); + T_lower = cvReadIntByName(fs, 0, "T_lower", 2); + T_upper = cvReadIntByName(fs, 0, "T_upper", 200); + + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/pt/PixelBasedAdaptiveSegmenter.h b/package_bgs/pt/PixelBasedAdaptiveSegmenter.h new file mode 100644 index 0000000000000000000000000000000000000000..35409b5b28e6db9b33bab34e92131a84c1c8402f --- /dev/null +++ b/package_bgs/pt/PixelBasedAdaptiveSegmenter.h @@ -0,0 +1,58 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "PBAS.h" +#include "../IBGS.h" + +class PixelBasedAdaptiveSegmenter : public IBGS +{ +private: + PBAS pbas; + + bool firstTime; + bool showOutput; + bool enableInputBlur; + bool enableOutputBlur; + + float alpha; + float beta; + int N; + int Raute_min; + float R_incdec; + int R_lower; + int R_scale; + float T_dec; + int T_inc; + int T_init; + int T_lower; + int T_upper; + +public: + PixelBasedAdaptiveSegmenter(); + ~PixelBasedAdaptiveSegmenter(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; \ No newline at end of file diff --git a/package_bgs/sjn/SJN_MultiCueBGS.cpp b/package_bgs/sjn/SJN_MultiCueBGS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..55a79373839976ace5803f06886fe31fc7a4d60a --- /dev/null +++ b/package_bgs/sjn/SJN_MultiCueBGS.cpp @@ -0,0 +1,2062 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +//------------------------------------------------------------------------------------------------------------------------------------// +// A BGS Method Using Multiple Color, Texture, Appearance Cues in the Scene // +// // +// - Paper: A New Framework for Background Subtraction Using Multiple Cues (ACCV2012) // +// - Code by: SeungJon Noh // +//------------------------------------------------------------------------------------------------------------------------------------// +//#include "StdAfx.h" +#include "SJN_MultiCueBGS.h" +SJN_MultiCueBGS::SJN_MultiCueBGS() : firstTime(true), showOutput(true) +{ + //---------------------------------- + // User adjustable parameters + //---------------------------------- + g_iTrainingPeriod = 20; //the training period (The parameter t in the paper) + g_iT_ModelThreshold = 1; //the threshold for texture-model based BGS. (The parameter tau_T in the paper) + g_iC_ModelThreshold = 10; //the threshold for appearance based verification. (The parameter tau_A in the paper) + + g_fLearningRate = 0.05; //the learning rate for background models. (The parameter alpha in the paper) + + g_nTextureTrainVolRange = 15; //the codebook size factor for texture models. (The parameter k in the paper) + g_nColorTrainVolRange = 20; //the codebook size factor for color models. (The parameter eta_1 in the paper) + + g_bAbsorptionEnable = TRUE; //If TRUE, cache-book is also modeled for ghost region removal. + g_iAbsortionPeriod = 200; //the period to absorb static ghost regions + + g_iRWidth = 160, g_iRHeight = 120; //Frames are precessed after reduced in this size . + + //------------------------------------ + // For codebook maintenance + //------------------------------------ + g_iBackClearPeriod = 300; //the period to clear background models + g_iCacheClearPeriod = 30; //the period to clear cache-book models + + //------------------------------------ + // Initialization of other parameters + //------------------------------------ + g_nNeighborNum = 6, g_nRadius = 2; + g_fConfidenceThre = g_iT_ModelThreshold/(float)g_nNeighborNum; //the final decision threshold + + g_iFrameCount = 0; + g_bForegroundMapEnable = FALSE; //TRUE only when BGS is successful + g_bModelMemAllocated = FALSE; //To handle memory.. + g_bNonModelMemAllocated = FALSE; //To handle memory.. +} + +SJN_MultiCueBGS::~SJN_MultiCueBGS(void){ + Destroy(); +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the main function to background modeling and subtraction // // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel){ + + if (img_input.empty()) + return; + + loadConfig(); + + if (firstTime) + saveConfig(); + + //--STep1: Background Modeling--// + IplImage* frame = &IplImage(img_input); + IplImage* result_image = cvCreateImage(cvGetSize(frame), IPL_DEPTH_8U, 3); + cvSetZero(result_image); + + if(g_iFrameCount <= g_iTrainingPeriod){ + BackgroundModeling_Par(frame); + g_iFrameCount++; + } + + //--Step2: Background Subtraction--// + else{ + g_bForegroundMapEnable = FALSE; + + ForegroundExtraction(frame); + UpdateModel_Par(); + + //Get BGS Results + GetForegroundMap(result_image, NULL); + } + + cv::Mat temp(result_image,TRUE); + temp.copyTo(img_output); + + cvReleaseImage(&result_image); + + if (showOutput) + { + cv::imshow("MultiCueBGS FG", img_output); + } + + firstTime = false; +} + +void SJN_MultiCueBGS::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/MultiCueBGS.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void SJN_MultiCueBGS::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/MultiCueBGS.xml", 0, CV_STORAGE_READ); + + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the system initialization function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::Initialize(IplImage* frame) +{ + int i,j; + + g_iHeight = frame->height; + g_iWidth = frame->width; + + Destroy(); + + //-------------------------------------------------------- + // memory initialization + //-------------------------------------------------------- + g_ResizedFrame = cvCreateImage(cvSize(g_iRWidth, g_iRHeight), IPL_DEPTH_8U, 3); + + g_aGaussFilteredFrame = (uchar***)malloc(sizeof(uchar**)*g_iRHeight); + for(i=0;i<g_iRHeight;i++){ + g_aGaussFilteredFrame[i] = (uchar**)malloc(sizeof(uchar*)*g_iRWidth); + for(j=0;j<g_iRWidth;j++) g_aGaussFilteredFrame[i][j] = (uchar*)malloc(sizeof(uchar)*3); + } + + g_aXYZFrame = (uchar***)malloc(sizeof(uchar**)*g_iRHeight); + for(i=0;i<g_iRHeight;i++){ + g_aXYZFrame[i] = (uchar**)malloc(sizeof(uchar*)*g_iRWidth); + for(j=0;j<g_iRWidth;j++) g_aXYZFrame[i][j] = (uchar*)malloc(sizeof(uchar)*3); + } + + g_aLandmarkArray = (uchar**)malloc(sizeof(uchar*)*g_iRHeight); + for(i=0;i<g_iRHeight;i++) g_aLandmarkArray[i] = (uchar*)malloc(sizeof(uchar)*g_iRWidth); + + g_aResizedForeMap = (uchar**)malloc(sizeof(uchar*)*g_iRHeight); + for(i=0;i<g_iRHeight;i++) g_aResizedForeMap[i] = (uchar*)malloc(sizeof(uchar)*g_iRWidth); + + g_aForegroundMap = (uchar**)malloc(sizeof(uchar*)*g_iHeight); + for(i=0;i<g_iHeight;i++) g_aForegroundMap[i] = (uchar*)malloc(sizeof(uchar)*g_iWidth); + + g_aUpdateMap = (BOOL**)malloc(sizeof(BOOL*)*g_iRHeight); + for(i=0;i<g_iRHeight;i++) g_aUpdateMap[i] = (BOOL*)malloc(sizeof(BOOL)*g_iRWidth); + + //Bound Box Related.. + int iElementNum = 300; + g_BoundBoxInfo = (BoundingBoxInfo*)malloc(sizeof(BoundingBoxInfo)); + g_BoundBoxInfo->m_iArraySize = iElementNum; + g_BoundBoxInfo->m_iBoundBoxNum = iElementNum; + + g_BoundBoxInfo->m_aLeft = (short*)malloc( sizeof(short) * iElementNum ); g_BoundBoxInfo->m_aRight = (short*)malloc( sizeof(short) * iElementNum ); + g_BoundBoxInfo->m_aBottom = (short*)malloc( sizeof(short) * iElementNum ); g_BoundBoxInfo->m_aUpper = (short*)malloc( sizeof(short) * iElementNum ); + + g_BoundBoxInfo->m_aRLeft = (short*)malloc( sizeof(short) * iElementNum ); g_BoundBoxInfo->m_aRRight = (short*)malloc( sizeof(short) * iElementNum ); + g_BoundBoxInfo->m_aRBottom = (short*)malloc( sizeof(short) * iElementNum ); g_BoundBoxInfo->m_aRUpper = (short*)malloc( sizeof(short) * iElementNum ); + + g_BoundBoxInfo->m_ValidBox = (BOOL*)malloc( sizeof(BOOL) * iElementNum ); + + //-------------------------------------------------------- + // texture model related + //-------------------------------------------------------- + T_AllocateTextureModelRelatedMemory(); + + //-------------------------------------------------------- + // color moddel related + //-------------------------------------------------------- + C_AllocateColorModelRelatedMemory(); + + g_bModelMemAllocated = TRUE; + g_bNonModelMemAllocated = TRUE; + +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the function to release allocated memories // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::Destroy() +{ + if(g_bModelMemAllocated==FALSE && g_bNonModelMemAllocated==FALSE) return; + + int i,j; + short nNeighborNum = g_nNeighborNum; + + if(g_bModelMemAllocated==TRUE){ + T_ReleaseTextureModelRelatedMemory(); + C_ReleaseColorModelRelatedMemory(); + + g_bModelMemAllocated = FALSE; + } + + if(g_bNonModelMemAllocated==TRUE){ + + cvReleaseImage(&g_ResizedFrame); + + for(i=0;i<g_iRHeight;i++){ + for(j=0;j<g_iRWidth;j++) free(g_aGaussFilteredFrame[i][j]); + free(g_aGaussFilteredFrame[i]); + } + free(g_aGaussFilteredFrame); + + for(i=0;i<g_iRHeight;i++){ + for(j=0;j<g_iRWidth;j++) free(g_aXYZFrame[i][j]); + free(g_aXYZFrame[i]); + } + free(g_aXYZFrame); + + for(i=0;i<g_iRHeight;i++) free(g_aLandmarkArray[i]); + free(g_aLandmarkArray); + + for(i=0;i<g_iRHeight;i++) free(g_aResizedForeMap[i]); + free(g_aResizedForeMap); + + for(i=0;i<g_iHeight;i++) free(g_aForegroundMap[i]); + free(g_aForegroundMap); + + for(i=0;i<g_iRHeight;i++) free(g_aUpdateMap[i]); + free(g_aUpdateMap); + + free(g_BoundBoxInfo->m_aLeft); free(g_BoundBoxInfo->m_aRight); free(g_BoundBoxInfo->m_aBottom); free(g_BoundBoxInfo->m_aUpper); + free(g_BoundBoxInfo->m_aRLeft); free(g_BoundBoxInfo->m_aRRight); free(g_BoundBoxInfo->m_aRBottom); free(g_BoundBoxInfo->m_aRUpper); + free(g_BoundBoxInfo->m_ValidBox); + free(g_BoundBoxInfo); + + g_bNonModelMemAllocated = FALSE; + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the preprocessing function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::PreProcessing(IplImage* frame){ + + //image resize + ReduceImageSize(frame, g_ResizedFrame); + + //Gaussian filtering + GaussianFiltering(g_ResizedFrame, g_aGaussFilteredFrame); + + //color space conversion + BGR2HSVxyz_Par(g_aGaussFilteredFrame,g_aXYZFrame); +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the background modeling function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::BackgroundModeling_Par(IplImage* frame){ + + //initialization + if(g_iFrameCount == 0) Initialize(frame); + + //Step1: pre-processing + PreProcessing(frame); + + int iH_Start = g_nRadius, iH_end = g_iRHeight-g_nRadius; + int iW_Start = g_nRadius, iW_end = g_iRWidth-g_nRadius; + + float fLearningRate = g_fLearningRate * 4; + + //Step2: background modeling + for(int i=iH_Start; i<iH_end; i++){ + for(int j=iW_Start;j<iW_end;j++){ + point center; + center.m_nX = j; + center.m_nY = i; + + T_ModelConstruction(g_nTextureTrainVolRange,fLearningRate,g_aXYZFrame,center,g_aNeighborDirection[i][j],g_TextureModel[i][j]); + C_CodebookConstruction(g_aXYZFrame[i][j], j, i, g_nColorTrainVolRange, fLearningRate, g_ColorModel[i][j]); + } + } + + //Step3: Clear non-essential codewords + if(g_iFrameCount==g_iTrainingPeriod){ + for(int i=0; i<g_iRHeight; i++){ + for(int j=0;j<g_iRWidth;j++){ + T_ClearNonEssentialEntries(g_iTrainingPeriod,g_TextureModel[i][j]); + + C_ClearNonEssentialEntries(g_iTrainingPeriod,g_ColorModel[i][j]); + } + } + g_iFrameCount++; + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the background subtraction function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::ForegroundExtraction(IplImage* frame){ + + //Step1:pre-processing + PreProcessing(frame); + + //Step3: texture-model based process + T_GetConfidenceMap_Par(g_aXYZFrame,g_aTextureConfMap,g_aNeighborDirection,g_TextureModel); + + //Step2: color-model based verification + CreateLandmarkArray_Par(g_fConfidenceThre,g_nColorTrainVolRange,g_aTextureConfMap,g_nNeighborNum,g_aXYZFrame, g_aNeighborDirection, + g_TextureModel,g_ColorModel,g_aLandmarkArray); + + //Step3: verification procedures + PostProcessing(frame); + +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the post-processing function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::PostProcessing(IplImage* frame){ + + //Step1: morphological operation + MorphologicalOpearions(g_aLandmarkArray,g_aResizedForeMap,0.5,5,g_iRWidth,g_iRHeight); + g_bForegroundMapEnable = TRUE; + + //Step2: labeling + int** aLabelTable = (int**)malloc(sizeof(int*)*g_iRHeight); + for(int i=0;i<g_iRHeight;i++) aLabelTable[i] = (int*)malloc(sizeof(int)*g_iRWidth); + + int iLabelCount; + Labeling(g_aResizedForeMap,&iLabelCount,aLabelTable); + + //Step3: getting bounding boxes for each candidate fore-blob + SetBoundingBox(iLabelCount, aLabelTable); + + //Step4: size and appearance based verification + BoundBoxVerification(frame,g_aResizedForeMap,g_BoundBoxInfo); + + //Step5: ��ȿ�� Foreground Region�鸸�� ��� �������� ���� + RemovingInvalidForeRegions(g_aResizedForeMap,g_BoundBoxInfo); + + for(int i=0;i<g_iRHeight;i++) free(aLabelTable[i]); + free(aLabelTable); +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the background-model update function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::UpdateModel_Par(){ + short nNeighborNum = g_nNeighborNum; + + //Step1: update map construction + for(int i=0;i<g_iRHeight;i++){ + for(int j=0;j<g_iRWidth;j++){ + g_aUpdateMap[i][j] = TRUE; + } + } + + for(int k=0;k<g_BoundBoxInfo->m_iBoundBoxNum;k++){ + if(g_BoundBoxInfo->m_ValidBox[k]==TRUE){ + for(int i=g_BoundBoxInfo->m_aRUpper[k]; i<=g_BoundBoxInfo->m_aRBottom[k]; i++){ + for(int j=g_BoundBoxInfo->m_aRLeft[k]; j<=g_BoundBoxInfo->m_aRRight[k]; j++){ + g_aUpdateMap[i][j] = FALSE; + } + } + } + } + + //Step2: update + int iH_Start = g_nRadius, iH_End = g_iRHeight-g_nRadius; + int iW_Start = g_nRadius, iW_End = g_iRWidth-g_nRadius; + + float fLearningRate = (float)g_fLearningRate; + + for(int i=iH_Start; i<iH_End; i++){ + for(int j=iW_Start;j<iW_End;j++){ + + point center; + + center.m_nX = j; + center.m_nY = i; + + if(g_aUpdateMap[i][j]==TRUE){ + //model update + T_ModelConstruction(g_nTextureTrainVolRange,fLearningRate,g_aXYZFrame,center,g_aNeighborDirection[i][j],g_TextureModel[i][j]); + C_CodebookConstruction(g_aXYZFrame[i][j],j,i,g_nColorTrainVolRange,fLearningRate,g_ColorModel[i][j]); + + //clearing non-essential codewords + T_ClearNonEssentialEntries(g_iBackClearPeriod,g_TextureModel[i][j]); + C_ClearNonEssentialEntries(g_iBackClearPeriod,g_ColorModel[i][j]); + + } + else { + if(g_bAbsorptionEnable==TRUE){ + //model update + T_ModelConstruction(g_nTextureTrainVolRange,fLearningRate,g_aXYZFrame,center,g_aNeighborDirection[i][j],g_TCacheBook[i][j]); + C_CodebookConstruction(g_aXYZFrame[i][j],j,i,g_nColorTrainVolRange,fLearningRate,g_CCacheBook[i][j]); + + //clearing non-essential codewords + T_Absorption(g_iAbsortionPeriod,center,g_aTContinuousCnt,g_aTReferredIndex,g_TextureModel[i][j],g_TCacheBook[i][j]); + C_Absorption(g_iAbsortionPeriod,center,g_aCContinuousCnt,g_aCReferredIndex,g_ColorModel[i][j],g_CCacheBook[i][j]); + + } + } + + //clearing non-essential codewords for cache-books + if(g_bAbsorptionEnable==TRUE){ + T_ClearNonEssentialEntriesForCachebook(g_aLandmarkArray[i][j],g_aTReferredIndex[i][j],10,g_TCacheBook[i][j]); + C_ClearNonEssentialEntriesForCachebook(g_aLandmarkArray[i][j],g_aCReferredIndex[i][j],10,g_CCacheBook[i][j]); + } + } + } + +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the color based verification function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::CreateLandmarkArray_Par(float fConfThre, short nTrainVolRange, float**aConfMap, int iNehborNum, uchar*** aXYZ, + point*** aNeiDir, TextureModel**** TModel, ColorModel*** CModel, uchar**aLandmarkArr){ + + int iBound_w = g_iRWidth-g_nRadius; + int iBound_h = g_iRHeight-g_nRadius; + + for(int i=0; i<g_iRHeight; i++){ + for(int j=0;j<g_iRWidth;j++){ + + if(i<g_nRadius || i>=iBound_h || j<g_nRadius || j>=iBound_w) { + aLandmarkArr[i][j] = 0; + continue; + } + + double tmp = aConfMap[i][j]; + + if(tmp > fConfThre) aLandmarkArr[i][j] = 255; + else{ + aLandmarkArr[i][j] = 0; + //Calculating texture amount in the background + double dBackAmt, dCnt; + dBackAmt = dCnt = 0; + + for(int m=0; m<iNehborNum; m++){ + for(int n=0;n<TModel[i][j][m]->m_iNumEntries;n++){ + dBackAmt += TModel[i][j][m]->m_Codewords[n]->m_fMean; + dCnt++; + } + } + dBackAmt /= dCnt; + + //Calculating texture amount in the input image + double dTemp, dInputAmt = 0; + for(int m=0; m<iNehborNum; m++){ + dTemp = aXYZ[i][j][2] - aXYZ[ aNeiDir[i][j][m].m_nY ][ aNeiDir[i][j][m].m_nX ][2]; + + if(dTemp>=0) dInputAmt += dTemp; + else dInputAmt -= dTemp; + + } + + //If there are only few textures in both background and input image + if(dBackAmt < 50 && dInputAmt <50){ + //Conduct color codebook matching + BOOL bMatched = FALSE; + for(int m=0;m<CModel[i][j]->m_iNumEntries;m++){ + + int iMatchedCount = 0; + for(int n=0;n<3;n++){ + double dLowThre = CModel[i][j]->m_Codewords[m]->m_dMean[n] - nTrainVolRange - 10; + double dHighThre = CModel[i][j]->m_Codewords[m]->m_dMean[n] + nTrainVolRange + 10; + + if(dLowThre <= aXYZ[i][j][n] && aXYZ[i][j][n] <= dHighThre) iMatchedCount++; + } + + if(iMatchedCount==3) { + bMatched = TRUE; + break; + } + + } + if(bMatched==TRUE) aLandmarkArr[i][j] = 125; + else aLandmarkArr[i][j] = 255; + + } + + } + } + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the Gaussian filtering function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::GaussianFiltering(IplImage* frame, uchar*** aFilteredFrame){ + + double dSigma = 0.7; + + if(dSigma==0){ + for(int i=0; i<g_iRHeight; i++){ + for(int j=0; j<g_iRWidth; j++){ + aFilteredFrame[i][j][0] = frame->imageData[i*frame->widthStep + j*3]; + aFilteredFrame[i][j][1] = frame->imageData[i*frame->widthStep + j*3 + 1]; + aFilteredFrame[i][j][2] = frame->imageData[i*frame->widthStep + j*3 + 2]; + } + } + } + + else{ + cv::Mat temp_img(frame,TRUE); + cv::GaussianBlur(temp_img, temp_img, cv::Size(7,7), dSigma); + + //Store results into aFilteredFrame[][][] + IplImage* img = &IplImage(temp_img); + int iWidthStep = img->widthStep; + + for(int i=0; i<g_iRHeight; i++){ + for(int j=0; j<g_iRWidth; j++){ + aFilteredFrame[i][j][0] = img->imageData[i*img->widthStep + j*3]; + aFilteredFrame[i][j][1] = img->imageData[i*img->widthStep + j*3 + 1]; + aFilteredFrame[i][j][2] = img->imageData[i*img->widthStep + j*3 + 2]; + } + } + + } + + +} + +//------------------------------------------------------------------------------------------------------------------------------------// +// the image resize function // +//------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::ReduceImageSize(IplImage* SrcImage, IplImage* DstImage){ + + int iChannel = 3; + + double dResizeFactor_w = (double)g_iWidth/(double)g_iRWidth; + double dResizeFactor_h = (double)g_iHeight/(double)g_iRHeight; + + + for(int i=0;i<g_iRHeight;i++){ + for(int j=0;j<g_iRWidth;j++){ + int iSrcY = (int)(i*dResizeFactor_h); + int iSrcX = (int)(j*dResizeFactor_w); + + for(int k=0;k<iChannel;k++) DstImage->imageData[i*DstImage->widthStep + j*3 +k] + = SrcImage->imageData[iSrcY*SrcImage->widthStep + iSrcX*3 + k]; + } + } + +} + +//------------------------------------------------------------------------------------------------------------------------------------// +// the color space conversion function // +//------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::BGR2HSVxyz_Par(uchar*** aBGR, uchar*** aXYZ){ + + double dH_ratio = (2*PI)/360; + + for(int i=0; i<g_iRHeight; i++){ + + double dR,dG,dB; + double dMax, dMin; + + double dH,dS, dV; + + for(int j=0;j<g_iRWidth;j++){ + + dB = (double)( aBGR[i][j][0] )/255; + dG = (double)( aBGR[i][j][1] )/255; + dR = (double)( aBGR[i][j][2] )/255; + + + //Find max, min + dMin = MIN3(dR,dG,dB); + dMax = MAX3(dR,dG,dB); + + + //Get V + dV = dMax; + + //Get S, H + if(dV==0) dS = dH = 0; + else{ + + //S value + dS = (dMax-dMin)/dMax; + + if(dS == 0) dH =0; + else{ + //H value + if(dMax == dR) { + dH = 60*(dG - dB)/dS; + if(dH < 0) dH = 360 + dH; + } + else if(dMax == dG) dH = 120 + 60*(dB - dR)/dS; + else dH = 240 + 60*(dR - dG)/dS; + } + } + dH = dH * dH_ratio; + + aXYZ[i][j][0] = (uchar)( (dV * dS * cos(dH) * 127.5) + 127.5 ); //X --> 0~255 + aXYZ[i][j][1] = (uchar)( (dV * dS * sin(dH) * 127.5) + 127.5 ); //Y --> 0~255 + aXYZ[i][j][2] = (uchar)( dV * 255 ); //Z --> 0~255 + + } + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the function to get enlarged confidence map // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::GetEnlargedMap(float** aOriginMap, float** aEnlargedMap){ + int i,j; + + short nSrcX; + short nSrcY; + + double dEWweight,dNSweight; + double dEWtop, dEWbottom; + + double dNW; //north-west + double dNE; //north-east + double dSW; //south-west + double dSE; //south-east + + double dScaleFactor_w = ( (double)g_iWidth ) / ( (double)g_iRWidth ); + double dScaleFactor_h = ( (double)g_iHeight ) / ( (double)g_iRHeight ); + + for(i=0;i<g_iHeight;i++){ + for(j=0;j<g_iWidth;j++){ + //backward mapping + nSrcY = (int)( i/dScaleFactor_h ); + nSrcX = (int)( j/dScaleFactor_w ); + + if( nSrcY == (g_iRHeight-1) ) nSrcY -= 1; + if( nSrcX == (g_iRWidth-1) ) nSrcX -= 1; + + dEWweight = i/dScaleFactor_h - nSrcY; + dNSweight = j/dScaleFactor_w - nSrcX; + + dNW = (double)aOriginMap[nSrcY][nSrcX]; + dNE = (double)aOriginMap[nSrcY][nSrcX+1]; + dSW = (double)aOriginMap[nSrcY+1][nSrcX]; + dSE = (double)aOriginMap[nSrcY+1][nSrcX+1]; + + // interpolation + dEWtop = dNW + dEWweight * (dNE - dNW); + dEWbottom = dSW + dEWweight * (dSE - dSW); + + aEnlargedMap[i][j] = (float)( dEWtop + dNSweight * (dEWbottom - dEWtop) ); + } + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the morphological operation function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::MorphologicalOpearions(uchar** aInput, uchar** aOutput, double dThresholdRatio, int iMaskSize, int iWidth, int iHeight){ + + int iOffset = (int)(iMaskSize/2); + + int iBound_w = iWidth-iOffset; + int iBound_h = iHeight-iOffset; + + uchar** aTemp = (uchar**)malloc(sizeof(uchar*)*iHeight); + for(int i=0;i<iHeight;i++){ + aTemp[i] = (uchar*)malloc(sizeof(uchar)*iWidth); + } + + for(int i=0;i<iHeight;i++){ + for(int j=0;j<iWidth;j++){ + aTemp[i][j] = aInput[i][j]; + } + } + + int iThreshold = (int)(iMaskSize*iMaskSize*dThresholdRatio); + for(int i=0;i<iHeight;i++){ + for(int j=0;j<iWidth;j++){ + + if(i<iOffset || i>=iBound_h || j<iOffset || j>=iBound_w){ + aOutput[i][j] = 0; + continue; + } + + int iCnt = 0; + for(int m=-iOffset;m<=iOffset;m++){ + for(int n=-iOffset;n<=iOffset;n++){ + if(aTemp[i+m][j+n]==255) iCnt++; + } + } + + if(iCnt >= iThreshold) aOutput[i][j] = 255; + else aOutput[i][j] = 0; + } + } + + + for(int i=0;i<iHeight;i++){ + free(aTemp[i]); + } + free(aTemp); + +} +//-----------------------------------------------------------------------------------------------------------------------------------------// +// 2-raster scan pass based labeling function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::Labeling(uchar** aBinaryArray,int* pLabelCount, int** aLabelTable){ + int x,y,i; // pass 1,2 + int cnt=0; // pass 1 + int label=0; // pass 2 + + int iSize = g_iRWidth * g_iRHeight; + int iTableSize = iSize/2; + + // initialize , table1 table1 + int* aPass1 = (int*)malloc(iSize * sizeof(int)); + int* aTable1 = (int*)malloc(iSize/2 * sizeof(int)); + int* aTable2 = (int*)malloc(iSize/2 * sizeof(int)); + + memset(aPass1, 0, (iSize)* sizeof(int)); + for (y=1; y < (g_iRHeight); y++){ + for (x = 1; x < (g_iRWidth); x++){ + aLabelTable[y][x] = 0; + } + } + + for(i=0; i < iTableSize; i++){ + aTable1[i] = i; + } + memset(aTable2, 0, iTableSize * sizeof(int)); + + // pass 1 + for (y=1; y < (g_iRHeight); y++){ + for (x = 1; x < (g_iRWidth); x++){ + + if (aBinaryArray[y][x] == 255){ // fore ground?? + int up, le; + + up = aPass1[ (y-1)*(g_iRWidth) + (x ) ]; // up index + le = aPass1[ (y )*(g_iRWidth) + (x-1) ]; // left index + + // case + if( up == 0 && le == 0){ + ++cnt; + aPass1[ y * g_iRWidth + x ] = cnt; + + }else if( up != 0 && le != 0){ + if ( up > le){ + aPass1[ y *g_iRWidth + x ] = le; + aTable1[up] = aTable1[le]; // update table1 table1 + }else{ + aPass1[ y * g_iRWidth + x ] = up; + aTable1[le] = aTable1[up]; // update table1 table1 + } + }else{ + aPass1[ y * g_iRWidth + x ] = up + le; + } + + } + + } + } + + // pass 2 + for (y=1; y < (g_iRHeight); y++){ + for (x = 1; x < (g_iRWidth); x++){ + + if (aPass1[ y * g_iRWidth + x]){ + int v = aTable1[aPass1[ y * g_iRWidth + x]]; + + if ( aTable2[v] == 0){ + ++label; + aTable2[v] = label; + } + + aLabelTable[y][x] = aTable2[v]; + } + } + } + + *pLabelCount = label; + + free(aPass1); + free(aTable1); + free(aTable2); +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the function to set bounding boxes for each candidate foreground regions // // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::SetBoundingBox(int iLabelCount, int** aLabelTable){ + int iBoundBoxIndex; + + g_BoundBoxInfo->m_iBoundBoxNum = iLabelCount; + + for (int i=0; i<g_BoundBoxInfo->m_iBoundBoxNum; i++ ){ + g_BoundBoxInfo->m_aRLeft[i] = 9999; //left + g_BoundBoxInfo->m_aRUpper[i] = 9999; //top + g_BoundBoxInfo->m_aRRight[i] = 0; //right + g_BoundBoxInfo->m_aRBottom[i] = 0; //bottom + } + + //Step1: Set tight bounding boxes + for(int i=1;i<g_iRHeight;i++){ + for(int j=1;j<g_iRWidth;j++){ + + if( (aLabelTable[i][j]==0) ) continue; + + iBoundBoxIndex = aLabelTable[i][j]-1; + + if( g_BoundBoxInfo->m_aRLeft[ iBoundBoxIndex ] > j ) g_BoundBoxInfo->m_aRLeft[ iBoundBoxIndex ] = j; //left + if( g_BoundBoxInfo->m_aRUpper[ iBoundBoxIndex ] > i ) g_BoundBoxInfo->m_aRUpper[ iBoundBoxIndex ] = i; //top + if( g_BoundBoxInfo->m_aRRight[ iBoundBoxIndex ] < j ) g_BoundBoxInfo->m_aRRight[ iBoundBoxIndex ] = j; //right + if( g_BoundBoxInfo->m_aRBottom[ iBoundBoxIndex ] < i ) g_BoundBoxInfo->m_aRBottom[ iBoundBoxIndex ] = i; //bottom + + } + } + + //Step2: Add margins. + int iBoundary_w = (int)(g_iRWidth / 80), iBoundary_h = (int)(g_iRHeight / 60); + + for( int i=0; i<g_BoundBoxInfo->m_iBoundBoxNum; i++ ){ + + g_BoundBoxInfo->m_aRLeft[i] -= iBoundary_w; + if(g_BoundBoxInfo->m_aRLeft[i] < g_nRadius) g_BoundBoxInfo->m_aRLeft[i] = g_nRadius; //left + + g_BoundBoxInfo->m_aRRight[i] += iBoundary_w; + if(g_BoundBoxInfo->m_aRRight[i] >= g_iRWidth-g_nRadius) g_BoundBoxInfo->m_aRRight[i] = g_iRWidth-g_nRadius-1; //Right + + g_BoundBoxInfo->m_aRUpper[i] -= iBoundary_h; + if(g_BoundBoxInfo->m_aRUpper[i] < g_nRadius) g_BoundBoxInfo->m_aRUpper[i] = g_nRadius; //Top + + g_BoundBoxInfo->m_aRBottom[i] += iBoundary_h; + if(g_BoundBoxInfo->m_aRBottom[i] >= g_iRHeight-g_nRadius) g_BoundBoxInfo->m_aRBottom[i] =g_iRHeight-g_nRadius-1; + } + + double dH_ratio = (double)g_iHeight/(double)g_iRHeight; + double dW_ratio = (double)g_iWidth/(double)g_iRWidth; + + for(int i=0;i<g_BoundBoxInfo->m_iBoundBoxNum;i++){ + g_BoundBoxInfo->m_aLeft[i] = (int)(g_BoundBoxInfo->m_aRLeft[i] * dW_ratio); + g_BoundBoxInfo->m_aUpper[i] = (int)(g_BoundBoxInfo->m_aRUpper[i] * dH_ratio); + g_BoundBoxInfo->m_aRight[i] = (int)(g_BoundBoxInfo->m_aRRight[i] * dW_ratio); + g_BoundBoxInfo->m_aBottom[i] = (int)(g_BoundBoxInfo->m_aRBottom[i] * dH_ratio); + } + +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the box verification function // // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::BoundBoxVerification(IplImage* frame, uchar** aResForeMap, BoundingBoxInfo* BoundBoxInfo){ + + //Step1: Verification by the bounding box size + EvaluateBoxSize(BoundBoxInfo); + + //Step2: Verification by checking whether the region is ghost + EvaluateGhostRegion(frame, aResForeMap, BoundBoxInfo); + + //Step3: Counting the # of valid box + g_iForegroundNum = 0; + for( int i=0; i<BoundBoxInfo->m_iBoundBoxNum; i++ ){ + if(BoundBoxInfo->m_ValidBox[i] == TRUE) g_iForegroundNum++; + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the size based verification // // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::EvaluateBoxSize(BoundingBoxInfo* BoundBoxInfo){ + + //Set thresholds + int iLowThreshold_w, iHighThreshold_w; + iLowThreshold_w = g_iRWidth / 32; if(iLowThreshold_w<5) iLowThreshold_w = 5; + iHighThreshold_w = g_iRWidth; + + int iLowThreshold_h, iHighThreshold_h; + iLowThreshold_h = g_iRHeight / 24; if(iLowThreshold_h<5) iLowThreshold_h = 5; + iHighThreshold_h = g_iRHeight; + + int iBoxWidth,iBoxHeight; + + //Perform verification. + for( int i=0; i<BoundBoxInfo->m_iBoundBoxNum; i++ ){ + + iBoxWidth = BoundBoxInfo->m_aRRight[i] - BoundBoxInfo->m_aRLeft[i]; + iBoxHeight = BoundBoxInfo->m_aRBottom[i] - BoundBoxInfo->m_aRUpper[i]; + + if(iLowThreshold_w<=iBoxWidth && iBoxWidth<=iHighThreshold_w && iLowThreshold_h<=iBoxHeight && iBoxHeight<= iHighThreshold_h) { + BoundBoxInfo->m_ValidBox[i] = TRUE; + } + else BoundBoxInfo->m_ValidBox[i] = FALSE; + } +} + +//------------------------------------------------------------------------------------------------------------------------------------// +// overlapped region removal // +//------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::EvaluateOverlapRegionSize(BoundingBoxInfo* SrcBoxInfo){ + + BOOL *aValidBoxFlag = new BOOL[SrcBoxInfo->m_iBoundBoxNum]; + for(int i=0;i<SrcBoxInfo->m_iBoundBoxNum;i++) aValidBoxFlag[i] = TRUE; + + int size1, size2; + short *aLeft=SrcBoxInfo->m_aRLeft, *aRight=SrcBoxInfo->m_aRRight; + short *aTop=SrcBoxInfo->m_aRUpper, *aBottom=SrcBoxInfo->m_aRBottom; + + int iThreshold,iCount, iSmall_Idx, iLarge_Idx; + double dThreRatio = 0.7; + + for(int i=0;i<SrcBoxInfo->m_iBoundBoxNum;i++){ + + if(SrcBoxInfo->m_ValidBox[i]==FALSE) { + aValidBoxFlag[i] = FALSE; + continue; + } + + size1 = (aRight[i] - aLeft[i]) * (aBottom[i] - aTop[i]); + + for(int j=i;j<SrcBoxInfo->m_iBoundBoxNum;j++){ + if( (i==j) || (SrcBoxInfo->m_ValidBox[j]==FALSE) ) continue; + + //Setting threshold for checking overlapped region size + size2 = (aRight[j] - aLeft[j]) * (aBottom[j] - aTop[j]); + + if(size1 >= size2) { + iThreshold = (int)( size2 * dThreRatio ); + iSmall_Idx = j; iLarge_Idx = i; + } + else { + iThreshold = (int)( size1 * dThreRatio ); + iSmall_Idx= i; iLarge_Idx = j; + } + + //Calculating overlapped region size + iCount = 0; + for(int m=aLeft[iSmall_Idx]; m<aRight[iSmall_Idx]; m++){ + for(int n=aTop[iSmall_Idx]; n<aBottom[iSmall_Idx]; n++){ + if( aLeft[iLarge_Idx]<=m && m<=aRight[iLarge_Idx] && aTop[iLarge_Idx]<=n && n<=aBottom[iLarge_Idx]) iCount++; + } + } + + //Evaluating overlapped region size + if(iCount > iThreshold) aValidBoxFlag[iSmall_Idx] = FALSE; + } + } + + for(int i=0;i<SrcBoxInfo->m_iBoundBoxNum;i++) SrcBoxInfo->m_ValidBox[i] = aValidBoxFlag[i]; + delete[] aValidBoxFlag; +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// appearance based verification // // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::EvaluateGhostRegion(IplImage* frame, uchar** aResForeMap, BoundingBoxInfo* BoundBoxInfo){ + + double dThreshold = 10; + + BOOL** aUpdateMap = (BOOL**)malloc(sizeof(BOOL*)*g_iRHeight); + for(int i=0;i<g_iRHeight;i++){ + aUpdateMap[i] = (BOOL*)malloc(sizeof(BOOL)*g_iRWidth); + for(int j=0;j<g_iRWidth;j++) aUpdateMap[i][j] = FALSE; + } + + //Step1: Conduct fore-region evaluation to identify ghost regions + + for(int i=0; i<BoundBoxInfo->m_iBoundBoxNum; i++){ + if(BoundBoxInfo->m_ValidBox[i]==TRUE){ + int iWin_w = BoundBoxInfo->m_aRRight[i] - BoundBoxInfo->m_aRLeft[i]; + int iWin_h = BoundBoxInfo->m_aRBottom[i] - BoundBoxInfo->m_aRUpper[i]; + + //Generating edge image from bound-boxed frame region + IplImage* resized_frame = cvCreateImage(cvSize(g_iRWidth,g_iRHeight),IPL_DEPTH_8U,3); + cvResize(frame,resized_frame,CV_INTER_NN); + + cvSetImageROI(resized_frame, cvRect(BoundBoxInfo->m_aRLeft[i], BoundBoxInfo->m_aRUpper[i], iWin_w, iWin_h)); + IplImage* edge_frame = cvCreateImage(cvSize(iWin_w,iWin_h),IPL_DEPTH_8U,1); + + cvCvtColor(resized_frame,edge_frame,CV_BGR2GRAY); + cvResetImageROI(resized_frame); + + cvCanny(edge_frame,edge_frame,100,150); + + //Generating edge image from aResForeMap + IplImage* edge_fore = cvCreateImage(cvSize(iWin_w,iWin_h),IPL_DEPTH_8U,1); + for(int m=BoundBoxInfo->m_aRUpper[i]; m<BoundBoxInfo->m_aRBottom[i]; m++){ + for(int n=BoundBoxInfo->m_aRLeft[i]; n<BoundBoxInfo->m_aRRight[i]; n++){ + edge_fore->imageData[(m-BoundBoxInfo->m_aRUpper[i])*edge_fore->widthStep + (n-BoundBoxInfo->m_aRLeft[i])] = (char)aResForeMap[m][n]; + } + } + cvCanny(edge_fore,edge_fore,100,150); + //Calculating partial undirected Hausdorff distance + double distance = CalculateHausdorffDist(edge_frame,edge_fore); + + //Recording evaluation result + if(distance > dThreshold) { + + for(int m=BoundBoxInfo->m_aRUpper[i]; m<BoundBoxInfo->m_aRBottom[i]; m++){ + for(int n=BoundBoxInfo->m_aRLeft[i]; n<BoundBoxInfo->m_aRRight[i]; n++){ + aUpdateMap[m][n] = TRUE; + } + } + + BoundBoxInfo->m_ValidBox[i]=FALSE; + } + + cvReleaseImage(&resized_frame); + cvReleaseImage(&edge_frame); + cvReleaseImage(&edge_fore); + } + } + + //Step2: Adding information fo ghost region pixels to background model + float fLearningRate = g_fLearningRate; + + for(int i=0;i<g_iRHeight;i++){ + for(int j=0;j<g_iRWidth;j++){ + if(aUpdateMap[i][j]==TRUE){ + point center; + center.m_nX=j; center.m_nY=i; + + T_ModelConstruction(g_nTextureTrainVolRange,fLearningRate,g_aXYZFrame,center,g_aNeighborDirection[i][j],g_TextureModel[i][j]); + C_CodebookConstruction(g_aXYZFrame[i][j],j,i,g_nColorTrainVolRange,fLearningRate,g_ColorModel[i][j]); + + T_ClearNonEssentialEntries(g_iBackClearPeriod,g_TextureModel[i][j]); + C_ClearNonEssentialEntries(g_iBackClearPeriod,g_ColorModel[i][j]); + + } + } + } + + for(int i=0;i<g_iRHeight;i++) free(aUpdateMap[i]); + free(aUpdateMap); +} + + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the function to calculate partial undirected Hausdorff distance(forward distance) // // +//-----------------------------------------------------------------------------------------------------------------------------------------// +double SJN_MultiCueBGS::CalculateHausdorffDist(IplImage* input_image, IplImage* model_image){ + + //Step1: Generating imag vectors + //For reduce errors, points at the image boundary are excluded + vector<point> vInput, vModel; + point temp; + + //input image --> input vector + for(int i=0;i<input_image->height;i++){ + for(int j=0;j<input_image->width;j++){ + + if( (uchar)input_image->imageData[i*input_image->widthStep + j]==0 ) continue; + + temp.m_nX = j; temp.m_nY = i; + vInput.push_back(temp); + } + } + //model image --> model vector + for(int i=0; i<model_image->height; i++){ + for(int j=0; j<model_image->width; j++){ + if( (uchar)model_image->imageData[i*model_image->widthStep + j]==0 ) continue; + + temp.m_nX = j; temp.m_nY = i; + vModel.push_back(temp); + } + } + + if( vInput.size()==0 && vModel.size()!=0 ) return (double)vModel.size(); + else if( vInput.size()!=0 && vModel.size()==0 ) return (double)vInput.size(); + else if( vInput.size()==0 && vModel.size()==0) return 0.0; + + //Step2: Calculating forward distance h(Model,Image) + double dDist, temp1, temp2, dMinDist; + vector<double> vTempDist; + + for( auto iter_m=vModel.begin(); iter_m<vModel.end(); iter_m++ ){ + + dMinDist = 9999999; + for( auto iter_i=vInput.begin(); iter_i<vInput.end(); iter_i++){ + temp1 = (*iter_m).m_nX - (*iter_i).m_nX; + temp2 = (*iter_m).m_nY - (*iter_i).m_nY; + dDist = temp1*temp1 + temp2*temp2; + + if(dDist < dMinDist) dMinDist = dDist; + } + vTempDist.push_back(dMinDist); + } + sort(vTempDist.begin(), vTempDist.end()); //in ascending order + + double dQuantileVal = 0.9, dForwardDistance; + int iDistIndex = (int)(dQuantileVal*vTempDist.size()); if(iDistIndex == vTempDist.size()) iDistIndex -= 1; + + dForwardDistance = sqrt( vTempDist[ iDistIndex ] ); + return dForwardDistance; + +} + + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// function to remove non-valid bounding boxes fore fore-candidates // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::RemovingInvalidForeRegions(uchar** aResForeMap, BoundingBoxInfo* BoundBoxInfo){ + + int iBoxNum = BoundBoxInfo->m_iBoundBoxNum; + + for(int k=0;k<iBoxNum; k++){ + + if(BoundBoxInfo->m_ValidBox[k]==FALSE){ + for(int i=BoundBoxInfo->m_aRUpper[k]; i<BoundBoxInfo->m_aRBottom[k]; i++){ + for(int j=BoundBoxInfo->m_aRLeft[k]; j<BoundBoxInfo->m_aRRight[k]; j++){ + if(aResForeMap[i][j]==255) aResForeMap[i][j] = 0; + } + } + } + + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the function returning a foreground binary-map // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::GetForegroundMap(IplImage* return_image, IplImage* input_frame){ + + if(g_bForegroundMapEnable == FALSE) return; + + IplImage* temp_image = cvCreateImage(cvSize(g_iRWidth,g_iRHeight),IPL_DEPTH_8U,3); + + if(input_frame==NULL){ + for(int i=0;i<g_iRHeight;i++){ + for(int j=0;j<g_iRWidth;j++){ + temp_image->imageData[i*temp_image->widthStep + j*3] = (char)g_aResizedForeMap[i][j]; + temp_image->imageData[i*temp_image->widthStep + j*3 + 1] = (char)g_aResizedForeMap[i][j]; + temp_image->imageData[i*temp_image->widthStep + j*3 + 2] = (char)g_aResizedForeMap[i][j]; + } + } + } + + else{ + + cvResize(input_frame,temp_image); + CvScalar MixColor; + MixColor.val[0] = 255; //B + MixColor.val[1] = 0; //G + MixColor.val[2] = 255; //R + + for(int i=0;i<g_iRHeight;i++){ + for(int j=0;j<g_iRWidth;j++){ + + if(g_aResizedForeMap[i][j]==255) { + + uchar B = (uchar)temp_image->imageData[i*temp_image->widthStep + j*3]; + uchar G = (uchar)temp_image->imageData[i*temp_image->widthStep + j*3]; + uchar R = (uchar)temp_image->imageData[i*temp_image->widthStep + j*3]; + + B = (uchar)(B*0.45 + MixColor.val[0]*0.55); + G = (uchar)(G*0.45 + MixColor.val[1]*0.55); + R = (uchar)(R*0.45 + MixColor.val[2]*0.55); + + temp_image->imageData[i*temp_image->widthStep + j*3] = (char)B; + temp_image->imageData[i*temp_image->widthStep + j*3 + 1] = (char)G; + temp_image->imageData[i*temp_image->widthStep + j*3 + 2] = (char)R; + } + } + } + } + + cvResize(temp_image,return_image); + + cvReleaseImage(&temp_image); +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the initialization function for the texture-models // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::T_AllocateTextureModelRelatedMemory(){ + int i,j,k; + + //neighborhood system related + int iMaxNeighborArraySize = 8; + g_aNeighborDirection = (point***)malloc(sizeof(point**)*g_iRHeight); + for(i=0;i<g_iRHeight;i++){ + g_aNeighborDirection[i] = (point**)malloc(sizeof(point*)*g_iRWidth); + for(j=0;j<g_iRWidth;j++){ + g_aNeighborDirection[i][j] = (point*)malloc(sizeof(point)*iMaxNeighborArraySize); + } + } + T_SetNeighborDirection(g_aNeighborDirection); + + //texture-model related + int iElementArraySize = 6; + g_TextureModel = (TextureModel****)malloc(sizeof(TextureModel***)*g_iRHeight); + for(i=0;i<g_iRHeight;i++){ + g_TextureModel[i] = (TextureModel***)malloc(sizeof(TextureModel**)*g_iRWidth); + for(j=0;j<g_iRWidth;j++){ + g_TextureModel[i][j] = (TextureModel**)malloc(sizeof(TextureModel*)*g_nNeighborNum); + for(k=0;k<g_nNeighborNum;k++){ + g_TextureModel[i][j][k] = (TextureModel*)malloc(sizeof(TextureModel)); + g_TextureModel[i][j][k]->m_Codewords = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*iElementArraySize); + g_TextureModel[i][j][k]->m_iElementArraySize = iElementArraySize; + g_TextureModel[i][j][k]->m_iNumEntries = 0; + g_TextureModel[i][j][k]->m_iTotal = 0; + g_TextureModel[i][j][k]->m_bID = 1; + } + } + } + + g_aTextureConfMap = (float**)malloc(sizeof(float*)*g_iRHeight); + for(i=0;i<g_iRHeight;i++) g_aTextureConfMap[i] = (float*)malloc(sizeof(float)*g_iRWidth); + + //cache-book related + if(g_bAbsorptionEnable==TRUE){ + iElementArraySize = iElementArraySize/2; + if(iElementArraySize<3)iElementArraySize = 3; + + g_TCacheBook = (TextureModel****)malloc(sizeof(TextureModel***)*g_iRHeight); + for(i=0;i<g_iRHeight;i++){ + g_TCacheBook[i] = (TextureModel***)malloc(sizeof(TextureModel**)*g_iRWidth); + for(j=0;j<g_iRWidth;j++){ + g_TCacheBook[i][j] = (TextureModel**)malloc(sizeof(TextureModel*)*g_nNeighborNum); + for(k=0;k<g_nNeighborNum;k++){ + g_TCacheBook[i][j][k] = (TextureModel*)malloc(sizeof(TextureModel)); + g_TCacheBook[i][j][k]->m_Codewords = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*iElementArraySize); + g_TCacheBook[i][j][k]->m_iElementArraySize = iElementArraySize; + g_TCacheBook[i][j][k]->m_iNumEntries = 0; + g_TCacheBook[i][j][k]->m_iTotal = 0; + g_TCacheBook[i][j][k]->m_bID = 0; + } + } + } + + g_aTReferredIndex = (short***)malloc(sizeof(short**)*g_iRHeight); + g_aTContinuousCnt = (short***)malloc(sizeof(short**)*g_iRHeight); + for(i=0;i<g_iRHeight;i++){ + g_aTReferredIndex[i] = (short**)malloc(sizeof(short*)*g_iRWidth); + g_aTContinuousCnt[i] = (short**)malloc(sizeof(short*)*g_iRWidth); + for(j=0;j<g_iRWidth;j++) { + g_aTReferredIndex[i][j] = (short*)malloc(sizeof(short)*g_nNeighborNum); + g_aTContinuousCnt[i][j] = (short*)malloc(sizeof(short)*g_nNeighborNum); + for(k=0;k<g_nNeighborNum;k++){ + g_aTReferredIndex[i][j][k] = -1; + g_aTContinuousCnt[i][j][k] = 0; + } + } + } + } +} +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the memory release function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::T_ReleaseTextureModelRelatedMemory(){ + int i,j,k,m; + short nNeighborNum = g_nNeighborNum; + + for(i=0;i<g_iRHeight;i++){ + for(j=0;j<g_iRWidth;j++){ + for(k=0;k<nNeighborNum;k++){ + for(m=0;m<g_TextureModel[i][j][k]->m_iNumEntries;m++) free(g_TextureModel[i][j][k]->m_Codewords[m]); + free(g_TextureModel[i][j][k]->m_Codewords); + free(g_TextureModel[i][j][k]); + } + free(g_TextureModel[i][j]); + }free(g_TextureModel[i]); + } + free(g_TextureModel); + + for(i=0;i<g_iRHeight;i++){ + for(j=0;j<g_iRWidth;j++) free(g_aNeighborDirection[i][j]); + free(g_aNeighborDirection[i]); + } + free(g_aNeighborDirection); + + for(i=0;i<g_iRHeight;i++) free(g_aTextureConfMap[i]); + free(g_aTextureConfMap); + + if(g_bAbsorptionEnable==TRUE){ + for(i=0;i<g_iRHeight;i++){ + for(j=0;j<g_iRWidth;j++){ + for(k=0;k<nNeighborNum;k++){ + for(m=0;m<g_TCacheBook[i][j][k]->m_iNumEntries;m++) free(g_TCacheBook[i][j][k]->m_Codewords[m]); + free(g_TCacheBook[i][j][k]->m_Codewords); + free(g_TCacheBook[i][j][k]); + } + free(g_TCacheBook[i][j]); + }free(g_TCacheBook[i]); + } + free(g_TCacheBook); + + for(i=0;i<g_iRHeight;i++){ + for(j=0;j<g_iRWidth;j++){ + free(g_aTReferredIndex[i][j]); + free(g_aTContinuousCnt[i][j]); + } + free(g_aTReferredIndex[i]); + free(g_aTContinuousCnt[i]); + } + free(g_aTReferredIndex); + free(g_aTContinuousCnt); + + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the codebook construction function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::T_ModelConstruction(short nTrainVolRange,float fLearningRate, uchar*** aXYZ,point center, point* aNei, TextureModel** aModel){ + + int i,j; + int iMatchedIndex; + + short nNeighborNum = g_nNeighborNum; + + float fDifference; + float fDiffMean; + + float fNegLearningRate = 1-fLearningRate; + + //for all neighboring pairs + for(i=0;i<nNeighborNum;i++){ + + fDifference = (float)(aXYZ[center.m_nY][center.m_nX][2] - aXYZ[aNei[i].m_nY][aNei[i].m_nX][2]); + + //Step1: matching + iMatchedIndex = -1; + for(j=0;j<aModel[i]->m_iNumEntries;j++){ + if(aModel[i]->m_Codewords[j]->m_fLowThre <= fDifference && fDifference <= aModel[i]->m_Codewords[j]->m_fHighThre){ + iMatchedIndex = j; + break; + } + } + + aModel[i]->m_iTotal++; + //Step2: adding a new element + if(iMatchedIndex == -1){ + //element array�� Ȯ���� �ʿ䰡 ������ Ȯ�� + if(aModel[i]->m_iElementArraySize == aModel[i]->m_iNumEntries){ + aModel[i]->m_iElementArraySize += 5; + TextureCodeword **temp = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*aModel[i]->m_iElementArraySize); + for(j=0;j<aModel[i]->m_iNumEntries;j++){ + temp[j] = aModel[i]->m_Codewords[j]; + aModel[i]->m_Codewords[j] = NULL; + } + free(aModel[i]->m_Codewords); + aModel[i]->m_Codewords = temp; + } + aModel[i]->m_Codewords[aModel[i]->m_iNumEntries] = (TextureCodeword*)malloc(sizeof(TextureCodeword)); + + aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_fMean = fDifference; + aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_fLowThre = aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_fMean - nTrainVolRange; + aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_fHighThre = aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_fMean + nTrainVolRange; + + aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_iT_first_time = aModel[i]->m_iTotal; + aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_iT_last_time = aModel[i]->m_iTotal; + aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_iMNRL = aModel[i]->m_iTotal-1; + aModel[i]->m_iNumEntries ++; + } + + //Step3: update + else{ + fDiffMean = aModel[i]->m_Codewords[iMatchedIndex]->m_fMean; + aModel[i]->m_Codewords[iMatchedIndex]->m_fMean = fLearningRate*fDifference + fNegLearningRate*fDiffMean; + + aModel[i]->m_Codewords[iMatchedIndex]->m_fLowThre = aModel[i]->m_Codewords[iMatchedIndex]->m_fMean - nTrainVolRange; + aModel[i]->m_Codewords[iMatchedIndex]->m_fHighThre = aModel[i]->m_Codewords[iMatchedIndex]->m_fMean + nTrainVolRange; + + aModel[i]->m_Codewords[iMatchedIndex]->m_iT_last_time = aModel[i]->m_iTotal; + } + + //cache-book handling + if(aModel[i]->m_bID==1){ + //1. m_iMNRL update + int negTime; + for(j=0;j<aModel[i]->m_iNumEntries;j++){ + //m_iMNRL update + negTime = aModel[i]->m_iTotal - aModel[i]->m_Codewords[j]->m_iT_last_time + aModel[i]->m_Codewords[j]->m_iT_first_time - 1; + if(aModel[i]->m_Codewords[j]->m_iMNRL < negTime) aModel[i]->m_Codewords[j]->m_iMNRL = negTime; + } + + + //2. g_aTReferredIndex[center.m_nY][center.m_nX][i] update + if(g_bAbsorptionEnable == TRUE) g_aTReferredIndex[center.m_nY][center.m_nX][i] = -1; + } + + else{ + //1. m_iMNRL update + if(iMatchedIndex == -1) aModel[i]->m_Codewords[aModel[i]->m_iNumEntries-1]->m_iMNRL = 0; + + //2. g_aTReferredIndex[center.m_nY][center.m_nX][i] update + if(iMatchedIndex==-1){ + g_aTReferredIndex[center.m_nY][center.m_nX][i] = aModel[i]->m_iNumEntries-1; + g_aTContinuousCnt[center.m_nY][center.m_nX][i] = 1; + } + else{ + if(iMatchedIndex == g_aTReferredIndex[center.m_nY][center.m_nX][i]) g_aTContinuousCnt[center.m_nY][center.m_nX][i]++; + else{ + g_aTReferredIndex[center.m_nY][center.m_nX][i] = iMatchedIndex; + g_aTContinuousCnt[center.m_nY][center.m_nX][i] = 1; + } + } + } + + } + +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// Clear non-essential codewords of the given codebook // // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::T_ClearNonEssentialEntries(short nClearNum, TextureModel** aModel){ + int i,n; + int iStaleThresh = (int)(nClearNum*0.5); + int iKeepCnt; + int* aKeep; + + short nNeighborNum = g_nNeighborNum; + + TextureModel* c; + + for(n=0;n<nNeighborNum;n++){ + c = aModel[n]; + + if(c->m_iTotal < nClearNum) continue; //(being operated only when c[w][h]->m_iTotal == nClearNum) + + //Step1: initialization + aKeep = (int*)malloc(sizeof(int)*c->m_iNumEntries); + + iKeepCnt = 0; + + //Step2: Find non-essential code-words + for(i=0;i<c->m_iNumEntries;i++){ + if(c->m_Codewords[i]->m_iMNRL > iStaleThresh) { + aKeep[i] = 0; //removal candidate + } + else { + aKeep[i] = 1; + iKeepCnt++; + } + } + + //Step3: Perform removal + if(iKeepCnt==0 || iKeepCnt==c->m_iNumEntries){ + for(i=0;i<c->m_iNumEntries;i++){ + c->m_Codewords[i]->m_iT_first_time = 1; + c->m_Codewords[i]->m_iT_last_time = 1; + c->m_Codewords[i]->m_iMNRL = 0; + } + } + + else{ + iKeepCnt = 0; + TextureCodeword** temp = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*c->m_iNumEntries); + + for(i=0;i<c->m_iNumEntries;i++){ + if(aKeep[i] == 1){ + temp[iKeepCnt] = c->m_Codewords[i]; + temp[iKeepCnt]->m_iT_first_time = 1; + temp[iKeepCnt]->m_iT_last_time = 1; + temp[iKeepCnt]->m_iMNRL = 0; + iKeepCnt++; + } + else free(c->m_Codewords[i]); + } + + //ending.. + free(c->m_Codewords); + c->m_Codewords = temp; + c->m_iElementArraySize = c->m_iNumEntries; + c->m_iNumEntries = iKeepCnt; + } + c->m_iTotal=0; + free(aKeep); + + } + +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// Clear non-essential codewords of the given codebook (only for the cache-book) // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::T_ClearNonEssentialEntriesForCachebook(uchar bLandmark, short* nReferredIdxArr, short nClearNum, TextureModel** pCachebook){ + int i,n; + short nNeighborNum = g_nNeighborNum; + + TextureModel* c; + short nReferredIdx; + + for(n=0;n<nNeighborNum;n++){ + + c = pCachebook[n]; + nReferredIdx = nReferredIdxArr[n]; + + //pCachebook->m_iTotal < nClearNum? --> MNRL update + if(c->m_iTotal<nClearNum) { + for(i=0;i<c->m_iNumEntries;i++){ + if(bLandmark == 255 && i == nReferredIdx) c->m_Codewords[i]->m_iMNRL = 0; + else c->m_Codewords[i]->m_iMNRL++; + } + + c->m_iTotal++; + } + + //Perform clearing + else{ + int iStaleThreshold = 5; + + int* aKeep; + short nKeepCnt; + + aKeep = (int*)malloc(sizeof(int)*c->m_iNumEntries); + nKeepCnt = 0; + + for(i=0;i<c->m_iNumEntries;i++){ + if(c->m_Codewords[i]->m_iMNRL<iStaleThreshold){ + aKeep[i] = 1; + nKeepCnt++; + } + else aKeep[i] = 0; + } + + c->m_iElementArraySize = nKeepCnt+2; + if(c->m_iElementArraySize<3) c->m_iElementArraySize = 3; + + TextureCodeword** temp = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*c->m_iElementArraySize); + nKeepCnt = 0; + + for(i=0;i<c->m_iNumEntries;i++){ + if(aKeep[i]==1){ + temp[nKeepCnt] = c->m_Codewords[i]; + temp[nKeepCnt]->m_iMNRL = 0; + nKeepCnt++; + } + else { + free(c->m_Codewords[i]); + } + + } + + //ending.. + free(c->m_Codewords); + c->m_Codewords = temp; + c->m_iNumEntries = nKeepCnt; + c->m_iTotal = 0; + + free(aKeep); + } + } + +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the function to generate texture confidence maps // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::T_GetConfidenceMap_Par(uchar*** aXYZ, float** aTextureMap, point*** aNeiDirArr, TextureModel**** aModel){ + + int iBound_w = g_iRWidth-g_nRadius; + int iBound_h = g_iRHeight-g_nRadius; + + short nNeighborNum = g_nNeighborNum; + float fPadding = 5; + + for(int h=0; h<g_iRHeight; h++){ + for(int w=0;w<g_iRWidth;w++){ + + if(h<g_nRadius || h>=iBound_h || w<g_nRadius || w>=iBound_w){ + aTextureMap[h][w] = 0; + continue; + } + + int nMatchedCount = 0; + float fDiffSum = 0; + float fDifference; + point nei; + + for(int i=0;i<nNeighborNum;i++){ + + nei.m_nX = aNeiDirArr[h][w][i].m_nX; + nei.m_nY = aNeiDirArr[h][w][i].m_nY; + + fDifference = (float)(aXYZ[h][w][2] - aXYZ[nei.m_nY][nei.m_nX][2]); + if(fDifference<0) fDiffSum -= fDifference; + else fDiffSum += fDifference; + + for(int j=0;j<aModel[h][w][i]->m_iNumEntries;j++){ + if(aModel[h][w][i]->m_Codewords[j]->m_fLowThre-fPadding <= fDifference && fDifference <= aModel[h][w][i]->m_Codewords[j]->m_fHighThre+fPadding){ + nMatchedCount++; + break; + } + } + + } + aTextureMap[h][w] = 1 - (float)nMatchedCount/nNeighborNum; + } + } +} +//-----------------------------------------------------------------------------------------------------------------------------------------// +// Absorbing Ghost Non-background Region Update // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::T_Absorption(int iAbsorbCnt, point pos, short*** aContinuCnt, short*** aRefferedIndex, TextureModel** pModel, TextureModel** pCache){ + int i,j,k; + int iLeavingIndex; + + short g_nRadius = 2; + short nNeighborNum = g_nNeighborNum; + + for(i=0;i<nNeighborNum;i++){ + //set iLeavingIndex + if(aContinuCnt[pos.m_nY][pos.m_nX][i]<iAbsorbCnt) continue; + + iLeavingIndex = aRefferedIndex[pos.m_nY][pos.m_nX][i]; + + //array expansion + if(pModel[i]->m_iElementArraySize==pModel[i]->m_iNumEntries){ + pModel[i]->m_iElementArraySize = pModel[i]->m_iElementArraySize+5; + TextureCodeword** temp = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*pModel[i]->m_iElementArraySize); + for(j=0;j<pModel[i]->m_iNumEntries;j++) temp[j] = pModel[i]->m_Codewords[j]; + free(pModel[i]->m_Codewords); + pModel[i]->m_Codewords = temp; + } + + //movement from the cache-book to the codebook + pModel[i]->m_Codewords[pModel[i]->m_iNumEntries] = pCache[i]->m_Codewords[iLeavingIndex]; + + pModel[i]->m_iTotal = pModel[i]->m_iTotal+1; + + pModel[i]->m_Codewords[pModel[i]->m_iNumEntries]->m_iT_first_time = pModel[i]->m_iTotal; + pModel[i]->m_Codewords[pModel[i]->m_iNumEntries]->m_iT_last_time = pModel[i]->m_iTotal; + pModel[i]->m_Codewords[pModel[i]->m_iNumEntries]->m_iMNRL = pModel[i]->m_iTotal-1; + pModel[i]->m_iNumEntries = pModel[i]->m_iNumEntries + 1; + + k=0; + TextureCodeword **temp_Cache = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*pCache[i]->m_iElementArraySize); + for(j=0;j<pCache[i]->m_iNumEntries;j++){ + if(j==iLeavingIndex) continue; + else{ + temp_Cache[k] = pCache[i]->m_Codewords[j]; + k++; + } + } + free(pCache[i]->m_Codewords); + pCache[i]->m_Codewords = temp_Cache; + pCache[i]->m_iNumEntries = k; + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the function to set neighborhood system // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::T_SetNeighborDirection(point*** aNeighborPos){ + int i,j,k; + point* aSearchDirection = (point*)malloc(sizeof(point)*g_nNeighborNum); + + aSearchDirection[0].m_nX = -2;//180 degree + aSearchDirection[0].m_nY = 0; + + aSearchDirection[1].m_nX = -1;//123 degree + aSearchDirection[1].m_nY = -2; + + aSearchDirection[2].m_nX = 1;//45 degree + aSearchDirection[2].m_nY = -2; + + aSearchDirection[3].m_nX = 2;//0 degree + aSearchDirection[3].m_nY = 0; + + aSearchDirection[4].m_nX = 1;//-45 degree + aSearchDirection[4].m_nY = 2; + + aSearchDirection[5].m_nX = -1;//-135 degree + aSearchDirection[5].m_nY = 2; + + point temp_pos; + + for(i=0;i<g_iRHeight;i++){ + for(j=0;j<g_iRWidth;j++){ + for(k=0;k<g_nNeighborNum;k++){ + temp_pos.m_nX = j+aSearchDirection[k].m_nX; + temp_pos.m_nY = i+aSearchDirection[k].m_nY; + + if(temp_pos.m_nX<0 || temp_pos.m_nX>=g_iRWidth || temp_pos.m_nY<0 || temp_pos.m_nY>=g_iRHeight) { + aNeighborPos[i][j][k].m_nX = -1; + aNeighborPos[i][j][k].m_nY = -1; + } + + else { + aNeighborPos[i][j][k].m_nX = temp_pos.m_nX; + aNeighborPos[i][j][k].m_nY = temp_pos.m_nY; + } + } + } + } + free(aSearchDirection); +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the color-model initialization function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::C_AllocateColorModelRelatedMemory(){ + int i,j; + + int iElementArraySize = 10; + + //codebook initialization + g_ColorModel = (ColorModel***)malloc(sizeof(ColorModel**)*g_iRHeight); + for(i=0;i<g_iRHeight;i++){ + g_ColorModel[i] = (ColorModel**)malloc(sizeof(ColorModel*)*g_iRWidth); + for(j=0;j<g_iRWidth;j++){ + //initialization of each CodeBookArray. + g_ColorModel[i][j] = (ColorModel*)malloc(sizeof(ColorModel)); + g_ColorModel[i][j]->m_Codewords = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*iElementArraySize); + g_ColorModel[i][j]->m_iNumEntries = 0; + g_ColorModel[i][j]->m_iElementArraySize = iElementArraySize; + g_ColorModel[i][j]->m_iTotal = 0; + g_ColorModel[i][j]->m_bID = 1; + } + } + + //cache-book initialization + if(g_bAbsorptionEnable==TRUE){ + iElementArraySize = 3; + + g_CCacheBook = (ColorModel***)malloc(sizeof(ColorModel**)*g_iRHeight); + for(i=0;i<g_iRHeight;i++){ + g_CCacheBook[i] = (ColorModel**)malloc(sizeof(ColorModel*)*g_iRWidth); + for(j=0;j<g_iRWidth;j++){ + //initialization of each CodeBookArray. + g_CCacheBook[i][j] = (ColorModel*)malloc(sizeof(ColorModel)); + g_CCacheBook[i][j]->m_Codewords = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*iElementArraySize); + g_CCacheBook[i][j]->m_iNumEntries = 0; + g_CCacheBook[i][j]->m_iElementArraySize = iElementArraySize; + g_CCacheBook[i][j]->m_iTotal = 0; + g_CCacheBook[i][j]->m_bID = 0; + } + } + + g_aCReferredIndex = (short**)malloc(sizeof(short*)*g_iRHeight); + g_aCContinuousCnt = (short**)malloc(sizeof(short*)*g_iRHeight); + for(i=0;i<g_iRHeight;i++){ + g_aCReferredIndex[i] = (short*)malloc(sizeof(short)*g_iRWidth); + g_aCContinuousCnt[i] = (short*)malloc(sizeof(short)*g_iRWidth); + for(j=0;j<g_iRWidth;j++){ + g_aCReferredIndex[i][j] = -1; + g_aCContinuousCnt[i][j] = 0; + } + } + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the memory release function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::C_ReleaseColorModelRelatedMemory(){ + int i,j,k; + + for(i=0;i<g_iRHeight;i++){ + for(j=0;j<g_iRWidth;j++){ + for(k=0;k<g_ColorModel[i][j]->m_iNumEntries;k++){ + free(g_ColorModel[i][j]->m_Codewords[k]); + } + free(g_ColorModel[i][j]->m_Codewords); + free(g_ColorModel[i][j]); + } + free(g_ColorModel[i]); + } + free(g_ColorModel); + + if(g_bAbsorptionEnable==TRUE){ + for(i=0;i<g_iRHeight;i++){ + for(j=0;j<g_iRWidth;j++){ + for(k=0;k<g_CCacheBook[i][j]->m_iNumEntries;k++){ + free(g_CCacheBook[i][j]->m_Codewords[k]); + } + free(g_CCacheBook[i][j]->m_Codewords); + free(g_CCacheBook[i][j]); + } + free(g_CCacheBook[i]); + } + free(g_CCacheBook); + + for(i=0;i<g_iRHeight;i++){ + free(g_aCReferredIndex[i]); + free(g_aCContinuousCnt[i]); + } + free(g_aCReferredIndex); + free(g_aCContinuousCnt); + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the codebook construction function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::C_CodebookConstruction(uchar* aP,int iPosX, int iPosY, short nTrainVolRange, float fLearningRate, ColorModel* pC){ + + //Step1: matching + int i,j; + short nMatchedIndex; + + float fNegLearningRate = 1-fLearningRate; + + nMatchedIndex = -1; + + for(i=0;i<pC->m_iNumEntries;i++){ + + //Checking X + if(pC->m_Codewords[i]->m_dMean[0]-nTrainVolRange <= aP[0] && aP[0] <= pC->m_Codewords[i]->m_dMean[0]+nTrainVolRange){ + //Checking Y + if(pC->m_Codewords[i]->m_dMean[1]-nTrainVolRange <= aP[1] && aP[1] <= pC->m_Codewords[i]->m_dMean[1]+nTrainVolRange){ + //Checking Z + if(pC->m_Codewords[i]->m_dMean[2]-nTrainVolRange <= aP[2] && aP[2] <= pC->m_Codewords[i]->m_dMean[2]+nTrainVolRange){ + nMatchedIndex = i; + break; + } + } + } + } + + pC->m_iTotal = pC->m_iTotal+1; + + //Step2 : adding a new element + if(nMatchedIndex==-1){ + if(pC->m_iElementArraySize == pC->m_iNumEntries){ + pC->m_iElementArraySize = pC->m_iElementArraySize + 5; + ColorCodeword **temp = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*pC->m_iElementArraySize); + for(j=0;j<pC->m_iNumEntries;j++){ + temp[j] = pC->m_Codewords[j]; + pC->m_Codewords[j]= NULL; + } + free(pC->m_Codewords); + pC->m_Codewords = temp; + + } + pC->m_Codewords[pC->m_iNumEntries] = (ColorCodeword*)malloc(sizeof(ColorCodeword)); + + pC->m_Codewords[pC->m_iNumEntries]->m_dMean[0] = aP[0];//X + pC->m_Codewords[pC->m_iNumEntries]->m_dMean[1] = aP[1];//Y + pC->m_Codewords[pC->m_iNumEntries]->m_dMean[2] = aP[2];//Z + + pC->m_Codewords[pC->m_iNumEntries]->m_iT_first_time = pC->m_iTotal; + pC->m_Codewords[pC->m_iNumEntries]->m_iT_last_time = pC->m_iTotal; + pC->m_Codewords[pC->m_iNumEntries]->m_iMNRL = pC->m_iTotal-1; + pC->m_iNumEntries = pC->m_iNumEntries + 1; + } + + //Step3 : update + else{ + //m_dMean update + pC->m_Codewords[nMatchedIndex]->m_dMean[0] = (fLearningRate*aP[0]) + fNegLearningRate*pC->m_Codewords[nMatchedIndex]->m_dMean[0];//X + pC->m_Codewords[nMatchedIndex]->m_dMean[1] = (fLearningRate*aP[1]) + fNegLearningRate*pC->m_Codewords[nMatchedIndex]->m_dMean[1];//Y + pC->m_Codewords[nMatchedIndex]->m_dMean[2] = (fLearningRate*aP[2]) + fNegLearningRate*pC->m_Codewords[nMatchedIndex]->m_dMean[2];//Z + + pC->m_Codewords[nMatchedIndex]->m_iT_last_time = pC->m_iTotal; + } + + //cache-book handling + if(pC->m_bID==1){ + //1. m_iMNRL update + int iNegTime; + for(i=0;i<pC->m_iNumEntries;i++){ + //m_iMNRL update + iNegTime = pC->m_iTotal - pC->m_Codewords[i]->m_iT_last_time + pC->m_Codewords[i]->m_iT_first_time - 1; + if(pC->m_Codewords[i]->m_iMNRL < iNegTime) pC->m_Codewords[i]->m_iMNRL = iNegTime; + } + + //2. g_aCReferredIndex[iPosY][iPosX] update + if(g_bAbsorptionEnable == TRUE) g_aCReferredIndex[iPosY][iPosX] = -1; + } + + else{ + //1. m_iMNRL update: + if(nMatchedIndex == -1) pC->m_Codewords[pC->m_iNumEntries-1]->m_iMNRL = 0; + + //2. g_aCReferredIndex[iPosY][iPosX] update + if(nMatchedIndex==-1){ + g_aCReferredIndex[iPosY][iPosX] = pC->m_iNumEntries-1; + g_aCContinuousCnt[iPosY][iPosX] = 1; + } + else{ + if(nMatchedIndex == g_aCReferredIndex[iPosY][iPosX]) g_aCContinuousCnt[iPosY][iPosX]++; + else{ + g_aCReferredIndex[iPosY][iPosX] = nMatchedIndex; + g_aCContinuousCnt[iPosY][iPosX] = 1; + } + } + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// Clear non-essential codewords of the given codebook // // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::C_ClearNonEssentialEntries(short nClearNum, ColorModel* pModel){ + int i; + short nStaleThresh = (int)(nClearNum*0.5); + short nKeepCnt; + int* aKeep; + + ColorModel* pC = pModel; + + if(pC->m_iTotal < nClearNum) return; //(Being operated only when pC->t >= nClearNum) + + //Step1:initialization + aKeep = (int*)malloc(sizeof(int)*pC->m_iNumEntries); + + nKeepCnt = 0; + + //Step2: Find non-essential codewords + for(i=0;i<pC->m_iNumEntries;i++){ + if(pC->m_Codewords[i]->m_iMNRL > nStaleThresh) { + aKeep[i] = 0; //removal + } + else { + aKeep[i] = 1; //keep + nKeepCnt++; + } + } + + //Step3: Perform removal + if(nKeepCnt==0 || nKeepCnt==pC->m_iNumEntries){ + for(i=0;i<pC->m_iNumEntries;i++){ + pC->m_Codewords[i]->m_iT_first_time = 1; + pC->m_Codewords[i]->m_iT_last_time = 1; + pC->m_Codewords[i]->m_iMNRL = 0; + } + } + else{ + nKeepCnt = 0; + ColorCodeword** temp = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*pC->m_iNumEntries); + + for(i=0;i<pC->m_iNumEntries;i++){ + if(aKeep[i] == 1){ + temp[nKeepCnt] = pC->m_Codewords[i]; + temp[nKeepCnt]->m_iT_first_time = 1; + temp[nKeepCnt]->m_iT_last_time = 1; + temp[nKeepCnt]->m_iMNRL = 0; + nKeepCnt++; + } + else free(pC->m_Codewords[i]); + } + + //ending.. + free(pC->m_Codewords); + pC->m_Codewords = temp; + pC->m_iElementArraySize = pC->m_iNumEntries; + pC->m_iNumEntries = nKeepCnt; + } + + pC->m_iTotal=0; + free(aKeep); + +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// Clear non-essential codewords of the given codebook (for cache-book) // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::C_ClearNonEssentialEntriesForCachebook(uchar bLandmark, short nReferredIdx, short nClearNum, ColorModel* pCachebook){ + int i; + + if(pCachebook->m_iTotal<nClearNum) { + for(i=0;i<pCachebook->m_iNumEntries;i++){ + if(bLandmark == 255 && i == nReferredIdx) pCachebook->m_Codewords[i]->m_iMNRL = 0; + else pCachebook->m_Codewords[i]->m_iMNRL++; + } + + pCachebook->m_iTotal++; + } + + else{ + int iStaleThreshold = 5; + + int* aKeep; + short nKeepCnt; + + aKeep = (int*)malloc(sizeof(int)*pCachebook->m_iNumEntries); + nKeepCnt = 0; + + for(i=0;i<pCachebook->m_iNumEntries;i++){ + if(pCachebook->m_Codewords[i]->m_iMNRL<iStaleThreshold){ + aKeep[i] = 1; + nKeepCnt++; + } + else aKeep[i] = 0; + } + + pCachebook->m_iElementArraySize = nKeepCnt+2; + if(pCachebook->m_iElementArraySize<3) pCachebook->m_iElementArraySize = 3; + + ColorCodeword** temp = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*pCachebook->m_iElementArraySize); + nKeepCnt = 0; + + for(i=0;i<pCachebook->m_iNumEntries;i++){ + if(aKeep[i]==1){ + temp[nKeepCnt] = pCachebook->m_Codewords[i]; + temp[nKeepCnt]->m_iMNRL = 0; + nKeepCnt++; + } + else { + free(pCachebook->m_Codewords[i]); + } + + } + + //ending.. + free(pCachebook->m_Codewords); + pCachebook->m_Codewords = temp; + pCachebook->m_iNumEntries = nKeepCnt; + pCachebook->m_iTotal = 0; + + free(aKeep); + } +} + +//-----------------------------------------------------------------------------------------------------------------------------------------// +// the ghost-region absorption function // +//-----------------------------------------------------------------------------------------------------------------------------------------// +void SJN_MultiCueBGS::C_Absorption(int iAbsorbCnt, point pos, short** aContinuCnt, short** aRefferedIndex, ColorModel* pModel, ColorModel* pCache){ + + //set iLeavingIndex + if(aContinuCnt[pos.m_nY][pos.m_nX]<iAbsorbCnt) return; + + int iLeavingIndex = aRefferedIndex[pos.m_nY][pos.m_nX]; + + //array expansion + if(pModel->m_iElementArraySize==pModel->m_iNumEntries){ + pModel->m_iElementArraySize = pModel->m_iElementArraySize+5; + ColorCodeword** temp = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*pModel->m_iElementArraySize); + for(int i=0;i<pModel->m_iNumEntries;i++) temp[i] = pModel->m_Codewords[i]; + free(pModel->m_Codewords); + pModel->m_Codewords = temp; + } + + //movement from the cache-book to the codebook + pModel->m_Codewords[pModel->m_iNumEntries] = pCache->m_Codewords[iLeavingIndex]; + + pModel->m_iTotal = pModel->m_iTotal+1; + + pModel->m_Codewords[pModel->m_iNumEntries]->m_iT_first_time = pModel->m_iTotal; + pModel->m_Codewords[pModel->m_iNumEntries]->m_iT_last_time = pModel->m_iTotal; + pModel->m_Codewords[pModel->m_iNumEntries]->m_iMNRL = pModel->m_iTotal-1; + + pModel->m_iNumEntries = pModel->m_iNumEntries + 1; + + int k=0; + ColorCodeword **pTempCache = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*pCache->m_iElementArraySize); + for(int i=0;i<pCache->m_iNumEntries;i++){ + if(i==iLeavingIndex) continue; + else{ + pTempCache[k] = pCache->m_Codewords[i]; + k++; + } + } + free(pCache->m_Codewords); + pCache->m_Codewords = pTempCache; + pCache->m_iNumEntries = k; +} \ No newline at end of file diff --git a/package_bgs/sjn/SJN_MultiCueBGS.h b/package_bgs/sjn/SJN_MultiCueBGS.h new file mode 100644 index 0000000000000000000000000000000000000000..1d5dd7321c8d234ac7748e6991b8e86ae729e639 --- /dev/null +++ b/package_bgs/sjn/SJN_MultiCueBGS.h @@ -0,0 +1,243 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#define MIN3(x,y,z) ((y) <= (z) ? ((x) <= (y) ? (x) : (y)) : ((x) <= (z) ? (x) : (z))) +#define MAX3(x,y,z) ((y) >= (z) ? ((x) >= (y) ? (x) : (y)) : ((x) >= (z) ? (x) : (z))) +#define PI 3.14159 + +typedef int BOOL; + +#ifndef FALSE + #define FALSE 0 +#endif + +#ifndef TRUE + #define TRUE 1 +#endif + +#include <malloc.h> +#include "math.h" + +#include <vector> +using std::vector; + +#include <algorithm> +using std::sort; + +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" + +//------------------------------------Structure Lists-------------------------------------// +struct point{ + short m_nX; + short m_nY; +}; + +struct neighbor_pos{ + short m_nX; + short m_nY; +}; +//1) Bounding Box Structure +struct BoundingBoxInfo{ + int m_iBoundBoxNum; //# of bounding boxes for all foreground and false-positive blobs + int m_iArraySize; //the size of the below arrays to store bounding box information + + short *m_aLeft, *m_aRight, *m_aUpper, *m_aBottom; //arrays to store bounding box information for (the original frame size) + short *m_aRLeft, *m_aRRight, *m_aRUpper, *m_aRBottom; //arrays to store bounding box information for (the reduced frame size) + BOOL* m_ValidBox; //If this value is true, the corresponding bounding box is for a foreground blob. + //Else, it is for a false-positive blob +}; + +//2) Texture Model Structure +struct TextureCodeword{ + int m_iMNRL; //the maximum negative run-length + int m_iT_first_time; //the first access time + int m_iT_last_time; //the last access time + + //��� MTLBP�� + float m_fLowThre; //a low threshold for the matching + float m_fHighThre; //a high threshold for the matching + float m_fMean; //mean of the codeword +}; + +struct TextureModel{ + TextureCodeword** m_Codewords; //the texture-codeword Array + + int m_iTotal; //# of learned samples after the last clear process + int m_iElementArraySize; //the array size of m_Codewords + int m_iNumEntries; //# of codewords + + BOOL m_bID; //id=1 --> background model, id=0 --> cachebook +}; + +//3) Color Model Structure +struct ColorCodeword{ + int m_iMNRL; //the maximum negative run-length + int m_iT_first_time; //the first access time + int m_iT_last_time; //the last access time + + double m_dMean[3]; //mean vector of the codeword + +}; + +struct ColorModel{ + ColorCodeword** m_Codewords; //the color-codeword Array + + int m_iTotal; //# of learned samples after the last clear process + int m_iElementArraySize; //the array size of m_Codewords + int m_iNumEntries; //# of codewords + + BOOL m_bID; //id=1 --> background model, id=0 --> cachebookk +}; + + +class SJN_MultiCueBGS : public IBGS +{ +private: + bool firstTime; + bool showOutput; + void saveConfig(); + void loadConfig(); + +public: + SJN_MultiCueBGS(); + ~SJN_MultiCueBGS(void); + +public: + //---------------------------------------------------- + // APIs and User-Adjustable Parameters + //---------------------------------------------------- + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); //the main function to background modeling and subtraction + + void GetForegroundMap(IplImage* return_image, IplImage* input_frame=NULL); //the function returning a foreground binary-map + void Destroy(); //the function to release allocated memories + + int g_iTrainingPeriod; //the training period (The parameter t in the paper) + int g_iT_ModelThreshold; //the threshold for texture-model based BGS. (The parameter tau_T in the paper) + int g_iC_ModelThreshold; //the threshold for appearance based verification. (The parameter tau_A in the paper) + + float g_fLearningRate; //the learning rate for background models. (The parameter alpha in the paper) + + short g_nTextureTrainVolRange; //the codebook size factor for texture models. (The parameter k in the paper) + short g_nColorTrainVolRange; //the codebook size factor for color models. (The parameter eta_1 in the paper) + +public: + //---------------------------------------------------- + // Implemented Function Lists + //---------------------------------------------------- + + //--1) General Functions + void Initialize(IplImage* frame); + + void PreProcessing(IplImage* frame); + void ReduceImageSize(IplImage* SrcImage, IplImage* DstImage); + void GaussianFiltering(IplImage* frame, uchar*** aFilteredFrame); + void BGR2HSVxyz_Par(uchar*** aBGR, uchar*** aXYZ); + + void BackgroundModeling_Par(IplImage* frame); + void ForegroundExtraction(IplImage* frame); + void CreateLandmarkArray_Par(float fConfThre, short nTrainVolRange, float**aConfMap, int iNehborNum, uchar*** aXYZ, + point*** aNeiDir, TextureModel**** TModel, ColorModel*** CModel, uchar**aLandmarkArr); + + void PostProcessing(IplImage* frame); + void MorphologicalOpearions(uchar** aInput, uchar** aOutput, double dThresholdRatio, int iMaskSize, int iWidth, int iHeight); + void Labeling(uchar** aBinaryArray, int* pLabelCount, int** aLabelTable); + void SetBoundingBox(int iLabelCount, int** aLabelTable); + void BoundBoxVerification(IplImage* frame, uchar** aResForeMap, BoundingBoxInfo* BoundBoxInfo); + void EvaluateBoxSize( BoundingBoxInfo* BoundBoxInfo); + void EvaluateOverlapRegionSize(BoundingBoxInfo* SrcBoxInfo); + void EvaluateGhostRegion(IplImage* frame, uchar** aResForeMap, BoundingBoxInfo* BoundBoxInfo); + double CalculateHausdorffDist(IplImage* input_image, IplImage* model_image); + void RemovingInvalidForeRegions(uchar** aResForeMap, BoundingBoxInfo* BoundBoxInfo); + + void UpdateModel_Par(); + void GetEnlargedMap(float** aOriginMap, float** aEnlargedMap); + + //--2) Texture Model Related Functions + void T_AllocateTextureModelRelatedMemory(); + void T_ReleaseTextureModelRelatedMemory(); + void T_SetNeighborDirection(point*** aNeighborPos); + void T_ModelConstruction(short nTrainVolRange,float fLearningRate, uchar*** aXYZ,point center, point* aNei, TextureModel** aModel); + void T_ClearNonEssentialEntries(short nClearNum, TextureModel** aModel); + void T_ClearNonEssentialEntriesForCachebook(uchar bLandmark, short* nReferredIdxArr, short nClearNum, TextureModel** pCachebook); + void T_GetConfidenceMap_Par(uchar*** aXYZ, float** aTextureMap, point*** aNeiDirArr, TextureModel**** aModel); + void T_Absorption(int iAbsorbCnt, point pos, short*** aContinuCnt, short*** aRefferedIndex, TextureModel** pModel, TextureModel** pCache); + + //--3) Color Model Related Functions + void C_AllocateColorModelRelatedMemory(); + void C_ReleaseColorModelRelatedMemory(); + void C_CodebookConstruction(uchar* aP,int iPosX, int iPosY, short nTrainVolRange, float fLearningRate, ColorModel* pC); + void C_ClearNonEssentialEntries(short nClearNum, ColorModel* pModel); + void C_ClearNonEssentialEntriesForCachebook(uchar bLandmark, short nReferredIdx, short nClearNum, ColorModel* pCachebook); + void C_Absorption(int iAbsorbCnt, point pos , short** aContinuCnt, short** aRefferedIndex, ColorModel* pModel, ColorModel* pCache); +public: + //---------------------------------------------------- + // Implemented Variable Lists + //---------------------------------------------------- + + //--1) General Variables + int g_iFrameCount; //the counter of processed frames + + int g_iBackClearPeriod; //the period to clear background models + int g_iCacheClearPeriod; //the period to clear cache-book models + + int g_iAbsortionPeriod; //the period to absorb static ghost regions + BOOL g_bAbsorptionEnable; //If True, procedures for ghost region absorption are activated. + + BOOL g_bModelMemAllocated; //To handle memory.. + BOOL g_bNonModelMemAllocated; //To handle memory.. + + float g_fConfidenceThre; //the final decision threshold + + int g_iWidth, g_iHeight; //width and height of input frames + int g_iRWidth, g_iRHeight; //width and height of reduced frames (For efficiency, the reduced size of frames are processed) + int g_iForegroundNum; //# of detected foreground regions + BOOL g_bForegroundMapEnable; //TRUE only when BGS is successful + + IplImage* g_ResizedFrame; //reduced size of frame (For efficiency, the reduced size of frames are processed) + uchar*** g_aGaussFilteredFrame; + uchar*** g_aXYZFrame; + uchar** g_aLandmarkArray; //the landmark map + uchar** g_aResizedForeMap; //the resized foreground map + uchar** g_aForegroundMap; //the final foreground map + BOOL** g_aUpdateMap; //the location map of update candidate pixels + + BoundingBoxInfo* g_BoundBoxInfo; //the array of bounding boxes of each foreground blob + + //--2) Texture Model Related + TextureModel**** g_TextureModel; //the texture background model + TextureModel**** g_TCacheBook; //the texture cache-book + short*** g_aTReferredIndex; //To handle cache-book + short*** g_aTContinuousCnt; //To handle cache-book + point*** g_aNeighborDirection; + float**g_aTextureConfMap; //the texture confidence map + + short g_nNeighborNum; //# of neighborhoods + short g_nRadius; + short g_nBoundarySize; + + //--3) Texture Model Related + ColorModel*** g_ColorModel; //the color background model + ColorModel*** g_CCacheBook; //the color cache-book + short** g_aCReferredIndex; //To handle cache-book + short** g_aCContinuousCnt; //To handle cache-book +}; + + diff --git a/package_bgs/tb/FuzzyChoquetIntegral.cpp b/package_bgs/tb/FuzzyChoquetIntegral.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d0bf908041a308543aff7c7510466bcb84a09bd --- /dev/null +++ b/package_bgs/tb/FuzzyChoquetIntegral.cpp @@ -0,0 +1,204 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "FuzzyChoquetIntegral.h" + +FuzzyChoquetIntegral::FuzzyChoquetIntegral() : firstTime(true), frameNumber(0), showOutput(true), + framesToLearn(10), alphaLearn(0.1), alphaUpdate(0.01), colorSpace(1), option(2), smooth(true), threshold(0.67) +{ + std::cout << "FuzzyChoquetIntegral()" << std::endl; +} + +FuzzyChoquetIntegral::~FuzzyChoquetIntegral() +{ + std::cout << "~FuzzyChoquetIntegral()" << std::endl; +} + +void FuzzyChoquetIntegral::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + cv::Mat img_input_f3(img_input.size(), CV_32F); + img_input.convertTo(img_input_f3, CV_32F, 1./255.); + + loadConfig(); + + if(firstTime) + { + std::cout << "FuzzyChoquetIntegral parameters:" << std::endl; + + std::string colorSpaceName = ""; + switch(colorSpace) + { + case 1: colorSpaceName = "RGB"; break; + case 2: colorSpaceName = "OHTA"; break; + case 3: colorSpaceName = "HSV"; break; + case 4: colorSpaceName = "YCrCb"; break; + } + std::cout << "Color space: " << colorSpaceName << std::endl; + + if(option == 1) + std::cout << "Fuzzing by 3 color components" << std::endl; + if(option == 2) + std::cout << "Fuzzing by 2 color components + 1 texture component" << std::endl; + + saveConfig(); + } + + if(frameNumber <= framesToLearn) + { + if(frameNumber == 0) + std::cout << "FuzzyChoquetIntegral initializing background model by adaptive learning..." << std::endl; + + if(img_background_f3.empty()) + img_input_f3.copyTo(img_background_f3); + else + img_background_f3 = alphaLearn*img_input_f3 + (1-alphaLearn)*img_background_f3; + + if(showOutput) + cv::imshow("CI BG Model", img_background_f3); + } + else + { + cv::Mat img_input_f1; + cv::cvtColor(img_input_f3, img_input_f1, CV_BGR2GRAY); + + cv::Mat img_background_f1; + cv::cvtColor(img_background_f3, img_background_f1, CV_BGR2GRAY); + + IplImage* input_f3 = new IplImage(img_input_f3); + IplImage* input_f1 = new IplImage(img_input_f1); + IplImage* background_f3 = new IplImage(img_background_f3); + IplImage* background_f1 = new IplImage(img_background_f1); + + IplImage* lbp_input_f1 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 1); + cvFillImage(lbp_input_f1, 0.0); + fu.LBP(input_f1, lbp_input_f1); + + IplImage* lbp_background_f1 = cvCreateImage(cvSize(background_f1->width, background_f1->height), IPL_DEPTH_32F , 1); + cvFillImage(lbp_background_f1, 0.0); + fu.LBP(background_f1, lbp_background_f1); + + IplImage* sim_texture_f1 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 1); + fu.SimilarityDegreesImage(lbp_input_f1, lbp_background_f1, sim_texture_f1, 1, colorSpace); + + IplImage* sim_color_f3 = cvCreateImage(cvSize(input_f3->width, input_f3->height), IPL_DEPTH_32F, 3); + fu.SimilarityDegreesImage(input_f3, background_f3, sim_color_f3, 3, colorSpace); + + float* measureG = (float*) malloc(3*(sizeof(float))); + IplImage* integral_choquet_f1 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 1); + + // 3 color components + if(option == 1) + { + fu.FuzzyMeasureG(0.4, 0.3, 0.3, measureG); + fu.getFuzzyIntegralChoquet(sim_texture_f1, sim_color_f3, option, measureG, integral_choquet_f1); + } + + // 2 color components + 1 texture component + if(option == 2) + { + fu.FuzzyMeasureG(0.6, 0.3, 0.1, measureG); + fu.getFuzzyIntegralChoquet(sim_texture_f1, sim_color_f3, option, measureG, integral_choquet_f1); + } + + free(measureG); + cv::Mat img_integral_choquet_f1(integral_choquet_f1); + + if(smooth) + cv::medianBlur(img_integral_choquet_f1, img_integral_choquet_f1, 3); + + cv::Mat img_foreground_f1(img_input.size(), CV_32F); + cv::threshold(img_integral_choquet_f1, img_foreground_f1, threshold, 255, cv::THRESH_BINARY_INV); + + cv::Mat img_foreground_u1(img_input.size(), CV_8U); + double minVal = 0., maxVal = 1.; + img_foreground_f1.convertTo(img_foreground_u1, CV_8U, 255.0/(maxVal - minVal), -minVal); + img_foreground_u1.copyTo(img_output); + + cv::Mat img_background_u3(img_input.size(), CV_8U); + //double minVal = 0., maxVal = 1.; + img_background_f3.convertTo(img_background_u3, CV_8U, 255.0/(maxVal - minVal), -minVal); + img_background_u3.copyTo(img_bgmodel); + + if(showOutput) + { + cvShowImage("CI LBP Input", lbp_input_f1); + cvShowImage("CI LBP Background", lbp_background_f1); + cvShowImage("CI Prob FG Mask", integral_choquet_f1); + + cv::imshow("CI BG Model", img_background_f3); + cv::imshow("CI FG Mask", img_foreground_u1); + } + + if(frameNumber == (framesToLearn + 1)) + std::cout << "FuzzyChoquetIntegral updating background model by adaptive-selective learning..." << std::endl; + + IplImage* updated_background_f3 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 3); + cvFillImage(updated_background_f3, 0.0); + fu.AdaptativeSelectiveBackgroundModelUpdate(input_f3, background_f3, updated_background_f3, integral_choquet_f1, threshold, alphaUpdate); + cv::Mat img_updated_background_f3(updated_background_f3); + img_updated_background_f3.copyTo(img_background_f3); + + cvReleaseImage(&lbp_input_f1); + cvReleaseImage(&lbp_background_f1); + cvReleaseImage(&sim_texture_f1); + cvReleaseImage(&sim_color_f3); + cvReleaseImage(&integral_choquet_f1); + cvReleaseImage(&updated_background_f3); + + delete background_f1; + delete background_f3; + delete input_f1; + delete input_f3; + } + + firstTime = false; + frameNumber++; +} + +void FuzzyChoquetIntegral::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/FuzzyChoquetIntegral.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "showOutput", showOutput); + cvWriteInt(fs, "framesToLearn", framesToLearn); + cvWriteReal(fs, "alphaLearn", alphaLearn); + cvWriteReal(fs, "alphaUpdate", alphaUpdate); + cvWriteInt(fs, "colorSpace", colorSpace); + cvWriteInt(fs, "option", option); + cvWriteInt(fs, "smooth", smooth); + cvWriteReal(fs, "threshold", threshold); + + cvReleaseFileStorage(&fs); +} + +void FuzzyChoquetIntegral::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/FuzzyChoquetIntegral.xml", 0, CV_STORAGE_READ); + + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + framesToLearn = cvReadIntByName(fs, 0, "framesToLearn", 10); + alphaLearn = cvReadRealByName(fs, 0, "alphaLearn", 0.1); + alphaUpdate = cvReadRealByName(fs, 0, "alphaUpdate", 0.01); + colorSpace = cvReadIntByName(fs, 0, "colorSpace", 1); + option = cvReadIntByName(fs, 0, "option", 2); + smooth = cvReadIntByName(fs, 0, "smooth", true); + threshold = cvReadRealByName(fs, 0, "threshold", 0.67); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/tb/FuzzyChoquetIntegral.h b/package_bgs/tb/FuzzyChoquetIntegral.h new file mode 100644 index 0000000000000000000000000000000000000000..a52a52d804aea91b9873994f3e59bcec02652a40 --- /dev/null +++ b/package_bgs/tb/FuzzyChoquetIntegral.h @@ -0,0 +1,55 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" + +#include "FuzzyUtils.h" + +class FuzzyChoquetIntegral : public IBGS +{ +private: + bool firstTime; + long frameNumber; + bool showOutput; + + int framesToLearn; + double alphaLearn; + double alphaUpdate; + int colorSpace; + int option; + bool smooth; + double threshold; + + FuzzyUtils fu; + cv::Mat img_background_f3; + +public: + FuzzyChoquetIntegral(); + ~FuzzyChoquetIntegral(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/tb/FuzzySugenoIntegral.cpp b/package_bgs/tb/FuzzySugenoIntegral.cpp new file mode 100644 index 0000000000000000000000000000000000000000..34b1c027ddd3a578afa13bd230deb2b448c014d1 --- /dev/null +++ b/package_bgs/tb/FuzzySugenoIntegral.cpp @@ -0,0 +1,204 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "FuzzySugenoIntegral.h" + +FuzzySugenoIntegral::FuzzySugenoIntegral() : firstTime(true), frameNumber(0), showOutput(true), + framesToLearn(10), alphaLearn(0.1), alphaUpdate(0.01), colorSpace(1), option(2), smooth(true), threshold(0.67) +{ + std::cout << "FuzzySugenoIntegral()" << std::endl; +} + +FuzzySugenoIntegral::~FuzzySugenoIntegral() +{ + std::cout << "~FuzzySugenoIntegral()" << std::endl; +} + +void FuzzySugenoIntegral::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + cv::Mat img_input_f3(img_input.size(), CV_32F); + img_input.convertTo(img_input_f3, CV_32F, 1./255.); + + loadConfig(); + + if(firstTime) + { + std::cout << "FuzzySugenoIntegral parameters:" << std::endl; + + std::string colorSpaceName = ""; + switch(colorSpace) + { + case 1: colorSpaceName = "RGB"; break; + case 2: colorSpaceName = "OHTA"; break; + case 3: colorSpaceName = "HSV"; break; + case 4: colorSpaceName = "YCrCb"; break; + } + std::cout << "Color space: " << colorSpaceName << std::endl; + + if(option == 1) + std::cout << "Fuzzing by 3 color components" << std::endl; + if(option == 2) + std::cout << "Fuzzing by 2 color components + 1 texture component" << std::endl; + + saveConfig(); + } + + if(frameNumber <= framesToLearn) + { + if(frameNumber == 0) + std::cout << "FuzzySugenoIntegral initializing background model by adaptive learning..." << std::endl; + + if(img_background_f3.empty()) + img_input_f3.copyTo(img_background_f3); + else + img_background_f3 = alphaLearn*img_input_f3 + (1-alphaLearn)*img_background_f3; + + if(showOutput) + cv::imshow("SI BG Model", img_background_f3); + } + else + { + cv::Mat img_input_f1; + cv::cvtColor(img_input_f3, img_input_f1, CV_BGR2GRAY); + + cv::Mat img_background_f1; + cv::cvtColor(img_background_f3, img_background_f1, CV_BGR2GRAY); + + IplImage* input_f3 = new IplImage(img_input_f3); + IplImage* input_f1 = new IplImage(img_input_f1); + IplImage* background_f3 = new IplImage(img_background_f3); + IplImage* background_f1 = new IplImage(img_background_f1); + + IplImage* lbp_input_f1 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 1); + cvFillImage(lbp_input_f1, 0.0); + fu.LBP(input_f1, lbp_input_f1); + + IplImage* lbp_background_f1 = cvCreateImage(cvSize(background_f1->width, background_f1->height), IPL_DEPTH_32F , 1); + cvFillImage(lbp_background_f1, 0.0); + fu.LBP(background_f1, lbp_background_f1); + + IplImage* sim_texture_f1 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 1); + fu.SimilarityDegreesImage(lbp_input_f1, lbp_background_f1, sim_texture_f1, 1, colorSpace); + + IplImage* sim_color_f3 = cvCreateImage(cvSize(input_f3->width, input_f3->height), IPL_DEPTH_32F, 3); + fu.SimilarityDegreesImage(input_f3, background_f3, sim_color_f3, 3, colorSpace); + + float* measureG = (float*) malloc(3*(sizeof(float))); + IplImage* integral_sugeno_f1 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 1); + + // 3 color components + if(option == 1) + { + fu.FuzzyMeasureG(0.4, 0.3, 0.3, measureG); + fu.getFuzzyIntegralSugeno(sim_texture_f1, sim_color_f3, option, measureG, integral_sugeno_f1); + } + + // 2 color components + 1 texture component + if(option == 2) + { + fu.FuzzyMeasureG(0.6, 0.3, 0.1, measureG); + fu.getFuzzyIntegralSugeno(sim_texture_f1, sim_color_f3, option, measureG, integral_sugeno_f1); + } + + free(measureG); + cv::Mat img_integral_sugeno_f1(integral_sugeno_f1); + + if(smooth) + cv::medianBlur(img_integral_sugeno_f1, img_integral_sugeno_f1, 3); + + cv::Mat img_foreground_f1(img_input.size(), CV_32F); + cv::threshold(img_integral_sugeno_f1, img_foreground_f1, threshold, 255, cv::THRESH_BINARY_INV); + + cv::Mat img_foreground_u1(img_input.size(), CV_8U); + double minVal = 0., maxVal = 1.; + img_foreground_f1.convertTo(img_foreground_u1, CV_8U, 255.0/(maxVal - minVal), -minVal); + img_foreground_u1.copyTo(img_output); + + cv::Mat img_background_u3(img_input.size(), CV_8U); + //double minVal = 0., maxVal = 1.; + img_background_f3.convertTo(img_background_u3, CV_8U, 255.0/(maxVal - minVal), -minVal); + img_background_u3.copyTo(img_bgmodel); + + if(showOutput) + { + cvShowImage("SI LBP Input", lbp_input_f1); + cvShowImage("SI LBP Background", lbp_background_f1); + cvShowImage("SI Prob FG Mask", integral_sugeno_f1); + + cv::imshow("SI BG Model", img_background_f3); + cv::imshow("SI FG Mask", img_foreground_u1); + } + + if(frameNumber == (framesToLearn + 1)) + std::cout << "FuzzySugenoIntegral updating background model by adaptive-selective learning..." << std::endl; + + IplImage* updated_background_f3 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 3); + cvFillImage(updated_background_f3, 0.0); + fu.AdaptativeSelectiveBackgroundModelUpdate(input_f3, background_f3, updated_background_f3, integral_sugeno_f1, threshold, alphaUpdate); + cv::Mat img_updated_background_f3(updated_background_f3); + img_updated_background_f3.copyTo(img_background_f3); + + cvReleaseImage(&lbp_input_f1); + cvReleaseImage(&lbp_background_f1); + cvReleaseImage(&sim_texture_f1); + cvReleaseImage(&sim_color_f3); + cvReleaseImage(&integral_sugeno_f1); + cvReleaseImage(&updated_background_f3); + + delete background_f1; + delete background_f3; + delete input_f1; + delete input_f3; + } + + firstTime = false; + frameNumber++; +} + +void FuzzySugenoIntegral::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/FuzzySugenoIntegral.xml", 0, CV_STORAGE_WRITE); + + cvWriteInt(fs, "showOutput", showOutput); + cvWriteInt(fs, "framesToLearn", framesToLearn); + cvWriteReal(fs, "alphaLearn", alphaLearn); + cvWriteReal(fs, "alphaUpdate", alphaUpdate); + cvWriteInt(fs, "colorSpace", colorSpace); + cvWriteInt(fs, "option", option); + cvWriteInt(fs, "smooth", smooth); + cvWriteReal(fs, "threshold", threshold); + + cvReleaseFileStorage(&fs); +} + +void FuzzySugenoIntegral::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/FuzzySugenoIntegral.xml", 0, CV_STORAGE_READ); + + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + framesToLearn = cvReadIntByName(fs, 0, "framesToLearn", 10); + alphaLearn = cvReadRealByName(fs, 0, "alphaLearn", 0.1); + alphaUpdate = cvReadRealByName(fs, 0, "alphaUpdate", 0.01); + colorSpace = cvReadIntByName(fs, 0, "colorSpace", 1); + option = cvReadIntByName(fs, 0, "option", 2); + smooth = cvReadIntByName(fs, 0, "smooth", true); + threshold = cvReadRealByName(fs, 0, "threshold", 0.67); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/tb/FuzzySugenoIntegral.h b/package_bgs/tb/FuzzySugenoIntegral.h new file mode 100644 index 0000000000000000000000000000000000000000..11445596948f402d8ccea0ebd18f4f3cccc7590c --- /dev/null +++ b/package_bgs/tb/FuzzySugenoIntegral.h @@ -0,0 +1,55 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" + +#include "FuzzyUtils.h" + +class FuzzySugenoIntegral : public IBGS +{ +private: + bool firstTime; + long long frameNumber; + bool showOutput; + + int framesToLearn; + double alphaLearn; + double alphaUpdate; + int colorSpace; + int option; + bool smooth; + double threshold; + + FuzzyUtils fu; + cv::Mat img_background_f3; + +public: + FuzzySugenoIntegral(); + ~FuzzySugenoIntegral(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/tb/FuzzyUtils.cpp b/package_bgs/tb/FuzzyUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..84d5a5092da97601ebe2608351362de7465e9206 --- /dev/null +++ b/package_bgs/tb/FuzzyUtils.cpp @@ -0,0 +1,511 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "FuzzyUtils.h" + +FuzzyUtils::FuzzyUtils(void){} + +FuzzyUtils::~FuzzyUtils(void){} + +void FuzzyUtils::LBP(IplImage* InputImage, IplImage* LBPimage) +{ + PixelUtils p; + + float* neighberPixel = (float*) malloc(9*sizeof(float)); + float* BinaryValue = (float*) malloc(9*sizeof(float)); + float* CarreExp = (float*) malloc(9*sizeof(float)); + float* valLBP = (float*) malloc(1*sizeof(float)); + + *valLBP = 0; + + int x = 0, y = 0; + + // on implemente les 8 valeurs puissance de 2 qui correspondent aux 8 elem. d'image voisins au elem. d'image central + *(CarreExp+0)=1.0; + *(CarreExp+1)=2.0; + *(CarreExp+2)=4.0; + *(CarreExp+3)=8.0; + *(CarreExp+4)=0.0; + *(CarreExp+5)=16.0; + *(CarreExp+6)=32.0; + *(CarreExp+7)=64.0; + *(CarreExp+8)=128.0; + + //le calcule de LBP + //pour les 4 coins + /* 1.*/ + if(x==0 && y==0) + { + p.getNeighberhoodGrayPixel(InputImage, x,y,neighberPixel); + getBinValue(neighberPixel,BinaryValue,4,0); + *valLBP=*valLBP+((*(BinaryValue+1))*(*(CarreExp+1))+(*(BinaryValue+2))*(*(CarreExp+2))+(*(BinaryValue+3))*(*(CarreExp+3)))/255.0; + p.PutGrayPixel(LBPimage,x,y,*valLBP); + } + + /* 2.*/ + if(x==0 && y==InputImage->width) + { + *valLBP=0; + p.getNeighberhoodGrayPixel(InputImage, x,y,neighberPixel); + getBinValue(neighberPixel,BinaryValue,4,1); + *valLBP=*valLBP+((*(BinaryValue))*(*(CarreExp))+(*(BinaryValue+2))*(*(CarreExp+2))+(*(BinaryValue+3))*(*(CarreExp+3)))/255.0; + p.PutGrayPixel(LBPimage,x,y,*valLBP); + } + + /* 3.*/ + if(x==InputImage->height && y==0) + { + *valLBP=0; + p.getNeighberhoodGrayPixel(InputImage, x,y,neighberPixel); + getBinValue(neighberPixel,BinaryValue,4,2); + *valLBP=*valLBP+((*(BinaryValue))*(*(CarreExp))+(*(BinaryValue+1))*(*(CarreExp+1))+(*(BinaryValue+3))*(*(CarreExp+3)))/255.0; + p.PutGrayPixel(LBPimage,x,y,*valLBP); + } + + /* 4.*/ + if(x==InputImage->height && y==InputImage->width) + { + *valLBP=0; + p.getNeighberhoodGrayPixel(InputImage, x,y,neighberPixel); + getBinValue(neighberPixel,BinaryValue,4,3); + *valLBP=*valLBP+((*(BinaryValue))*(*(CarreExp))+(*(BinaryValue+1))*(*(CarreExp+1))+(*(BinaryValue+2))*(*(CarreExp+2)))/255.0; + p.PutGrayPixel(LBPimage,x,y,*valLBP); + } + + //le calcul de LBP pour la premi�re ligne : L(0) + if(x==0 && (y!=0 && y!=InputImage->width)) + { + for(int y = 1; y < InputImage->width-1; y++) + { + p.getNeighberhoodGrayPixel(InputImage, x,y,neighberPixel); + getBinValue(neighberPixel,BinaryValue,6,4); + *valLBP=0; + *valLBP=*valLBP+((*(BinaryValue))*(*(CarreExp))+(*(BinaryValue+1))*(*(CarreExp+1))+(*(BinaryValue+2))*(*(CarreExp+2))+(*(BinaryValue+3))*(*(CarreExp+3))+(*(BinaryValue+5))*(*(CarreExp+5)))/255.0; + p.PutGrayPixel(LBPimage,x,y,*valLBP); + } + } + + //le calcul de LBP pour la derni�re colonne : C(w) + if((x!=0 && x!=InputImage->height) && y==InputImage->width) + { + for(int x = 1; x < InputImage->height-1; x++) + { + p.getNeighberhoodGrayPixel(InputImage, x,y,neighberPixel); + getBinValue(neighberPixel,BinaryValue,6,4); + *valLBP=0; + *valLBP=*valLBP+((*(BinaryValue))*(*(CarreExp))+(*(BinaryValue+1))*(*(CarreExp+1))+(*(BinaryValue+2))*(*(CarreExp+2))+(*(BinaryValue+3))*(*(CarreExp+3))+(*(BinaryValue+5))*(*(CarreExp+5)))/255.0; + p.PutGrayPixel(LBPimage,x,y,*valLBP); + } + } + + //le calcul de LBP pour la derni�re ligne : L(h) + if(x==InputImage->height && (y!=0 && y!=InputImage->width)) + { + for(int y = 1; y < InputImage->width-1; y++) + { + p.getNeighberhoodGrayPixel(InputImage, x,y,neighberPixel); + getBinValue(neighberPixel,BinaryValue,6,1); + *valLBP=0; + *valLBP=*valLBP+((*(BinaryValue))*(*(CarreExp))+(*(BinaryValue+2))*(*(CarreExp+2))+(*(BinaryValue+3))*(*(CarreExp+3))+(*(BinaryValue+4))*(*(CarreExp+4))+(*(BinaryValue+5))*(*(CarreExp+5)))/255.0; + p.PutGrayPixel(LBPimage,x,y,*valLBP); + } + } + + //le calcul de LBP pour la premi�re colonne : C(0) + if((x!=0 && x!=InputImage->height) && y==0) + { + for(int x = 1; x <InputImage->height-1; x++) + { + p.getNeighberhoodGrayPixel(InputImage, x,y,neighberPixel); + getBinValue(neighberPixel,BinaryValue,6,2); + *valLBP=0; + *valLBP=*valLBP+((*(BinaryValue))*(*(CarreExp+5))+(*(BinaryValue+1))*(*(CarreExp+6))+(*(BinaryValue+3))*(*(CarreExp+3))+(*(BinaryValue+4))*(*(CarreExp))+(*(BinaryValue+5))*(*(CarreExp+1)))/255.0; + p.PutGrayPixel(LBPimage,x,y,*valLBP); + } + } + + //pour le reste des elements d'image + for(int y = 1; y < InputImage->height-1; y++) + { + for(int x = 1; x < InputImage->width-1; x++) + { + p.getNeighberhoodGrayPixel(InputImage, x,y,neighberPixel); + getBinValue(neighberPixel,BinaryValue,9,4); + //le calcul de la valeur du LBP pour chaque elem. d'im. + *valLBP=0; + for(int l = 0; l < 9; l++) + *valLBP = *valLBP + ((*(BinaryValue+l)) * (*(CarreExp+l))) / 255.0; + //printf("\nvalLBP(%d,%d)=%f",x,y,*valLBP); + p.PutGrayPixel(LBPimage,x,y,*valLBP); + } + } + + free(neighberPixel); + free(BinaryValue); + free(CarreExp); + free(valLBP); +} + +void FuzzyUtils::getBinValue(float* neighberGrayPixel, float* BinaryValue, int m, int n) +{ + // la comparaison entre la valeur d'elem d'image central et les valeurs des elem. d'im. voisins + // m = le numero des elements (4, 6 ou 9); + // n = la position de l'element central; + + int h = 0; + for(int k = 0; k < m; k++) + { + if(*(neighberGrayPixel+k) >= *(neighberGrayPixel+n)) + { + *(BinaryValue+h)=1; + h++; + } + else + { + *(BinaryValue+h)=0; + h++; + } + } +} + +void FuzzyUtils::SimilarityDegreesImage(IplImage* CurrentImage, IplImage* BGImage, IplImage* DeltaImage, int n, int color_space) +{ + PixelUtils p; + int i, j; + + if(n == 1) + { + float* CurrentGrayPixel = (float*) malloc (1*(sizeof(float))); + float* BGGrayPixel = (float*) malloc (1*(sizeof(float))); + float* DeltaGrayPixel = (float*) malloc (1*(sizeof(float))); + + for(i = 0; i < CurrentImage->width; i++) + { + for(j = 0; j < CurrentImage->height; j++) + { + p.GetGrayPixel(CurrentImage,i,j,CurrentGrayPixel); + p.GetGrayPixel(BGImage,i,j,BGGrayPixel); + RatioPixels(CurrentGrayPixel,BGGrayPixel,DeltaGrayPixel,1); + p.PutGrayPixel(DeltaImage,i,j,*DeltaGrayPixel); + } + } + + free(CurrentGrayPixel); + free(BGGrayPixel); + free(DeltaGrayPixel); + } + + if(n != 1) + { + IplImage* ConvertedCurrentImage = cvCreateImage(cvSize(CurrentImage->width, CurrentImage->height), IPL_DEPTH_32F, 3); + IplImage* ConvertedBGImage = cvCreateImage(cvSize(CurrentImage->width, CurrentImage->height), IPL_DEPTH_32F, 3); + + float* ConvertedCurrentPixel = (float*) malloc(3*(sizeof(float))); + float* ConvertedBGPixel = (float*) malloc(3*(sizeof(float))); + float* DeltaConvertedPixel = (float*) malloc(3*(sizeof(float))); + + p.ColorConversion(CurrentImage,ConvertedCurrentImage,color_space); + p.ColorConversion(BGImage,ConvertedBGImage,color_space); + + for(i = 0; i < CurrentImage->width; i++) + { + for(j = 0; j < CurrentImage->height; j++) + { + p.GetPixel(ConvertedCurrentImage,i,j,ConvertedCurrentPixel); + p.GetPixel(ConvertedBGImage,i,j,ConvertedBGPixel); + RatioPixels(ConvertedCurrentPixel,ConvertedBGPixel,DeltaConvertedPixel,3); + p.PutPixel(DeltaImage,i,j,DeltaConvertedPixel); + } + } + + free(ConvertedCurrentPixel); + free(ConvertedBGPixel); + free(DeltaConvertedPixel); + + cvReleaseImage(&ConvertedCurrentImage); + cvReleaseImage(&ConvertedBGImage); + } +} + +void FuzzyUtils::RatioPixels(float* CurrentPixel, float* BGPixel, float* DeltaPixel, int n) +{ + if(n == 1) + { + if(*CurrentPixel < *BGPixel) + *DeltaPixel = *CurrentPixel / *BGPixel; + + if(*CurrentPixel > *BGPixel) + *DeltaPixel = *BGPixel / *CurrentPixel; + + if(*CurrentPixel == *BGPixel) + *DeltaPixel = 1.0; + } + + if(n == 3) + for(int i = 0; i < 3; i++) + { + if(*(CurrentPixel+i) < *(BGPixel+i)) + *(DeltaPixel+i) = *(CurrentPixel+i) / *(BGPixel+i); + + if(*(CurrentPixel+i) > *(BGPixel+i)) + *(DeltaPixel+i) = *(BGPixel+i) / *(CurrentPixel+i); + + if(*(CurrentPixel+i) == *(BGPixel+i)) + *(DeltaPixel+i) = 1.0; + } +} + +void FuzzyUtils::getFuzzyIntegralSugeno(IplImage* H, IplImage* Delta, int n, float *MeasureG, IplImage* OutputImage) +{ + // MeasureG : est un vecteur contenant 3 mesure g (g1,g2,g3) tel que : g1+g2+g3=1 + // n : =2 cad aggreger les 2 images "H" et "Delta" + // =1 cad aggreger uniquement les valeurs des composantes couleurs de l'image "Delta" + + PixelUtils p; + + float* HTexturePixel = (float*) malloc(1*sizeof(float)); + float* DeltaOhtaPixel = (float*) malloc(3*(sizeof(float))); + int *Indice = (int*) malloc(3*(sizeof(int))); + float *HI = (float*) malloc(3*(sizeof(float))); + float *Integral = (float*) malloc(3*(sizeof(float))); + float* X = (float*) malloc(1*sizeof(float)); + float* XiXj = (float*) malloc(1*sizeof(float)); + float IntegralFlou; + + *Indice = 0; + *(Indice+1) = 1; + *(Indice+2) = 2; + *X = 1.0; + + for(int i = 0; i < H->width; i++) + { + for(int j = 0; j < H->height; j++) + { + p.GetGrayPixel(H,i,j,HTexturePixel); + p.GetPixel(Delta,i,j,DeltaOhtaPixel); + + *(HI+0) = *(HTexturePixel+0); + *(HI+1) = *(DeltaOhtaPixel+0); + *(HI+2) = *(DeltaOhtaPixel+1); + + Trier(HI,3,Indice); + + *XiXj = *(MeasureG + (*(Indice+1))) + (*(MeasureG + (*(Indice+2)))); + + *(Integral+0) = min((HI + (*(Indice+0))), X); + *(Integral+1) = min((HI + (*(Indice+1))), XiXj); + *(Integral+2) = min((HI + (*(Indice+2))), ((MeasureG+(*(Indice+2))))); + + IntegralFlou = max(Integral,3); + p.PutGrayPixel(OutputImage,i,j,IntegralFlou); + } + } + + free(HTexturePixel); + free(DeltaOhtaPixel); + free(Indice); + free(HI); + free(X); + free(XiXj); + free(Integral); +} + +void FuzzyUtils::getFuzzyIntegralChoquet(IplImage* H, IplImage* Delta, int n, float *MeasureG, IplImage* OutputImage) +{ + // MeasureG : est un vecteur contenant 3 mesure g (g1,g2,g3) tel que : g1+g2+g3=1 + // n : =2 cad aggreger les 2 images "H" et "Delta" + // =1 cad aggreger uniquement les valeurs des composantes couleurs de l'image "Delta" + + PixelUtils p; + + float* HTexturePixel = (float*) malloc(1*sizeof(float)); + float* DeltaOhtaPixel = (float*) malloc(3*(sizeof(float))); + int *Indice = (int*) malloc(3*(sizeof(int))); + float *HI = (float*) malloc(3*(sizeof(float))); + float *Integral = (float*) malloc(3*(sizeof(float))); + float* X = (float*) malloc(1*sizeof(float)); + float* XiXj = (float*) malloc(1*sizeof(float)); + float* IntegralFlou1 = (float*) malloc(1*sizeof(float)); + float IntegralFlou; + + *Indice = 0; + *(Indice+1) = 1; + *(Indice+2) = 2; + *X = 1.0; + + for(int i = 0; i < Delta->width; i++) + { + for(int j = 0; j < Delta->height; j++) + { + if(n == 2) + { + p.GetGrayPixel(H,i,j,HTexturePixel); + p.GetPixel(Delta,i,j,DeltaOhtaPixel); + + *(HI+0) = *(HTexturePixel+0); + *(HI+1) = *(DeltaOhtaPixel+0); + *(HI+2) = *(DeltaOhtaPixel+1); + } + + if(n==1) + { + //remplir HI par les valeurs des 3 composantes couleurs uniquement + p.GetPixel(Delta,i,j,DeltaOhtaPixel); + + *(HI+0) = *(DeltaOhtaPixel+0); + //*(HI+0) = *(DeltaOhtaPixel+2); + *(HI+1) = *(DeltaOhtaPixel+1); + *(HI+2) = *(DeltaOhtaPixel+2); + } + + Trier(HI,3,Indice); + *XiXj = *(MeasureG + (*(Indice+1))) + (*(MeasureG + (*(Indice+2)))); + + *(Integral+0) = *(HI+(*(Indice+0)))* (*X-*XiXj); + *(Integral+1) = *(HI+(*(Indice+1)))* (*XiXj-*(MeasureG+(*(Indice+2)))); + *(Integral+2) = *(HI+(*(Indice+2)))* (*(MeasureG+(*(Indice+2)))); + + IntegralFlou = *(Integral+0) + *(Integral+1) + *(Integral+2); + p.PutGrayPixel(OutputImage,i,j,IntegralFlou); + } + } + + free(HTexturePixel); + free(DeltaOhtaPixel); + free(Indice); + free(HI); + free(X); + free(XiXj); + free(Integral); +} + +void FuzzyUtils::FuzzyMeasureG(float g1, float g2, float g3, float *G) +{ + *(G+0) = g1; + *(G+1) = g2; + *(G+2) = g3; +} + +void FuzzyUtils::Trier(float* g,int n,int* index) +{ + // Cette fonction trie un vecteur g par ordre croissant et + // sort egalement l'indice des elements selon le trie dans le vecteur "index" suppos� initialis� par des valeurs de 1 a n + + float t; + int r,a,b; + + for(a = 1; a <= n; a++) + { + for(b = n-1; b >= a; b--) + if(*(g + b-1) < (*(g + b))) + { + // ordre croissant des �lements + t = *(g + b-1); + *(g + b-1) = *(g + b); + *(g + b) = t; + + // ordre des indices des �lements du vecteur g + r = *(index + b-1); + *(index + b-1) = *(index + b); + *(index + b) = r; + } + } +} + +float FuzzyUtils::min(float *a,float *b) +{ + float min = 0; + + if(*a >= (*b)) + min = *b; + else + min = *a; + + return min; +} + +float FuzzyUtils::max(float* g , int n) +{ + float max = 0; + + for(int i = 0; i < n; i++) + { + if(*(g+i) >= max) + max = *(g+i); + } + + return max; +} + +void FuzzyUtils::gDeDeux(float* a, float* b, float* lambda) +{ + float* c = (float*) malloc(1*sizeof(float)); + *c = *a + (*b) + (*lambda) * (*a) * (*b); +} + +void FuzzyUtils::getLambda(float* g) +{ + float a,b; + float* lambda = (float*) malloc(1*sizeof(float)); + + a = (*(g+0) * (*(g+1)) + (*(g+1)) * (*(g+2)) + (*(g+0)) * (*(g+2))); + *lambda = -(*(g+0) * (*(g+1)) + (*(g+1)) * (*(g+2)) + (*(g+0)) * (*(g+2))) / (*(g+0) * (*(g+1)) * (*(g+2))); + b = (*(g+0) * (*(g+1)) * (*(g+2))); + + //printf("\na:%f",a); + //printf("\nb:%f",b); + //printf("\nlambda:%f", *lambda); + + free(lambda); +} + +void FuzzyUtils::AdaptativeSelectiveBackgroundModelUpdate(IplImage* CurrentImage, IplImage* BGImage, IplImage* OutputImage, IplImage* Integral, float seuil, float alpha) +{ + PixelUtils p; + + float beta = 0.0; + float* CurentImagePixel = (float*) malloc(3*sizeof(float)); + float* BGImagePixel = (float*) malloc(3*sizeof(float)); + float* OutputImagePixel = (float*) malloc(3*sizeof(float)); + float* IntegralImagePixel = (float*) malloc(1*sizeof(float)); + float *Maximum = (float*) malloc(1*sizeof(float)); + float *Minimum = (float*) malloc(1*sizeof(float)); + + p.ForegroundMaximum(Integral, Maximum, 1); + p.ForegroundMinimum(Integral, Minimum, 1); + + for(int i = 0; i < CurrentImage->width; i++) + { + for(int j = 0; j < CurrentImage->height; j++) + { + p.GetPixel(CurrentImage, i, j, CurentImagePixel); + p.GetPixel(BGImage, i, j, BGImagePixel); + p.GetGrayPixel(Integral, i, j, IntegralImagePixel); + + beta = 1 - ((*IntegralImagePixel) - ((*Minimum / (*Minimum - *Maximum)) * (*IntegralImagePixel) - (*Minimum * (*Maximum) / (*Minimum - *Maximum)))); + + for(int k = 0; k < 3; k++) + *(OutputImagePixel + k) = beta * (*(BGImagePixel + k)) + (1 - beta) * (alpha * (*(CurentImagePixel+k)) + (1-alpha) * (*(BGImagePixel+k))); + + p.PutPixel(OutputImage, i, j, OutputImagePixel); + } + } + + free(CurentImagePixel); + free(BGImagePixel); + free(OutputImagePixel); + free(IntegralImagePixel); +} diff --git a/package_bgs/tb/FuzzyUtils.h b/package_bgs/tb/FuzzyUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..43fc9ad4b0fdbefb084655c7469f5a913e617050 --- /dev/null +++ b/package_bgs/tb/FuzzyUtils.h @@ -0,0 +1,54 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once +/* +Code provided by Thierry BOUWMANS + +Maitre de Conf�rences +Laboratoire MIA +Universit� de La Rochelle +17000 La Rochelle +France +tbouwman@univ-lr.fr + +http://sites.google.com/site/thierrybouwmans/ +*/ +#include "PixelUtils.h" + +class FuzzyUtils +{ +public: + FuzzyUtils(void); + ~FuzzyUtils(void); + + void LBP(IplImage* InputImage, IplImage* LBP); + void getBinValue(float* neighberGrayPixel, float* BinaryValue, int m, int n); + + void SimilarityDegreesImage(IplImage* CurrentImage, IplImage* BGImage, IplImage* DeltaImage, int n, int color_space); + void RatioPixels(float* CurrentPixel, float* BGPixel, float* DeltaPixel, int n); + + void getFuzzyIntegralSugeno(IplImage* H, IplImage* Delta, int n, float *MeasureG, IplImage* OutputImage); + void getFuzzyIntegralChoquet(IplImage* H, IplImage* Delta, int n, float *MeasureG, IplImage* OutputImage); + void FuzzyMeasureG(float g1, float g2, float g3, float *G); + void Trier(float* g, int n, int* index); + float min(float *a, float *b); + float max(float *g, int n); + void gDeDeux(float* a, float* b, float* lambda); + void getLambda(float* g); + + void AdaptativeSelectiveBackgroundModelUpdate(IplImage* CurrentImage, IplImage* BGImage, IplImage* OutputImage, IplImage* Integral, float seuil, float alpha); +}; diff --git a/package_bgs/tb/MRF.cpp b/package_bgs/tb/MRF.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1dbc69ed7993f20a3519f57c904880d8d814bd4b --- /dev/null +++ b/package_bgs/tb/MRF.cpp @@ -0,0 +1,339 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "MRF.h" + +using namespace Algorithms::BackgroundSubtraction; + +//init the basic MRF +MRF::MRF() +{ + in_image = out_image = 0; + width = height = 0; + + ////////////////////////////////////////////////////////////////////////// + no_regions = 2; + beta = 2.8;// 0.9; + t = 10;//0.05 + + ////////////////////////////////////////////////////////////////////////// + K = 0; + E = E_old = 0; + + ////////////////////////////////////////////////////////////////////////// + T0 = 4; + c = 0.98; + T = 0; + + ////////////////////////////////////////////////////////////////////////// + alpha = 0.1; + + ////////////////////////////////////////////////////////////////////////// + classes = 0; + in_image_data = 0; + local_evidence = 0; +} + +/************************************************************************/ +/* the Markov Random Field with time constraints for T2FGMM */ +/************************************************************************/ + +MRF_TC::MRF_TC() +{ + beta_time = 0.9; +} + +MRF_TC::~MRF_TC() +{ + delete []classes; + delete []old_labeling; + delete []in_image_data; + delete []local_evidence; +} + +double MRF_TC::TimeEnergy2(int i, int j, int label) +{ + double energy = 0.0; + + if(old_labeling[i][j] == (label*255)) + energy -= beta_time; + else + energy += beta_time; + + if(i != height-1) // south + { + if(label*255 == old_labeling[i+1][j]) + energy -= beta_time; + else + energy += beta_time; + + if((j != width-1) && (label*255 == old_labeling[i+1][j+1])) + energy -= beta_time; + else + energy += beta_time; + + if((j != 0) && (label*255 == old_labeling[i+1][j-1])) + energy -= beta_time; + else + energy += beta_time; + } + + if(j != width-1) // east + { + if(label*255 == old_labeling[i][j+1]) + energy -= beta_time; + else + energy += beta_time; + } + + if(i != 0) // nord + { + if(label*255 == old_labeling[i-1][j]) + energy -= beta_time; + else + energy += beta_time; + + if((j != width-1) && (label*255 == old_labeling[i-1][j+1])) + energy -= beta_time; + else + energy += beta_time; + + if((j != 0) && (label*255 == old_labeling[i-1][j-1])) + energy -= beta_time; + else + energy += beta_time; + } + + if(j != 0) // west + { + if(label*255 == old_labeling[i][j-1]) + energy -= beta_time; + else + energy += beta_time; + } + + return energy; +} + +double MRF_TC::Doubleton2(int i, int j, int label) +{ + double energy = 0.0; + + if(i != height-1) // south + { + if(label == classes[i+1][j]) + energy -= beta; + else + energy += beta; + + if((j != width-1) && (label == classes[i+1][j+1])) + energy -= beta; + else + energy += beta; + + if((j != 0) && (label == classes[i+1][j-1])) + energy -= beta; + else + energy += beta; + } + + if(j != width-1) // east + { + if(label == classes[i][j+1]) + energy -= beta; + else + energy += beta; + } + + if(i != 0) // nord + { + if(label == classes[i-1][j]) + energy -= beta; + else + energy += beta; + + if((j != width-1) && (label == classes[i-1][j+1])) + energy -= beta; + else + energy += beta; + + if((j != 0) && (label == classes[i-1][j-1])) + energy -= beta; + else + energy += beta; + } + + if(j != 0) // west + { + if(label == classes[i][j-1]) + energy -= beta; + else + energy += beta; + } + + return energy; +} + +void MRF_TC::OnIterationOver2(void) +{ + CreateOutput2(); + //cout<<"\rI="<<K<<", "; +} + +void MRF_TC::Build_Classes_OldLabeling_InImage_LocalEnergy() +{ + int i; + classes = new int* [height]; + old_labeling = new int *[height]; + in_image_data = new int* [height]; + local_evidence = new float* [height]; + + for(i = 0; i < height; ++i) + { + classes[i] = new int[width]; + old_labeling[i] = new int[width]; + in_image_data[i] = new int[width]; + local_evidence[i] = new float[width*2]; + } +} + +void MRF_TC::InitEvidence2(GMM *gmm, HMM *hmm, IplImage *labeling) +{ + int i, j; + + background = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3); + cvCopyImage(background2,background.Ptr()); + + unsigned char *in_data = (unsigned char *)(in_image->imageData); + unsigned char *labeling_data = (unsigned char *)(labeling->imageData); + + for(i = 0; i < height; ++i) + { + for(j = 0; j < width; ++j) + { + in_image_data[i][j] = in_data[(i*in_image->widthStep)+j]; + old_labeling[i][j] = labeling_data[i*width+j]; + + if(in_image_data[i][j] == 255) + classes[i][j] = 1; + else + classes[i][j] = 0; + } + + float variance; + float muR; + float muG; + float muB; + + float pixel; + + int modes = 3; + float mu; + + for(i = 0; i < height; ++i) + { + for(j = 0; j < width; ++j) + { + variance = gmm[(i*width+j) * modes+0].variance; + muR = gmm[(i*width+j) * modes+0].muR; + muG = gmm[(i*width+j) * modes+0].muG; + muB = gmm[(i*width+j) * modes+0].muB; + + mu = (muR + muG + muB)/3; + + pixel = (background(i,j,0) + background(i,j,1) + background(i,j,2))/3; + + if(variance == 0) variance = 1; + + local_evidence[i][j*2+0] = pow((pixel-mu),2)/2/variance; + + if(pixel >= mu) + local_evidence[i][j*2+1] = pow((pixel - mu - 2.5*sqrt(variance)),2)/2/variance; + else + local_evidence[i][j*2+1] = pow((pixel - mu + 2.5*sqrt(variance)),2)/2/variance; + } + } + } +} + +void MRF_TC::CreateOutput2() +{ + int i, j; + unsigned char *out_data; + + out_data = (unsigned char *) out_image->imageData; + + // create output image + for (i = 0; i < height; ++i) + for(j = 0; j < width; ++j) + out_data[(i*width) + j] = (unsigned char)((classes[i][j])*255); +} + +//calculate the whole energy +double MRF_TC::CalculateEnergy2() +{ + double sum = 0.0; + int i, j, k; + // !FAIL! + for(i = 0; i < height; ++i) + { + for(j = 0; j < width; ++j) + { + k = classes[i][j]; + sum = sum + local_evidence[i][j*2+k] + Doubleton2(i,j,k) + TimeEnergy2(i, j, k);//min the value + } + } + //sum = 0.1; + return sum; +} + +// local energy +double MRF_TC::LocalEnergy2(int i, int j, int label) +{ + return local_evidence[i][j*2+label] + Doubleton2(i,j,label) + TimeEnergy2(i,j,label); +} + +void MRF_TC::ICM2() +{ + int i, j; + int r; + //double summa_deltaE = 0; + double localenergy0 = 0, localenergy1 = 0; + + K = 0; + //E_old = CalculateEnergy2(); + + do + { + for(i = 0; i < height; ++i) + for(j = 0; j < width; ++j) + { + localenergy0 = LocalEnergy2(i,j,0); + localenergy1 = LocalEnergy2(i,j,1); + + if(localenergy0 < localenergy1) + classes[i][j] = 0; + else + classes[i][j] = 1; + } + + //E = CalculateEnergy2(); + //summa_deltaE = fabs(E_old-E); + //E_old = E; + ++K; + OnIterationOver2(); + }while(K < 2); +} diff --git a/package_bgs/tb/MRF.h b/package_bgs/tb/MRF.h new file mode 100644 index 0000000000000000000000000000000000000000..457bbbc600c81486a7c1c60ffc7c7d664f29065c --- /dev/null +++ b/package_bgs/tb/MRF.h @@ -0,0 +1,107 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef MRF_H +#define MRF_H + +#include "T2FMRF.h" + +namespace Algorithms +{ + namespace BackgroundSubtraction + { + // base class + class MRF + { + public: + IplImage *in_image, *out_image; + //image's width and height + int width, height; + + public: + MRF(); + + protected: + + ////////////////////////////////////////////////////////////////////////// + //the number of labeling + int no_regions; + //potential of Space Constraint + double beta; + //terminal condition when (deltaE < t) + double t; + + ////////////////////////////////////////////////////////////////////////// + //for gibbs + double T0; + //current temperature + double T; + double c; + + ////////////////////////////////////////////////////////////////////////// + // alpha value for MMD + double alpha; + + ////////////////////////////////////////////////////////////////////////// + //current global energy + double E; + //old global energy + double E_old; + //number of iteration + int K; + + ////////////////////////////////////////////////////////////////////////// + //labeling image + int **classes; + //input image + int **in_image_data; + //evidence + float ** local_evidence; + }; + + /************************************************************************/ + /* the Markov Random Field with time constraints for T2FGMM */ + /************************************************************************/ + class MRF_TC: public MRF + { + private: + double beta_time; + + public: + IplImage *background2; + RgbImage background; + int **old_labeling; + + public: + MRF_TC(); + ~MRF_TC(); + double TimeEnergy2(int i, int j, int label); + void OnIterationOver2(void); + void Build_Classes_OldLabeling_InImage_LocalEnergy(); + void InitEvidence2(GMM *gmm, HMM *hmm, IplImage *labeling); + void CreateOutput2(); + double CalculateEnergy2(); + double LocalEnergy2(int i, int j, int label); + double Doubleton2(int i, int j, int label); + + void Gibbs2(); + void ICM2(); + void Metropolis2(bool mmd); + }; + }; +}; + +#endif \ No newline at end of file diff --git a/package_bgs/tb/PerformanceUtils.cpp b/package_bgs/tb/PerformanceUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30f89b774a8e6959c23cde6f8bb45a036325d13e --- /dev/null +++ b/package_bgs/tb/PerformanceUtils.cpp @@ -0,0 +1,521 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "PerformanceUtils.h" + +PerformanceUtils::PerformanceUtils(void){} + +PerformanceUtils::~PerformanceUtils(void){} + +float PerformanceUtils::NrPixels(IplImage *image) +{ + return (float) (image->width * image->height); +} + +float PerformanceUtils::NrAllDetectedPixNotNULL(IplImage *image, IplImage *ground_truth) +{ + //Nombre de tous les pixels non nuls dans Groundthruth et dans image + float Union12 = 0.0; + + unsigned char *pixelGT = (unsigned char*) malloc(1*sizeof(unsigned char)); + unsigned char *pixelI = (unsigned char*) malloc(1*sizeof(unsigned char)); + + PixelUtils p; + + for(int y = 0; y < image->height; y++) + { + for(int x = 0; x < image->width; x++) + { + p.GetGrayPixel(ground_truth,x,y,pixelGT); + p.GetGrayPixel(image,x,y,pixelI); + + if((pixelGT[0] != 0) || (pixelI[0] != 0)) + Union12++; + } + } + + free(pixelGT); + free(pixelI); + + return Union12; +} + +float PerformanceUtils::NrTruePositives(IplImage *image, IplImage *ground_truth, bool debug) +{ + float nTP = 0.0; + + unsigned char *pixelGT = (unsigned char*) malloc(1*sizeof(unsigned char)); + unsigned char *pixelI = (unsigned char*) malloc(1*sizeof(unsigned char)); + + IplImage *TPimage = 0; + + if(debug) + { + TPimage = cvCreateImage(cvSize(image->width,image->height),image->depth,image->nChannels); + cvFillImage(TPimage,0.0); + } + + PixelUtils p; + + for(int y = 0; y < image->height; y++) + { + for(int x = 0; x < image->width; x++) + { + p.GetGrayPixel(ground_truth,x,y,pixelGT); + p.GetGrayPixel(image,x,y,pixelI); + + if((pixelGT[0] != 0) && (pixelI[0] != 0)) + { + if(debug) + p.PutGrayPixel(TPimage,x,y,*pixelI); + + nTP++; + } + } + } + + if(debug) + { + cvNamedWindow("TPImage", 0); + cvShowImage("TPImage", TPimage); + //std::cout << "True Positives: " << nTP << std::endl; + //<< " press ENTER to continue" << std::endl; + //cvWaitKey(0); + cvReleaseImage(&TPimage); + } + + free(pixelGT); + free(pixelI); + + return nTP; +} + +float PerformanceUtils::NrTrueNegatives(IplImage* image, IplImage* ground_truth, bool debug) +{ + float nTN = 0.0; + + unsigned char *pixelGT = (unsigned char *)malloc(1*sizeof(unsigned char)); + unsigned char *pixelI = (unsigned char *)malloc(1*sizeof(unsigned char)); + + IplImage *TNimage = 0; + + if(debug) + { + TNimage = cvCreateImage(cvSize(image->width,image->height),image->depth,image->nChannels); + cvFillImage(TNimage, 0.0); + } + + PixelUtils p; + + for(int y = 0; y < image->height; y++) + { + for(int x = 0; x < image->width; x++) + { + p.GetGrayPixel(ground_truth,x,y,pixelGT); + p.GetGrayPixel(image,x,y,pixelI); + + if((pixelGT[0] == 0) && (pixelI[0] == 0.0)) + { + *pixelI = 255; + + if(debug) + p.PutGrayPixel(TNimage,x,y,*pixelI); + + nTN++; + } + } + } + + if(debug) + { + cvNamedWindow("TNImage", 0); + cvShowImage("TNImage", TNimage); + //std::cout << "True Negatives: " << nTN << std::endl; + //<< " press ENTER to continue" << std::endl; + //cvWaitKey(0); + cvReleaseImage(&TNimage); + } + + free(pixelGT); + free(pixelI); + + return nTN; +} + +float PerformanceUtils::NrFalsePositives(IplImage *image, IplImage *ground_truth,bool debug) +{ + float nFP = 0.0; + + unsigned char *pixelGT = (unsigned char*) malloc(1*sizeof(unsigned char)); + unsigned char *pixelI = (unsigned char*) malloc(1*sizeof(unsigned char)); + + IplImage *FPimage = 0; + + if(debug) + { + FPimage = cvCreateImage(cvSize(image->width,image->height),image->depth,image->nChannels); + cvFillImage(FPimage, 0.0); + } + + PixelUtils p; + + for(int y = 0; y < image->height; y++) + { + for(int x = 0; x < image->width; x++) + { + p.GetGrayPixel(ground_truth,x,y,pixelGT); + p.GetGrayPixel(image,x,y,pixelI); + + if((pixelGT[0] == 0) && (pixelI[0] != 0)) + { + if(debug) + p.PutGrayPixel(FPimage,x,y,*pixelI); + + nFP++; + } + } + } + + if(debug) + { + cvNamedWindow("FPImage", 0); + cvShowImage("FPImage", FPimage); + //std::cout << "False Positives: " << nFP << std::endl; + //<< " press ENTER to continue" << std::endl; + //cvWaitKey(0); + cvReleaseImage(&FPimage); + } + + free(pixelGT); + free(pixelI); + + return nFP; +} + +float PerformanceUtils::NrFalseNegatives(IplImage * image, IplImage *ground_truth, bool debug) +{ + float nFN = 0.0; + + unsigned char *pixelGT = (unsigned char*) malloc(1*sizeof(unsigned char)); + unsigned char *pixelI = (unsigned char*) malloc(1*sizeof(unsigned char)); + + IplImage *FNimage = 0; + + if(debug) + { + FNimage = cvCreateImage(cvSize(image->width,image->height),image->depth,image->nChannels); + cvFillImage(FNimage, 0.0); + } + + PixelUtils p; + + for(int y = 0; y < image->height; y++) + { + for(int x = 0; x < image->width; x++) + { + p.GetGrayPixel(ground_truth,x,y,pixelGT); + p.GetGrayPixel(image,x,y,pixelI); + + if((pixelGT[0] != 0) && (pixelI[0] == 0)) + { + if(debug) + p.PutGrayPixel(FNimage,x,y,*pixelGT); + + nFN++; + } + } + } + + if(debug) + { + cvNamedWindow("FNImage", 0); + cvShowImage("FNImage", FNimage); + //std::cout << "False Negatives: " << nFN << std::endl; + //<< " press ENTER to continue" << std::endl; + //cvWaitKey(0); + cvReleaseImage(&FNimage); + } + + free(pixelGT); + free(pixelI); + + return nFN; +} + +float PerformanceUtils::SimilarityMeasure(IplImage *image, IplImage *ground_truth, bool debug) +{ + cv::Mat img_input(image,true); + cv::Mat img_ref(ground_truth,true); + + int rn = cv::countNonZero(img_ref); + cv::Mat i; + cv::Mat u; + + if(rn > 0) + { + i = img_input & img_ref; + u = img_input | img_ref; + } + else + { + i = (~img_input) & (~img_ref); + u = (~img_input) | (~img_ref); + } + + int in = cv::countNonZero(i); + int un = cv::countNonZero(u); + + double s = (((double)in) / ((double)un)); + + if(debug) + { + cv::imshow("A^B", i); + cv::imshow("AvB", u); + + //std::cout << "Similarity Measure: " << s << std::endl; + + //<< " press ENTER to continue" << std::endl; + //cv::waitKey(0); + } + + return s; +} + +void PerformanceUtils::ImageROC(IplImage *image, IplImage* ground_truth, bool saveResults, char* filename) +{ + unsigned char *pixelGT = (unsigned char*) malloc(1*sizeof(unsigned char)); + unsigned char *pixelI = (unsigned char*) malloc(1*sizeof(unsigned char)); + + IplImage *ROCimage = cvCreateImage(cvSize(image->width,image->height),image->depth,image->nChannels); + cvFillImage(ROCimage, 0.0); + + PixelUtils p; + + for(int y = 0; y < image->height; y++) + { + for(int x = 0; x < image->width; x++) + { + p.GetGrayPixel(ground_truth,x,y,pixelGT); + p.GetGrayPixel(image,x,y,pixelI); + + if((pixelGT[0] != 0) && (pixelI[0] != 0)) // TP + { + *pixelI = 30; + p.PutGrayPixel(ROCimage,x,y,*pixelI); + } + + if((pixelGT[0] == 0) && (pixelI[0] == 0.0)) // TN + { + *pixelI = 0; + p.PutGrayPixel(ROCimage,x,y,*pixelI); + } + + if((pixelGT[0] == 0) && (pixelI[0] != 0)) // FP + { + *pixelI = 255; + p.PutGrayPixel(ROCimage,x,y,*pixelI); + } + + if((pixelGT[0] != 0) && (pixelI[0] == 0)) // FN + { + *pixelI = 100; + p.PutGrayPixel(ROCimage,x,y,*pixelI); + } + } + } + + cvNamedWindow("ROC image", 0); + cvShowImage("ROC image", ROCimage); + + if(saveResults) + { + unsigned char *pixelOI = (unsigned char*) malloc(1*sizeof(unsigned char)); + unsigned char *pixelROC = (unsigned char*) malloc(1*sizeof(unsigned char)); + + float** freq; + float nTP = 0.0; + float nTN = 0.0; + float nFP = 0.0; + float nFN = 0.0; + + freq = (float**) malloc(256*(sizeof(float*))); + for(int i = 0; i < 256; i++) + freq[i] = (float*) malloc(7 * (sizeof(float))); + + for(int i = 0; i < 256; i++) + for(int j = 0; j < 6; j++) + freq[i][j] = 0.0; + + for(int y = 0; y < image->height; y++) + { + for(int x = 0; x < image->width; x++) + { + for(int i = 0; i < 256; i++) + { + p.GetGrayPixel(image,x,y,pixelOI); + p.GetGrayPixel(ROCimage,x,y,pixelROC); + + if((pixelOI[0] == i) && (pixelROC[0] == 30.0)) // TP + { + nTP++; + freq[i][0] = nTP; + break; + } + + if((pixelOI[0] == i) && (pixelROC[0] == 0.0)) // TN + { + nTN++; + freq[i][1] = nTN; + break; + } + + if((pixelOI[0] == i) && (pixelROC[0] == 255.0)) // FP + { + nFP++; + freq[i][2] = nFP; + break; + } + + if((pixelOI[0] == i) && (pixelROC[0] == 100)) // FN + { + nFN++; + freq[i][3] = nFN; + break; + } + } + } + } + + //freq[i][0] = TP + //freq[i][1] = TN + //freq[i][2] = FP + //freq[i][3] = FN + //freq[i][4] = FNR + //freq[i][5] = FPR + + std::ofstream f(filename); + + if(!f.is_open()) + std::cout << "Failed to open file " << filename << " for writing!" << std::endl; + else + { + f << " I TP TN FP FN FPR FNR DR \n" << std::endl; + + for(int i = 0; i < 256; i++) + { + //printf("%4d - TP:%5.0f, TN:%5.0f, FP:%5.0f, FN:%5.0f,", i, freq[i][0], freq[i][1], freq[i][2], freq[i][3]); + + if((freq[i][3] + freq[i][0] != 0.0) && (freq[i][2] + freq[i][1] != 0.0)) + { + freq[i][4] = freq[i][3] / (freq[i][3] + freq[i][0]); // FNR = FN / (TP + FN); + freq[i][5] = freq[i][2] / (freq[i][2] + freq[i][1]); // FPR = FP / (FP + TN); + freq[i][6] = freq[i][0] / (freq[i][0] + freq[i][3]); // DR = TP / (TP+FN); + + //printf(" FPR:%1.5f, FNR:%1.5f, D:%1.5f\n", freq[i][5], freq[i][4], freq[i][6]); + ////fprintf(f," %4d %1.6f %1.6f\n",i,freq[i][5],freq[i][4]); + ////fprintf(f," %1.6f %1.6f\n",freq[i][5],freq[i][4]); + char line[255]; + sprintf(line,"%3d %6.0f %6.0f %6.0f %6.0f %1.6f %1.6f %1.6f\n", + i, freq[i][0], freq[i][1], freq[i][2], freq[i][3], freq[i][5], freq[i][4], freq[i][6]); + f << line; + } + //else + //printf("\n"); + } + + std::cout << "Results saved in " << filename << std::endl; + f.close(); + } + + free(freq); + free(pixelOI); + free(pixelROC); + } + + //std::cout << "press ENTER to continue" << std::endl; + //cvWaitKey(0); + cvReleaseImage(&ROCimage); + + free(pixelGT); + free(pixelI); +} + +void PerformanceUtils::PerformanceEvaluation(IplImage *image, IplImage *ground_truth, bool saveResults, char* filename, bool debug) +{ + float N = 0; + N = NrPixels(image); + + float U = 0; + U = NrAllDetectedPixNotNULL(image, ground_truth); + + float TP = 0; + TP = NrTruePositives(image, ground_truth, debug); + + float TN = 0; + TN = NrTrueNegatives(image, ground_truth, debug); + + float FP = 0; + FP = NrFalsePositives(image, ground_truth, debug); + + float FN = 0; + FN = NrFalseNegatives(image, ground_truth, debug); + + float DetectionRate = TP / (TP + FN); + float Precision = TP / (TP + FP); + float Fmeasure = (2 * DetectionRate * Precision) / (DetectionRate + Precision); + + float Accuracy = (TN + TP) / N; + float FalseNegativeRate = FN / (TP + FN); + + float FalsePositiveRate = FP / (FP + TN); + float TruePositiveRate = TP / (TP + FN); + + float SM = 0; + SM = SimilarityMeasure(image, ground_truth, debug); + + std::stringstream sstm; + sstm << "N = " << N << std::endl; + sstm << "U = " << U << std::endl; + sstm << "TP = " << TP << std::endl; + sstm << "TN = " << TN << std::endl; + sstm << "FP = " << FP << std::endl; + sstm << "FN = " << FN << std::endl; + sstm << "DetectionRate = " << DetectionRate << std::endl; + sstm << "Precision = " << Precision << std::endl; + sstm << "Fmeasure = " << Fmeasure << std::endl; + sstm << "Accuracy = " << Accuracy << std::endl; + sstm << "FalseNegativeRate = " << FalseNegativeRate << std::endl; + sstm << "FalsePositiveRate = " << FalsePositiveRate << std::endl; + sstm << "TruePositiveRate = " << TruePositiveRate << std::endl; + sstm << "SimilarityMeasure = " << SM << std::endl; + + std::string results = sstm.str(); + std::cout << results; + + if(saveResults) + { + std::ofstream f(filename); + + if(!f.is_open()) + std::cout << "Failed to open file " << filename << " for writing!" << std::endl; + else + { + f << results; + std::cout << "Results saved in " << filename << std::endl; + f.close(); + } + } +} \ No newline at end of file diff --git a/package_bgs/tb/PerformanceUtils.h b/package_bgs/tb/PerformanceUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..c60826422aab262d37aa4d7edde841e00cc4cdf8 --- /dev/null +++ b/package_bgs/tb/PerformanceUtils.h @@ -0,0 +1,54 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once +/* +Code provided by Thierry BOUWMANS + +Maitre de Conf�rences +Laboratoire MIA +Universit� de La Rochelle +17000 La Rochelle +France +tbouwman@univ-lr.fr + +http://sites.google.com/site/thierrybouwmans/ +*/ +#include <stdio.h> +#include <fstream> +#include <cv.h> +#include <highgui.h> + +#include "PixelUtils.h" + +class PerformanceUtils +{ +public: + PerformanceUtils(void); + ~PerformanceUtils(void); + + float NrPixels(IplImage *image); + float NrAllDetectedPixNotNULL(IplImage *image, IplImage *ground_truth); + float NrTruePositives(IplImage *image, IplImage *ground_truth, bool debug = false); + float NrTrueNegatives(IplImage *image, IplImage *ground_truth, bool debug = false); + float NrFalsePositives(IplImage *image, IplImage *ground_truth, bool debug = false); + float NrFalseNegatives(IplImage *image, IplImage *ground_truth, bool debug = false); + float SimilarityMeasure(IplImage *image, IplImage *ground_truth, bool debug = false); + + void ImageROC(IplImage *image, IplImage* ground_truth, bool saveResults = false, char* filename = ""); + void PerformanceEvaluation(IplImage *image, IplImage *ground_truth, bool saveResults = false, char* filename = "", bool debug = false); +}; + diff --git a/package_bgs/tb/PixelUtils.cpp b/package_bgs/tb/PixelUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc61cc5f738e5184b08e475f3f96128a74050ffd --- /dev/null +++ b/package_bgs/tb/PixelUtils.cpp @@ -0,0 +1,351 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "PixelUtils.h" + +PixelUtils::PixelUtils(void){} +PixelUtils::~PixelUtils(void){} + +void PixelUtils::ColorConversion(IplImage* RGBImage, IplImage* ConvertedImage, int color_space) +{ + // Space Color RGB - Nothing to do! + if(color_space == 1) + cvCopyImage(RGBImage, ConvertedImage); + + // Space Color Ohta + if(color_space == 2) + cvttoOTHA(RGBImage, ConvertedImage); + + // Space Color HSV - V Intensity - (H,S) Chromaticity + if(color_space == 3) + cvCvtColor(RGBImage, ConvertedImage, CV_BGR2HSV); + + // Space Color YCrCb - Y Intensity - (Cr,Cb) Chromaticity + if(color_space == 4) + cvCvtColor(RGBImage,ConvertedImage,CV_BGR2YCrCb); +} + +void PixelUtils::cvttoOTHA(IplImage* RGBImage, IplImage* OthaImage) +{ + float* OhtaPixel = (float*) malloc(3*(sizeof(float))); + float* RGBPixel = (float*) malloc(3*(sizeof(float))); + + for(int i = 0; i < RGBImage->width; i++) + { + for(int j = 0;j < RGBImage->height; j++) + { + GetPixel(RGBImage, i, j, RGBPixel); + + // I1 = (R + G + B) / 3 + *OhtaPixel = (*(RGBPixel) + (*(RGBPixel + 1)) + (*(RGBPixel + 2))) / 3.0; + + // I2 = (R - B) / 2 + *(OhtaPixel+1) = (*RGBPixel - (*(RGBPixel + 2))) / 2.0; + + // I3 = (2G - R - B) / 4 + *(OhtaPixel+2) = (2 * (*(RGBPixel + 1)) - (*RGBPixel) - (*(RGBPixel + 2))) / 4.0; + + PutPixel(OthaImage, i, j, OhtaPixel); + } + } + + free(OhtaPixel); + free(RGBPixel); +} + +void PixelUtils::PostProcessing(IplImage *InputImage) +{ + IplImage *ResultImage = cvCreateImage(cvSize(InputImage->width, InputImage->height), IPL_DEPTH_32F, 3); + + cvErode(InputImage, ResultImage, NULL, 1); + cvDilate(ResultImage, InputImage, NULL, 0); + + cvReleaseImage(&ResultImage); +} + +void PixelUtils::GetPixel(IplImage *image, int m, int n, unsigned char *pixelcourant) +{ + for(int k = 0; k < 3; k++) + pixelcourant[k] = ((unsigned char*)(image->imageData + image->widthStep*n))[m*3 + k]; +} + +void PixelUtils::GetGrayPixel(IplImage *image, int m, int n, unsigned char *pixelcourant) +{ + *pixelcourant = ((unsigned char*)(image->imageData + image->widthStep*n))[m]; +} + +void PixelUtils::PutPixel(IplImage *image,int p,int q,unsigned char *pixelcourant) +{ + for(int r = 0; r < 3; r++) + ((unsigned char*)(image->imageData + image->widthStep*q))[p*3 + r] = pixelcourant[r]; +} + +void PixelUtils::PutGrayPixel(IplImage *image, int p, int q, unsigned char pixelcourant) +{ + ((unsigned char*)(image->imageData + image->widthStep*q))[p] = pixelcourant; +} + +void PixelUtils::GetPixel(IplImage *image, int m, int n, float *pixelcourant) +{ + for(int k = 0; k < 3; k++) + pixelcourant[k] = ((float*)(image->imageData + image->widthStep*n))[m*3 + k]; +} + +void PixelUtils::GetGrayPixel(IplImage *image, int m, int n, float *pixelcourant) +{ + *pixelcourant = ((float*)(image->imageData + image->widthStep*n))[m]; +} + +void PixelUtils::PutPixel(IplImage *image, int p, int q, float *pixelcourant) +{ + for(int r = 0; r < 3; r++) + ((float*)(image->imageData + image->widthStep*q))[p*3 + r] = pixelcourant[r]; +} + +void PixelUtils::PutGrayPixel(IplImage *image,int p,int q,float pixelcourant) +{ + ((float*)(image->imageData + image->widthStep*q))[p] = pixelcourant; +} + +void PixelUtils::getNeighberhoodGrayPixel(IplImage* InputImage, int x, int y, float* neighberPixel) +{ + int i,j,k; + + float* pixelCourant = (float*) malloc(1*(sizeof(float))); + + //le calcul de voisinage pour les 4 coins; + /* 1.*/ + if(x==0 && y==0) + { + k = 0; + for(i = x; i < x+2; i++) + for(j = y; j < y+2; j++) + { + GetGrayPixel(InputImage,i,j,pixelCourant); + *(neighberPixel+k) = *pixelCourant; + k++; + } + } + + /* 2.*/ + if(x==0 && y==InputImage->width) + { + k = 0; + for(i = x; i < x+2; i++) + for(j = y-1; j < y+1; j++) + { + GetGrayPixel(InputImage,i,j,pixelCourant); + *(neighberPixel+k) = *pixelCourant; + k++; + } + } + + /* 3.*/ + if(x==InputImage->height && y==0) + { + k = 0; + for(i = x-1; i < x+1; i++) + for(j = y; j < y+2; j++) + { + GetGrayPixel(InputImage,i,j,pixelCourant); + *(neighberPixel+k) = *pixelCourant; + k++; + } + } + + /* 4.*/ + if(x==InputImage->height && y==InputImage->width) + { + k = 0; + for(i = x-1; i <x+1; i++) + for(j = y-1; j < y+1; j++) + { + GetGrayPixel(InputImage,i,j,pixelCourant); + *(neighberPixel+k) = *pixelCourant; + k++; + } + } + + // Voisinage de la premiere ligne : L(0) + if(x==0 && (y!=0 && y!=InputImage->width)) + { + k = 0; + for(i = x+1; i >= x; i--) + for(j = y-1; j < y+2; j++) + { + GetGrayPixel(InputImage,i,j,pixelCourant); + *(neighberPixel+k) = *pixelCourant; + k++; + } + } + + // Voisinage de la derni�re colonne : C(w) + if((x!=0 && x!=InputImage->height) && y==InputImage->width) + { + k = 0; + for(i = x+1; i > x-2; i--) + for(j = y-1; j < y+1; j++) + { + GetGrayPixel(InputImage,i,j,pixelCourant); + *(neighberPixel+k) = *pixelCourant; + k++; + } + } + + // Voisinage de la derni�re ligne : L(h) + if(x==InputImage->height && (y!=0 && y!=InputImage->width)) + { + k = 0; + for(i = x; i > x-2; i--) + for(j = y-1; j < y+2; j++) + { + GetGrayPixel(InputImage,i,j,pixelCourant); + *(neighberPixel+k) = *pixelCourant; + k++; + } + } + + // Voisinage de la premiere colonne : C(0) + if((x!=0 && x!=InputImage->height) && y==0) + { + k = 0; + for(i = x-1; i < x+2; i++) + for(j = y; j < y+2; j++) + { + GetGrayPixel(InputImage,i,j,pixelCourant); + *(neighberPixel+k) = *pixelCourant; + k++; + } + } + + //le calcul du voisinage pour le reste des elementes d'image + if((x!=0 && x!=InputImage->height)&&(y!=0 && y!=InputImage->width)) + { + k = 0; + for(i = x+1;i > x-2; i--) + for(j = y-1; j < y+2; j++) + { + GetGrayPixel(InputImage,i,j,pixelCourant); + *(neighberPixel+k) = *pixelCourant; + k++; + } + } + + free(pixelCourant); +} + +void PixelUtils::ForegroundMinimum(IplImage *Foreground, float *Minimum, int n) +{ + int i,j,k; + float *pixelcourant; + + pixelcourant = (float *) malloc(n*sizeof(float)); + + for(k = 0; k < n; k++) + *(Minimum + k) = 255; + + for(i = 0; i < Foreground->width; i++) + for(j = 0; j < Foreground->height; j++) + { + if(n == 3) + { + GetPixel(Foreground,i,j,pixelcourant); + + for(k = 0; k < n; k++) + if(*(pixelcourant + k) < *(Minimum + k)) + *(Minimum + k) = *(pixelcourant + k); + } + + if(n==1) + { + GetGrayPixel(Foreground,i,j,pixelcourant); + + if(*pixelcourant < *Minimum) + *Minimum = *pixelcourant; + } + } + + free(pixelcourant); +} + +void PixelUtils::ForegroundMaximum(IplImage *Foreground, float *Maximum, int n) +{ + int i,j,k; + float *pixelcourant; + + pixelcourant = (float *) malloc(n*sizeof(float)); + + for(k = 0; k < n; k++) + *(Maximum + k) = 0; + + for(i = 0; i < Foreground->width; i++) + for(j = 0; j < Foreground->height; j++) + { + if(n == 3) + { + GetPixel(Foreground,i,j,pixelcourant); + + for(k = 0; k < n; k++) + if(*(pixelcourant + k) > *(Maximum + k)) + *(Maximum + k) = *(pixelcourant + k); + } + + if(n == 1) + { + GetGrayPixel(Foreground,i,j,pixelcourant); + + if(*pixelcourant > *Maximum) + *Maximum = *pixelcourant; + } + } + + free(pixelcourant); +} + +void PixelUtils::ComplementaryAlphaImageCreation(IplImage *AlphaImage, IplImage *ComplementaryAlphaImage, int n) +{ + int i,j,k; + float *pixelcourant, *pixelcourant1; + + pixelcourant = (float *) malloc(n * sizeof(float)); + pixelcourant1 = (float *) malloc(n * sizeof(float)); + + for(i = 0; i < AlphaImage->width; i++) + for(j = 0; j < AlphaImage->height; j++) + { + if(n == 1) + { + GetGrayPixel(AlphaImage,i,j,pixelcourant); + *pixelcourant1 = 1 - *(pixelcourant); + PutGrayPixel(ComplementaryAlphaImage,i,j,*pixelcourant1); + } + + if(n == 3) + { + GetPixel(AlphaImage,i,j,pixelcourant); + for(k = 0; k < 3; k++) + { + *pixelcourant1 = 1.0 - *(pixelcourant); + *(pixelcourant1+1) = 1.0 - *(pixelcourant+1); + *(pixelcourant1+2) = 1.0 - *(pixelcourant+2); + } + PutPixel(ComplementaryAlphaImage,i,j,pixelcourant1); + } + } + + free(pixelcourant); + free(pixelcourant1); +} diff --git a/package_bgs/tb/PixelUtils.h b/package_bgs/tb/PixelUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..efbfdd9c9c3d5af95329a5fc0ce51659578e357f --- /dev/null +++ b/package_bgs/tb/PixelUtils.h @@ -0,0 +1,61 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once +/* +Code provided by Thierry BOUWMANS + +Maitre de Conf�rences +Laboratoire MIA +Universit� de La Rochelle +17000 La Rochelle +France +tbouwman@univ-lr.fr + +http://sites.google.com/site/thierrybouwmans/ +*/ +#include <stdio.h> +#include <cv.h> +#include <highgui.h> + +class PixelUtils +{ +public: + PixelUtils(void); + ~PixelUtils(void); + + void ColorConversion(IplImage* RGBImage, IplImage* ConvertedImage, int color_space); + void cvttoOTHA(IplImage* RGBImage, IplImage* OthaImage); + + void PostProcessing(IplImage *InputImage); + + void GetPixel(IplImage *image, int m, int n, unsigned char *pixelcourant); + void GetGrayPixel(IplImage *image, int m, int n, unsigned char *pixelcourant); + + void PutPixel(IplImage *image, int p, int q, unsigned char *pixelcourant); + void PutGrayPixel(IplImage *image, int p, int q, unsigned char pixelcourant); + + void GetPixel(IplImage *image, int m, int n, float *pixelcourant); + void GetGrayPixel(IplImage *image, int m, int n, float *pixelcourant); + + void PutPixel(IplImage *image, int p, int q, float *pixelcourant); + void PutGrayPixel(IplImage *image, int p, int q, float pixelcourant); + + void getNeighberhoodGrayPixel(IplImage* InputImage, int x, int y, float* neighberPixel); + void ForegroundMaximum(IplImage *Foreground, float *Maximum, int n); + void ForegroundMinimum(IplImage *Foreground, float *Minimum, int n); + void ComplementaryAlphaImageCreation(IplImage *AlphaImage, IplImage *ComplementaryAlphaImage, int n); +}; \ No newline at end of file diff --git a/package_bgs/tb/T2FGMM.cpp b/package_bgs/tb/T2FGMM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..70d5749c32bb6b765cc51b0a1a263f5936b5b6d2 --- /dev/null +++ b/package_bgs/tb/T2FGMM.cpp @@ -0,0 +1,337 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* T2FGMM.cpp +* +* Purpose: Implementation of the T2 Fuzzy Gaussian Mixture Models (T2GMMs) +* "Modeling of Dynamic Backgrounds by Type-2 Fuzzy Gaussians Mixture Models" +* Author: Fida El Baf, Thierry Bouwmans, September 2008. + +******************************************************************************/ + +#include "T2FGMM.h" + +using namespace Algorithms::BackgroundSubtraction; + +int compareT2FGMM(const void* _gmm1, const void* _gmm2) +{ + GMM gmm1 = *(GMM*)_gmm1; + GMM gmm2 = *(GMM*)_gmm2; + + if(gmm1.significants < gmm2.significants) + return 1; + else if(gmm1.significants == gmm2.significants) + return 0; + else + return -1; +} + +T2FGMM::T2FGMM() +{ + m_modes = NULL; +} + +T2FGMM::~T2FGMM() +{ + if(m_modes != NULL) + delete[] m_modes; +} + +void T2FGMM::Initalize(const BgsParams& param) +{ + m_params = (T2FGMMParams&) param; + + // Tbf - the threshold + m_bg_threshold = 0.75f; // 1-cf from the paper + + // Tgenerate - the threshold + m_variance = 36.0f; // sigma for the new mode + + // GMM for each pixel + m_modes = new GMM[m_params.Size()*m_params.MaxModes()]; + + // used modes per pixel + m_modes_per_pixel = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1); + + m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); + + // Factor control for the T2FGMM-UM [0,3] + //km = (float) 1.5; + km = (float) m_params.KM(); + + // Factor control for the T2FGMM-UV [0.3,1] + //kv = (float) 0.6; + kv = (float) m_params.KV(); +} + +RgbImage* T2FGMM::Background() +{ + return &m_background; +} + +void T2FGMM::InitModel(const RgbImage& data) +{ + m_modes_per_pixel.Clear(); + + for(unsigned int i = 0; i < m_params.Size()*m_params.MaxModes(); ++i) + { + m_modes[i].weight = 0; + m_modes[i].variance = 0; + m_modes[i].muR = 0; + m_modes[i].muG = 0; + m_modes[i].muB = 0; + m_modes[i].significants = 0; + } +} + +void T2FGMM::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) +{ + // it doesn't make sense to have conditional updates in the GMM framework +} + +void T2FGMM::SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char& numModes, + unsigned char& low_threshold, unsigned char& high_threshold) +{ + // calculate distances to the modes (+ sort???) + // here we need to go in descending order!!! + long pos; + bool bFitsPDF = false; + bool bBackgroundLow = false; + bool bBackgroundHigh = false; + + float fOneMinAlpha = 1 - m_params.Alpha(); + float totalWeight = 0.0f; + + // calculate number of Gaussians to include in the background model + int backgroundGaussians = 0; + double sum = 0.0; + for(int i = 0; i < numModes; ++i) + { + if(sum < m_bg_threshold) + { + backgroundGaussians++; + sum += m_modes[posPixel+i].weight; + } + else + break; + } + + // update all distributions and check for match with current pixel + for(int iModes = 0; iModes < numModes; iModes++) + { + pos = posPixel + iModes; + float weight = m_modes[pos].weight; + + // fit not found yet + if (!bFitsPDF) + { + //check if it belongs to some of the modes + //calculate distance + float var = m_modes[pos].variance; + float muR = m_modes[pos].muR; + float muG = m_modes[pos].muG; + float muB = m_modes[pos].muB; + + //float km = 2; + //float kv = 0.9; + + float dR = fabs(muR - pixel(0)); + float dG = fabs(muG - pixel(1)); + float dB = fabs(muB - pixel(2)); + + // calculate the squared distance + float HR; + float HG; + float HB; + + // T2FGMM-UM + if(m_params.Type() == TYPE_T2FGMM_UM) + { + if((pixel(0)<muR-km*var)|| (pixel(0)>muR+km*var)) + HR=2*km*dR/var; + else + HR=dR*dR/(2*var*var)+km*dR/var+km*km/2; + + if((pixel(1)<muG-km*var)|| (pixel(1)>muG+km*var)) + HG=2*km*dG/var; + else + HG=dG*dG/(2*var*var)+km*dG/var+km*km/2; + + if((pixel(2)<muB-km*var)|| (pixel(2)>muB+km*var)) + HB=2*km*dB/var; + else + HB=dB*dB/(2*var*var)+km*dB/var+km*km/2; + } + + // T2FGMM-UV + if(m_params.Type() == TYPE_T2FGMM_UV) + { + HR = (1/(kv*kv)-kv*kv) * (pixel(0)-muR) * (pixel(0)-muR)/(2*var); + HG = (1/(kv*kv)-kv*kv) * (pixel(1)-muG) * (pixel(1)-muG)/(2*var); + HB = (1/(kv*kv)-kv*kv) * (pixel(2)-muB) * (pixel(2)-muB)/(2*var); + } + + // calculate the squared distance + float dist = (HR*HR + HG*HG + HB*HB); + + if(dist < m_params.HighThreshold()*var && iModes < backgroundGaussians) + bBackgroundHigh = true; + + // a match occurs when the pixel is within sqrt(fTg) standard deviations of the distribution + if(dist < m_params.LowThreshold()*var) + { + bFitsPDF = true; + + // check if this Gaussian is part of the background model + if(iModes < backgroundGaussians) + bBackgroundLow = true; + + //update distribution + float k = m_params.Alpha() / weight; + weight = fOneMinAlpha*weight + m_params.Alpha(); + m_modes[pos].weight = weight; + m_modes[pos].muR = muR - k*(dR); + m_modes[pos].muG = muG - k*(dG); + m_modes[pos].muB = muB - k*(dB); + + //limit the variance + float sigmanew = var + k*(dist-var); + m_modes[pos].variance = sigmanew < 4 ? 4 : sigmanew > 5*m_variance ? 5*m_variance : sigmanew; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + else + { + weight = fOneMinAlpha*weight; + if(weight < 0.0) + { + weight=0.0; + numModes--; + } + + m_modes[pos].weight = weight; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + } + else + { + weight = fOneMinAlpha*weight; + if(weight < 0.0) + { + weight=0.0; + numModes--; + } + m_modes[pos].weight = weight; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + + totalWeight += weight; + } + + // renormalize weights so they add to one + double invTotalWeight = 1.0 / totalWeight; + for(int iLocal = 0; iLocal < numModes; iLocal++) + { + m_modes[posPixel + iLocal].weight *= (float)invTotalWeight; + m_modes[posPixel + iLocal].significants = m_modes[posPixel + iLocal].weight + / sqrt(m_modes[posPixel + iLocal].variance); + } + + // Sort significance values so they are in desending order. + qsort(&m_modes[posPixel], numModes, sizeof(GMM), compareT2FGMM); + + // make new mode if needed and exit + if(!bFitsPDF) + { + if(numModes < m_params.MaxModes()) + numModes++; + //else + // the weakest mode will be replaced + + pos = posPixel + numModes-1; + + m_modes[pos].muR = pixel.ch[0]; + m_modes[pos].muG = pixel.ch[1]; + m_modes[pos].muB = pixel.ch[2]; + m_modes[pos].variance = m_variance; + m_modes[pos].significants = 0; // will be set below + + if (numModes == 1) + m_modes[pos].weight = 1; + else + m_modes[pos].weight = m_params.Alpha(); + + //renormalize weights + int iLocal; + float sum = 0.0; + for(iLocal = 0; iLocal < numModes; iLocal++) + sum += m_modes[posPixel+ iLocal].weight; + + double invSum = 1.0/sum; + for(iLocal = 0; iLocal < numModes; iLocal++) + { + m_modes[posPixel + iLocal].weight *= (float)invSum; + m_modes[posPixel + iLocal].significants = m_modes[posPixel + iLocal].weight / sqrt(m_modes[posPixel + iLocal].variance); + } + } + + // Sort significance values so they are in desending order. + qsort(&(m_modes[posPixel]), numModes, sizeof(GMM), compareT2FGMM); + + if(bBackgroundLow) + low_threshold = BACKGROUND; + else + low_threshold = FOREGROUND; + + if(bBackgroundHigh) + high_threshold = BACKGROUND; + else + high_threshold = FOREGROUND; +} + +/////////////////////////////////////////////////////////////////////////////// +//Input: +// data - a pointer to the data of a RGB image of the same size +//Output: +// output - a pointer to the data of a gray value image of the same size +// (the memory should already be reserved) +// values: 255-foreground, 125-shadow, 0-background +/////////////////////////////////////////////////////////////////////////////// +void T2FGMM::Subtract(int frame_num, const RgbImage& data, BwImage& low_threshold_mask, BwImage& high_threshold_mask) +{ + unsigned char low_threshold, high_threshold; + long posPixel; + + // update each pixel of the image + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + // update model + background subtract + posPixel = (r*m_params.Width() + c) * m_params.MaxModes(); + + SubtractPixel(posPixel, data(r,c), m_modes_per_pixel(r,c), low_threshold, high_threshold); + + low_threshold_mask(r,c) = low_threshold; + high_threshold_mask(r,c) = high_threshold; + + m_background(r,c,0) = (unsigned char) m_modes[posPixel].muR; + m_background(r,c,1) = (unsigned char) m_modes[posPixel].muG; + m_background(r,c,2) = (unsigned char) m_modes[posPixel].muB; + } + } +} diff --git a/package_bgs/tb/T2FGMM.h b/package_bgs/tb/T2FGMM.h new file mode 100644 index 0000000000000000000000000000000000000000..a698e5a821b454c0e9d8def8aea344037cbf88dd --- /dev/null +++ b/package_bgs/tb/T2FGMM.h @@ -0,0 +1,135 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* T2FGMM.h +* +* Purpose: Implementation of the T2 Fuzzy Gaussian Mixture Models (T2GMMs) +* "Modeling of Dynamic Backgrounds by Type-2 Fuzzy Gaussians Mixture Models" +* Author: Fida El Baf, Thierry Bouwmans, September 2008 +* +* This code is based on code by Z. Zivkovic's written for his enhanced GMM +* background subtraction algorithm: +* +* Zivkovic's code can be obtained at: www.zoranz.net +******************************************************************************/ + +#ifndef T2F_GMM_ +#define T2F_GMM_ + +#include "../dp/Bgs.h" +#include "../dp/GrimsonGMM.h" + +namespace Algorithms +{ + namespace BackgroundSubtraction + { + const int TYPE_T2FGMM_UM = 0; + const int TYPE_T2FGMM_UV = 1; + + // --- User adjustable parameters used by the T2F GMM BGS algorithm --- + class T2FGMMParams : public BgsParams + { + public: + float &LowThreshold() { return m_low_threshold; } + float &HighThreshold() { return m_high_threshold; } + + float &Alpha() { return m_alpha; } + int &MaxModes() { return m_max_modes; } + int &Type() { return m_type; } + float &KM() { return m_km; } + float &KV() { return m_kv; } + + private: + // Threshold on the squared dist. to decide when a sample is close to an existing + // components. If it is not close to any a new component will be generated. + // Smaller threshold values lead to more generated components and higher threshold values + // lead to a small number of components but they can grow too large. + // + // It is usual easiest to think of these thresholds as being the number of variances away + // from the mean of a pixel before it is considered to be from the foreground. + float m_low_threshold; + float m_high_threshold; + + // alpha - speed of update - if the time interval you want to average over is T + // set alpha=1/T. + float m_alpha; + + // Maximum number of modes (Gaussian components) that will be used per pixel + int m_max_modes; + + // T2FGMM_UM / T2FGMM_UV + int m_type; + + // Factor control for the T2FGMM-UM + float m_km; + + // Factor control for the T2FGMM-UV + float m_kv; + }; + + // --- T2FGMM BGS algorithm --- + class T2FGMM : public Bgs + { + public: + T2FGMM(); + ~T2FGMM(); + + void Initalize(const BgsParams& param); + + void InitModel(const RgbImage& data); + void Subtract(int frame_num, const RgbImage& data, BwImage& low_threshold_mask, BwImage& high_threshold_mask); + void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); + + RgbImage* Background(); + + private: + void SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char& numModes, unsigned char& lowThreshold, unsigned char& highThreshold); + + // User adjustable parameters + T2FGMMParams m_params; + + // Threshold when the component becomes significant enough to be included into + // the background model. It is the TB = 1-cf from the paper. So I use cf=0.1 => TB=0.9 + // For alpha=0.001 it means that the mode should exist for approximately 105 frames before + // it is considered foreground + float m_bg_threshold; //1-cf from the paper + + // Initial variance for the newly generated components. + // It will will influence the speed of adaptation. A good guess should be made. + // A simple way is to estimate the typical standard deviation from the images. + float m_variance; + + // Dynamic array for the mixture of Gaussians + GMM* m_modes; + + // Number of Gaussian components per pixel + BwImage m_modes_per_pixel; + + // Current background model + RgbImage m_background; + + // Factor control for the T2FGMM-UM + float km; + + // Factor control for the T2FGMM-UV + float kv; + }; + }; +}; + +#endif diff --git a/package_bgs/tb/T2FGMM_UM.cpp b/package_bgs/tb/T2FGMM_UM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aaf280f3cac0f78661cceb6479162b4af0643fb3 --- /dev/null +++ b/package_bgs/tb/T2FGMM_UM.cpp @@ -0,0 +1,111 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "T2FGMM_UM.h" + +T2FGMM_UM::T2FGMM_UM() : firstTime(true), frameNumber(0), showOutput(true), threshold(9.0), alpha(0.01), gaussians(3), km(1.5), kv(0.6) +{ + std::cout << "T2FGMM_UM()" << std::endl; +} + +T2FGMM_UM::~T2FGMM_UM() +{ + std::cout << "~T2FGMM_UM()" << std::endl; +} + +void T2FGMM_UM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + frame = new IplImage(img_input); + + if(firstTime) + frame_data.ReleaseMemory(false); + frame_data = frame; + + if(firstTime) + { + int width = img_input.size().width; + int height = img_input.size().height; + + lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + params.SetFrameSize(width, height); + params.LowThreshold() = threshold; + params.HighThreshold() = 2*params.LowThreshold(); + params.Alpha() = alpha; + params.MaxModes() = gaussians; + params.Type() = TYPE_T2FGMM_UM; + params.KM() = km; // Factor control for the T2FGMM-UM [0,3] default: 1.5 + params.KV() = kv; // Factor control for the T2FGMM-UV [0.3,1] default: 0.6 + + bgs.Initalize(params); + bgs.InitModel(frame_data); + } + + bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask); + lowThresholdMask.Clear(); + bgs.Update(frameNumber, frame_data, lowThresholdMask); + + cv::Mat foreground(highThresholdMask.Ptr()); + + if(showOutput) + cv::imshow("T2FGMM-UM", foreground); + + foreground.copyTo(img_output); + + delete frame; + firstTime = false; + frameNumber++; +} + +void T2FGMM_UM::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/T2FGMM_UM.xml", 0, CV_STORAGE_WRITE); + + cvWriteReal(fs, "threshold", threshold); + cvWriteReal(fs, "alpha", alpha); + cvWriteReal(fs, "km", km); + cvWriteReal(fs, "kv", kv); + cvWriteInt(fs, "gaussians", gaussians); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void T2FGMM_UM::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/T2FGMM_UM.xml", 0, CV_STORAGE_READ); + + threshold = cvReadRealByName(fs, 0, "threshold", 9.0); + alpha = cvReadRealByName(fs, 0, "alpha", 0.01); + km = cvReadRealByName(fs, 0, "km", 1.5); + kv = cvReadRealByName(fs, 0, "kv", 0.6); + gaussians = cvReadIntByName(fs, 0, "gaussians", 3); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/tb/T2FGMM_UM.h b/package_bgs/tb/T2FGMM_UM.h new file mode 100644 index 0000000000000000000000000000000000000000..17e06c58877333d06f3bc5bc779641819eea7aa2 --- /dev/null +++ b/package_bgs/tb/T2FGMM_UM.h @@ -0,0 +1,58 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "T2FGMM.h" + +using namespace Algorithms::BackgroundSubtraction; + +class T2FGMM_UM : public IBGS +{ +private: + bool firstTime; + long frameNumber; + IplImage* frame; + RgbImage frame_data; + + T2FGMMParams params; + T2FGMM bgs; + BwImage lowThresholdMask; + BwImage highThresholdMask; + + double threshold; + double alpha; + float km; + float kv; + int gaussians; + bool showOutput; + +public: + T2FGMM_UM(); + ~T2FGMM_UM(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/tb/T2FGMM_UV.cpp b/package_bgs/tb/T2FGMM_UV.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6094fe30d9dce9d74672bf44b4d80384e56b1bfd --- /dev/null +++ b/package_bgs/tb/T2FGMM_UV.cpp @@ -0,0 +1,111 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "T2FGMM_UV.h" + +T2FGMM_UV::T2FGMM_UV() : firstTime(true), frameNumber(0), showOutput(true), threshold(9.0), alpha(0.01), gaussians(3), km(1.5), kv(0.6) +{ + std::cout << "T2FGMM_UV()" << std::endl; +} + +T2FGMM_UV::~T2FGMM_UV() +{ + std::cout << "~T2FGMM_UV()" << std::endl; +} + +void T2FGMM_UV::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + frame = new IplImage(img_input); + + if(firstTime) + frame_data.ReleaseMemory(false); + frame_data = frame; + + if(firstTime) + { + int width = img_input.size().width; + int height = img_input.size().height; + + lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + params.SetFrameSize(width, height); + params.LowThreshold() = threshold; + params.HighThreshold() = 2*params.LowThreshold(); + params.Alpha() = alpha; + params.MaxModes() = gaussians; + params.Type() = TYPE_T2FGMM_UV; + params.KM() = km; // Factor control for the T2FGMM-UM [0,3] default: 1.5 + params.KV() = kv; // Factor control for the T2FGMM-UV [0.3,1] default: 0.6 + + bgs.Initalize(params); + bgs.InitModel(frame_data); + } + + bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask); + lowThresholdMask.Clear(); + bgs.Update(frameNumber, frame_data, lowThresholdMask); + + cv::Mat foreground(highThresholdMask.Ptr()); + + if(showOutput) + cv::imshow("T2FGMM-UV", foreground); + + foreground.copyTo(img_output); + + delete frame; + firstTime = false; + frameNumber++; +} + +void T2FGMM_UV::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/T2FGMM_UV.xml", 0, CV_STORAGE_WRITE); + + cvWriteReal(fs, "threshold", threshold); + cvWriteReal(fs, "alpha", alpha); + cvWriteReal(fs, "km", km); + cvWriteReal(fs, "kv", kv); + cvWriteInt(fs, "gaussians", gaussians); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void T2FGMM_UV::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/T2FGMM_UV.xml", 0, CV_STORAGE_READ); + + threshold = cvReadRealByName(fs, 0, "threshold", 9.0); + alpha = cvReadRealByName(fs, 0, "alpha", 0.01); + km = cvReadRealByName(fs, 0, "km", 1.5); + kv = cvReadRealByName(fs, 0, "kv", 0.6); + gaussians = cvReadIntByName(fs, 0, "gaussians", 3); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} \ No newline at end of file diff --git a/package_bgs/tb/T2FGMM_UV.h b/package_bgs/tb/T2FGMM_UV.h new file mode 100644 index 0000000000000000000000000000000000000000..23c0bf1a4bfbbe88913d4808aaabdbdfc742f4b8 --- /dev/null +++ b/package_bgs/tb/T2FGMM_UV.h @@ -0,0 +1,58 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "T2FGMM.h" + +using namespace Algorithms::BackgroundSubtraction; + +class T2FGMM_UV : public IBGS +{ +private: + bool firstTime; + long frameNumber; + IplImage* frame; + RgbImage frame_data; + + T2FGMMParams params; + T2FGMM bgs; + BwImage lowThresholdMask; + BwImage highThresholdMask; + + double threshold; + double alpha; + float km; + float kv; + int gaussians; + bool showOutput; + +public: + T2FGMM_UV(); + ~T2FGMM_UV(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; + diff --git a/package_bgs/tb/T2FMRF.cpp b/package_bgs/tb/T2FMRF.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d486bb79c8f2b73ef40339308f5db007ae3973c --- /dev/null +++ b/package_bgs/tb/T2FMRF.cpp @@ -0,0 +1,432 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* T2FMRF.cpp +* +* Purpose: Implementation of the T2 Fuzzy Gaussian Mixture Models (T2GMMs) +* "Modeling of Dynamic Backgrounds by Type-2 Fuzzy Gaussians Mixture Models" +* Author: Fida El Baf, Thierry Bouwmans, September 2008 +* +* This code is based on code by Z. Zivkovic's written for his enhanced GMM +* background subtraction algorithm: +* +* Zivkovic's code can be obtained at: www.zoranz.net +******************************************************************************/ + +#include "T2FMRF.h" + +using namespace Algorithms::BackgroundSubtraction; + +int compareT2FMRF(const void* _gmm1, const void* _gmm2) +{ + GMM gmm1 = *(GMM*)_gmm1; + GMM gmm2 = *(GMM*)_gmm2; + + if(gmm1.significants < gmm2.significants) + return 1; + else if(gmm1.significants == gmm2.significants) + return 0; + else + return -1; +} + +GMM* T2FMRF::gmm() +{ + return m_modes; +} +HMM* T2FMRF::hmm() +{ + return m_state; +} + +T2FMRF::T2FMRF() +{ + m_modes = NULL; +} + +T2FMRF::~T2FMRF() +{ + if(m_modes != NULL) + delete[] m_modes; +} + +void T2FMRF::Initalize(const BgsParams& param) +{ + m_params = (T2FMRFParams&) param; + + // Tbf - the threshold + m_bg_threshold = 0.75f; // 1-cf from the paper + + // Tgenerate - the threshold + m_variance = 36.0f; // sigma for the new mode + + // GMM for each pixel + m_modes = new GMM[m_params.Size()*m_params.MaxModes()]; + + //HMM for each pixel + m_state = new HMM[m_params.Size()]; + + // used modes per pixel + m_modes_per_pixel = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1); + + m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); + + // Factor control for the T2FGMM-UM [0,3] + // km = (float) 2; //1.5; + km = (float) m_params.KM(); + + // Factor control for the T2FGMM-UV [0.3,1] + // kv = (float) 0.9; //0.6; + kv = (float) m_params.KV(); +} + +RgbImage* T2FMRF::Background() +{ + return &m_background; +} + +void T2FMRF::InitModel(const RgbImage& data) +{ + m_modes_per_pixel.Clear(); + + for(unsigned int i = 0; i < m_params.Size()*m_params.MaxModes(); ++i) + { + m_modes[i].weight = 0; + m_modes[i].variance = 0; + m_modes[i].muR = 0; + m_modes[i].muG = 0; + m_modes[i].muB = 0; + m_modes[i].significants = 0; + } + + for (unsigned int j = 0; j < m_params.Size(); ++j) + { + m_state[j].State = background; + m_state[j].Ab2b = 0.7; + m_state[j].Ab2f = 0.3; + m_state[j].Af2b = 0.4; + m_state[j].Af2f = 0.6; + m_state[j].T = 0.7; + } +} + +void T2FMRF::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) +{ + // it doesn't make sense to have conditional updates in the GMM framework +} + +void T2FMRF::SubtractPixel(long posPixel, long posGMode, const RgbPixel& pixel, unsigned char& numModes, + unsigned char& low_threshold, unsigned char& high_threshold) +{ + // calculate distances to the modes (+ sort???) + // here we need to go in descending order!!! + long pos; + bool bFitsPDF = false; + bool bBackgroundLow = false; + bool bBackgroundHigh = false; + + HiddenState CurrentState = m_state[posPixel].State; + float Ab2b = m_state[posPixel].Ab2b; + float Ab2f = m_state[posPixel].Ab2f; + float Af2b = m_state[posPixel].Af2b; + float Af2f = m_state[posPixel].Af2f; + float T = m_state[posPixel].T; + + float fOneMinAlpha = 1 - m_params.Alpha(); + float totalWeight = 0.0f; + + // calculate number of Gaussians to include in the background model + int backgroundGaussians = 0; + double sum = 0.0; + for(int i = 0; i < numModes; ++i) + { + if(sum < m_bg_threshold) + { + backgroundGaussians++; + sum += m_modes[posGMode+i].weight; + } + else + break; + } + + // update all distributions and check for match with current pixel + for (int iModes = 0; iModes < numModes; iModes++) + { + pos = posGMode + iModes; + float weight = m_modes[pos].weight; + + // fit not found yet + if (!bFitsPDF) + { + //check if it belongs to some of the modes + //calculate distance + float var = m_modes[pos].variance; + float muR = m_modes[pos].muR; + float muG = m_modes[pos].muG; + float muB = m_modes[pos].muB; + + //float km = 2; + //float kv = 0.9; + + float dR = fabs(muR - pixel(0)); + float dG = fabs(muG - pixel(1)); + float dB = fabs(muB - pixel(2)); + + // calculate the squared distance + float HR; + float HG; + float HB; + + // T2FMRF-UM + if(m_params.Type() == TYPE_T2FMRF_UM) + { + if((pixel(0) < muR-km*var) || (pixel(0) > muR+km*var)) + HR = 2*km*dR/var; + else + HR = dR*dR/(2*var*var)+km*dR/var+km*km/2; + + if((pixel(1) < muG-km*var) || (pixel(1) > muG+km*var)) + HG = 2*km*dG/var; + else + HG = dG*dG/(2*var*var)+km*dG/var+km*km/2; + + if((pixel(2) < muB-km*var) || (pixel(2) > muB+km*var)) + HB = 2*km*dB/var; + else + HB = dB*dB/(2*var*var)+km*dB/var+km*km/2; + } + + // T2FGMM-UV + if(m_params.Type() == TYPE_T2FMRF_UV) + { + HR = (1/(kv*kv)-kv*kv) * (pixel(0)-muR) * (pixel(0)-muR)/(2*var); + HG = (1/(kv*kv)-kv*kv) * (pixel(1)-muG) * (pixel(1)-muG)/(2*var); + HB = (1/(kv*kv)-kv*kv) * (pixel(2)-muB) * (pixel(2)-muB)/(2*var); + } + + float ro; + if (CurrentState == background) + { + if (Ab2b!=0) ro = (Ab2f/Ab2b); + else ro = 10; + } + else + { + if(Af2b!=0) ro = (Af2f/Af2b); + else ro = 10; + } + + // calculate the squared distance + float dist = (HR*HR + HG*HG + HB*HB); + + if(dist < m_params.HighThreshold()*var && iModes < backgroundGaussians) + bBackgroundHigh = true; + + // a match occurs when the pixel is within sqrt(fTg) standard deviations of the distribution + if(dist < m_params.LowThreshold()*var) + { + bFitsPDF = true; + + // check if this Gaussian is part of the background model + if(iModes < backgroundGaussians) + bBackgroundLow = true; + + //update distribution + float k = m_params.Alpha() / weight; + weight = fOneMinAlpha*weight + m_params.Alpha(); + m_modes[pos].weight = weight; + m_modes[pos].muR = muR - k*(dR); + m_modes[pos].muG = muG - k*(dG); + m_modes[pos].muB = muB - k*(dB); + + //limit the variance + float sigmanew = var + k*(dist-var); + m_modes[pos].variance = sigmanew < 4 ? 4 : sigmanew > 5*m_variance ? 5*m_variance : sigmanew; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + else + { + weight = fOneMinAlpha*weight; + if (weight < 0.0) + { + weight=0.0; + numModes--; + } + + m_modes[pos].weight = weight; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + } + else + { + weight = fOneMinAlpha*weight; + if (weight < 0.0) + { + weight=0.0; + numModes--; + } + m_modes[pos].weight = weight; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + + totalWeight += weight; + } + + // renormalize weights so they add to one + double invTotalWeight = 1.0 / totalWeight; + for (int iLocal = 0; iLocal < numModes; iLocal++) + { + m_modes[posGMode + iLocal].weight *= (float)invTotalWeight; + m_modes[posGMode + iLocal].significants = m_modes[posGMode + iLocal].weight / sqrt(m_modes[posGMode + iLocal].variance); + } + + // Sort significance values so they are in desending order. + qsort(&m_modes[posGMode], numModes, sizeof(GMM), compareT2FMRF); + + // make new mode if needed and exit + if(!bFitsPDF) + { + if(numModes < m_params.MaxModes()) + numModes++; + //else + // the weakest mode will be replaced + + pos = posGMode + numModes-1; + + m_modes[pos].muR = pixel.ch[0]; + m_modes[pos].muG = pixel.ch[1]; + m_modes[pos].muB = pixel.ch[2]; + m_modes[pos].variance = m_variance; + m_modes[pos].significants = 0; // will be set below + + if(numModes == 1) + m_modes[pos].weight = 1; + else + m_modes[pos].weight = m_params.Alpha(); + + //renormalize weights + int iLocal; + float sum = 0.0; + for(iLocal = 0; iLocal < numModes; iLocal++) + sum += m_modes[posGMode+ iLocal].weight; + + double invSum = 1.0/sum; + for(iLocal = 0; iLocal < numModes; iLocal++) + { + m_modes[posGMode + iLocal].weight *= (float)invSum; + m_modes[posGMode + iLocal].significants = m_modes[posPixel + iLocal].weight / sqrt(m_modes[posGMode + iLocal].variance); + } + } + + // Sort significance values so they are in desending order. + qsort(&(m_modes[posGMode]), numModes, sizeof(GMM), compareT2FMRF); + + if(bBackgroundLow) + { + low_threshold = BACKGROUND; + m_state[posPixel].State = background; + + if(CurrentState == background) + { + float b2b = fOneMinAlpha*Ab2b + m_params.Alpha(); + float b2f = fOneMinAlpha*Ab2f; + + float b = b2b + b2f; + m_state[posPixel].Ab2b = b2b/b; + m_state[posPixel].Ab2f = b2f/b; + m_state[posPixel].T = m_state[posPixel].Ab2b; + } + else + { + float f2b = fOneMinAlpha*Af2b + m_params.Alpha(); + float f2f = fOneMinAlpha*Af2f; + + float f = f2b + f2f; + m_state[posPixel].Af2b = f2b/f; + m_state[posPixel].Af2f = f2f/f; + m_state[posPixel].T = m_state[posPixel].Af2b; + } + } + else + { + low_threshold = FOREGROUND; + m_state[posPixel].State = foreground; + + if(CurrentState == background) + { + float b2b = fOneMinAlpha*Ab2b; + float b2f = fOneMinAlpha*Ab2f + m_params.Alpha(); + + float b = b2b + b2f; + m_state[posPixel].Ab2b = b2b/b; + m_state[posPixel].Ab2f = b2f/b; + m_state[posPixel].T = m_state[posPixel].Ab2b; + } + else + { + float f2b = fOneMinAlpha*Af2b; + float f2f = fOneMinAlpha*Af2f + m_params.Alpha(); + + float f = f2b + f2f; + m_state[posPixel].Af2b = f2b/f; + m_state[posPixel].Af2f = f2f/f; + m_state[posPixel].T = m_state[posPixel].Af2b; + } + } + + if(bBackgroundHigh) + high_threshold = BACKGROUND; + else + high_threshold = FOREGROUND; +} + +/////////////////////////////////////////////////////////////////////////////// +//Input: +// data - a pointer to the data of a RGB image of the same size +//Output: +// output - a pointer to the data of a gray value image of the same size +// (the memory should already be reserved) +// values: 255-foreground, 125-shadow, 0-background +/////////////////////////////////////////////////////////////////////////////// +void T2FMRF::Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask) +{ + unsigned char low_threshold, high_threshold; + long posPixel; + long posGMode; + + // update each pixel of the image + for(unsigned int r = 0; r < m_params.Height(); ++r) + { + for(unsigned int c = 0; c < m_params.Width(); ++c) + { + // update model + background subtract + posPixel = r*m_params.Width() + c; + posGMode = (r*m_params.Width() + c) * m_params.MaxModes(); + + SubtractPixel(posPixel, posGMode, data(r,c), m_modes_per_pixel(r,c), low_threshold, high_threshold); + + low_threshold_mask(r,c) = low_threshold; + high_threshold_mask(r,c) = high_threshold; + + m_background(r,c,0) = (unsigned char) m_modes[posGMode].muR; + m_background(r,c,1) = (unsigned char) m_modes[posGMode].muG; + m_background(r,c,2) = (unsigned char) m_modes[posGMode].muB; + } + } +} diff --git a/package_bgs/tb/T2FMRF.h b/package_bgs/tb/T2FMRF.h new file mode 100644 index 0000000000000000000000000000000000000000..e6fb4495638622431c2345a44ce0fd6bc485a595 --- /dev/null +++ b/package_bgs/tb/T2FMRF.h @@ -0,0 +1,164 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +/**************************************************************************** +* +* T2FMRF.h +* +* Purpose: Implementation of the T2 Fuzzy Gaussian Mixture Models (T2GMMs) +* "Modeling of Dynamic Backgrounds by Type-2 Fuzzy Gaussians Mixture Models" +* Author: Fida El Baf, Thierry Bouwmans, September 2008 +* +* This code is based on code by Z. Zivkovic's written for his enhanced GMM +* background subtraction algorithm: +* +* Zivkovic's code can be obtained at: www.zoranz.net +******************************************************************************/ + +#ifndef T2F_MRF_ +#define T2F_MRF_ + +#include "../dp/Bgs.h" +#include "../dp/GrimsonGMM.h" + +namespace Algorithms +{ + namespace BackgroundSubtraction + { + const int TYPE_T2FMRF_UM = 0; + const int TYPE_T2FMRF_UV = 1; + + enum HiddenState {background, foreground}; + + typedef struct HMMState + { + float T; + //Hidden State + HiddenState State; + //transition probability + float Ab2b; + float Ab2f; + float Af2f; + float Af2b; + } HMM; + + //typedef struct GMMGaussian + //{ + // float variance; + // float muR; + // float muG; + // float muB; + // float weight; + // float significants; // this is equal to weight / standard deviation and is used to + // // determine which Gaussians should be part of the background model + //} GMM; + + // --- User adjustable parameters used by the T2F GMM BGS algorithm --- + class T2FMRFParams : public BgsParams + { + public: + float &LowThreshold() { return m_low_threshold; } + float &HighThreshold() { return m_high_threshold; } + + float &Alpha() { return m_alpha; } + int &MaxModes() { return m_max_modes; } + int &Type() { return m_type; } + float &KM() { return m_km; } + float &KV() { return m_kv; } + + private: + // Threshold on the squared dist. to decide when a sample is close to an existing + // components. If it is not close to any a new component will be generated. + // Smaller threshold values lead to more generated components and higher threshold values + // lead to a small number of components but they can grow too large. + // + // It is usual easiest to think of these thresholds as being the number of variances away + // from the mean of a pixel before it is considered to be from the foreground. + float m_low_threshold; + float m_high_threshold; + + // alpha - speed of update - if the time interval you want to average over is T + // set alpha=1/T. + float m_alpha; + + // Maximum number of modes (Gaussian components) that will be used per pixel + int m_max_modes; + + // T2FMRF_UM / T2FMRF_UV + int m_type; + + // Factor control for the T2FMRF-UM + float m_km; + + // Factor control for the T2FMRF-UV + float m_kv; + }; + + // --- T2FGMM BGS algorithm --- + class T2FMRF : public Bgs + { + public: + T2FMRF(); + ~T2FMRF(); + + void Initalize(const BgsParams& param); + void InitModel(const RgbImage& data); + void Subtract(int frame_num, const RgbImage& data, BwImage& low_threshold_mask, BwImage& high_threshold_mask); + void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); + + RgbImage* Background(); + + GMM *gmm(void); + HMM *hmm(void); + + private: + void SubtractPixel(long posPixel, long posGMode, const RgbPixel& pixel, unsigned char& numModes, unsigned char& lowThreshold, unsigned char& highThreshold); + + // User adjustable parameters + T2FMRFParams m_params; + + // Threshold when the component becomes significant enough to be included into + // the background model. It is the TB = 1-cf from the paper. So I use cf=0.1 => TB=0.9 + // For alpha=0.001 it means that the mode should exist for approximately 105 frames before + // it is considered foreground + float m_bg_threshold; //1-cf from the paper + + // Initial variance for the newly generated components. + // It will will influence the speed of adaptation. A good guess should be made. + // A simple way is to estimate the typical standard deviation from the images. + float m_variance; + + // Dynamic array for the mixture of Gaussians + GMM* m_modes; + + //Dynamic array for the hidden state + HMM* m_state; + + // Number of Gaussian components per pixel + BwImage m_modes_per_pixel; + + // Current background model + RgbImage m_background; + + // Factor control for the T2FGMM-UM + float km; + // Factor control for the T2FGMM-UV + float kv; + }; + }; +}; + +#endif diff --git a/package_bgs/tb/T2FMRF_UM.cpp b/package_bgs/tb/T2FMRF_UM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..768db3931902d908664045c30ea46ff86838e504 --- /dev/null +++ b/package_bgs/tb/T2FMRF_UM.cpp @@ -0,0 +1,140 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "T2FMRF_UM.h" + +T2FMRF_UM::T2FMRF_UM() : firstTime(true), frameNumber(0), showOutput(true), threshold(9.0), alpha(0.01), +gaussians(3), km(2), kv(0.9) +{ + std::cout << "T2FMRF_UM()" << std::endl; +} + +T2FMRF_UM::~T2FMRF_UM() +{ + std::cout << "~T2FMRF_UM()" << std::endl; +} + +void T2FMRF_UM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + frame = new IplImage(img_input); + + if(firstTime) + frame_data.ReleaseMemory(false); + frame_data = frame; + + if(firstTime) + { + int width = img_input.size().width; + int height = img_input.size().height; + + lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + params.SetFrameSize(width, height); + params.LowThreshold() = threshold; + params.HighThreshold() = 2*params.LowThreshold(); + params.Alpha() = alpha; + params.MaxModes() = gaussians; + params.Type() = TYPE_T2FMRF_UM; + params.KM() = km; // Factor control for the T2FMRF-UM [0,3] default: 2 + params.KV() = kv; // Factor control for the T2FMRF-UV [0.3,1] default: 0.9 + + bgs.Initalize(params); + bgs.InitModel(frame_data); + + old_labeling = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + old = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + + mrf.height = height; + mrf.width = width; + mrf.Build_Classes_OldLabeling_InImage_LocalEnergy(); + + firstTime = false; + } + + bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask); + cvCopyImage(lowThresholdMask.Ptr(), old); + + /************************************************************************/ + /* the code for MRF, it can be noted when using other methods */ + /************************************************************************/ + //the optimization process is done when the foreground detection is stable, + if(frameNumber >= 10) + { + gmm = bgs.gmm(); + hmm = bgs.hmm(); + mrf.background2 = frame_data.Ptr(); + mrf.in_image = lowThresholdMask.Ptr(); + mrf.out_image = lowThresholdMask.Ptr(); + mrf.InitEvidence2(gmm,hmm,old_labeling); + mrf.ICM2(); + cvCopyImage(mrf.out_image, lowThresholdMask.Ptr()); + } + + cvCopyImage(old, old_labeling); + + lowThresholdMask.Clear(); + bgs.Update(frameNumber, frame_data, lowThresholdMask); + + cv::Mat foreground(highThresholdMask.Ptr()); + + if(showOutput) + cv::imshow("T2FMRF-UM", foreground); + + foreground.copyTo(img_output); + + delete frame; + frameNumber++; +} + +void T2FMRF_UM::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/T2FMRF_UM.xml", 0, CV_STORAGE_WRITE); + + cvWriteReal(fs, "threshold", threshold); + cvWriteReal(fs, "alpha", alpha); + cvWriteReal(fs, "km", km); + cvWriteReal(fs, "kv", kv); + cvWriteInt(fs, "gaussians", gaussians); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void T2FMRF_UM::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/T2FMRF_UM.xml", 0, CV_STORAGE_READ); + + threshold = cvReadRealByName(fs, 0, "threshold", 9.0); + alpha = cvReadRealByName(fs, 0, "alpha", 0.01); + km = cvReadRealByName(fs, 0, "km", 2); + kv = cvReadRealByName(fs, 0, "kv", 0.9); + gaussians = cvReadIntByName(fs, 0, "gaussians", 3); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/tb/T2FMRF_UM.h b/package_bgs/tb/T2FMRF_UM.h new file mode 100644 index 0000000000000000000000000000000000000000..6066834957460f6a787a1672f68332eee010d06e --- /dev/null +++ b/package_bgs/tb/T2FMRF_UM.h @@ -0,0 +1,64 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "MRF.h" + +using namespace Algorithms::BackgroundSubtraction; + +class T2FMRF_UM : public IBGS +{ +private: + bool firstTime; + long frameNumber; + IplImage *frame; + RgbImage frame_data; + + IplImage *old_labeling; + IplImage *old; + + T2FMRFParams params; + T2FMRF bgs; + BwImage lowThresholdMask; + BwImage highThresholdMask; + + double threshold; + double alpha; + float km; + float kv; + int gaussians; + bool showOutput; + + MRF_TC mrf; + GMM *gmm; + HMM *hmm; + +public: + T2FMRF_UM(); + ~T2FMRF_UM(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; diff --git a/package_bgs/tb/T2FMRF_UV.cpp b/package_bgs/tb/T2FMRF_UV.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b6fcc012e719097fed353638862a034d7ccdb9c --- /dev/null +++ b/package_bgs/tb/T2FMRF_UV.cpp @@ -0,0 +1,140 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "T2FMRF_UV.h" + +T2FMRF_UV::T2FMRF_UV() : firstTime(true), frameNumber(0), showOutput(true), threshold(9.0), alpha(0.01), +gaussians(3), km(2), kv(0.9) +{ + std::cout << "T2FMRF_UV()" << std::endl; +} + +T2FMRF_UV::~T2FMRF_UV() +{ + std::cout << "~T2FMRF_UV()" << std::endl; +} + +void T2FMRF_UV::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) +{ + if(img_input.empty()) + return; + + loadConfig(); + + if(firstTime) + saveConfig(); + + frame = new IplImage(img_input); + + if(firstTime) + frame_data.ReleaseMemory(false); + frame_data = frame; + + if(firstTime) + { + int width = img_input.size().width; + int height = img_input.size().height; + + lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL; + + params.SetFrameSize(width, height); + params.LowThreshold() = threshold; + params.HighThreshold() = 2*params.LowThreshold(); + params.Alpha() = alpha; + params.MaxModes() = gaussians; + params.Type() = TYPE_T2FMRF_UV; + params.KM() = km; // Factor control for the T2FMRF-UM [0,3] default: 2 + params.KV() = kv; // Factor control for the T2FMRF-UV [0.3,1] default: 0.9 + + bgs.Initalize(params); + bgs.InitModel(frame_data); + + old_labeling = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + old = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); + + mrf.height = height; + mrf.width = width; + mrf.Build_Classes_OldLabeling_InImage_LocalEnergy(); + + firstTime = false; + } + + bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask); + cvCopyImage(lowThresholdMask.Ptr(), old); + + /************************************************************************/ + /* the code for MRF, it can be noted when using other methods */ + /************************************************************************/ + //the optimization process is done when the foreground detection is stable, + if(frameNumber >= 10) + { + gmm = bgs.gmm(); + hmm = bgs.hmm(); + mrf.background2 = frame_data.Ptr(); + mrf.in_image = lowThresholdMask.Ptr(); + mrf.out_image = lowThresholdMask.Ptr(); + mrf.InitEvidence2(gmm,hmm,old_labeling); + mrf.ICM2(); + cvCopyImage(mrf.out_image, lowThresholdMask.Ptr()); + } + + cvCopyImage(old, old_labeling); + + lowThresholdMask.Clear(); + bgs.Update(frameNumber, frame_data, lowThresholdMask); + + cv::Mat foreground(highThresholdMask.Ptr()); + + if(showOutput) + cv::imshow("T2FMRF-UV", foreground); + + foreground.copyTo(img_output); + + delete frame; + frameNumber++; +} + +void T2FMRF_UV::saveConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/T2FMRF_UV.xml", 0, CV_STORAGE_WRITE); + + cvWriteReal(fs, "threshold", threshold); + cvWriteReal(fs, "alpha", alpha); + cvWriteReal(fs, "km", km); + cvWriteReal(fs, "kv", kv); + cvWriteInt(fs, "gaussians", gaussians); + cvWriteInt(fs, "showOutput", showOutput); + + cvReleaseFileStorage(&fs); +} + +void T2FMRF_UV::loadConfig() +{ + CvFileStorage* fs = cvOpenFileStorage("./config/T2FMRF_UV.xml", 0, CV_STORAGE_READ); + + threshold = cvReadRealByName(fs, 0, "threshold", 9.0); + alpha = cvReadRealByName(fs, 0, "alpha", 0.01); + km = cvReadRealByName(fs, 0, "km", 2); + kv = cvReadRealByName(fs, 0, "kv", 0.9); + gaussians = cvReadIntByName(fs, 0, "gaussians", 3); + showOutput = cvReadIntByName(fs, 0, "showOutput", true); + + cvReleaseFileStorage(&fs); +} diff --git a/package_bgs/tb/T2FMRF_UV.h b/package_bgs/tb/T2FMRF_UV.h new file mode 100644 index 0000000000000000000000000000000000000000..b8c0ff072a1cba191e6d848faf06a81c12d42db5 --- /dev/null +++ b/package_bgs/tb/T2FMRF_UV.h @@ -0,0 +1,64 @@ +/* +This file is part of BGSLibrary. + +BGSLibrary is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +BGSLibrary is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <iostream> +#include <cv.h> +#include <highgui.h> + +#include "../IBGS.h" +#include "MRF.h" + +using namespace Algorithms::BackgroundSubtraction; + +class T2FMRF_UV : public IBGS +{ +private: + bool firstTime; + long frameNumber; + IplImage *frame; + RgbImage frame_data; + + IplImage *old_labeling; + IplImage *old; + + T2FMRFParams params; + T2FMRF bgs; + BwImage lowThresholdMask; + BwImage highThresholdMask; + + double threshold; + double alpha; + float km; + float kv; + int gaussians; + bool showOutput; + + MRF_TC mrf; + GMM *gmm; + HMM *hmm; + +public: + T2FMRF_UV(); + ~T2FMRF_UV(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + +private: + void saveConfig(); + void loadConfig(); +}; diff --git a/run_camera.sh b/run_camera.sh new file mode 100644 index 0000000000000000000000000000000000000000..f000519fdbe3b6a6d9c0b38d8a08e55ecdee364b --- /dev/null +++ b/run_camera.sh @@ -0,0 +1,3 @@ +#!/bin/bash +./build/bgs --use_cam --camera=0 + diff --git a/run_demo.sh b/run_demo.sh new file mode 100644 index 0000000000000000000000000000000000000000..e734f061f62f56b7d0877bb63bbaa243d2762359 --- /dev/null +++ b/run_demo.sh @@ -0,0 +1,3 @@ +#!/bin/bash +./build/bgs_demo dataset/video.avi + diff --git a/run_video.sh b/run_video.sh new file mode 100644 index 0000000000000000000000000000000000000000..0ae1bcb7b9f036e44010800a5133ed6896fbbc42 --- /dev/null +++ b/run_video.sh @@ -0,0 +1,3 @@ +#!/bin/bash +./build/bgs -uf -fn=dataset/video.avi + diff --git a/vs2010/bgslibrary.sln b/vs2010/bgslibrary.sln new file mode 100644 index 0000000000000000000000000000000000000000..06187d0549eb46226ede71906d13e14a2d563691 --- /dev/null +++ b/vs2010/bgslibrary.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bgslibrary", "bgslibrary.vcxproj", "{3B6BF763-9CDE-4859-ADD9-8EB7B282659F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3B6BF763-9CDE-4859-ADD9-8EB7B282659F}.Debug|Win32.ActiveCfg = Debug|Win32 + {3B6BF763-9CDE-4859-ADD9-8EB7B282659F}.Debug|Win32.Build.0 = Debug|Win32 + {3B6BF763-9CDE-4859-ADD9-8EB7B282659F}.Release|Win32.ActiveCfg = Release|Win32 + {3B6BF763-9CDE-4859-ADD9-8EB7B282659F}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vs2010/bgslibrary.suo b/vs2010/bgslibrary.suo new file mode 100644 index 0000000000000000000000000000000000000000..3dc8e43797f0d1ee8f60b5d84b6328c180a83479 Binary files /dev/null and b/vs2010/bgslibrary.suo differ diff --git a/vs2010/bgslibrary.vcxproj b/vs2010/bgslibrary.vcxproj new file mode 100644 index 0000000000000000000000000000000000000000..5a1b8298ba7f330757e5a77c772c05a6ef22c767 --- /dev/null +++ b/vs2010/bgslibrary.vcxproj @@ -0,0 +1,220 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{3B6BF763-9CDE-4859-ADD9-8EB7B282659F}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>bgslibrary</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\</OutDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>C:\OpenCV2.4.5\build\include;C:\OpenCV2.4.5\build\include\opencv;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalDependencies>C:\OpenCV2.4.5\build\x86\vc10\staticlib\*.lib;comctl32.lib;VFW32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\Demo.cpp" /> + <ClCompile Include="..\package_analysis\ForegroundMaskAnalysis.cpp" /> + <ClCompile Include="..\package_bgs\AdaptiveBackgroundLearning.cpp" /> + <ClCompile Include="..\package_bgs\ae\KDE.cpp" /> + <ClCompile Include="..\package_bgs\ae\KernelTable.cpp" /> + <ClCompile Include="..\package_bgs\ae\NPBGmodel.cpp" /> + <ClCompile Include="..\package_bgs\ae\NPBGSubtractor.cpp" /> + <ClCompile Include="..\package_bgs\av\TBackground.cpp" /> + <ClCompile Include="..\package_bgs\av\TBackgroundVuMeter.cpp" /> + <ClCompile Include="..\package_bgs\av\VuMeter.cpp" /> + <ClCompile Include="..\package_bgs\dp\AdaptiveMedianBGS.cpp" /> + <ClCompile Include="..\package_bgs\dp\Blob.cpp" /> + <ClCompile Include="..\package_bgs\dp\BlobExtraction.cpp" /> + <ClCompile Include="..\package_bgs\dp\BlobResult.cpp" /> + <ClCompile Include="..\package_bgs\dp\ConnectedComponents.cpp" /> + <ClCompile Include="..\package_bgs\dp\DPAdaptiveMedianBGS.cpp" /> + <ClCompile Include="..\package_bgs\dp\DPEigenbackgroundBGS.cpp" /> + <ClCompile Include="..\package_bgs\dp\DPGrimsonGMMBGS.cpp" /> + <ClCompile Include="..\package_bgs\dp\DPMeanBGS.cpp" /> + <ClCompile Include="..\package_bgs\dp\DPPratiMediodBGS.cpp" /> + <ClCompile Include="..\package_bgs\dp\DPTextureBGS.cpp" /> + <ClCompile Include="..\package_bgs\dp\DPWrenGABGS.cpp" /> + <ClCompile Include="..\package_bgs\dp\DPZivkovicAGMMBGS.cpp" /> + <ClCompile Include="..\package_bgs\dp\Eigenbackground.cpp" /> + <ClCompile Include="..\package_bgs\dp\Error.cpp" /> + <ClCompile Include="..\package_bgs\dp\GrimsonGMM.cpp" /> + <ClCompile Include="..\package_bgs\dp\Image.cpp" /> + <ClCompile Include="..\package_bgs\dp\MeanBGS.cpp" /> + <ClCompile Include="..\package_bgs\dp\PratiMediodBGS.cpp" /> + <ClCompile Include="..\package_bgs\dp\TextureBGS.cpp" /> + <ClCompile Include="..\package_bgs\dp\WrenGA.cpp" /> + <ClCompile Include="..\package_bgs\dp\ZivkovicAGMM.cpp" /> + <ClCompile Include="..\package_bgs\FrameDifferenceBGS.cpp" /> + <ClCompile Include="..\package_bgs\jmo\blob.cpp" /> + <ClCompile Include="..\package_bgs\jmo\BlobExtraction.cpp" /> + <ClCompile Include="..\package_bgs\jmo\BlobResult.cpp" /> + <ClCompile Include="..\package_bgs\jmo\CMultiLayerBGS.cpp" /> + <ClCompile Include="..\package_bgs\jmo\LocalBinaryPattern.cpp" /> + <ClCompile Include="..\package_bgs\jmo\MultiLayerBGS.cpp" /> + <ClCompile Include="..\package_bgs\lb\BGModel.cpp" /> + <ClCompile Include="..\package_bgs\lb\BGModelFuzzyGauss.cpp" /> + <ClCompile Include="..\package_bgs\lb\BGModelFuzzySom.cpp" /> + <ClCompile Include="..\package_bgs\lb\BGModelGauss.cpp" /> + <ClCompile Include="..\package_bgs\lb\BGModelMog.cpp" /> + <ClCompile Include="..\package_bgs\lb\BGModelSom.cpp" /> + <ClCompile Include="..\package_bgs\lb\LBAdaptiveSOM.cpp" /> + <ClCompile Include="..\package_bgs\lb\LBFuzzyAdaptiveSOM.cpp" /> + <ClCompile Include="..\package_bgs\lb\LBFuzzyGaussian.cpp" /> + <ClCompile Include="..\package_bgs\lb\LBMixtureOfGaussians.cpp" /> + <ClCompile Include="..\package_bgs\lb\LBSimpleGaussian.cpp" /> + <ClCompile Include="..\package_bgs\MixtureOfGaussianV1BGS.cpp" /> + <ClCompile Include="..\package_bgs\MixtureOfGaussianV2BGS.cpp" /> + <ClCompile Include="..\package_bgs\StaticFrameDifferenceBGS.cpp" /> + <ClCompile Include="..\package_bgs\tb\FuzzyChoquetIntegral.cpp" /> + <ClCompile Include="..\package_bgs\tb\FuzzySugenoIntegral.cpp" /> + <ClCompile Include="..\package_bgs\tb\FuzzyUtils.cpp" /> + <ClCompile Include="..\package_bgs\tb\PerformanceUtils.cpp" /> + <ClCompile Include="..\package_bgs\tb\PixelUtils.cpp" /> + <ClCompile Include="..\package_bgs\tb\T2FGMM.cpp" /> + <ClCompile Include="..\package_bgs\tb\T2FGMM_UM.cpp" /> + <ClCompile Include="..\package_bgs\tb\T2FGMM_UV.cpp" /> + <ClCompile Include="..\package_bgs\WeightedMovingMeanBGS.cpp" /> + <ClCompile Include="..\package_bgs\WeightedMovingVarianceBGS.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\package_analysis\ForegroundMaskAnalysis.h" /> + <ClInclude Include="..\package_bgs\AdaptiveBackgroundLearning.h" /> + <ClInclude Include="..\package_bgs\ae\KDE.h" /> + <ClInclude Include="..\package_bgs\ae\KernelTable.h" /> + <ClInclude Include="..\package_bgs\ae\NPBGmodel.h" /> + <ClInclude Include="..\package_bgs\ae\NPBGSubtractor.h" /> + <ClInclude Include="..\package_bgs\av\TBackground.h" /> + <ClInclude Include="..\package_bgs\av\TBackgroundVuMeter.h" /> + <ClInclude Include="..\package_bgs\av\VuMeter.h" /> + <ClInclude Include="..\package_bgs\dp\AdaptiveMedianBGS.h" /> + <ClInclude Include="..\package_bgs\dp\Bgs.h" /> + <ClInclude Include="..\package_bgs\dp\BgsParams.h" /> + <ClInclude Include="..\package_bgs\dp\Blob.h" /> + <ClInclude Include="..\package_bgs\dp\BlobExtraction.h" /> + <ClInclude Include="..\package_bgs\dp\BlobResult.h" /> + <ClInclude Include="..\package_bgs\dp\ConnectedComponents.h" /> + <ClInclude Include="..\package_bgs\dp\DPAdaptiveMedianBGS.h" /> + <ClInclude Include="..\package_bgs\dp\DPEigenbackgroundBGS.h" /> + <ClInclude Include="..\package_bgs\dp\DPGrimsonGMMBGS.h" /> + <ClInclude Include="..\package_bgs\dp\DPMeanBGS.h" /> + <ClInclude Include="..\package_bgs\dp\DPPratiMediodBGS.h" /> + <ClInclude Include="..\package_bgs\dp\DPTextureBGS.h" /> + <ClInclude Include="..\package_bgs\dp\DPWrenGABGS.h" /> + <ClInclude Include="..\package_bgs\dp\DPZivkovicAGMMBGS.h" /> + <ClInclude Include="..\package_bgs\dp\Eigenbackground.h" /> + <ClInclude Include="..\package_bgs\dp\Error.h" /> + <ClInclude Include="..\package_bgs\dp\GrimsonGMM.h" /> + <ClInclude Include="..\package_bgs\dp\Image.h" /> + <ClInclude Include="..\package_bgs\dp\MeanBGS.h" /> + <ClInclude Include="..\package_bgs\dp\PratiMediodBGS.h" /> + <ClInclude Include="..\package_bgs\dp\TextureBGS.h" /> + <ClInclude Include="..\package_bgs\dp\WrenGA.h" /> + <ClInclude Include="..\package_bgs\dp\ZivkovicAGMM.h" /> + <ClInclude Include="..\package_bgs\FrameDifferenceBGS.h" /> + <ClInclude Include="..\package_bgs\IBGS.h" /> + <ClInclude Include="..\package_bgs\jmo\BackgroundSubtractionAPI.h" /> + <ClInclude Include="..\package_bgs\jmo\BGS.h" /> + <ClInclude Include="..\package_bgs\jmo\blob.h" /> + <ClInclude Include="..\package_bgs\jmo\BlobExtraction.h" /> + <ClInclude Include="..\package_bgs\jmo\BlobLibraryConfiguration.h" /> + <ClInclude Include="..\package_bgs\jmo\BlobResult.h" /> + <ClInclude Include="..\package_bgs\jmo\CMultiLayerBGS.h" /> + <ClInclude Include="..\package_bgs\jmo\LocalBinaryPattern.h" /> + <ClInclude Include="..\package_bgs\jmo\MultiLayerBGS.h" /> + <ClInclude Include="..\package_bgs\jmo\OpenCvDataConversion.h" /> + <ClInclude Include="..\package_bgs\lb\BGModel.h" /> + <ClInclude Include="..\package_bgs\lb\BGModelFuzzyGauss.h" /> + <ClInclude Include="..\package_bgs\lb\BGModelFuzzySom.h" /> + <ClInclude Include="..\package_bgs\lb\BGModelGauss.h" /> + <ClInclude Include="..\package_bgs\lb\BGModelMog.h" /> + <ClInclude Include="..\package_bgs\lb\BGModelSom.h" /> + <ClInclude Include="..\package_bgs\lb\LBAdaptiveSOM.h" /> + <ClInclude Include="..\package_bgs\lb\LBFuzzyAdaptiveSOM.h" /> + <ClInclude Include="..\package_bgs\lb\LBFuzzyGaussian.h" /> + <ClInclude Include="..\package_bgs\lb\LBMixtureOfGaussians.h" /> + <ClInclude Include="..\package_bgs\lb\LBSimpleGaussian.h" /> + <ClInclude Include="..\package_bgs\lb\Types.h" /> + <ClInclude Include="..\package_bgs\MixtureOfGaussianV1BGS.h" /> + <ClInclude Include="..\package_bgs\MixtureOfGaussianV2BGS.h" /> + <ClInclude Include="..\package_bgs\StaticFrameDifferenceBGS.h" /> + <ClInclude Include="..\package_bgs\tb\FuzzyChoquetIntegral.h" /> + <ClInclude Include="..\package_bgs\tb\FuzzySugenoIntegral.h" /> + <ClInclude Include="..\package_bgs\tb\FuzzyUtils.h" /> + <ClInclude Include="..\package_bgs\tb\PerformanceUtils.h" /> + <ClInclude Include="..\package_bgs\tb\PixelUtils.h" /> + <ClInclude Include="..\package_bgs\tb\T2FGMM.h" /> + <ClInclude Include="..\package_bgs\tb\T2FGMM_UM.h" /> + <ClInclude Include="..\package_bgs\tb\T2FGMM_UV.h" /> + <ClInclude Include="..\package_bgs\WeightedMovingMeanBGS.h" /> + <ClInclude Include="..\package_bgs\WeightedMovingVarianceBGS.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/vs2010/bgslibrary.vcxproj.filters b/vs2010/bgslibrary.vcxproj.filters new file mode 100644 index 0000000000000000000000000000000000000000..7e5b5bb2b4947a8f0b08a91faf990095fcd09936 --- /dev/null +++ b/vs2010/bgslibrary.vcxproj.filters @@ -0,0 +1,444 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Header Files\package_analysis"> + <UniqueIdentifier>{c0cc904e-4bfa-4fd6-ae45-1fe712768308}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\package_bgs"> + <UniqueIdentifier>{7d606ab8-5bf7-4ec2-b897-d15eb1de0263}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\package_bgs\dp"> + <UniqueIdentifier>{dba27670-a93f-4a25-99e1-948bac19456b}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\package_bgs\jmo"> + <UniqueIdentifier>{c2f770a4-2284-4a79-a4e3-f4cbf57b3f29}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\package_bgs\lb"> + <UniqueIdentifier>{6830e1db-4298-4dae-87c8-615340f28f8f}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\package_bgs\tb"> + <UniqueIdentifier>{781e472a-2256-437a-ae26-8611653f8882}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\package_bgs\ae"> + <UniqueIdentifier>{ec53c3c1-ec12-4d06-a330-dcc626d220f8}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\package_bgs\av"> + <UniqueIdentifier>{5c8f2d51-2d62-4d20-a7bb-b18a28688acd}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\Demo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\package_analysis\ForegroundMaskAnalysis.cpp"> + <Filter>Header Files\package_analysis</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\AdaptiveBackgroundLearning.cpp"> + <Filter>Header Files\package_bgs</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\FrameDifferenceBGS.cpp"> + <Filter>Header Files\package_bgs</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\MixtureOfGaussianV1BGS.cpp"> + <Filter>Header Files\package_bgs</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\MixtureOfGaussianV2BGS.cpp"> + <Filter>Header Files\package_bgs</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\StaticFrameDifferenceBGS.cpp"> + <Filter>Header Files\package_bgs</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\WeightedMovingMeanBGS.cpp"> + <Filter>Header Files\package_bgs</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\WeightedMovingVarianceBGS.cpp"> + <Filter>Header Files\package_bgs</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\jmo\blob.cpp"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\jmo\BlobExtraction.cpp"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\jmo\BlobResult.cpp"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\jmo\CMultiLayerBGS.cpp"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\jmo\LocalBinaryPattern.cpp"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\jmo\MultiLayerBGS.cpp"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\lb\BGModel.cpp"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\lb\BGModelFuzzyGauss.cpp"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\lb\BGModelFuzzySom.cpp"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\lb\BGModelGauss.cpp"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\lb\BGModelMog.cpp"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\lb\BGModelSom.cpp"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\lb\LBAdaptiveSOM.cpp"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\lb\LBFuzzyAdaptiveSOM.cpp"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\lb\LBFuzzyGaussian.cpp"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\lb\LBMixtureOfGaussians.cpp"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\lb\LBSimpleGaussian.cpp"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\tb\FuzzyChoquetIntegral.cpp"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\tb\FuzzySugenoIntegral.cpp"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\tb\FuzzyUtils.cpp"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\tb\PerformanceUtils.cpp"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\tb\PixelUtils.cpp"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\tb\T2FGMM.cpp"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\tb\T2FGMM_UM.cpp"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\tb\T2FGMM_UV.cpp"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\ae\KDE.cpp"> + <Filter>Header Files\package_bgs\ae</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\ae\KernelTable.cpp"> + <Filter>Header Files\package_bgs\ae</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\ae\NPBGmodel.cpp"> + <Filter>Header Files\package_bgs\ae</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\ae\NPBGSubtractor.cpp"> + <Filter>Header Files\package_bgs\ae</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\av\TBackground.cpp"> + <Filter>Header Files\package_bgs\av</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\av\TBackgroundVuMeter.cpp"> + <Filter>Header Files\package_bgs\av</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\av\VuMeter.cpp"> + <Filter>Header Files\package_bgs\av</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\AdaptiveMedianBGS.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\Blob.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\BlobExtraction.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\BlobResult.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\ConnectedComponents.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\DPAdaptiveMedianBGS.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\DPEigenbackgroundBGS.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\DPGrimsonGMMBGS.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\DPMeanBGS.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\DPPratiMediodBGS.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\DPTextureBGS.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\DPWrenGABGS.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\DPZivkovicAGMMBGS.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\Eigenbackground.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\Error.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\GrimsonGMM.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\Image.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\MeanBGS.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\PratiMediodBGS.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\TextureBGS.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\WrenGA.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + <ClCompile Include="..\package_bgs\dp\ZivkovicAGMM.cpp"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\package_analysis\ForegroundMaskAnalysis.h"> + <Filter>Header Files\package_analysis</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\AdaptiveBackgroundLearning.h"> + <Filter>Header Files\package_bgs</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\FrameDifferenceBGS.h"> + <Filter>Header Files\package_bgs</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\IBGS.h"> + <Filter>Header Files\package_bgs</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\MixtureOfGaussianV1BGS.h"> + <Filter>Header Files\package_bgs</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\MixtureOfGaussianV2BGS.h"> + <Filter>Header Files\package_bgs</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\StaticFrameDifferenceBGS.h"> + <Filter>Header Files\package_bgs</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\WeightedMovingMeanBGS.h"> + <Filter>Header Files\package_bgs</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\WeightedMovingVarianceBGS.h"> + <Filter>Header Files\package_bgs</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\jmo\BackgroundSubtractionAPI.h"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\jmo\BGS.h"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\jmo\blob.h"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\jmo\BlobExtraction.h"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\jmo\BlobLibraryConfiguration.h"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\jmo\BlobResult.h"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\jmo\CMultiLayerBGS.h"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\jmo\LocalBinaryPattern.h"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\jmo\MultiLayerBGS.h"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\jmo\OpenCvDataConversion.h"> + <Filter>Header Files\package_bgs\jmo</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\lb\BGModel.h"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\lb\BGModelFuzzyGauss.h"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\lb\BGModelFuzzySom.h"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\lb\BGModelGauss.h"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\lb\BGModelMog.h"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\lb\BGModelSom.h"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\lb\LBAdaptiveSOM.h"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\lb\LBFuzzyAdaptiveSOM.h"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\lb\LBFuzzyGaussian.h"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\lb\LBMixtureOfGaussians.h"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\lb\LBSimpleGaussian.h"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\lb\Types.h"> + <Filter>Header Files\package_bgs\lb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\tb\FuzzyChoquetIntegral.h"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\tb\FuzzySugenoIntegral.h"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\tb\FuzzyUtils.h"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\tb\PerformanceUtils.h"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\tb\PixelUtils.h"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\tb\T2FGMM.h"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\tb\T2FGMM_UM.h"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\tb\T2FGMM_UV.h"> + <Filter>Header Files\package_bgs\tb</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\ae\KDE.h"> + <Filter>Header Files\package_bgs\ae</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\ae\KernelTable.h"> + <Filter>Header Files\package_bgs\ae</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\ae\NPBGmodel.h"> + <Filter>Header Files\package_bgs\ae</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\ae\NPBGSubtractor.h"> + <Filter>Header Files\package_bgs\ae</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\av\TBackground.h"> + <Filter>Header Files\package_bgs\av</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\av\TBackgroundVuMeter.h"> + <Filter>Header Files\package_bgs\av</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\av\VuMeter.h"> + <Filter>Header Files\package_bgs\av</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\AdaptiveMedianBGS.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\Bgs.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\BgsParams.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\Blob.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\BlobExtraction.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\BlobResult.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\ConnectedComponents.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\DPAdaptiveMedianBGS.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\DPEigenbackgroundBGS.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\DPGrimsonGMMBGS.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\DPMeanBGS.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\DPPratiMediodBGS.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\DPTextureBGS.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\DPWrenGABGS.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\DPZivkovicAGMMBGS.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\Eigenbackground.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\Error.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\GrimsonGMM.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\Image.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\MeanBGS.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\PratiMediodBGS.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\TextureBGS.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\WrenGA.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + <ClInclude Include="..\package_bgs\dp\ZivkovicAGMM.h"> + <Filter>Header Files\package_bgs\dp</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/vs2010/bgslibrary.vcxproj.user b/vs2010/bgslibrary.vcxproj.user new file mode 100644 index 0000000000000000000000000000000000000000..2dd78fa19ea4b48204bf74bf752ff5ee1be6887b --- /dev/null +++ b/vs2010/bgslibrary.vcxproj.user @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LocalDebuggerWorkingDirectory>../</LocalDebuggerWorkingDirectory> + <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> + <LocalDebuggerCommandArguments>./dataset/video.avi</LocalDebuggerCommandArguments> + </PropertyGroup> +</Project> \ No newline at end of file