diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..9e26507
--- /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 0000000..94a9ed0
--- /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.
+ 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.
+
+
+ Copyright (C)
+
+ 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 .
+
+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:
+
+ Copyright (C)
+ 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
+.
+
+ 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
+.
diff --git a/Config.h b/Config.h
new file mode 100644
index 0000000..01cf721
--- /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 .
+*/
+#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 0000000..3936976
--- /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 .
+*/
+#include
+#include
+#include
+
+#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 0000000..585cb9b
--- /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 .
+*/
+#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(cv::getTickCount());
+}
+
+void FrameProcessor::toc()
+{
+ duration = (static_cast(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 0000000..44ad2aa
--- /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 .
+*/
+#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 0000000..ef50b1f
--- /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 .
+*/
+#pragma once
+
+#include
+
+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 0000000..e2ce0d8
--- /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 .
\ No newline at end of file
diff --git a/Main.cpp b/Main.cpp
new file mode 100644
index 0000000..4e59ee5
--- /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 .
+*/
+#include "Config.h"
+#include "VideoAnalysis.h"
+#include
+
+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 0000000..834996b
--- /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 .
+*/
+#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 0000000..9a04623
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+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 0000000..b7fe6c5
--- /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 0000000..0123933
--- /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 .
+*/
+#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("help") == true)
+ {
+ std::cout << "Usage: " << argv[0] << " [options]" << std::endl;
+ std::cout << "Avaible options:" << std::endl;
+ cmd.printParams();
+ return false;
+ }
+
+ use_file = cmd.get("use_file");
+ if(use_file)
+ {
+ filename = cmd.get("filename");
+
+ if(filename.empty())
+ {
+ std::cout << "Specify filename"<< std::endl;
+ return false;
+ }
+
+ flag = true;
+ }
+
+ use_camera = cmd.get("use_cam");
+ if(use_camera)
+ {
+ cameraIndex = cmd.get("camera");
+ flag = true;
+ }
+
+ if(flag == true)
+ {
+ use_comp = cmd.get("use_comp");
+ if(use_comp)
+ {
+ frameToStop = cmd.get("stopAt");
+ imgref = cmd.get("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 0000000..74eacf7
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+
+#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 0000000..8952053
--- /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 .
+*/
+#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 0000000..caf6f6c
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..6ea8256
--- /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 0000000..c76f826
--- /dev/null
+++ b/config/FrameProcessor.xml
@@ -0,0 +1,38 @@
+
+
+""
+1
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+
diff --git a/config/PreProcessor.xml b/config/PreProcessor.xml
new file mode 100644
index 0000000..e58b318
--- /dev/null
+++ b/config/PreProcessor.xml
@@ -0,0 +1,6 @@
+
+
+0
+0
+1
+
diff --git a/config/VideoCapture.xml b/config/VideoCapture.xml
new file mode 100644
index 0000000..60dd9aa
--- /dev/null
+++ b/config/VideoCapture.xml
@@ -0,0 +1,13 @@
+
+
+0
+100
+0
+0
+0
+0
+0
+0
+0
+1
+
diff --git a/dataset/video.avi b/dataset/video.avi
new file mode 100644
index 0000000..a29f006
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 0000000..5f81242
--- /dev/null
+++ b/demos/DemoFrameDifferenceBGS.cpp
@@ -0,0 +1,49 @@
+#include
+#include
+#include
+
+#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 0000000..bf2bbad
--- /dev/null
+++ b/demos/DemoMultiLayerBGS.cpp
@@ -0,0 +1,49 @@
+#include
+#include
+#include
+
+#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 0000000..45645a0
--- /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 .
+*/
+#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 0000000..d6bfdae
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+#include
+
+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 0000000..b111e65
--- /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 .
+*/
+#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 0000000..eb07a64
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..cd3e7d3
--- /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 .
+*/
+#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(i, j) == 0)
+ {
+ img_background_f.at(i, j) = alphaDetection * img_input_f.at(i, j) + (1 - alphaDetection) * img_background_f.at(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 0000000..cb9f4d5
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..e871651
--- /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 .
+*/
+#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 0000000..3fab44d
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..675b23c
--- /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 .
+*/
+#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("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 0000000..9da28ad
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+
+#include "IBGS.h"
+
+class GMG : public IBGS
+{
+private:
+ bool firstTime;
+ cv::Ptr 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 0000000..0b57658
--- /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 .
+*/
+#pragma once
+
+#include
+
+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 0000000..51d41eb
--- /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 .
+*/
+#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 0000000..8a67acc
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#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 0000000..5ce33a3
--- /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 .
+*/
+#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 0000000..495d701
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#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 0000000..3463c45
--- /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 .
+*/
+#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 0000000..416d13b
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..841c467
--- /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 .
+*/
+#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 0000000..a6684e7
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..7e691e9
--- /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 .
+*/
+#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 &v_img_input_f, const std::vector 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 0000000..f07cf59
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 &v_img_input_f, const std::vector 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 0000000..2cbbd8c
--- /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 .
+*/
+#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 0000000..39f01dc
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..9e20d7c
--- /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 .
+*/
+/*
+*
+* 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
+
+#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 0000000..bc37cc4
--- /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 .
+*/
+/*
+*
+* 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
+
+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 0000000..0f77a9a
--- /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 .
+*/
+/*
+*
+* 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
+#include
+#include
+
+//#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; icnt;
+ 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=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=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 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 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 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 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.
+*/
+/*
+*
+* 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 0000000..2c8b074
--- /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 .
+*/
+/*
+*
+* 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 0000000..9e28510
--- /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 .
+*/
+/*
+*
+* 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
+
+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 0000000..2e97d93
--- /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 .
+*/
+/*
+* 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 0000000..974cab9
--- /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 .
+*/
+/*
+* TBackground.h
+* Framework
+*
+* Created by Robinault Lionel on 07/12/11.
+*
+*/
+#pragma once
+
+#include
+#include
+#include
+
+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 0000000..4597621
--- /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 .
+*/
+/*
+* 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 0000000..1a19324
--- /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 .
+*/
+/*
+* 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 0000000..29a1477
--- /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 .
+*/
+#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 0000000..7884ff6
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..f3d068c
--- /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 .
+
+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 0000000..6fd7267
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+
+#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 0000000..9347353
--- /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
+
+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 0000000..d5bc246
--- /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
+const T& MEMin(const T& a, const T& b)
+{
+ if (a < b)
+ return a;
+ return b;
+}
+
+template
+const T& MEMax(const T& a, const T& b)
+{
+ if (a < b)
+ return b;
+ return a;
+}
+
+template
+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 0000000..126fd25
--- /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
+#else
+#include
+#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 0000000..5b2b47f
--- /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 0000000..5a625a9
--- /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
+#include
+#else
+#include
+#include
+#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 0000000..41ada3b
--- /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 0000000..9cb8e79
--- /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
+#else
+#include
+#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 0000000..6e11521
--- /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 0000000..d76c6a3
--- /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
+#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 0000000..a243ed1
--- /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 *block = new Block(BLOCK_SIZE);
+
+ // adding items
+ for (int i=0; i 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 *dblock = new DBlock(BLOCK_SIZE);
+
+ // adding items
+ for (int i=0; i New();
+ }
+
+ // deleting items
+ for (int i=0; i Delete(array[i]);
+ }
+
+ // adding items
+ for (int i=0; i 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
+#include
+
+/***********************************************************************/
+/***********************************************************************/
+/***********************************************************************/
+
+template 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 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 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 0000000..fb4cf9f
--- /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
+#include "graph.h"
+
+Graph::Graph(void (*err_function)(char *))
+{
+ error_function = err_function;
+ node_block = new Block(NODE_BLOCK_SIZE, error_function);
+ arc_block = new Block(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 0000000..fdae566
--- /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_block;
+ Block *arc_block;
+ DBlock *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 0000000..8812a4a
--- /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
+#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 (dhead; 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 (dhead; 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_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 0000000..c05ee5c
--- /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 0000000..b6f9a5d
--- /dev/null
+++ b/package_bgs/db/IndependentMultimodalBGS.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include
+#include
+
+#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 0000000..715d699
--- /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 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(0,0) = bgModel[p].values[n];
+
+ cv::Mat hsvPixel = convertImageRGBtoHSV(bgrPixel);
+
+ h_b = hsvPixel.at(0,0)[0];
+ s_b = hsvPixel.at(0,0)[1];
+ v_b = hsvPixel.at(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(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 > 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(i,j)) {
+ fgfiltered.at(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(y,x)[0]; //*(uchar*)(pRGB+0); // Blue component
+ int bG = imageRGB.at(y,x)[1]; //*(uchar*)(pRGB+1); // Green component
+ int bR = imageRGB.at(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(y, x)[0] = bH; // H component
+ imageHSV.at(y, x)[1] = bS; // S component
+ imageHSV.at(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 0000000..fd7faf1
--- /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
+#include
+#include
+#include
+//C++
+#include
+#include
+
+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 frameBGR;
+ //frame size
+ Size frameSize;
+ //frame type
+ int frameType;
+ //total number of pixels in frame
+ unsigned int numPixels;
+ //current background sample
+ Mat bgSample;
+ vector 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 0000000..16b3988
--- /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 .
+*/
+/****************************************************************************
+*
+* 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
+#include
+#include
+
+#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 0000000..3ea25af
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..c1ae23c
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..c3bad83
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..d7ebcd1
--- /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 .
+*/
+#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 0000000..05ac9c3
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..6d2c6ba
--- /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 .
+*/
+#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 0000000..df558fe
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..0d6ad42
--- /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 .
+*/
+#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 0000000..37e2a33
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..13260b4
--- /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 .
+*/
+#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 0000000..b119ac4
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..65c3ef3
--- /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 .
+*/
+#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 0000000..657dc1b
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..2d34342
--- /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 .
+*/
+#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 0000000..daa6a55
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..d7241b1
--- /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 .
+*/
+#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 0000000..7e545c5
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..16f991c
--- /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 .
+*/
+#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 0000000..d213341
--- /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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+
+#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 0000000..2d2c3ef
--- /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 .
+*/
+/****************************************************************************
+*
+* 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(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 0000000..b86ac92
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..1bd87db
--- /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 .
+*/
+/****************************************************************************
+*
+* Error.cpp
+*
+* Purpose: Error checking routines.
+*
+* Author: Donovan Parks, July 2007
+*
+******************************************************************************/
+
+#include
+#include
+
+#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 0000000..cdbb222
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..5788dc9
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..2d11a28
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..f00febd
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..40d327b
--- /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 .
+*/
+/****************************************************************************
+*
+* 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
+#include
+
+// --- Image Iterator ---------------------------------------------------------
+
+template
+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(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 0000000..b8df895
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..881beb7
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..2ef44c0
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..0b373d7
--- /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 .
+*/
+/****************************************************************************
+*
+* 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
+#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 pixels; // vector of pixels at give location in image
+ std::vector 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 0000000..7ccac37
--- /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 .
+*/
+#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 0000000..573e6f9
--- /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 .
+*/
+#include
+#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 0000000..8ae10dd
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..d623e20
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..c260132
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..c58fac4
--- /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 .
+*/
+/****************************************************************************
+*
+* 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 0000000..1fc0e44
--- /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 .
+*/
+/* --- --- ---
+* 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
+
+
+// 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 0000000..7d01d69
--- /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 .
+*/
+/* --- --- ---
+* 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 0000000..04560b9
--- /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 .
+*/
+/* --- --- ---
+* 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 0000000..157ee58
--- /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 .
+*/
+/* --- --- ---
+* 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 0000000..48119f7
--- /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 .
+*/
+/* --- --- ---
+* 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 0000000..fbd312a
--- /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 .
+*/
+/* --- --- ---
+* 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
+#include
+#include
+#include
+#include "BlobResult.h"
+#include "BlobExtraction.h"
+#ifdef _DEBUG
+#include //suport per a CStrings
+#include //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 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= 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= 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 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() );
+
+ 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\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 0000000..ef1fd40
--- /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 .
+*/
+/* --- --- ---
+ * 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
+#include "cxcore.h"
+
+#ifdef MATRIXCV_ACTIU
+ #include "matrixCV.h"
+#else
+ // llibreria STL
+ #include "vector"
+ //! Vector de doubles
+ typedef std::vector double_stl_vector;
+#endif
+
+#include // vectors de la STL
+#include
+#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 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
+ //! 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 0000000..134a24c
--- /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 .
+*/
+/* --- --- ---
+* 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 // clock
+#include // C standard library
+#include // C I/O (for sscanf)
+#include // string manipulation
+#include // file I/O
+#include // math includes
+#include // 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 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 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 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 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 ODC1;
+ COpencvDataConversion ODC2;
+ COpencvDataConversion 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*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 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 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 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 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 ODC1;
+ COpencvDataConversion 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 0000000..1eda55b
--- /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 .
+*/
+/* --- --- ---
+* 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
+#include
+#include "BlobResult.h"
+#include "OpenCvDataConversion.h"
+
+#include "BackgroundSubtractionAPI.h"
+
+#ifdef LINUX_BILATERAL_FILTER
+#include "CrossBilateralFilter.h" // cross bilateral filter
+#endif
+
+#include // clock
+#include // C standard library
+#include // C I/O (for sscanf)
+#include // string manipulation
+#include // file I/O
+#include // math includes
+#include // 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 0000000..45528aa
--- /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 .
+*/
+/* --- --- ---
+* 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 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 0000000..307d94f
--- /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 .
+*/
+/* --- --- ---
+* 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
+#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 // 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 0000000..dc0a6e8
--- /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