pax_global_header00006660000000000000000000000064126646425240014525gustar00rootroot0000000000000052 comment=a07b91963381b54b4340de95d52eb384947de99f openalpr_2.2.4.orig/000077500000000000000000000000001266464252400143735ustar00rootroot00000000000000openalpr_2.2.4.orig/.dockerignore000066400000000000000000000000201266464252400170370ustar00rootroot00000000000000.git Dockerfile openalpr_2.2.4.orig/.travis.yml000066400000000000000000000010671266464252400165100ustar00rootroot00000000000000before_install: - sudo add-apt-repository ppa:yjwong/opencv2 -y - sudo add-apt-repository ppa:lyrasis/precise-backports -y - sudo apt-get update -q install: sudo apt-get -y install libopencv-dev libtesseract-dev git cmake build-essential libleptonica-dev liblog4cplus-dev libcurl3-dev beanstalkd before_script: - mkdir -p ./src/build/ - cd ./src/build/ - cmake .. script: - make - make openalpr-utils-classifychars openalpr-utils-tagplates openalpr-utils-benchmark openalpr-utils-prepcharsfortraining - sudo make install - ./tests/unittests openalpr_2.2.4.orig/CONTRIBUTING.md000066400000000000000000000001611266464252400166220ustar00rootroot00000000000000To get started, sign the Contributor License Agreement. openalpr_2.2.4.orig/Dockerfile000066400000000000000000000012301266464252400163610ustar00rootroot00000000000000from ubuntu:14.04 # Install prerequisites run apt-get update && apt-get install -y \ build-essential \ cmake \ curl \ git \ libcurl3-dev \ libleptonica-dev \ liblog4cplus-dev \ libopencv-dev \ libtesseract-dev \ wget # Copy all data copy . /srv/openalpr # Setup the build directory run mkdir /srv/openalpr/src/build workdir /srv/openalpr/src/build # Setup the compile environment run cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_INSTALL_SYSCONFDIR:PATH=/etc .. # Compile the library run make # Install the binaries/libraries to your local system (prefix is /usr) run make install workdir /data entrypoint ["alpr"] openalpr_2.2.4.orig/LICENSE000066400000000000000000001033301266464252400154000ustar00rootroot00000000000000 GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 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 Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are 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. 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. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. 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 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 work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. 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 AGPL, see . openalpr_2.2.4.orig/README.md000066400000000000000000000131611266464252400156540ustar00rootroot00000000000000openalpr ======== OpenALPR is an open source *Automatic License Plate Recognition* library written in C++ with bindings in C#, Java, Node.js, Go, and Python. The library analyzes images and video streams to identify license plates. The output is the text representation of any license plate characters. Check out a live online demo here: http://www.openalpr.com/demo-image.html User Guide ----------- OpenALPR includes a command line utility. Simply typing "alpr [image file path]" is enough to get started recognizing license plate images. For example, the following output is created by analyzing this image: ![Plate Image](http://www.openalpr.com/images/demoscreenshots/plate3.png "Input image") ``` user@linux:~/openalpr$ alpr ./samplecar.png plate0: top 10 results -- Processing Time = 58.1879ms. - PE3R2X confidence: 88.9371 - PE32X confidence: 78.1385 - PE3R2 confidence: 77.5444 - PE3R2Y confidence: 76.1448 - P63R2X confidence: 72.9016 - FE3R2X confidence: 72.1147 - PE32 confidence: 66.7458 - PE32Y confidence: 65.3462 - P632X confidence: 62.1031 - P63R2 confidence: 61.5089 ``` Detailed command line usage: ``` user@linux:~/openalpr$ alpr --help USAGE: alpr [-c ] [--config ] [-n ] [--seek ] [-p ] [--clock] [-d] [-j] [--] [--version] [-h] Where: -c , --country Country code to identify (either us for USA or eu for Europe). Default=us --config Path to the openalpr.conf file -n , --topn Max number of possible plate numbers to return. Default=10 --seek Seek to the specified millisecond in a video file. Default=0 -p , --pattern Attempt to match the plate number against a plate pattern (e.g., md for Maryland, ca for California) --clock Measure/print the total time to process image and all plates. Default=off -d, --detect_region Attempt to detect the region of the plate image. [Experimental] Default=off -j, --json Output recognition results in JSON format. Default=off --, --ignore_rest Ignores the rest of the labeled arguments following this flag. --version Displays version information and exits. -h, --help Displays usage information and exits. Image containing license plates OpenAlpr Command Line Utility ``` Binaries ---------- Pre-compiled Windows binaries can be downloaded on the [releases page] (https://github.com/openalpr/openalpr/releases) Install OpenALPR on Ubuntu 14.04 x64 with the following commands: wget -O - http://deb.openalpr.com/openalpr.gpg.key | sudo apt-key add - echo "deb http://deb.openalpr.com/master/ openalpr main" | sudo tee /etc/apt/sources.list.d/openalpr.list sudo apt-get update sudo apt-get install openalpr openalpr-daemon openalpr-utils libopenalpr-dev Documentation --------------- Detailed documentation is available at [doc.openalpr.com] (http://doc.openalpr.com/) Integrating the Library ----------------------- OpenALPR is written in C++ and has bindings in C#, Python, Node.js, Go, and Java. Please see this guide for examples showing how to run OpenALPR in your application: http://doc.openalpr.com/bindings.html Compiling ----------- [![Build Status](https://travis-ci.org/openalpr/openalpr.svg?branch=master)](https://travis-ci.org/openalpr/openalpr) OpenALPR compiles and runs on Linux, Mac OSX and Windows. OpenALPR requires the following additional libraries: - Tesseract OCR v3.0.3 (https://code.google.com/p/tesseract-ocr/) - OpenCV v2.4.8+ (http://opencv.org/) After cloning this GitHub repository, you should download and extract Tesseract and OpenCV source code into their own directories. Compile both libraries. Please follow these detailed compilation guides for your respective operating system: * [Windows] (https://github.com/openalpr/openalpr/wiki/Compilation-instructions-(Windows)) * [Ubuntu Linux] (https://github.com/openalpr/openalpr/wiki/Compilation-instructions-(Ubuntu-Linux)) * [OS X] (https://github.com/openalpr/openalpr/wiki/Compilation-instructions-(OS-X)) * [Android Library] (https://github.com/SandroMachado/openalpr-android) * [Android Application Sample] (https://github.com/sujaybhowmick/OpenAlprDroidApp) * [iOS] (https://github.com/twelve17/openalpr-ios) If all went well, there should be an executable named *alpr* along with *libopenalpr-static.a* and *libopenalpr.so* that can be linked into your project. Docker ------ ``` shell # Build docker image docker build -t openalpr https://github.com/openalpr/openalpr.git # Download test image wget http://plates.openalpr.com/h786poj.jpg # Run alpr on image docker run -it --rm -v $(pwd):/data:ro openalpr -c eu h786poj.jpg ``` Questions --------- Please post questions or comments to the Google group list: https://groups.google.com/forum/#!forum/openalpr Contributions ------------- Improvements to the OpenALPR library are always welcome. Please review the [OpenALPR design description](https://github.com/openalpr/openalpr/wiki/OpenALPR-Design) and get started. Code contributions are not the only way to help out. Do you have a large library of license plate images? If so, please upload your data to the anonymous FTP located at upload.openalpr.com. Do you have time to "tag" plate images in an input image or help in other ways? Please let everyone know by posting a note in the forum. License ------- Affero GPLv3 http://www.gnu.org/licenses/agpl-3.0.html openalpr_2.2.4.orig/cla.txt000066400000000000000000000127071266464252400157020ustar00rootroot00000000000000In order to clarify the intellectual property license granted with Contributions from any person or entity, OpenALPR Technology, Inc. ("OpenALPR") must have a Contributor License Agreement ("CLA") on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of OpenALPR; it does not change your rights to use your own Contributions for any other purpose. You accept and agree to the following terms and conditions for Your present and future Contributions submitted to OpenALPR. Except for the license granted herein to OpenALPR and recipients of software distributed by OpenALPR, You reserve all right, title, and interest in and to Your Contributions. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with OpenALPR. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to OpenALPR for inclusion in, or documentation of, any of the products owned or managed by OpenALPR (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to OpenALPR or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, OpenALPR for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to OpenALPR and to recipients of software distributed by OpenALPR a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to OpenALPR and to recipients of software distributed by OpenALPR a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to OpenALPR, or that your employer has executed a separate Corporate CLA with OpenALPR. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. Should You wish to submit work that is not Your original creation, You may submit it to OpenALPR separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [[]named here]". You agree to notify OpenALPR of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. openalpr_2.2.4.orig/config/000077500000000000000000000000001266464252400156405ustar00rootroot00000000000000openalpr_2.2.4.orig/config/alprd.conf000066400000000000000000000013631266464252400176140ustar00rootroot00000000000000[daemon] ; country determines the training dataset used for recognizing plates. Valid values are: us, eu country = us ; text name identifier for this location site_id = your-unique-sitename ; Declare each stream on a separate line ; each unique stream should be defined as stream = [url] stream = http://127.0.0.1/example_video_stream.mjpeg ;stream = http://127.0.0.1/example_second_stream.mjpeg ;stream = webcam ; topn is the number of possible plate character variations to report topn = 10 ; Determines whether images that contain plates should be stored to disk store_plates = 0 store_plates_location = /var/lib/openalpr/plateimages/ ; upload address is the destination to POST to upload_data = 0 upload_address = http://localhost:9000/push/ openalpr_2.2.4.orig/config/openalpr.conf.in000066400000000000000000000064501266464252400207410ustar00rootroot00000000000000 ; Specify the path to the runtime data directory runtime_dir = ${CMAKE_INSTALL_PREFIX}/share/openalpr/runtime_data ocr_img_size_percent = 1.33333333 state_id_img_size_percent = 2.0 ; Calibrating your camera improves detection accuracy in cases where vehicle plates are captured at a steep angle ; Use the openalpr-utils-calibrate utility to calibrate your fixed camera to adjust for an angle ; Once done, update the prewarp config with the values obtained from the tool prewarp = ; detection will ignore plates that are too large. This is a good efficiency technique to use if the ; plates are going to be a fixed distance away from the camera (e.g., you will never see plates that fill ; up the entire image max_plate_width_percent = 100 max_plate_height_percent = 100 ; detection_iteration_increase is the percentage that the LBP frame increases each iteration. ; It must be greater than 1.0. A value of 1.01 means increase by 1%, 1.10 increases it by 10% each time. ; So a 1% increase would be ~10x slower than 10% to process, but it has a higher chance of landing ; directly on the plate and getting a strong detection detection_iteration_increase = 1.1 ; The minimum detection strength determines how sure the detection algorithm must be before signaling that ; a plate region exists. Technically this corresponds to LBP nearest neighbors (e.g., how many detections ; are clustered around the same area). For example, 2 = very lenient, 9 = very strict. detection_strictness = 3 ; The detection doesn't necessarily need an extremely high resolution image in order to detect plates ; Using a smaller input image should still find the plates and will do it faster ; Tweaking the max_detection_input values will resize the input image if it is larger than these sizes ; max_detection_input_width/height are specified in pixels max_detection_input_width = 1280 max_detection_input_height = 720 ; detector is the technique used to find license plate regions in an image. Value can be set to ; lbpcpu - default LBP-based detector uses the system CPU ; lbpgpu - LBP-based detector that uses Nvidia GPU to increase recognition speed. ; lbpopencl - LBP-based detector that uses OpenCL GPU to increase recognition speed. Requires OpenCV 3.0 ; morphcpu - Experimental detector that detects white rectangles in an image. Does not require training. detector = lbpcpu ; If set to true, all results must match a postprocess text pattern if a pattern is available. ; If not, the result is disqualified. must_match_pattern = 0 ; Bypasses plate detection. If this is set to 1, the library assumes that each region provided is a likely plate area. skip_detection = 0 max_plate_angle_degrees = 15 ocr_min_font_point = 6 ; Minimum OCR confidence percent to consider. postprocess_min_confidence = 65 ; Any OCR character lower than this will also add an equally likely ; chance that the character is incorrect and will be skipped. Value is a confidence percent postprocess_confidence_skip_level = 80 debug_general = 0 debug_timing = 0 debug_detector = 0 debug_state_id = 0 debug_plate_lines = 0 debug_plate_corners = 0 debug_char_segment = 0 debug_char_analysis = 0 debug_color_filter = 0 debug_ocr = 0 debug_postprocess = 0 debug_show_images = 0 debug_pause_on_frame = 0 openalpr_2.2.4.orig/distros/000077500000000000000000000000001266464252400160625ustar00rootroot00000000000000openalpr_2.2.4.orig/distros/debian/000077500000000000000000000000001266464252400173045ustar00rootroot00000000000000openalpr_2.2.4.orig/distros/debian/README.source000066400000000000000000000010061266464252400214600ustar00rootroot00000000000000This package uses quilt to manage all modifications to the upstream source. Changes are stored in the source package as diffs in debian/patches and applied during the build. See /usr/share/doc/quilt/README.source for a detailed explanation. This package uses the version 3.0 of the debian source package; An upstream tarball is required to build it. This tarball can be generated automagically by downloading the sources from the github repository with the following command: fakeroot debian/rules get-orig-source openalpr_2.2.4.orig/distros/debian/changelog000066400000000000000000000055041266464252400211620ustar00rootroot00000000000000openalpr (2.2.4-1) unstable; urgency=low * Added Python3 bindings (Closes: #815961) * Remove frame number from JSON output (Closes: #813290) * Use dh-python to generate proper depends for python-openalpr (Closes: #815960) * Change python-openalpr from arch any to arch all (Closes: #815959) * Fixed build failure on Hurd (Closes: #812738) * Add bindings package for python3, python3-openalpr * Upload sponsored by Petter Reinholdtsen -- Matthew Hill Sun, 28 Feb 2016 09:51:25 -0500 openalpr (2.2.3-1) unstable; urgency=low * Changed Debian config to build on all architectures * Upload sponsored by Petter Reinholdtsen -- Matthew Hill Sun, 24 Jan 2016 15:33:25 -0500 openalpr (2.2.2-1) unstable; urgency=low * Minor bug fixes * Upload sponsored by Petter Reinholdtsen -- Matthew Hill Wed, 6 Jan 2016 06:48:05 -0500 openalpr (2.2.1-1) unstable; urgency=low * Initial upload to Debian (Closes: #747509) * Debian Packaging enhancements * Added binding for Go * Upload sponsored by Petter Reinholdtsen -- Matthew Hill Thu, 17 Dec 2015 18:10:05 -0500 openalpr (2.2.0-0.1) experimental; urgency=low * Non-maintainer upload * Added training data for Singapore and Great Britain * Improved accuracy and reduced false positives for US plate detector * Improved support for 2-line plates (requires training data) * Supports analyzing multiple training datasets in one pass * Added OpenCL GPU support (works with Nvidia, AMD, and Intel GPUs) * Upgraded to OpenCV 3.0 * Support Tesseract 3.04 * .NET library API improvements * Bug Fixes -- Matthew Hill Mon, 19 Oct 2015 18:15:00 -0500 openalpr (2.1.0-0.1) experimental; urgency=low * Non-maintainer upload * Added per-character details to API output * Bug fixes * Added camera calibration utility * Motion detector for video frames * Full UTF-8 character support * Support for South Korean and Australian plates * Experimental morphological plate detector * Pattern matching for European countries -- Matthew Hill Thu, 18 Jun 2015 20:31:00 -0500 openalpr (2.0.1-0.1) experimental; urgency=low * Non-maintainer upload * Bindings for Java, C#, and Python * API changes/simplification * CUDA support * Bug fixes * Faster initial load time * Additional Config options * Library supports multi-line plates -- Matthew Hill Thu, 12 Mar 2015 23:46:00 -0500 openalpr (2.0.0-0.1) experimental; urgency=low * Non-maintainer upload * Upstream release with several fixes: - Compilation of utilities enabled - installation of static library - alpr build against shared library -- Emmanuel Papin Sun, 14 Dec 2014 19:39:07 +0200 openalpr_2.2.4.orig/distros/debian/compat000066400000000000000000000000021266464252400205020ustar00rootroot000000000000009 openalpr_2.2.4.orig/distros/debian/control000066400000000000000000000124421266464252400207120ustar00rootroot00000000000000Source: openalpr Section: video Priority: optional Maintainer: Matthew Hill Build-Depends: debhelper (>= 9), cmake, quilt, libtesseract-dev, libleptonica-dev, liblog4cplus-dev, libcurl3-dev, libopencv-dev, default-jdk, python, python3, dh-python Standards-Version: 3.9.6 Homepage: https://github.com/openalpr/openalpr Vcs-Browser: https://github.com/openalpr/openalpr Vcs-Git: https://github.com/openalpr/openalpr.git Package: libopenalpr2 Section: libs Architecture: any Depends: libopenalpr-data (= ${source:Version}), ${shlibs:Depends}, ${misc:Depends} Description: Automated License Plate Recognition library (OpenALPR) OpenALPR is an open source Automatic License Plate Recognition library written in C++. The library analyzes images and identifies license plates. The output is the text representation of any license plate characters found in the image. . This package contains the runtime libraries used to interface with OpenALPR. Package: libopenalpr-dev Section: libdevel Architecture: any Depends: libopenalpr2 (= ${binary:Version}), ${misc:Depends} Description: Development files for the OpenALPR library OpenALPR is an open source Automatic License Plate Recognition library written in C++. The library analyzes images and identifies license plates. The output is the text representation of any license plate characters found in the image. . This package contains the header files and static libraries used to link OpenALPR into another program. Package: libopenalpr-data Section: misc Architecture: all Depends: ${misc:Depends} Description: Runtime data for the OpenALPR library OpenALPR is an open source Automatic License Plate Recognition library written in C++. The library analyzes images and identifies license plates. The output is the text representation of any license plate characters found in the image. . This package contains the runtime training data used by OpenALPR to recognize license plates from various countries. Package: openalpr Architecture: any Depends: libopenalpr2 (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends} Description: Command line program to operate the OpenALPR library OpenALPR is an open source Automatic License Plate Recognition library written in C++. The library analyzes images and identifies license plates. The output is the text representation of any license plate characters found in the image. . This package contains a command-line program to analyze images for license plates. Package: openalpr-daemon Architecture: any Depends: libopenalpr2 (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, adduser, beanstalkd Description: Daemon to run OpenALPR in the background OpenALPR is an open source Automatic License Plate Recognition library written in C++. The library analyzes images and identifies license plates. The output is the text representation of any license plate characters found in the image. . The OpenALPR daemon allows you to monitor a camera stream for license plate numbers in the background. Alprd runs as a daemon process on Linux. . The plate numbers can be streamed to another server (via HTTP posts) or can be consumed programmatically via a beanstalkd queue. Package: openalpr-utils Section: utils Architecture: any Depends: libopenalpr2 (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends} Description: Utilities for the OpenALPR library OpenALPR is an open source Automatic License Plate Recognition library written in C++. The library analyzes images and identifies license plates. The output is the text representation of any license plate characters found in the image. . The OpenALPR utils package contains a collection of binaries that help with training new data. The following binaries are included: . openalpr-utils-calibrate - Calibrates the software for cameras capturing plate images at an angle . openalpr-utils-benchmark - Benchmarks accuracy/speed against a testing dataset . openalpr-utils-tagplates - Utility to tag the location and plate number for testing and training . openalpr-utils-classifychars - Extracts character images from cropped plate images for OCR training . openalpr-utils-prepcharsfortraining - Reformates character images into a format that Tesseract can use for OCR training Package: python-openalpr Section: python Architecture: all Depends: libopenalpr-dev (>= ${binary:Version}), ${python:Depends}, ${misc:Depends} Description: Python binding for OpenALPR library OpenALPR is an open source Automatic License Plate Recognition library written in C++. The library analyzes images and identifies license plates. The output is the text representation of any license plate characters found in the image. . The Python package allows Python code to interface with OpenALPR directly via native Python bindings. Package: python3-openalpr Section: python Architecture: all Depends: libopenalpr-dev (>= ${source:Version}), ${python3:Depends}, ${misc:Depends} Description: Python 3 binding for OpenALPR library OpenALPR is an open source Automatic License Plate Recognition library written in C++. The library analyzes images and identifies license plates. The output is the text representation of any license plate characters found in the image. . The Python 3 package allows Python 3 code to interface with OpenALPR directly via native Python 3 bindings. openalpr_2.2.4.orig/distros/debian/copyright000066400000000000000000000322451266464252400212450ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: openalpr Source: https://github.com/openalpr/openalpr Files: * Copyright: 2013-2016 Matt Hill License: AGPL-3+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero 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 Affero General Public License for more details. . You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Files: src/openalpr/support/re2.h src/openalpr/support/re2/* Copyright: 2008 The RE2 Authors. License: BSD-3-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. Files: src/openalpr/support/re2/util/rune.cc Copyright: 2002 by Lucent Technologies License: Expat-Lucent Permission to use, copy, modify, and distribute this software for any purpose without fee is hereby granted, provided that this entire notice is included in all copies of any software which is or includes a copy or modification of this software and in all copies of the supporting documentation for such software. . THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. Files: src/openalpr/support/tinydir.h Copyright: 2013, Cong Xu License: BSD-2-clause 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. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: src/tclap/* Copyright: 2004 Michael E. Smoot, Daniel Aarno. 2004 Michael E. Smoot, Daniel Aarno. All rights reverved. 2003 Michael E. Smoot. All rights reverved. 2006 Oliver Kiddle All rights reverved. 2005 Michael E. Smoot, Daniel Aarno, Erik Zeek. All rights reverved. 2003-2005 Michael E. Smoot . All rights reverved. 2004-2005 Michael E. Smoot All rights reverved. 2003 Michael E. Smoot . 2007 Daniel Aarno, Michael E. Smoot . All rights reverved. 2004 Michael E. Smoot, Daniel Aarno . All rights reverved. License: Expat Files: src/openalpr/simpleini/* Copyright: 2006-2013 Brodie Thiesfield License: Expat License: Zlib This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. . Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: . 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. . 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. . 3. This notice may not be removed or altered from any source distribution. Files: src/openalpr/TRexpp.h Copyright: 2003-2004 Alberto Demichelis License: Zlib Files: src/openalpr/support/tinythread.* src/openalpr/support/fast_mutex.h Copyright: 2010-2012 Marcus Geelnard License: Zlib Files: src/openalpr/support/utf8.h src/openalpr/support/utf8.cpp src/openalpr/support/utf8/* Copyright: 2006 Nemanja Trifunovic License: Boost Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: . The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: src/openalpr/support/re2/util/valgrind.h Copyright: 2000-2009 Julian Seward. All rights reserved. License: BSD-4-clause 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. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. . 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. . 4. 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. Files: src/bindings/java/src/com/openalpr/jni/json/* Copyright: 2010 The Android Open Source Project License: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at . http://www.apache.org/licenses/LICENSE-2.0 /usr/share/common-licenses/Apache-2.0 (on Debian systems) . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Files: src/tests/catch.hpp Copyright: Two Blue Cubes Ltd. All rights reserved. License: Boost-1.0 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and/or associated documentation files (the "Materials"), to deal in the Materials without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Materials, and to permit persons to whom the Materials are furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Materials. . THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. Files: src/openalpr/cjson.* Copyright: 2009 Dave Gamble License: Expat Files: src/openalpr/support/windows/dirent.h Copyright: 2006-2012 Toni Ronkko License: Expat Files: src/openalpr/simpleini/ConvertUTF.* Copyright: 2001-2004 Unicode, Inc. License: Unicode This source code is provided as is by Unicode, Inc. No claims are made as to fitness for any particular purpose. No warranties of any kind are expressed or implied. The recipient agrees to determine applicability of information provided. If this file has been purchased on magnetic or optical media from Unicode, Inc., the sole remedy for any claim will be exchange of defective media within 90 days of receipt. . Limitations on Rights to Redistribute This Code . Unicode, Inc. hereby grants the right to freely use the information supplied in this file in the creation of products supporting the Unicode Standard, and to make copies of this file in any form for internal or external distribution as long as this notice remains attached. openalpr_2.2.4.orig/distros/debian/docs000066400000000000000000000000221266464252400201510ustar00rootroot00000000000000cla.txt README.md openalpr_2.2.4.orig/distros/debian/libopenalpr-data.install000066400000000000000000000000731266464252400241120ustar00rootroot00000000000000etc/openalpr/openalpr.conf usr/share/openalpr/runtime_data openalpr_2.2.4.orig/distros/debian/libopenalpr-dev.install000066400000000000000000000000611266464252400237540ustar00rootroot00000000000000usr/include/alpr.h usr/lib/lib*.a usr/lib/lib*so openalpr_2.2.4.orig/distros/debian/libopenalpr2.install000066400000000000000000000000221266464252400232570ustar00rootroot00000000000000usr/lib/lib*.so.* openalpr_2.2.4.orig/distros/debian/openalpr-daemon.default000066400000000000000000000010671266464252400237370ustar00rootroot00000000000000# Defaults for alprd initscript # sourced by /etc/init.d/openalpr-daemon # installed at /etc/default/openalpr-daemon by the maintainer scripts # # This is a POSIX shell fragment # # These options can be set to modify the behavior of the openalr-daemon init script. # The options commented out show the default values. # Start the daemon if set to "yes" START_DAEMON="yes" # Path to the log file #LOGFILE="/var/log/alpr.log" # User and group the daemon should run as #USER="daemon" #GROUP="daemon" # Additional options that are passed to the daemon #DAEMON_OPTS= openalpr_2.2.4.orig/distros/debian/openalpr-daemon.dirs000066400000000000000000000000361266464252400232470ustar00rootroot00000000000000var/lib/openalpr/plateimages/ openalpr_2.2.4.orig/distros/debian/openalpr-daemon.init000066400000000000000000000103451266464252400232550ustar00rootroot00000000000000#!/bin/sh ### BEGIN INIT INFO # Provides: openalpr-daemon # Required-Start: $local_fs $network $remote_fs $syslog # Required-Stop: $local_fs $network $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: OpenALPR daemon for license plate recognition # Description: OpenALPR daemon detects license plate in the background ### END INIT INFO # Author: Emmanuel Papin unset USER # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="OpenALPR daemon" NAME=openalpr-daemon DAEMON=/usr/bin/alprd PIDFILE=/var/run/alprd.pid SCRIPTNAME=/etc/init.d/$NAME DEFAULT=/etc/default/$NAME USER=openalpr # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Read configuration variable file if it is present [ -r $DEFAULT ] && . $DEFAULT # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.2-14) to ensure that this file is present # and status_of_proc is working. . /lib/lsb/init-functions # Do not start the daemon if NO_START is enabled in DEFAULT if [ "$START_DAEMON" != "yes" ] && [ "$1" != "stop" ]; then log_warning_msg "$NAME: Not starting $DESC." log_warning_msg "$NAME: Disabled in $DEFAULT." exit 0 fi # Set the default log file if [ -z $LOGFILE ]; then LOGFILE=/var/log/alpr.log fi # Run as `daemon' if USER is not specified or is `root' if [ -z $USER ]; then USER=daemon fi # If no group is specified, use USER if [ -z $GROUP ]; then GROUP=$USER fi DAEMON_ARGS="-l $LOGFILE $DAEMON_OPTS" # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started [ ! -f $LOGFILE ] && touch $LOGFILE && chown $USER:$GROUP $LOGFILE start-stop-daemon --start --quiet --pidfile $PIDFILE \ --chuid $USER:$GROUP --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE \ --chuid $USER:$GROUP --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON [ "$?" = 2 ] && return 2 # Many daemons don't delete their pidfiles when they exit. rm -f $PIDFILE return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { # # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME return 0 } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; restart|force-reload) # # If the "reload" option is implemented then remove the # 'force-reload' alias # log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 exit 3 ;; esac : openalpr_2.2.4.orig/distros/debian/openalpr-daemon.install000066400000000000000000000000461266464252400237550ustar00rootroot00000000000000etc/openalpr/alprd.conf usr/bin/alprd openalpr_2.2.4.orig/distros/debian/openalpr-daemon.postinst000066400000000000000000000006261266464252400241760ustar00rootroot00000000000000#!/bin/bash set -e # Source debconf library. . /usr/share/debconf/confmodule # Create openalpr:openalpr if it doesn't already exist USER=openalpr GROUP=openalpr getent group $GROUP >/dev/null || addgroup $GROUP >/dev/null getent passwd $USER >/dev/null || adduser --system --no-create-home --home /nonexistent $USER >/dev/null chown openalpr:openalpr /var/lib/openalpr/plateimages #DEBHELPER# db_stop openalpr_2.2.4.orig/distros/debian/openalpr-utils.install000066400000000000000000000002631266464252400236530ustar00rootroot00000000000000usr/bin/openalpr-utils-classifychars usr/bin/openalpr-utils-benchmark usr/bin/openalpr-utils-prepcharsfortraining usr/bin/openalpr-utils-tagplates usr/bin/openalpr-utils-calibrateopenalpr_2.2.4.orig/distros/debian/openalpr.install000066400000000000000000000000151266464252400225100ustar00rootroot00000000000000usr/bin/alpr openalpr_2.2.4.orig/distros/debian/openalpr.manpages000066400000000000000000000000171266464252400226370ustar00rootroot00000000000000doc/man/alpr.1 openalpr_2.2.4.orig/distros/debian/patches/000077500000000000000000000000001266464252400207335ustar00rootroot00000000000000openalpr_2.2.4.orig/distros/debian/patches/series000066400000000000000000000000001266464252400221360ustar00rootroot00000000000000openalpr_2.2.4.orig/distros/debian/python-openalpr.install000066400000000000000000000000501266464252400240260ustar00rootroot00000000000000usr/lib/python2.7/dist-packages/openalpropenalpr_2.2.4.orig/distros/debian/rules000077500000000000000000000057541266464252400203770ustar00rootroot00000000000000#!/usr/bin/make -f # See debhelper(7) (uncomment to enable) # output every command that modifies files on the build system. #DH_VERBOSE = 1 # see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* # DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/default.mk # # These are used to get the most recent version of the original sources from github UURL = $(shell git config --get remote.origin.url) BRANCH = $(shell git rev-parse --abbrev-ref HEAD) HEAD = $(shell git rev-parse HEAD) PKD = $(shell pwd)/debian PKG = $(word 2,$(shell dpkg-parsechangelog -l$(PKD)/changelog | grep ^Source)) VER ?= $(shell dpkg-parsechangelog -l$(PKD)/changelog | perl -ne 'print $$1 if m{^Version:\s+(?:\d+:)?(\d.*)(?:\-\d+.*)};') DTYPE = TARBALL = ../$(PKG)_$(VER)$(DTYPE).orig.tar.xz # see FEATURE AREAS in dpkg-buildflags(1) #export DEB_BUILD_MAINT_OPTIONS = hardening=+all # see ENVIRONMENT in dpkg-buildflags(1) # package maintainers to append CFLAGS #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic # package maintainers to append LDFLAGS #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed # main packaging script based on dh7 syntax %: dh $@ --sourcedirectory=src --with quilt,python2,python3 override_dh_auto_configure: dh_auto_configure -- \ -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_INSTALL_SYSCONFDIR=/etc \ -DCMAKE_VERBOSE_MAKEFILE=OFF \ -DCMAKE_COLOR_MAKEFILE=ON override_dh_auto_install: dh_auto_install cd $(CURDIR)/src/bindings/python ; python3 ./setup.py install --install-layout=deb --root $(CURDIR)/debian/python3-openalpr override_dh_auto_clean: dh_auto_clean rm -rf $(CURDIR)/src/bindings/python/build # Inspired by https://wiki.debian.org/onlyjob/get-orig-source .PHONY: get-orig-source get-orig-source: $(TARBALL) $(info I: $(PKG)_$(VER)$(DTYPE)) @ $(TARBALL): $(if $(wildcard $(PKG)-$(VER)),$(error folder '$(PKG)-$(VER)' exists, aborting...)) @echo "# Cloning origin repository..."; \ if ! git clone $(UURL) $(PKG)-$(VER); then \ $(RM) -r $(PKG)-$(VER); \ echo "failed to clone repository, aborting..."; \ false; \ fi @if [ $(BRANCH) != "master" ]; then \ cd $(PKG)-$(VER); \ echo "# Not on master branch, fetching origin branch '$(BRANCH)'..."; \ git fetch origin $(BRANCH):$(BRANCH) || false; \ echo "# Switching to branch '$(BRANCH)'..."; \ git checkout $(BRANCH) || false; \ fi @echo "# Checking local source..." @if [ $$(cd $(PKG)-$(VER) && git rev-parse HEAD) = $(HEAD) ]; then \ echo "even with origin, ok"; \ true; \ else \ echo "not even with origin, aborting..."; \ false; \ fi @echo "# Setting times..." @cd $(PKG)-$(VER) \ && for F in $$(git ls-tree -r --name-only HEAD | sed -e "s/\s/\*/g"); do \ touch --no-dereference -d "$$(git log -1 --format="%ai" -- $$F)" "$$F"; \ done @echo "# Cleaning-up..." cd $(PKG)-$(VER) && $(RM) -r .git @echo "# Packing file '$(TARBALL)'..." @find -L "$(PKG)-$(VER)" -xdev -type f -print | sort \ | XZ_OPT="-6v" tar -caf "$(TARBALL)" -T- --owner=root --group=root --mode=a+rX \ && $(RM) -r "$(PKG)-$(VER)" openalpr_2.2.4.orig/distros/debian/source/000077500000000000000000000000001266464252400206045ustar00rootroot00000000000000openalpr_2.2.4.orig/distros/debian/source/format000066400000000000000000000000141266464252400220120ustar00rootroot000000000000003.0 (quilt) openalpr_2.2.4.orig/distros/debian/source/local-options000066400000000000000000000000521266464252400233070ustar00rootroot00000000000000unapply-patches abort-on-upstream-changes openalpr_2.2.4.orig/doc/000077500000000000000000000000001266464252400151405ustar00rootroot00000000000000openalpr_2.2.4.orig/doc/generate000077500000000000000000000002021266464252400166520ustar00rootroot00000000000000#!/bin/bash make -f makefile clean && make -f makefile html cp -R swagger/ build/html/api cp source/*.yaml build/html/api/specs/ openalpr_2.2.4.orig/doc/generate.bat000077500000000000000000000161311266464252400174270ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source set I18NSPHINXOPTS=%SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled echo. coverage to run coverage check of the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) REM Check if sphinx-build is available and fallback to Python version if any %SPHINXBUILD% 2> nul if errorlevel 9009 goto sphinx_python goto sphinx_ok :sphinx_python set SPHINXBUILD=python -m sphinx.__init__ %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) :sphinx_ok if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\openalpr.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\openalpr.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "coverage" ( %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage if errorlevel 1 exit /b 1 echo. echo.Testing of coverage in the sources finished, look at the ^ results in %BUILDDIR%/coverage/python.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end openalpr_2.2.4.orig/doc/makefile000077500000000000000000000164021266464252400166460ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/openalpr.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/openalpr.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/openalpr" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/openalpr" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." openalpr_2.2.4.orig/doc/man/000077500000000000000000000000001266464252400157135ustar00rootroot00000000000000openalpr_2.2.4.orig/doc/man/alpr.1000066400000000000000000000124031266464252400167330ustar00rootroot00000000000000.TH "openalpr" "1" "10 May 2014" "" "" .SH "NAME" OpenALPR \- Automatic License Plate Recognition Library .SH "SYNOPSIS" .PP .nf Plate detection: alpr [OPTION...] [IMAGE_PATH] .fi .SH "DESCRIPTION" .PP OpenALPR is an open source Automatic License Plate Recognition library written in C++. The library analyzes images and identifies license plates. The output is the text representation of any license plate characters found in the image. .PP Check out a live online demo here: http://www.openalpr.com/demo.html .PP OpenALPR supports license plates from the USA as well as number plates from Europe. The library can also be used to identify plates from other countries. By training the library, the accuracy for these other countries can be increased. .SH "USAGE" .PP alpr [\-c ] [\-\-config ] [\-n ] [\-\-seek ] [\-p ] [\-\-clock] [\-d] [\-j] [\-\-] [\-\-version] [\-h] Where: \-c , \-\-country Country code to identify (either us for USA or eu for Europe). Default=us \-\-config Path to the openalpr.conf file \-n , \-\-topn Max number of possible plate numbers to return. Default=10 \-\-seek Seek to the specified millisecond in a video file. Default=0 \-p , \-\-pattern Attempt to match the plate number against a plate pattern (e.g., md for Maryland, ca for California) \-\-clock Measure/print the total time to process image and all plates. Default=off \-d, \-\-detect_region Attempt to detect the region of the plate image. [Experimental] Default=off \-j, \-\-json Output recognition results in JSON format. Default=off \-\-, \-\-ignore_rest Ignores the rest of the labeled arguments following this flag. \-\-version Displays version information and exits. \-h, \-\-help Displays usage information and exits. Image containing license plates .SH "EXAMPLES" .PP .RS .PP \f(CW$ alpr \-c eu /source/image.jpg .RE .PP This command will attempt to recognize number plates in the /source/image.jpg image using the European\-style recognition data. The config file is not provided on the CLI, so it will use the value in the environment variable 'OPENALPR_CONFIG_FILE' if provided, or the default location. .PP .RS \f(CW$ alpr \-\-config /tmp/openalpr.conf /source/image.png .RE .PP This command will attempt to recognize number plates in the /source/image.png image using the default USA\-style recognition data. The config file is not provided on the CLI, so it will read the configuration data from /tmp/openalpr.conf .PP .RS \f(CW$ alpr -c us *.jpg .RE .PP This command will attempt to recognize number plates in all jpeg images in the current directory image using the USA\-style recognition data. .PP .RS \f(CW$ alpr \-j /source/video.mp4 .RE .PP This command reads data from an input video (/source/video.mp4) and outputs recognition data as JSON. .PP .RS \f(CW$ alpr \-j stdin < /source/imagefilelist.txt > /out/recognitionresults.txt .RE .PP This command processes a list of image files provided in /source/imagefilelist.txt and writes JSON results to /out/recognitionresults.txt. .PP .RS \f(CW$ alpr webcam .RE .PP This command processes video from your webcam. You can also use /dev/video0, /dev/video1, etc. if you have multiple webcams. .PP .RE .SH "DIAGNOSTICS" .PP License plate recognition software can never achieve 100% accuracy. In cases where the plate is not recognized correctly, there is diagnostic information available. You can modify the openalpr.conf file to turn "debug" information on. .PP In the [debug] section toggle the various debug options to enabled by changing the '0' value to a '1'. Some of these options will output text to the CLI and others may output images to the GUI. .SH "BUGS" .PP Please report bugs! See the web site at https://github.com/openalpr/openalpr/issues .PP .SH "CREDITS" .PP OpenALPR is distributed under the GNU Affero General Public License version 3. See the file LICENSE for details. .PP The OpenALPR site is available at: http://www.openalpr.com/. .PP We would be delighted to hear about the creative ways that you are using this program. Please contact the mailing\-list at openalpr@googlegroups.com. .PP This program uses the following libraries: .IP * OpenCV \- Computer vision (http://www.opencv.org) .IP * Tesseract \- Optical Character Recognition (https://code.google.com/p/tesseract\-ocr) .IP * T\-Rex \- Regular Expression processing (http://tiny\-rex.sourceforge.net) .IP * TinyThread++ \- Multi\-threaded analysis (http://tinythreadpp.bitsnbites.eu) .IP * TClap \- CLI Argument parsing (http://tclap.sourceforge.net) .IP * SimpleINI \- INI file configuration (https://github.com/brofield/simpleini) .PP .SH "THANKS" .PP Special thanks go out to: Stefan Bauer, Philippe Vaucher, Kristians Vebers, and all contributors to the project. .SH "AUTHOR" .PP OpenALPR is written and maintained by Matthew Hill (matthill@openalpr.com) and contributors from the open source community. .PP Mailing lists for support and development are available at https://groups.google.com/forum/#!forum/openalpr openalpr_2.2.4.orig/doc/requirements.txt000077500000000000000000000003171266464252400204300ustar00rootroot00000000000000Babel==2.1.1 Jinja2==2.8 MarkupSafe==0.23 Pygments==2.0.2 Sphinx==1.3.1 alabaster==0.7.6 argparse==1.2.1 docutils==0.12 pytz==2015.7 six==1.10.0 snowballstemmer==1.2.0 sphinx-rtd-theme==0.1.9 wsgiref==0.1.2 openalpr_2.2.4.orig/doc/source/000077500000000000000000000000001266464252400164405ustar00rootroot00000000000000openalpr_2.2.4.orig/doc/source/accuracy_improvements.rst000066400000000000000000000562711266464252400236070ustar00rootroot00000000000000************************ Accuracy Improvements ************************ Calibration ============= Calibrating your camera improves detection accuracy in cases where vehicle plates are captured at a steep angle. For example, the the plate below is captured at 40+ degree horizontal angle, and will normally not be recognized as a license plate. .. image:: images/configuration_calibration_before.jpg :scale: 100% :alt: Calibration before adjustments The camera calibration helps in cases where the camera is capturing from a fixed position, and all plates generally are seen at the same angle. This feature is best for short-angle cameras or cameras capturing at a close distance. Use the tool named "openalpr-utils-calibrate" to calibrate your camera. The utility needs a single example image to begin. This image should be taken from the camera you wish to calibrate and should have a license plate that represents the typical distance/angle of plate captured on this camera. ./openalpr-utils-calibrate camera_image.jpg There are sliders at the top of the screen that control the skew. Adjust the sliders until the plate looks like it is seen from straight-on with no angle. Left-click and drag to draw a box (with the correct aspect ratio) to test if the plate is the correct size. You can also right-click and drag to move the image within the frame. Because we are distorting the original image, the frame will be clipped. If frames are unlikely to be seen in certain areas (on the ceiling for example) you may want to adjust the plate image to ensure that those areas will be cropped. .. image:: images/configuration_calibration_tool.jpg :scale: 100% :alt: Calibration utility Once you're satisified with the parameters you've chosen, press the 'o' key. This produces a line of configuration in the terminal that can be copied directly into openalpr.conf as the "prewarp" config. This will apply the camera calibration settings against each image before it is used for plate recognition. Now test the new settings on a few images from this camera to make sure that the new calibration improves accuracy. ./alpr camera_image.jpg .. image:: images/configuration_calibration_after.jpg :scale: 100% :alt: Calibration after adjustments Notice that the license plate is now correctly detected. You can view the calibrated image results by enabling the "prewarp" debug option in the openalpr.conf file. Test the accuracy on many different images from the camera before accepting the configuration. Other calibration angles could produce superior results. Pattern Matching =================== The pattern matching feature runs the topN results against a Regular expression matcher to find results that match common license plate patterns. The regex patterns are customizable and can be found in runtime_data/postprocess/``*``.patterns For example, using a pattern against this Czechoslovakian plate results in only one possible match (which is the correct one). .. image:: images/configuration_patternmatch.jpg :scale: 100% :alt: Czechoslovakian number plate The cz patterns are: - cz #@##### - cz #@@#### Results for this plate, notice the pattern matches 4S50233: :: [mhill@mhill-linux tmp]$ alpr -c eu -p cz cz_4s50233.jpg -n 40 plate0: 40 results - 4S5O233 confidence: 90.947 pattern_match: 0 - 4S5O23 confidence: 87.8683 pattern_match: 0 - 4S5O23 confidence: 85.1644 pattern_match: 0 - 4S5O23S confidence: 84.5445 pattern_match: 0 - 4S5O23B confidence: 83.7395 pattern_match: 0 - 4S5O2S3 confidence: 83.3698 pattern_match: 0 - 4S5O23G confidence: 83.1375 pattern_match: 0 - 4S50233 confidence: 83.0457 pattern_match: 1 - 4S5O2B3 confidence: 82.5635 pattern_match: 0 - 4S5O2 confidence: 82.0857 pattern_match: 0 - 4S5O2G3 confidence: 81.5684 pattern_match: 0 - 4S5O2J3 confidence: 81.0409 pattern_match: 0 - 4S5O2S confidence: 80.2911 pattern_match: 0 ... more results that do not match ... You can utilize this from the library code by calling "setDefaultRegion(string region)" with the name of the pattern you wish to use: Configuration ================= The OpenALPR library is configured with the openalpr.conf file. On Linux, this is typically located in /etc/openalpr/openalpr.conf. On Windows, it is usually in the same directory as the binary. Many of the configuration options in this file are documented with comments. Training OCR =============== Training the OpenALPR OCR is a quick way to improve the accuracy for a particular country. To do this, you will need: 1. Around 200 clear images of your country's license plates. 2. 16 hours of free time `This code repository `_ provides code and data that can be used to train custom license plate fonts in support of the OpenALPR library. The OCR library used by OpenALPR is Tesseract. Many of the tedious aspects of OCR training have been automated via a Python script. However, the input data still needs to be in a specific format to satisfy Tesseract. For more information about training using Tesseract OCR, please read this tutorial: https://code.google.com/p/tesseract-ocr/wiki/TrainingTesseract3 To get started, first clone the repository and get familiar with the input files. In the "eu/input" folder, there are a number of tif files and box files. Each "font" will have at least one tif and box file. A country's license plate may have many fonts, each one would just use a different name. The naming convention is: l[country_code].[fontname].exp[pagenumber].box For example, the European German license plate font would look like: leu.germany.exp0.box Open up a tif file. Notice, these are a series of similar looking letters and numbers. The best way to generate these is from actual license plate images. OpenALPR has a couple utilities to help generate these input files. The first step is to find many pictures of your license plates. Make sure to separate them by font. Sometimes, even within a single region, the license plate fonts will vary (e.g., between old plates and new plates, or digital vs stamped plates, or vehicle plates vs bicycle plates). Each unique font should be a different file in order to achieve the highest accuracy. Adding a new Country -------------------- If you plan on training OCR for a completely new country, you will first need to configure the dimensions of the plate and characters. Add a new file in runtime_data/config/ with your country's 2-digit code. You can copy and paste a section from another country (e.g., us or eu). You should tweak the following values: - plate_width_mm = [width of full plate in mm] - plate_height_mm = [height of full plate in mm] - char_width_mm = [width of a single character in mm] - char_height_mm = [height of a single character in mm] - char_whitespace_top_mm = [whitespace between the character and the top of the plate in mm] - char_whitespace_bot_mm = [whitespace between the character and the bottom of the plate in mm] - template_max_width_px = [maximum width of the plate before processing. Should be proportional to the plate dimensions] - template_max_height_px = [maximum height of the plate before processing. Should be proportional to the plate dimensions] - min_plate_size_width_px = [Minimum size of a plate region to consider it valid.] - min_plate_size_height_px = [Minimum size of a plate region to consider it valid.] - ocr_language = [name of the OCR language -- typically just the letter l followed by your country code] Understanding Your Country's Plates ------------------------------------ The first thing you need to know is how many fonts your country's license plates have. In the US, for example, many states use very different fonts for their plates. Some countries only use one font. Here is an example of New York and West Virginia,. Notice how different the "6" character is in both plates: .. image:: images/training_ocr_plateny.png :scale: 100% :alt: west virginia license plate .. image:: images/training_ocr_platewv.png :scale: 100% :alt: new york license plate Each font needs to be trained separately. You do not want to combine characters across fonts, this will greatly decrease your accuracy. After each font is trained, they can be combined into one dataset for your entire country. Creating the character tiles ---------------------------- Once you're ready to start training, you'll need to create a library of character tiles. Each tile is a small image file that contains the black-and-white character and is named after the character. For example, here are a few character tile examples: .. image:: images/training_ocr_char1.png :scale: 100% :alt: character tile 1 부-0-0-2.png .. image:: images/training_ocr_char2.png :scale: 100% :alt: character tile 2 0-0-az2012.png .. image:: images/training_ocr_char3.png :scale: 100% :alt: character tile 3 c-1-az2012.png .. image:: images/training_ocr_char4.png :scale: 100% :alt: character tile 4 d-9-az2012.jpg .. image:: images/training_ocr_char5.png :scale: 100% :alt: character tile 5 d-9-2-az2012.jpg You will want many of these character tiles for each character and each font. The character tiles are all going to be slightly different, this is necessary for the OCR training to understand how to detect characters. Notice in the above examples, the "D" characters have pixels located in different places, but they're clearly the same character. Producing Tiles ---------------- There are two good ways to produce character tiles. 1. Use actual images from license plates 2. Use a TTF font that looks like the license plate font Producing Tiles from Actual Plates ------------------------------------ You should gather a large library of license plate images (At least 100). These license plate images should be cropped around the plate and the aspect ratio should match your configured width/height for your license plates. Make sure each image is at least 250px wide. The imageclipper program (separate repo) is helpful for quickly cropping large numbers of images. Save them as png files. Each file should be prefaced with a two character identifier for the font/region. For example, for Maryland plates, we would name the file: **md**\ plate1.png Create an empty output directory. To start classifying characters, use the classifychars utility program included in OpenALPR. Execute the command: classifychars [country] [input image directory] [empty output directory] A GUI will open up and analyze each license plate image in your input folder. The steps to classify each plate are: 1. Press the "Enter" key and type the letter or number for each position that you wish to classify. Pressing 'Space' will skip the character. 2. Use the arrow keys and press 'Space' to select the rendering that you wish to extract characters for. The box will be highlighted in blue if it is selected. For each plate, there may be good characters and bad characters. You want to pick the best characters, since significant imperfections may confuse the OCR. 3. Press the 's' key to save each character as a separate file in your out folder. 4. Press the 'n' key to move onto the next plate and repeat this process until you've classified all the plates. Producing Tiles from a TTF Font ------------------------------- A TTF font can be used to produce tiles. However, we need to add some realistic distortion to the characters. This is necessary to make a robust OCR detector. The process is as follows: 1. Figure out all the characters that could possibly be in a license plate. 2. Create a word document with all of these characters. Make sure there is plenty of spacing between lines and characters. 3. Copy and paste all of these characters to a text file (no spaces or line breaks) 4. Print this word document. 5. Take a few pictures (5 would be sufficient) of the word document with a digital camera. Vary the angle/rotation very slightly (1-2 degrees) with each picture. 6. Save the pictures to a folder. 7. Run the openalpr-utils-binarizefontsheet program to produce tiles from each of the images. Provide the program with the text file from step #3 and each image file. Building a Tesseract Training Sheet ----------------------------------- Once you've classified all the characters, it may be a good idea to scan through the directory to make sure that the classifications match the images. Each image filename should be prefaced with the character that it represents. Once you've done this, it's time to create a training sheet. The "openalpr-utils-prepcharsfortraining" utility program in OpenALPR will create the Tesseract training sheet for you. Execute the following command: openalpr-utils-prepcharsfortraining [output directory from above] The output will be: - combined.box - combined.tif Rename these files to match the naming convention used by Tesseract (explained above). For example, leu.germany.exp0.box You should create a training sheet for each unique license plate font that you wish to train. Finish OCR Training --------------------- Lastly, you'll use the box/tif files created above to train your country's license plate OCR. Create a new directory using your country code, and create an input directory within it. Copy all the box/tif files created in the previous steps into this directory. Execute the "train.py" file. Type in your country code. If all went well, you should have a new file named l[countrycode].traineddata. Copy this file into your runtime_directory (runtime_data/ocr/tessdata/) and it is now ready for OpenALPR to use. Tesseract may report issues. Most commonly it will complain that it could not line up the boxes on the provided image. If you are getting many of these warnings, you can re-run the openalpr-utils-prepcharsfortraining utility and provide values for --tile_width and --tile_height. Using different values will change how Tesseract sees the image and potentially improve results. Training the Detector ======================== The detector finds the general location of a license plate in an image. A single detector can support many different plate styles, as long as they generally have the same aspect ratio. For example, in the USA, license plates are 12 inches by 6 inches (i.e., an aspect ratio of 2:1). To train a license plate detector, you will need: 1. 3000+ clear images of license plates 2. 40-60 hours of free time `This repository `_ contains scripts that will help train a license plate detector for a particular region. Your trained region detector can then be used in OpenALPR. The license plate region detector uses the Local Binary Pattern (LBP) algorithm. In order to train the detector, you will need many positive and negative images. This repository already contains a collection of negative images. You will need to add your own positive images. To get started, you will first need many cropped plate images containing positive license plate matches. Please see the "eu" positive image folder in this repository to understand the types of plate images required. The imageclipper program is helpful for creating these cropped images. After you've collected many (hundreds to thousands) of positive plate images, the next step is to train the detector. First you must configure the training script to use the correct dimensions. Edit the prep.py script and change the WIDTH, HEIGHT, and COUNTRY variables to match the country that you are training. The width and height should be proportional to the plate size (slightly larger is OK). A total pixel area of around 650 seems to work best. Also, adjust the path to your OpenCV libraries, if that needs to be changed. Once you are ready to start training, enter the following commands: - rm ./out/``*`` (clear the out folder in case it has data from previous runs) - ./prep.py neg - ./prep.py pos - ./prep.py train - Copy the output from the above command onto the command line. You should adjust the numStages to a smaller value (usually 12 stages works well, but it will depend on your input images). You may also need to adjust the numPos value to a smaller number in order to complete the training. Copy the out/cascade.xml file to your OpenALPR runtime directory (runtime_data/region/[countrycode].xml). You should now be able to use the region for plate detection. Developers Guide ================= Accuracy can also be improved by modifying the recognition code, itself. The OpenALPR library is binary-compatible with the commercial software. Any improvements/modifications you make can be swapped in by replacing the openalpr.dll/libopenalpr.so with your modified version. The information below describes the various stages involved in recognizing license plates. OpenALPR Design ---------------- OpenALPR operates as a pipeline. The input is an image, various processing occurs in stages, and the output is the possible plate numbers in the image. The pipeline stages occur in the following order: ======================= ===================================== ============================================================================================== Pipeline Phase C++ class Description ======================= ===================================== ============================================================================================== Detection regiondetector.cpp Finds potential license plate regions Binarization binarizewolf.cpp Converts the plate region image into black and white Char Analysis characteranalysis.cpp Finds character-sized "blobs" in the plate region Plate Edges platelines.cpp and platecorners.cpp Finds the edges/shape of the license plate Deskew licenseplatecandidate.cpp Transforms the perspective to a straight-on view based on the ideal license plate size. Character Segmentation charactersegmenter.cpp Isolates and cleans up the characters so that they can be processed individually OCR ocr.cpp Analyzes each character image and provides multiple possible letters/confidences Post Processing postprocess.cpp Creates a top n list of plate possibilities based on OCR confidences. Also performs a Regex match against region templates if requested. ======================= ===================================== ============================================================================================== Detection --------- The detection phase happens one time for each input image. It uses the LBP algorithm (generally used for face detection) to find possible license plate regions (x,y, width, height). Each of these regions is sent to the later pipeline phases for further processing. The detection phase is usually the most processing-intensive phase. It can be GPU accelerated to improve performance. Binarization ------------ This phase (and all subsequent phases) occur multiple times -- once for each possible license plate region. The binarization phase creates multiple binary images for each plate region. The reason multiple binary images are used is to give us the best possible chance of finding all the characters. A single binarized image may miss characters if the image is too dark or too light for example. Binarization uses the Wolf-Jolien method as well as the Sauovola method with various parameters. Each of the binary images are processed in subsequent phases. Character Analysis ------------------ Character analysis attempts to find character-sized regions in the plate region. It does this by first finding all connected blobs in the license plate region. Then it looks for blobs that are roughly the width and height of a license plate character and have tops/bottoms that are in a straight line with other blobs of similar width/height. This analysis is done multiple times in the region. It starts by looking for small characters, then gradually looks for larger characters. If nothing is found in the region, then the region is thrown out and no further processing takes place. If it finds some potential characters, then the character region is saved and further processing takes place. Plate Edges ----------- The next phase is to find the edges of the license plate. Keep in mind that the detection phase is only responsible for identifying a possible region where a license plate may exist. It often is going to provide a region that is a little larger or smaller than the actual plate. The plate edges tries to find the precise top/bottom/left/right edges of the license plate. The first step is to find all of the hough lines for the license plate region. platelines.cpp processes the plate image and computes a list of horizontal and vertical lines. platecorners uses this list as well as the character height (computed in Character Analysis) to find the likeliest plate line edges. It uses a number of configurable weights to determine which edge makes the most sense. It will try using a default edge (based on the ideal width/height of the plate) to see if that makes a good match. Deskew ------ Given the plate edges, the deskew stage remaps the plate region to a standard size and orientation. Ideally this will give us a correctly oriented plate image (no rotation or skew). Character Segmentation ---------------------- The character segmentation phase tries to isolate all the characters that make up the plate image. It uses a vertical histogram to find gaps in the plate characters. This phase also cleans up the character boxes by removing small, disconnected speckles and disqualifying character regions that are not tall enough. It also tries to remove "edge" regions so that the edge of the license plate doesn't inappropriately get classified as a '1' or an 'I' OCR --- The OCR phase analyzes each character independently. For each character image, it computes all possible characters and their confidences. Post Processing --------------- Given a list of all possible OCR characters and confidences, post processing determines the best possible plate letter combinations. It is organized as a top N list. Post processing disqualifies all characters below a particular threshold. It also has a "soft" thresholds -- characters that are below this threshold will still be added to the possible list, but they also add a possible blank character -- since it's possible that the low confidence character is not really part of the plate. The post processing also handles region validation if requested. For example, if I tell OpenALPR that this is a "Missouri" plate, then it will try and match the results against a template that matches the Missouri format (e.g., [char][char][number]-[char][number][char]). So, for example, if the top 3 list was: - CFOCIG - CF0CIG - CF0C1G The third entry matches the template, but the other two do not. So, post processing will signal that the third entry is our best match. openalpr_2.2.4.orig/doc/source/alprd.rst000077500000000000000000000502621266464252400203040ustar00rootroot00000000000000.. _alprd: ************************ OpenALPR Agent (alprd) ************************ The OpenALPR daemon allows you to monitor a camera stream for license plate numbers in the background. Alprd runs as a daemon process on Linux. The plate numbers can be streamed to another server (via HTTP posts) or can be consumed programmatically via a beanstalkd queue. Architecture ============= Alprd operates as follows: 1. The image stream is constantly pulled from the IP camera via MJPEG over HTTP 2. alprd processes the stream as fast as it can looking for plate images. The daemon automatically skips frames to stay in-sync with clock time. 3. When one or more plates are detected, the information is written to a local beanstalkd queue (tube name is alprd) as JSON data. 4. Optionally, alprd will also save the image as a jpeg and save it to a configurable location. 5. Optionally, alprd also runs a separate process that drains the beanstalkd queue and uploads data to a remote HTTP server via POST. Alprd can be used in two modes: 1. Recognition results are streamed to an HTTP server 2. Recognition results can be read from the beanstalkd queue :: +------------------+                     +-------------+          |                  |  MJPEG       POST   |             |          |  Network Camera  | <---+      +------> | HTTP Server |          |                  |     |      |        |             |          +------------------+     |      |        +-------------+                                   |      |                                                          |      |                                                          |      |                                                  +-------+------+                                                  |              |                                                  | alprd server |                                                  |              |                                                  +---------+----+------------+                                               |                 |                                               | Beastalkd queue |                                               |                 |                                               +-----------------+                    The diagram above shows alprd being used to stream data to another HTTP server. alprd is configured with a remote HTTP address. As plates are identified, the server sends the JSON data to the remote HTTP server. The beanstalkd queue and the alprd process are colocated on the same server. :: +------------------+ | | MJPEG | Network Camera | <---+ | | | +------------------+ | | +----------+ | |Processing| | +----+-----+ +-------+------+ | | | | | alprd server | | | | | +---------+----+------------+ | | | | | Beastalkd queue | <------+ | | +-----------------+ The diagram above shows alprd being used without the HTTP server. In this case, a beanstalkd consumer can be used to drain the results from the beanstalkd queue. The beanstalkd tube name is "alprd." Beanstalkd consumers can be written in any language, and can be colocated on the alprd server or located elsewhere. Open Source Agent ==================== Configuration ------------- .. code-block:: ini [daemon] ; Declare each stream on a separate line ; each unique stream should be defined as stream = [url] stream = http://10.1.2.3/camera1/stream.mjpeg stream = http://10.1.2.5/camera2/stream.mjpeg site_id = headquarters-usa store_plates = 1 store_plates_location = /var/www/html/plates/ ; upload address is the destination to POST to upload_data = 0 upload_address = http://localhost:9000/alpr/push/ alprd needs at least one "stream" defined. This is just the URL for the mjpeg stream. You may use multiple streams on one server -- each stream spawns a separate process that attempts to use a full CPU core. The site-id will be stored along with the JSON plate results. This is especially useful if you have multiple servers and need to keep track of where the results are coming from. Additionally, each result will contain a camera ID (numbered 1 to n) based on the order of your "stream" statements in the alprd.conf file Results --------- The following is an example of the JSON results. These results are initially stored in the beanstalkd queue, and then optionally sent in an HTTP post. .. code-block:: json { "uuid": "f11e0acc-6aaf-4817-9299-9e6773043b8e", "camera_id": 1, "site_id": "headquarters", "img_width": 640, "img_height": 480, "epoch_time": 1402161050, "processing_time_ms": 138.669163, "results": [ { "plate": "S11FRE", "confidence": 77.130661, "matches_template": 0, "region": "", "region_confidence": 0, "coordinates": [ { "x": 218, "y": 342 }, { "x": 407, "y": 325 }, { "x": 407, "y": 413 }, { "x": 218, "y": 431 } ], "candidates": [ { "plate": "S11FRE", "confidence": 77.130661, "matches_template": 0 }, { "plate": "S11ERE", "confidence": 75.496307, "matches_template": 0 }, { "plate": "S11RE", "confidence": 75.440361, "matches_template": 0 }, { "plate": "S11CRE", "confidence": 75.340179, "matches_template": 0 }, { "plate": "S11FHE", "confidence": 75.240974, "matches_template": 0 }, { "plate": "S11EHE", "confidence": 73.606621, "matches_template": 0 }, { "plate": "S11HE", "confidence": 73.550682, "matches_template": 0 }, { "plate": "S11CHE", "confidence": 73.450493, "matches_template": 0 }, { "plate": "S11FBE", "confidence": 71.782944, "matches_template": 0 }, { "plate": "S11FE", "confidence": 71.762756, "matches_template": 0 } ] }, { "plate": "EJLESSIE", "epoch_time": 1402158050, "confidence": 78.167984, "matches_template": 0, "region": "", "region_confidence": 0, "processing_time_ms": 51.650604, "coordinates": [ { "x": 226, "y": 369 }, { "x": 348, "y": 348 }, { "x": 355, "y": 406 }, { "x": 231, "y": 429 } ], "candidates": [ { "plate": "EJLESSIE", "confidence": 78.167984, "matches_template": 0 }, { "plate": "EDLESSIE", "confidence": 77.61319, "matches_template": 0 } ] } ] } Commercial Agent ==================== `*` Requires Commercial License The commercial OpenALPR Agent has a number of :ref:`enhancements `. With these enhancements, the commercial agent is significantly faster when analyzing video streams. Installation -------------- On an Ubuntu 14.04 64-bit server: Add the OpenALPR GPG key and setup the OpenALPR deb repository .. code-block:: bash wget -O - http://deb.openalpr.com/openalpr.gpg.key | sudo apt-key add - echo "deb http://deb.openalpr.com/commercial/ trusty main" | sudo tee /etc/apt/sources.list.d/openalpr.list sudo apt-get update && sudo apt-get -y install openalpr openalpr-daemon Edit the configuration file .. code-block:: bash sudo nano /etc/openalpr/alprd.conf Configure the following properties: - company_id (using the value found on the web server) - site_id (a unique text value that identifies this agent) - stream (The mjpeg URL for the camera stream. Multiple streams are added as additional stream entries) - analysis_threads (number of CPU cores dedicated to OpenALPR processing) - upload_address (set this to http://[*Web_server_IP*]/push/) - store_plates_maxsize_mb (amount of space used for storing license plate images) Test the daemon from the CLI to make sure your settings are correct. Press CTRL+C once you see that the video stream is connected. alprd -f If the MJPEG stream is configured correctly, the logs should show that the video is being retrieved DEBUG - Video FPS: 10 Generate a license key for the agent: .. code-block:: bash cd ~ wget http://deb.openalpr.com/register.py sudo openalpr-licenserequest --license_count 0 > /dev/null && sudo python register.py -u [Username] -p [Password] -c [Number of camera licenses] The number of camera licenses corresponds to the maximum number of simultaneous video streams that this agent will support. Each time you run this utility, it will transfer the requested number of licenses from our online registration server to your agent server. Commercial Configuration Options ----------------------------------- The /etc/openalpr/alprd.conf file has additional properties that control the commercial-only features: The commercial software will group similar plate numbers together and send a JSON post that contains the UUIDs of all of the plate images in the group. These configuration files control the grouping sensitivity. .. code-block:: ini plate_groups_enabled = 1 plate_groups_time_delta_ms = 4500 plate_groups_min_plates_to_group = 3 The commercial daemon will maintain a disk quota and will automatically clean up after itself. Images stored to disk will be removed on a FIFO-basis. These parameters control the maximum size on disk and the frequency of clean-up. .. code-block:: ini store_plates_maxsize_mb = 8000 store_plates_cleanup_interval_seconds = 120 The upload threads control the number of simultaneous data uploads. Generally, the number of threads would only need to be increased if the web server is slow to respond. .. code-block:: ini upload_threads = 2 Motion detection is an effective way to improve efficiency and get more plate reads from the available processors. The ALPR processing will only analyze frames with movement, and will ignore areas of the image that have not changed. This parameter controls whether motion detection is enabled (1) or disabled (0). .. code-block:: ini motion_detection = 1 The ALPR processing occurs in parallel, each thread will consume an entire CPU core. Allocating more CPUs for ALPR processing will linearly increase the number of plate reads per second .. code-block:: ini analysis_threads = 2 With motion detection enabled, it's possible to buffer moving images and analyze them as CPU becomes available. For example, if a car moves past the camera over 10 frames, and the CPU can only analyze 3 of those frames during that period, the buffer will allow you to analyze all 10 frames. After the movement is complete and there is no other activity, the ALPR process will drain the buffer looking for license plates during the period of movement. Increasing the buffer_size will increase the duration of this period, but will also consume more memory. The camera_buffer_size unit is number of image frames. .. code-block:: ini camera_buffer_size = 60 Group Results -------------- ALPR group results are sent in the following JSON format: .. code-block:: json { "version": 1, "data_type": "alpr_group", "epoch_start": 1448326208789, "epoch_end": 1448326210046, "company_id": "44c9274f-8d57-432a-8cd7-8c991f22ead3", "site_id": "dimitar-video", "camera_id": 1414799424, "uuids": [ "dimitar-video-cam1414799424-1448326208789", "dimitar-video-cam1414799424-1448326209041", "dimitar-video-cam1414799424-1448326209648", "dimitar-video-cam1414799424-1448326209975", "dimitar-video-cam1414799424-1448326210046" ], "best_plate": { "plate": "A13709", "confidence": 92.075203, "matches_template": 0, "plate_index": 0, "region": "mo", "region_confidence": 0, "processing_time_ms": 96.942459, "requested_topn": 10, "coordinates": [ { "x": 204, "y": 134 }, { "x": 709, "y": 222 }, { "x": 658, "y": 500 }, { "x": 167, "y": 383 } ], "candidates": [ { "plate": "A13709", "confidence": 92.075203, "matches_template": 0 }, { "plate": "A137O9", "confidence": 83.32373, "matches_template": 0 }, { "plate": "A137Q9", "confidence": 83.001274, "matches_template": 0 }, { "plate": "A137D9", "confidence": 82.240067, "matches_template": 0 }, { "plate": "AI3709", "confidence": 80.574394, "matches_template": 0 }, { "plate": "A137U9", "confidence": 80.238014, "matches_template": 0 }, { "plate": "A137G9", "confidence": 78.271233, "matches_template": 0 }, { "plate": "A137B9", "confidence": 78.039688, "matches_template": 0 }, { "plate": "A3709", "confidence": 77.410858, "matches_template": 0 }, { "plate": "AI37O9", "confidence": 71.822922, "matches_template": 0 } ] }, "best_confidence": 92.075203, "best_uuid": "dimitar-video-cam1414799424-1448326208789", "best_plate_number": "A13709" } Heartbeat ------------- Every minute, the OpenALPR agent adds a heartbeat message to the queue. The heartbeat provides general health and status information. The format is as follows: .. code-block:: json { "version": 1, "data_type": "heartbeat", "company_id": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz", "site_id": "your-unique-sitename", "timestamp": 1453426302097, "system_uptime_seconds": 2595123, "daemon_uptime_seconds": 2594832, "internal_ip_address": "192.168.0.54", "cpu_cores": 2, "cpu_last_update": 1453426297878, "cpu_usage_percent": 18.579235, "disk_quota_total_bytes": 8000000000, "disk_quota_consumed_bytes": 0, "disk_quota_last_update": 1453408254586, "memory_consumed_bytes": 2083704832, "memory_last_update": 1453426297878, "memory_swapused_bytes": 0, "memory_swaptotal_bytes": 1608511488, "memory_total_bytes": 2099093504, "processing_threads_active": 1, "processing_threads_configured": 1, "beanstalk_queue_size": 0, "video_streams": [ { "camera_id": 1630410444, "fps": 12, "is_streaming": true, "url": "rtsp://192.168.0.5:554/axis-media/media.amp?videocodec=h264&resolution=1280x720&compression=30&mirror=0&rotation=0&textposition=top&text=1&clock=1&date=0&overlayimage=0&fps=11&keyframe_interval=32&videobitrate=0&maxframesize=0", "last_update": 1453426302086 } ] } Web Services ------------- The OpenALPR daemon exposes a simple web service for retrieving license plate images. Each image is referenced by a UUID that is sent along with the JSON metadata. Assuming that the daemon port is set to the default (8355), the full image is referenced using the following URL: - http://[*Agent_IP*]:8355/img/[*plate_event_uuid*].jpg In some cases, you may prefer to just retrieve a cropped image around the license plate. This would require significantly less bandwidth than downloading the entire source image. The X and Y coordinates can be computed from the JSON metadata x/y coordinates of the license plate. The x1/y1 coordinates reference the top-left of the license plate crop region, and the x2/y2 coordinates reference the bottom right. For example, assuming the crop is located at (477,258), (632,297): - http://[*Agent_IP*]:8355/crop/[*plate_event_uuid*]?x1=477&y1=258&x2=632&y2=297 Additionally, the web server exposes a `web service API `_ for searching license plates and groups. Detailed documentation is available in the :ref:`web server section ` OpenALPR Agent Docker Container ---------------------------------- The OpenALPR Agent Docker container runs the OpenALPR agent within Docker given a license.conf and alprd.conf file that you supply. The image is built on top of Ubuntu 14.04 64-bit. When the container is launched, it runs both Beanstalkd and the alprd daemon. The container exposes the following ports: 1. Port 11300 - Beanstalkd 2. Port 8355 - The OpenALPR daemon web service The /var/lib/openalpr/plateimages/ folder is exposed as a volume that can be attached to the host file system. This location will contain license plate images in a rolling buffer with a maximum size specified in alprd.conf openalpr_2.2.4.orig/doc/source/bindings.rst000077500000000000000000000242021266464252400207720ustar00rootroot00000000000000 .. _language_bindings: ******************** OpenALPR API ******************** OpenALPR is available as a C/C++ library and has bindings in C#, Java, and Python. Additionally, the software can be used as a "black box" that can process video streams and make the data available to another system (using any programming language). C/C++ ================= First, download or compile the OpenALPR library onto your target platform. Make sure that the software runs by testing it using the alpr command-line executable. Pre-compiled binaries are available for 32/64-bit Windows and Ubuntu Linux. 1. Add "alpr.h" as an include file to your project. 2. Include the openalpr.dll (Windows) or libopenalpr.so (Unix) file with your binaries 3. Include all other required shared libraries 4. Put the openalpr.conf and runtime_data directory in the same location as the binaries. Alternatively, you can specify the location of the runtime_data in openalpr.conf or directly in the code. Below is a simple usage example of using the OpenALPR library: .. code-block:: c++ :linenos: // Initialize the library using United States style license plates. // You can use other countries/regions as well (for example: "eu", "au", or "kr") alpr::Alpr openalpr("us", "/path/to/openalpr.conf"); // Optionally specify the top N possible plates to return (with confidences). Default is 10 openalpr.setTopN(20); // Optionally, provide the library with a region for pattern matching. This improves accuracy by // comparing the plate text with the regional pattern. openalpr.setDefaultRegion("md"); // Make sure the library loaded before continuing. // For example, it could fail if the config/runtime_data is not found if (openalpr.isLoaded() == false) { std::cerr << "Error loading OpenALPR" << std::endl; return 1; } // Recognize an image file. You could alternatively provide the image bytes in-memory. alpr::AlprResults results = openalpr.recognize("/path/to/image.jpg"); // Iterate through the results. There may be multiple plates in an image, // and each plate return sthe top N candidates. for (int i = 0; i < results.plates.size(); i++) { alpr::AlprPlateResult plate = results.plates[i]; std::cout << "plate" << i << ": " << plate.topNPlates.size() << " results" << std::endl; for (int k = 0; k < plate.topNPlates.size(); k++) { alpr::AlprPlate candidate = plate.topNPlates[k]; std::cout << " - " << candidate.characters << "\t confidence: " << candidate.overall_confidence; std::cout << "\t pattern_match: " << candidate.matches_template << std::endl; } } C# and VB.NET ==================== .. code-block:: c# :linenos: using openalprnet; var alpr = new AlprNet("us", "/path/to/openalpr.conf", "/path/to/runtime_data"); if (!alpr.IsLoaded()) { Console.WriteLine("OpenAlpr failed to load!"); return; } // Optionally apply pattern matching for a particular region alpr.DefaultRegion = "md"; var results = alpr.Recognize("/path/to/image.jpg"); foreach (var result in results.Plates) { Console.WriteLine("Plate {0}: {1} result(s)", i++, result.TopNPlates.Count); Console.WriteLine(" Processing Time: {0} msec(s)", result.ProcessingTimeMs); foreach (var plate in result.TopNPlates) { Console.WriteLine(" - {0}\t Confidence: {1}\tMatches Template: {2}", plate.Characters, plate.OverallConfidence, plate.MatchesTemplate); } } Python ==================== .. code-block:: python :linenos: from openalpr import Alpr alpr = Alpr("us", "/path/to/openalpr.conf", "/path/to/runtime_data") if not alpr.is_loaded(): print("Error loading OpenALPR") sys.exit(1) alpr.set_top_n(20) alpr.set_default_region("md") results = alpr.recognize_file("/path/to/image.jpg") i = 0 for plate in results['results']: i += 1 print("Plate #%d" % i) print(" %12s %12s" % ("Plate", "Confidence")) for candidate in plate['candidates']: prefix = "-" if candidate['matches_template']: prefix = "*" print(" %s %12s%12f" % (prefix, candidate['plate'], candidate['confidence'])) # Call when completely done to release memory alpr.unload() Java ==================== .. code-block:: java :linenos: import com.openalpr.jni.Alpr; import com.openalpr.jni.AlprPlate; import com.openalpr.jni.AlprPlateResult; import com.openalpr.jni.AlprResults; Alpr alpr = new Alpr("us", "/path/to/openalpr.conf", "/path/to/runtime_data"); // Set top N candidates returned to 20 alpr.setTopN(20); // Set pattern to Maryland alpr.setDefaultRegion("md"); AlprResults results = alpr.recognize("/path/to/image.jpg"); System.out.format(" %-15s%-8s\n", "Plate Number", "Confidence"); for (AlprPlateResult result : results.getPlates()) { for (AlprPlate plate : result.getTopNPlates()) { if (plate.isMatchesTemplate()) System.out.print(" * "); else System.out.print(" - "); System.out.format("%-15s%-8f\n", plate.getCharacters(), plate.getOverallConfidence()); } } // Make sure to call this to release memory alpr.unload(); Node.js ==================== A Node.js binding to OpenALPR is available here: https://www.npmjs.com/package/node-openalpr The source code is available here: https://github.com/netPark/node-openalpr .. _cloud_api: Cloud API (Commercial) ======================= The OpenALPR Cloud API is a web-based service that analyzes images for license plates as well as vehicle information such as make, model, and color. The Cloud API service is easy to integrate into your application via a web-based REST service. You send image data to the OpenALPR API, we process the data, and return JSON data describing the license plate and vehicle. Check out the online demo: http://www.openalpr.com/demo-image.html Sign-Up --------- When you're ready to get started, sign-up for an account at https://cloud.openalpr.com/ Once enrolled, you will automatically be assigned a free account that has a limited number of API credits per month. Each time you use the service, you use one or more API credits. You may enter your credit card information and upgrade your plan to give you access to more credits per month. Integrate ---------- Because the OpenALPR Cloud API is REST-based, it works with any programming language on any operating system. You can make API calls using whatever method you prefer. To make integration easier, the OpenALPR Cloud API also includes permissively licensed open source client libraries in a variety of languages. The GitHub repo is available here: https://github.com/openalpr/cloudapi Check out the `REST API documentation `_ for more detailed information about the REST service. This is generated from the `OpenALPR Cloud API Swagger definition `_ .. _alpr_web_service: Docker-Based Web Service (Commercial) ====================================== The OpenALPR Library Docker container provides the OpenALPR image processing as a web service. In this mode, images are sent to OpenALPR via HTTP POST, and OpenALPR responds with the metadata describing all license plates in the image. This docker image exposes port 8080. Requests into this service are sent as HTTP POST requests to: http://[*ip_address*]:8080/v1/identify/plate The post should contain this parameter: image - A file containing a JPEG image Results will be sent back in the following JSON format: .. code-block:: json { "data_type": "alpr_results", "epoch_time": 1448299357883, "img_height": 480, "img_width": 640, "results": [ { "plate": "AKS4329", "confidence": 86.457352, "region_confidence": 95, "region": "ga", "plate_index": 0, "processing_time_ms": 84.982811, "candidates": [ { "matches_template": 0, "plate": "AKS43Z9", "confidence": 88.429092 }, { "matches_template": 1, "plate": "AKS4329", "confidence": 86.457352 }, { "matches_template": 0, "plate": "AKS3Z9", "confidence": 79.028625 }, { "matches_template": 0, "plate": "AKS329", "confidence": 77.056877 } ], "coordinates": [ { "y": 128, "x": 286 }, { "y": 129, "x": 360 }, { "y": 159, "x": 360 }, { "y": 157, "x": 286 } ], "matches_template": 1, "requested_topn": 20 } ], "version": 2, "processing_time_ms": 172.226624, "regions_of_interest": [] } OpenALPR Agent ==================== OpenALPR can also be configured as a "black box" that makes data available to other systems. When configured in this mode, OpenALPR is installed as a Linux daemon, and is configured to monitor one or more MJPEG video streams. It automatically processes the images and produces JSON data describing the license plates found int he images. This data can either be pushed to another server (as an HTTP POST) or pulled from another server (via beanstalkd queue). More information about the OpenALPR agent is available here: :ref:`alprd`openalpr_2.2.4.orig/doc/source/cloudapi.yaml000066400000000000000000000243641266464252400211350ustar00rootroot00000000000000swagger: '2.0' info: version: "1.0.1" title: OpenALPR Cloud API host: api.openalpr.com basePath: /v1 schemes: - https produces: - application/json paths: # Send an image to OpenALPR and get the analyzed results /recognize: post: description: | Send an image for OpenALPR to analyze and provide metadata back consumes: - "multipart/form-data" produces: - application/json parameters: - name: secret_key in: query description: | The secret key used to authenticate your account. You can view your secret key by visiting https://cloud.openalpr.com/ required: true type: string - name: tasks in: query description: | Tasks to execute. You can choose to detect the license plate, as well as additional metadata about the vehicle. Each additional option costs an API credit. required: true type: array items: type: string enum: [ "plate", "color", "make", "makemodel"] - name: image in: formData description: | The image file that you wish to analyze At least one value for image, image_bytes, or image_url must be provided required: true type: file - name: image_bytes in: query description: | The image file that you wish to analyze encoded in base64 At least one value for image, image_bytes, or image_url must be provided required: false type: string - name: image_url in: query description: | A URL to an image that you wish to analyze At least one value for image, image_bytes, or image_url must be provided required: false type: string - name: country in: query description: | Defines the training data used by OpenALPR. "us" analyzes North-American style plates. "eu" analyzes European-style plates. This field is required if using the "plate" task You may use multiple datasets by using commas between the country codes. For example, 'au,auwide' would analyze using both the Australian plate styles. A full list of supported country codes can be found here https://github.com/openalpr/openalpr/tree/master/runtime_data/config required: false type: string - name: state in: query description: | Corresponds to a US state or EU country code used by OpenALPR pattern recognition. For example, using "md" matches US plates against the Maryland plate patterns. Using "fr" matches European plates against the French plate patterns. required: false type: string - name: return_image in: query description: | If set to 1, the image you uploaded will be encoded in base64 and sent back along with the response required: false type: integer enum: [0, 1] - name: topn in: query description: | The number of results you would like to be returned for plate candidates and vehicle classifications required: false type: integer minimum: 1 maximum: 1000 - name: prewarp in: query description: | Prewarp configuration is used to calibrate the analyses for the angle of a particular camera. More information is available here http://doc.openalpr.com/accuracy_improvements.html#calibration required: false type: string # Expected responses for this operation: responses: # Response code 200: description: OK headers: X-RateLimit-Limit: description: Maximum number of requests allowed from your IP in a period type: integer X-Ratelimit-Remaining: description: Number of remaining requests allowed during this period type: integer X-Ratelimit-Reset: description: Epoch time when the next period begins type: integer schema: properties: total_processing_time: type: number description: Time spent processing all tasks (in milliseconds) img_width: type: integer description: Width of the uploaded image in pixels img_height: type: integer description: Height of the input image in pixels credit_cost: type: integer description: The number of API credits that were used to process this image credits_monthly_used: type: integer description: The number of API credits used this month credits_monthly_total: type: integer description: The maximum number of API credits available this month according to your plan makemodel: type: array description: Results from makemodel vehicle analysis task items: $ref: '#/definitions/classification' make: type: array description: Results from make vehicle analysis task items: $ref: '#/definitions/classification' color: type: array description: Results from color vehicle analysis task items: $ref: '#/definitions/classification' plate: $ref: '#/definitions/alpr_plate' 400: description: Parameter is invalid schema: properties: error: type: string description: Text error message describing the invalid input 401: description: User not authorized or invalid secret_key schema: properties: error: type: string description: Text error message describing the invalid input 402: description: Monthly usage limit exceeded schema: properties: error: type: string description: Text error message describing the invalid input 403: description: Temporary rate-limit exceeded schema: properties: error: type: string description: Text error message describing the invalid input definitions: classification: type: object properties: confidence: type: number value: type: string coordinate: type: object properties: x: type: integer y: type: integer region_of_interest: type: object properties: x: type: integer y: type: integer width: type: integer height: type: integer plate_candidate: type: object properties: plate: type: string description: Plate number confidence: type: number description: Confidence percentage that the plate number is correct matches_template: type: integer description: Indicates whether the plate matched a regional text pattern plate_details: type: object properties: plate: type: string description: Best plate number for this plate matches_template: type: integer description: Indicates whether the plate matched a regional text pattern requested_topn: type: integer description: The max number of results requested processing_time_ms: type: number description: Number of milliseconds to process the license plate confidence: type: number description: Confidence percentage that the plate number is correct region: type: string description: Specified or detected region (e.g., tx for Texas) region_confidence: type: number description: Confidence percentage that the plate region is correct coordinates: type: array description: | The X/Y coordinates of the license plate in the image Four coordinates are provided that form a polygon starting from the top-left and moving clockwise ending in the bottom left items: $ref: '#/definitions/coordinate' candidates: type: array description: All the top N candidates that could be the correct plate number items: $ref: '#/definitions/plate_candidate' alpr_plate: description: Results from plate analysis task type: object properties: results: type: array items: $ref: '#/definitions/plate_details' img_width: type: integer description: Width of the uploaded image in pixels img_height: type: integer description: Height of the input image in pixels regions_of_interest: type: array description: Describes the areas analyzed in the input image items: $ref: '#/definitions/region_of_interest' epoch_time: # This is actually an integer, but setting to number to avoid issues with overflow type: number description: Epoch time that the image was processed in milliseconds version: type: integer description: API format version data_type: type: string enum: ["alpr_results", "alpr_group", "heartbeat"] description: Specifies the type of data in this response processing_time_ms: type: number description: Number of milliseconds to process all license plates openalpr_2.2.4.orig/doc/source/commandline.rst000077500000000000000000000063261266464252400214720ustar00rootroot00000000000000.. _alpr_command_line: ************************ Command Line Utility ************************ The OpenALPR Command Line Interface (CLI) utility is a great way to quickly test ALPR against images, videos, or webcams. It is not recommended for sophisticated integration, since each time the CLI utility loads, it takes a number of seconds to initialize all of the OpenALPR recognition data. Usage ------ :: alpr [-c ] [--config ] [-n ] [--seek ] [-p ] [--motion] [--clock] [-d] [-j] [--] [--version] [-h] <> ... Where: -c , --country Country code to identify (either us for USA or eu for Europe). Default=us --config Path to the openalpr.conf file -n , --topn Max number of possible plate numbers to return. Default=10 --seek Seek to the specied millisecond in a video file. Default=0 -p , --pattern Attempt to match the plate number against a plate pattern (e.g., md for Maryland, ca for California) --motion Use motion detection on video file or stream. Default=off --clock Measure/print the total time to process image and all plates. Default=off -d, --detect_region Attempt to detect the region of the plate image. [Experimental] Default=off -j, --json Output recognition results in JSON format. Default=off --, --ignore_rest Ignores the rest of the labeled arguments following this flag. --version Displays version information and exits. -h, --help Displays usage information and exits. <> (accepted multiple times) (required) Image containing license plates Examples ----------- This command will attempt to recognize number plates in the /source/image.jpg image using the European-style recognition data. The config file is not provided on the CLI, so it will use the value in the environment variable 'OPENALPR_CONFIG_FILE' if provided, or the default location. :: $ alpr -c eu /source/image.jpg This command will attempt to recognize number plates in the /source/image.png image using the default USA-style recognition data. The config file is not provided on the CLI, so it will read the configuration data from /tmp/openalpr.conf :: $ alpr --config /tmp/openalpr.conf /source/image.png This command will attempt to recognize number plates in all jpeg images in the current directory image using the USA-style recognition data. :: $ alpr -c us *.jpg This command reads data from an input video (/source/video.mp4) and outputs recognition data as JSON. :: $ alpr -j /source/video.mp4 This command processes a list of image files provided in /source/imagefilelist.txt and writes JSON results to /out/recognitionresults.txt. :: $ alpr -j stdin < /source/imagefilelist.txt > /out/recognitionresults.txt This command processes video from your webcam. You can also use /dev/video0, /dev/video1, etc. if you have multiple webcams. :: $ alpr webcam openalpr_2.2.4.orig/doc/source/commercial.rst000077500000000000000000000103351266464252400213120ustar00rootroot00000000000000.. _commercial: ************************* Commercial Enhancements ************************* OpenALPR is commercially supported open source software. OpenALPR is licensed under dual licenses to meet the needs of open source users as well as for-profit commercial entities. The software may be used under the terms of the Affero `GNU Public License v3 (AGPL) `_. However, this license has strong copyleft requirements that most for-profit companies cannot use. For this reason, we also offer the software under a commercial license. Please contact info@openalpr.com for license pricing and terms. The OpenALPR commercial license overrides the AGPL terms and allows OpenALPR to be used without copyleft requirements. The software may then be used, integrated, and distributed in closed-source proprietary applications. Additionally, there are a number of features and enhancements that are available exclusively to commercial customers. .. _commercial_enhancements: - Multi-core processing - Efficient processing via motion detection - License plate grouping - High-accuracy US state recognition - Support for H264 video streams - On-Premises web server - Video file processing Multi-Core Processing ----------------------- The OpenALPR agent utilizes multiple CPU cores in parallel to improve the analysis frame rate. Faster processing allows OpenALPR to record number plates for vehicles at higher speeds and will also contribute to higher accuracy at lower speeds due to plate grouping. The "analysis_threads" configuration property in alprd.conf controls the number of simmultaneous CPU cores used to process license plates. Additionally, if a GPU is available (either via OpenCL or Nvidia CUDA) the agent can make use of this to accelerate license plate processing. Efficient Processing via Motion Detection ------------------------------------------- Utilizing motion detection greatly increases the efficiency of the OpenALPR agent. Rather than monitoring all pixels of every frame in a video, the software ignores areas in the video that have not changed (and therefore could not contain a license plate). When motion is detected, only the portion where the vehicle is located will be analyzed. To provide the most possible reads, OpenALPR also utilizes a configurable image buffer. When there is lots of motion detected, the video frames are placed into this buffer and processed. Therefore, if the video has moments of inactivity, the CPU resources will remain utilized processing older video data in order to provide the most possible license plate reads. License Plate Grouping ----------------------- In a video stream, a single license plate is often seen many times as it travels past the camera. For example, if the vehicle passes the camera over the course of 2 seconds at 30 frames per second, OpenALPR may recognize that same license plate 60 times. The plate grouping feature tracks the license plate as it moves, and delivers a single result for the license plate number that is scored based on the number of recognitions. Therefore, high-speed processing produces a highly accurate license plate number. High-Accuracy US State Recognition ------------------------------------ This feature determines the US state for a given license plate. For example, OpenALPR will differentiate a Maryland license plate versus one from California. This also increases accuracy, since each state has a unique text pattern. Knowing the originating state for the license plate allows OpenALPR to match the text results against the unique state pattern. Support for H264 Video Streams -------------------------------- The OpenALPR Commercial agent has more ubiquitous support for connecting to IP camera video streams. In addition to MJPEG, OpenALPR also supports H264 over both RTSP and HTTP. On-Premises Web Server ------------------------- The commercial web server is a data repository for license plate information. License plates is browsable, searchable, and triggers e-mail alerts for matching plate numbers. Video File Processing ---------------------- OpenALPR has a utility that efficiently processes video files to produce a CSV output containing all the license plates found in the video stream. openalpr_2.2.4.orig/doc/source/compiling.rst000077500000000000000000000200461266464252400211600ustar00rootroot00000000000000************************ Compiling ************************ Windows ============= Using Precompiled Binaries --------------------------- Precompiled binaries are available for Windows in both 32 and 64-bit format. You can find the latest binaries on the `GitHub releases page `_. This is the simplest option for installing OpenALPR onto a Windows machine. Included in the package are the Visual Studio 2015 runtime drivers. You must install this first in order to use the OpenALPR software. Compiling OpenALPR via Build Script ------------------------------------ `OpenALPR Windows Build Scripts `_ Compile OpenALPR and Dependencies Manually -------------------------------------------- `Video Tutorial `_ * Ensure you have a version of Visual Studio that is at least 2008 or above * Download and install `cmake for windows `_ * Download `OpenCV for Windows `_ (3.0.0 at this time) * Download `Tesseract OCR source code and VS2008 project files `_ (3.04 at this time) * Download `Leptonica vs2008 development package `_ (Tesseract requirement) * Download `OpenALPR source code `_ from GitHub * Create a libraries directory and put opencv and Tesseract into them * Compile OpenCV. `cd` into the opencv directory and type cmake . to create the VisualStudio project. * Compile Tesseract. Tesseract requires that you point it to the leptonica headers and the library binaries before it will compile. Note that tesseract needs to be compiled as a LIBRARY. Make sure also that your compile mode matches for each of the projects (e.g., Release vs Debug). * Update the CMakeLists.txt file in the OpenALPR src directory to point to the folders for your Tesseract and OpenCV libraries * Depending on the versions of the libraries, you may need to tweak the linked libraries in here as well (e.g., liblept168 may be liblept173 at some point in the future). These correspond to the library files for Leptonica and Tesseract. * CD into the openalpr src directory and type "cmake ." * Open the Visual Studio solution and compile. * If all goes well, there should be an "alpr" executable. In order to execute it, you will need a number of DLLs from OpenCV. simply finding them in the OpenCV directories and copying them over to your executable should do the trick. Linux ============= OpenALPR compiles on many flavors of Linux. However, the software is officially supported on Ubuntu 14.04 64-bit. Using Precompiled Binaries --------------------------- Precompiled binaries are available for Ubuntu 14.04 64-bit OS. This is the simplest option for installing OpenALPR onto a Linux machine. The precompiled binaries are managed via APT, so upgrades will automatically be installed when you use the *apt-get update && apt-get upgrade* command. To install the precompiled binaries, run the following commands on your Ubuntu machine. .. code-block:: bash wget -O - http://deb.openalpr.com/openalpr.gpg.key | sudo apt-key add - echo "deb http://deb.openalpr.com/master/ trusty main" | sudo tee /etc/apt/sources.list.d/openalpr.list sudo apt-get update sudo apt-get install openalpr openalpr-daemon openalpr-utils libopenalpr-dev Test the library .. code-block:: bash # Test US plates wget http://plates.openalpr.com/ea7the.jpg alpr -c us ea7the.jpg # Test European plates wget http://plates.openalpr.com/h786poj.jpg alpr -c eu h786poj.jpg Compiling OpenALPR ------------------- .. code-block:: bash # Install prerequisites sudo apt-get install libopencv-dev libtesseract-dev git cmake build-essential libleptonica-dev sudo apt-get install liblog4cplus-dev libcurl3-dev # If using the daemon, install beanstalkd sudo apt-get install beanstalkd # Clone the latest code from GitHub git clone https://github.com/openalpr/openalpr.git # Setup the build directory cd openalpr/src mkdir build cd build # setup the compile environment cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_INSTALL_SYSCONFDIR:PATH=/etc .. # compile the library make # Install the binaries/libraries to your local system (prefix is /usr) sudo make install # Test the library wget http://plates.openalpr.com/h786poj.jpg -O lp.jpg alpr lp.jpg Compile OpenALPR and all Dependencies -------------------------------------- 1. Make sure that dependencies and required tools are installed * sudo apt-get install libpng12-dev libjpeg62-dev libtiff4-dev zlib1g-dev * sudo apt-get install build-essential * sudo apt-get install autoconf automake libtool * sudo apt-get install git-core * sudo apt-get install cmake 2. install opencv (tutorial) * http://docs.opencv.org/2.4/doc/tutorials/introduction/linux_install/linux_install.html 3. download and install leptonica and tesseract-ocr * tesseract-ocr requires leptonica and at least one language package. * http://www.leptonica.org/source/leptonica-1.70.tar.gz * https://tesseract-ocr.googlecode.com/files/tesseract-ocr-3.02.02.tar.gz * https://tesseract-ocr.googlecode.com/files/tesseract-ocr-3.02.eng.tar.gz * move the downloaded tarballs to some directory. I will assume that they are located at /usr/local/src/openalpr/. 4. unpack the tarballs: * tar xf /usr/local/src/openalpr/tesseract-ocr-3.02.02.tar.gz * tar xf /usr/local/src/openalpr/tesseract-ocr-3.02.02.eng.tar.gz * tar xf /usr/local/src/openalpr/leptonica-1.70.tar.gz 5. compile leptonica: * cd /usr/local/src/openalpr/leptonica-1.70/ * ./configure --prefix=/usr/local * make * make install 6. compile tesseract: * cd /usr/local/src/openalpr/tesseract-ocr/ * ./autogen.sh * ./configure * make * sudo make install * sudo ldconfig 7. clone the openalpr repo to /usr/local/src/openalpr/ directory * cd /usr/local/src/openalpr/ * git clone https://github.com/openalpr/openalpr.git 8. update CMakeLists.txt compile openalpr * cd /usr/local/src/openalpr/openalpr/ * gedit CMakeLists.txt & * SET(OpenCV_DIR "/usr/local/lib") * SET(Tesseract_DIR "/usr/local/src/openalpr/tesseract-ocr") * cmake ./ * make Note: For Tesseract 3.04 the source files can be downloaded from the main svn branch or https://drive.google.com/folderview?id=0B7l10Bj_LprhQnpSRkpGMGV2eE0&usp=sharing#list. Mac OS X ========= Instructions for compiling on OS X, tested on OS X 10.9.5 (Mavericks). Using Homebrew --------------- * brew tap homebrew/science * brew install openalpr Compiling OpenALPR Manually ---------------------------- .. code-block:: bash # Clone the latest code from GitHub git clone https://github.com/openalpr/openalpr.git # Setup the build directory cd openalpr/src mkdir build cd build # setup the compile environment cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_INSTALL_SYSCONFDIR:PATH=/etc .. # compile the library make # Install the binaries/libraries to your local system (prefix is /usr) sudo make install # Test the library wget http://easy-clan.com/ski/pics/license_plate.JPG -O lp.jpg alpr lp.jpg Mobile (iOS and Android) ============================== The OpenALPR library compiles on Android and iOS. Example reference apps are available: - `Android `_ - `iOS `_ Docker ============= OpenALPR supports containerization inside Docker. It uses Ubuntu 14.04 as a base image, and installs all the software using pre-compiled binaries. Download the OpenALPR DockerFile and run the following commands to build it: .. code-block:: bash # Build docker image docker build -t openalpr https://github.com/openalpr/openalpr.git # Download test image wget http://plates.openalpr.com/h786poj.jpg # Run alpr on image docker run -it --rm -v $(pwd):/data:ro openalpr -c eu h786poj.jpgopenalpr_2.2.4.orig/doc/source/conf.py000077500000000000000000000217521266464252400177510ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # openalpr documentation build configuration file, created by # sphinx-quickstart on Sun Nov 15 23:25:01 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import shlex # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'openalpr' copyright = u'2015, OpenALPR Technology, Inc.' author = u'Matthew Hill' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '2.2.0' # The full version, including alpha/beta/rc tags. release = '2.2.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' #html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value #html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'openalprdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', # Latex figure (float) alignment #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'openalpr.tex', u'openalpr Documentation', u'Matthew Hill', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'openalpr', u'openalpr Documentation', [author], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'openalpr', u'openalpr Documentation', author, 'openalpr', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False openalpr_2.2.4.orig/doc/source/index.rst000077500000000000000000000035271266464252400203130ustar00rootroot00000000000000.. openalpr documentation master file, created by sphinx-quickstart on Sun Nov 15 23:25:01 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. OpenALPR Documentation ==================================== Getting Started ------------------ OpenALPR is an open source Automatic License Plate Recognition library written in C++ with bindings in C#, Java, Node.js, and Python. The library analyzes images and video streams to identify license plates. The output is the text representation of any license plate characters. The software can be used in many different ways. For example, with OpenALPR you can: #. Recognize license plates from camera streams. The results are :ref:`browsable, searchable and can trigger alerts `. The data repository can be in the cloud or stored entirely within your network on-premises. `*` #. Recognize license plates from camera streams and :ref:`send the results to your own application `. #. :ref:`Process a video file ` and store the license plate results in a CSV and SQLite database. `*` #. Analyze still images from the :ref:`command-line ` #. Integrate license plate recognition into your application :ref:`directly in-code (C/C++, C#, VB.NET, Java, Python, Node.js) ` #. Run OpenALPR as a :ref:`web service `. A JPG image is sent over HTTP POST, the OpenALPR web service responds with the license plate information in the image. `*` `*` Requires :ref:`OpenALPR Commercial License ` Contents: .. toctree:: :maxdepth: 2 compiling commandline bindings alprd web_server video_processing accuracy_improvements commercial Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` openalpr_2.2.4.orig/doc/source/video_processing.rst000066400000000000000000000106421266464252400225370ustar00rootroot00000000000000 .. _video_processing: ****************** Video Processing ****************** `*` Requires Commercial License The video processing is available as both a C/C++ embeddable library as well as a command-line utility. The utility is given a video file (e.g., MPG, AVI, MP4, etc.) and processes it to find all of the license plates in the video. The output is a CSV file that lists the license plate information Command-Line Usage --------------------- The alpr_video command line utility can be executed to analyze video. You will launch the program with a path to a video file. Also include an output directory, this is where the csv and SQLite files are saved. The threads option provides more threads to process the video simmultaneously (this should not exceed the number of CPU cores on the machine). .. code-block:: bash USAGE: ./alpr_video --output_dir [-c ] [--config ] [-n ] [--threads ] [-p ] [--save_frames] [--debug] [--motion] [--] [--version] [-h] Where: --output_dir (required) Path to the output directory -c , --country Country code to identify (either us for USA or eu for Europe). Default=us --config Path to the openalpr.conf file -n , --topn Max number of possible plate numbers to return. Default=10 --threads Number of simmultaneous processing threads. Default=1 -p , --pattern Attempt to match the plate number against a plate pattern (e.g., md for Maryland, ca for California) --save_frames Save the image frames for recognized license plates. Default=off --debug Print diagnostic information to the console. Default=off --motion Use motion detection on video file or stream. Default=on --, --ignore_rest Ignores the rest of the labeled arguments following this flag. --version Displays version information and exits. -h, --help Displays usage information and exits. (required) Video file containing license plates OpenAlpr Video Processing Utility Library Usage -------------------- The library can be executed directly in C/C++. Include the alprvideo.h file, and link the openalprvideo.dll (or libopenalprvideo.so in Unix). The following example will analyze the "license_plate_video.mp4" file. .. code-block:: c++ #include "alprvideo.h" using namespace alpr; int main(int argc, char** argv) { AlprVideo alpr_video("c:\\temp\\license_plate_video.mp4", "us", "c:\\temp\\openalpr.conf"); alpr_video.setOutputDir("C:\\temp\\lpdata\\"); alpr_video.setNumThreads(2); alpr_video.setPatternMatch("md"); alpr_video.setSaveFrames(false); // Video processing can either run in the foreground or background. For background, use start_async() alpr_video.start(); std::cout << "Video processing complete" << std::endl; } Results --------- The results are made available in CSV files as well as a SQLite database for querying. The image frames where license plates were found are also optionally saved to a folder on the disk. The CSV data is exported in two files. One file shows the individual plate reads, and the other shows the plate groups. For example: Plate Results: .. csv-table:: Plate Results id,group_id,country,plate_number,confidence,frame_num,video_time_s,matches_pattern,tracking_hash,x1,y1,x2,y2,x3,y3,x4,y4,img_name,region,region_confidence 1,2,gb,CV60UWK,78.4882,70,2.84,1,,36,235,176,280,169,314,29,267,,gb,0 2,2,gb,CV60UWK,75.4025,73,2.96,1,,134,180,269,220,260,251,127,209,,gb,0 3,2,gb,CV60UWK,83.4606,74,3,1,,167,159,310,199,300,238,159,196,,gb,0 4,2,gb,CV60UWK,82.3763,75,3.04,1,,198,141,332,180,322,215,189,177,,gb,0 Plate Groups: .. csv-table:: Plate Groups id,country,plate_number,matches_pattern,plate_count,frame_start,frame_end,video_time_start_s,video_time_end_s,best_plate_id,confidence,region,region_confidence 2,gb,CV60UK,0,6,70,77,2.84,3.12,6,82.6058,gb,0 1,gb,GP0VZC,0,9,199,211,8,8.48,18,84.4312,gb,0 4,gb,GR15RYT,1,2,981,994,39.28,39.8,39,82.4912,gb,0openalpr_2.2.4.orig/doc/source/web_server.rst000066400000000000000000000276531266464252400213520ustar00rootroot00000000000000.. _web_server: *********************** OpenALPR Web Server *********************** `*` Requires Commercial License Requirements ================ OpenALPR Cloud collects metadata about license plates seen by your cameras. This data is browsable, searchable, and can trigger alerts. The Web server can be available either: #. In the cloud #. Installed On-Premises within your network If you haven't seen the `live demo `_, check it out! It shows a realistic example of the type of information you will soon be collecting from your cameras. In order to get started, you will need: 1. An IP camera capable of serving MJPEG or H264 video 2. An OpenALPR agent installed on your network First, configure your IP camera to capture the area that you wish to monitor. Generally, you'll want the video to be able to capture a clear image of the license plate in order for OpenALPR to properly identify the numbers. You may want to experiment with different optical zoom levels and resolutions to get the best image quality. A straight-on shot of the license plate is best, but OpenALPR can work with shots at an angle. Once your camera is setup, make sure that it has an IP address and that you can connect to the MJPEG or H264 stream. Some cameras support arguments in the URL to control resolution, frame-rate, etc. The Firefox web browser has the best MJPEG support among major browsers. Type in the MJPEG or H264 stream URL to Firefox and you should be able to clearly see your video. Some users reduce the frames per second (fps) of their video feed in order to reduce the stream bandwidth. Architecture ============= .. image:: images/webserver_architecture.png :scale: 100% :alt: OpenALPR Web Server Architecture As depicted in the diagram above, the OpenALPR agent reads a video stream from your IP camera, processes it, and uploads plate metadata to the OpenALPR cloud. The agent also stores all of the plate images on a rolling buffer in its hard drive. There is a constant stream of data flowing between the camera and the agent as well as between the agent and the cloud. The data sent to the cloud is relatively low-bandwidth because it contains text metadata describing the license plates, and not the images. When you log into the OpenALPR Cloud web portal to view your data, that information is retrieved on-demand. OpenALPR Cloud does not store your plate images, these are downloaded directly from the agent when you select a plate to view. Web Server Installation =============================== On an Ubuntu 14.04 64-bit server: Add the OpenALPR GPG key and setup the OpenALPR deb repository .. code-block:: bash wget -O - http://deb.openalpr.com/openalpr.gpg.key | sudo apt-key add - echo "deb http://deb.openalpr.com/commercial/ trusty main" | sudo tee /etc/apt/sources.list.d/openalpr.list sudo apt-get update && sudo apt-get -y install openalpr-web You will be prompted to create a MySQL database password. Type in a value that is secure and that you will remember. Configure OpenALPR by typing: .. code-block:: bash openalpr-web-createdb You will be prompted for the MySQL database password. Type in the same value entered above. Next, you will need to create an administrative user account. Type in an e-mail address and a password for this new user. You will use this to login to the web interface once it's started Connect to the web interface by opening a browser and going to: *http://[System_IP_address]* If you don't already know the system IP address, it can be found by typing ifconfig. Login with the user e-mail and password that you just created. Click on the "Getting Started" link and make note of your "Company ID." You will need this to configure the agents and to setup API integrations. This value should be kept secure and should be treated like a password. Agent Installation =================== The :ref:`OpenALPR agent ` is installed as either a Virtual Machine (VM) or a Debian install for 64-bit Ubuntu Linux. The VM installs in a few minutes and can run on any operating system (e.g., Windows, Linux, Mac OS X). The Debian installer for 64-bit Ubuntu Linux is a more advanced install option. Virtual Machine Install ------------------------- - Start downloading the `latest OpenALPR Virtual Machine `_. - Download and install the `VirtualBox installer `_ for your operating system. The OpenALPR VM will also work with other hypervisors such as VMware, Xen, or HyperV if you prefer to use one of those. - Open VirtualBox and choose File → Import Appliance - Choose the openalpr.ova file downloaded in step #1 - Accept the default memory and CPU settings and click "Import" .. image:: images/webserver_vminstall1.png :scale: 100% :alt: OpenALPR VM installation step 1 - Select the openalpr-agent-vm and click "Start" - The VM should boot up quickly and provide you with a login prompt. Login with the default credentials: admin/admin - You should see a menu like the one below. Use the up/down, tab, and enter keys to navigate the menu. .. image:: images/webserver_vminstall2.png :scale: 100% :alt: OpenALPR VM installation step 2 - First setup the network by choosing **Network** → **eth0**. - Select either DHCP or static depending on your LAN configuration - Select Network → Test and make sure that you can successfully ping www.google.com .. image:: images/webserver_vminstall3.png :scale: 100% :alt: OpenALPR VM installation step 3 - Select **Upgrade** from the main menu. The OpenALPR software is updated more frequently than the VM, there may be updates available. - Optional Steps: - Optionally, select **Password** from the main menu to change your password to something more secure. - You may optionally install the OpenALPR web server onto this VM. Select **Install Web** to start the install process. Doing this will allow you to search, browse, and alert on license plates from your local VM rather than from the cloud. This requires an on-premises license, please contact info@openalpr.com for an evaluation license. - Select **Configure** from the main menu. This will provide you with a URL to connect to in a browser to complete the process. - You will be directed to: http://[Agent-IP-Address]/agentconfig .. image:: images/webserver_vminstall4.png :scale: 100% :alt: OpenALPR VM installation step 4 - In the agentconfig web interface, select **Remote Connection**. This connects your local agent with your web server. If using the cloud service, the web server is located at cloud.openalpr.com. Otherwise it is the IP/hostname of the server where you installed the OpenALPR web software. If you installed OpenALPR on the same system, use **localhost**. - Type in your username (e-mail address) and password, and click **Connect**. You should see a message indicating that the connection was successful. - Click **Agent Config** - You may be prompted to login. Use the e-mail address and password that you just used to authenticate with the web server. .. image:: images/webserver_vminstall5.png :scale: 100% :alt: OpenALPR VM installation step 4 - Scroll to the bottom and add a video stream. - Select the model of IP camera you wish to connect to. Fill in the IP address. If the camera requires credentials, check the box and enter your camera's username and password. - Click **Test**. After a few seconds, you will see a window indicating whether the connection was successful or not. If it was successful, click **Save Camera**. Otherwise, try another option (such as H264 Alt1 or MJPEG) and click **Test** again until you succeed. .. image:: images/webserver_vminstall-testsuccess.png :scale: 100% :alt: OpenALPR VM installation step 4 - Next, configure the **Agent Parameters**. - Choose a sensible name for your **Site ID**. This is usually the location of the agent system (e.g., headquarters, dallas-branch, warehouse3, etc.). Each agent should be given a unique Site ID. - Choose the **country** where the camera is located. US will recognize North American-style plates (12 inches x 6 inches). EU will recognize European-style plates. There is also support for other countries that have plates with different dimensions. - The number of **Processing Cores** controls how much CPU is allocated to the LPR process. The more processing cores you provide (up to the number of CPU cores on the system) the more frames per second (fps) you can process. Higher fps generally contributes to better accuracy and capability to detect plates on faster moving vehicles. - **Disk Quota** controls how much space is reserved for storing vehicle and license plate images. It operates as a rolling buffer, so once it runs out of space, the oldest images are removed. - **Pattern** should be set to the State (in the USA) or country (in Europe) that the camera is located in. This gives OpenALPR additional information about the patterns on the plates. It won't disqualify values from other states/countries, but it will help weigh results to give higher confidence to plates that do match the pattern. - Click **Update**. .. image:: images/webserver_vminstall6.png :scale: 100% :alt: OpenALPR VM installation step 4 - Lastly, if you scroll to the top of the page you can watch the agent status. At this point you should see **Video FPS** and other information indicating that video is being pulled from the camera and license plates are being recognized. Now that the agent is configured, it will continue collecting data from the configured video streams. If the agent is rebooted, it will pick back up on restart. If the camera goes down and comes back, or the network is down temporarily, the agent will retry until connectivity is restored. All results are queued, so no data is lost in the event of an outage. .. image:: images/webserver_vminstall7.png :scale: 100% :alt: OpenALPR VM installation step 4 - Click on the **Web Server** link at the top of the page to start browsing license plate results. On an Ubuntu 14.04 64-bit Linux Install --------------------------------------- Alternatively, you may prefer to install the OpenALPR agent directly onto the server. These steps are not required if you installed the Virtual Machine referenced above. First install a copy of `Ubuntu 14.04 64-bit Linux server `_ and gain console access. From the terminal: .. code-block:: bash # Install the OpenALPR repository GPG key wget -O - http://deb.openalpr.com/openalpr.gpg.key | sudo apt-key add - # Add the OpenALPR repository to your server echo "deb http://deb.openalpr.com/commercial/ trusty main" | sudo tee /etc/apt/sources.list.d/openalpr.list # Install the OpenALPR software sudo apt-get update sudo apt-get install openalpr openalpr-daemon openalpr-utils libopenalpr-dev openalpr-daemonconfig You may use the web interface to configure the agent (http://[agent-ip-address]/agentconfig or you may edit the configuration file /etc/openalpr/alprd.conf Configure the company_id, site_id, country, and stream values as described in the Virtual Machine section. Make sure that the value for upload_data is set to 1 and that the upload_address setting is configured to http://[on_premises_webserver]/push .. code-block:: bash # Restart the alprd process sudo service openalprd-daemon start # Tail the logs to see if the daemon is running successfully tail -f /var/log/alpr.log If all goes well, the log should show that the video stream is being processed and plates are being identified and uploaded. Once a plate is uploaded it should show up on the OpenALPR Cloud dashboard after a few seconds. .. _web_services_api: Web Services API ------------------ The `Web Services API `_ can be used to query your On-Premises server for data. The API is documented `here `_openalpr_2.2.4.orig/doc/source/web_server_api.yaml000066400000000000000000000317701266464252400223300ustar00rootroot00000000000000--- swagger: '2.0' info: title: OpenALPR REST API version: v1.0 description: | The OpenALPR REST API allows you to search license plate data programmatically from the OpenALPR Web server. Using this API you can search for license plate groups, individual reads, and alerts. You can also retrieve the plate images from the agent. tags: - name: webserver description: REST API for the Web Server and plate metadata - name: agent description: REST API for the agent and image data paths: /api/search/group: get: tags: - webserver description: Search for ALPR group results parameters: - description: Company UUID used for authentication in: query name: company_id required: true type: string - description: unique ID for the site in: query name: site required: false type: string - description: unique camera number in: query name: camera required: false type: number - description: Start time in ISO 8601 format. At least start or end must be defined. in: query name: start required: false type: string - description: End time in ISO 8601 format. At least start or end must be defined. in: query name: end required: false type: string - description: Plate Number to search for in: query name: plate required: false type: string - description: Search plate using regular expression. Disabled (0) or Enabled (1) in: query name: wildcard required: false type: number - description: Maximum number of results to return in: query name: topn required: false type: number - description: Order can be either desc or asc in: query name: order required: false type: string responses: '200': description: OK schema: type: array items: $ref: '#/definitions/group' '400': description: "Bad input provided. Adjust the query parameters and try again.\nThe text accompanying the error will provide more detail about which query \nparameters are incorrect.\n" '401': description: "Unauthorized. You must either provide a valid company_id or \nhave a logged in session\n" /api/search/plate: get: tags: - webserver description: Search for individual plate results parameters: - description: Company UUID used for authentication in: query name: company_id required: true type: string - description: unique ID for the site in: query name: site required: false type: string - description: unique camera number in: query name: camera required: false type: number - description: Start time in ISO 8601 format. At least start or end must be defined. in: query name: start required: false type: string - description: End time in ISO 8601 format. At least start or end must be defined. in: query name: end required: false type: string - description: Plate Number to search for in: query name: plate required: false type: string - description: Search plate using regular expression. Disabled (0) or Enabled (1) in: query name: wildcard required: false type: number - description: Maximum number of results to return in: query name: topn required: false type: number - description: Order can be either desc or asc in: query name: order required: false type: string responses: '200': description: OK schema: type: array items: $ref: '#/definitions/plate' '400': description: "Bad input provided. Adjust the query parameters and try again.\nThe text accompanying the error will provide more detail about which query \nparameters are incorrect.\n" '401': description: "Unauthorized. You must either provide a valid company_id or \nhave a logged in session\n" '/api/detail/group/{group_id}': get: tags: - webserver description: Get detailed results for a group record parameters: - in: path name: group_id required: true type: string - description: Company UUID used for authentication in: query name: company_id required: true type: string responses: '200': description: OK schema: $ref: '#/definitions/group_detail' '401': description: "Unauthorized. You must either provide a valid company_id or \nhave a logged in session\n" '404': description: | The requested ID was not found '/api/detail/plate/{plate_id}': get: tags: - webserver description: Get detailed results for a plate record parameters: - in: path name: plate_id required: true type: string - description: Company UUID used for authentication in: query name: company_id required: true type: string responses: '200': description: OK schema: $ref: '#/definitions/plate_detail' '401': description: "Unauthorized. You must either provide a valid company_id or \nhave a logged in session\n" '404': description: | The requested ID was not found '/img/{uuid}.jpg': get: tags: - agent description: Get an image for the requested plate. This web service is served on port 8355. parameters: - in: path name: uuid required: true type: string produces: - image/jpeg responses: '200': description: OK schema: type: array items: $ref: '#/definitions/plate' '404': description: not found securityDefinitions: {} swagger: '2.0' definitions: plate: type: object properties: pk: type: integer description: "Unique integer ID for this plate" model: type: string description: "Name of the data type" fields: type: object properties: best_confidence: type: number description: "Percent confidence of the license plate result" best_plate: type: string description: "The plate number with the highest confidence" uuid: type: string description: "Unique identifier for the image where the license plate was found" epoch_time: type: string description: "Time that the plate was seen in ISO-8601 format" camera: type: integer description: "Unique ID of the camera" company: type: integer description: "Company ID" site: type: string description: "Site ID where the plate was captured" plate_index: type: integer description: "Within a single image (UUID) there may be multiple plates. This is the unique ID of the single plate within an image" img_width: type: integer description: "Image width in pixels" img_height: type: integer description: "Image height in pixels" processing_time_ms: type: number description: "Time in milliseconds taken to identify the license plate from an image" x1: type: integer description: "Pixel coordinates for the license plate. x1,y1 -> x4,y4 forms a polygon starting from the top-left and working clockwise to the bottom-left corners." y1: type: integer x2: type: integer y2: type: integer x3: type: integer y3: type: integer x4: type: integer y4: type: integer group: type: object properties: pk: type: integer description: "Unique integer ID for this plate" model: type: string description: "Name of the data type" fields: type: object properties: best_confidence: type: number description: "Percent confidence of the license plate result" best_plate: type: string description: "The plate number with the highest confidence" best_index: type: integer description: "Within a single image (UUID) there may be multiple plates. This is the unique ID of the single plate within an image" best_uuid: type: string description: "Unique identifier for the image where the highest confidence license plate was found" epoch_time_start: type: string description: "Time that the plate was initially seen in ISO-8601 format" epoch_time_end: type: string description: "Time that the plate was last seen in ISO-8601 format" camera: type: integer description: "Unique ID of the camera" company: type: integer description: "Company ID" site: type: string description: "Site ID where the plate was captured" plate_detail: type: object properties: best_confidence: type: number description: "Percent confidence of the license plate result" best_plate: type: string description: "The plate number with the highest confidence" uuid: type: string description: "Unique identifier for the image where the license plate was found" epoch_time: type: string description: "Time that the plate was seen in ISO-8601 format" camera: $ref: '#/definitions/camera_info' regions_of_interest: type: object description: "Areas that were analyzed. Typically wherever motion is found" properties: x: { type: integer } y: { type: integer } width: { type: integer } height: { type: integer } candidates: type: array items: type: object properties: plate_number: { type: string } confidence: { type: number } coordinates: type: array items: type: object properties: x: { type: integer } y: { type: integer } site: type: string description: "Site ID where the plate was captured" plate_index: type: integer description: "Within a single image (UUID) there may be multiple plates. This is the unique ID of the single plate within an image" img_width: type: integer description: "Image width in pixels" img_height: type: integer description: "Image height in pixels" processing_time_ms: type: number description: "Time in milliseconds taken to identify the license plate from an image" group_detail: type: object properties: id: type: integer description: "Unique ID for the group" camera: $ref: '#/definitions/camera_info' site: type: string description: "Site ID where the plate was captured" epoch_time_start: type: string description: "Time that the plate was initially seen in ISO-8601 format" epoch_time_end: type: string description: "Time that the plate was last seen in ISO-8601 format" best_confidence: type: number description: "Percent confidence of the license plate result" best_plate: type: string description: "The plate number with the highest confidence" best_index: type: integer description: "Within a single image (UUID) there may be multiple plates. This is the unique ID of the single plate within an image" best_uuid: type: string description: "Unique identifier for the image where the highest confidence license plate was found" uuids: type: array items: type: string camera_info: type: object properties: camera_number: type: integer description: "Unique ID of the camera" camera_name: type: string description: "Friendly name of the camera"openalpr_2.2.4.orig/doc/watch_for_changes.sh000077500000000000000000000000561266464252400211440ustar00rootroot00000000000000#!/bin/bash find source/ | entr ./generate openalpr_2.2.4.orig/runtime_data/000077500000000000000000000000001266464252400170475ustar00rootroot00000000000000openalpr_2.2.4.orig/runtime_data/cameras.yaml000066400000000000000000000214561266464252400213560ustar00rootroot00000000000000--- version: 1 manufacturers: A-Linking: mjpeg: - "http://[username]:[password]@[ip_address]/GetData.cgi" Airlink: mjpeg: - "http://[username]:[password]@[ip_address]/mjpeg.cgi" - "http://[username]:[password]@[ip_address]/cgi/mjpg/mjpeg.cgi" - "http://[username]:[password]@[ip_address]/cgi/jpg/image.cgi" Airlive: mjpeg: - "http://[username]:[password]@[ip_address]/video.mjpg" - "http://[username]:[password]@[ip_address]/mjpg/video.mjpg" Airwave: mjpeg: - "http://[username]:[password]@[ip_address]/cgi-bin/pusher.cgi" Arecont: mjpeg: - "http://[username]:[password]@[ip_address]/mjpeg?res=full&x0=0&y0=0&x1=100%&y1=100%&quality=12&doublescan=0&fps=15&ver=HTTP/1.1" - "http://[username]:[password]@[ip_address]/image?res=half&x0=0&y0=0&x1=1600&y1=1200&quality=15&doublescan=0" Aviosys: mjpeg: - "http://[username]:[password]@[ip_address]/GetData.cgi" - "http://[username]:[password]@[ip_address]/cgi-bin/Stream?Video?Authorization=" Axis: mjpeg: - "http://[username]:[password]@[ip_address]/axis-cgi/mjpg/video.cgi?fps=15" h264: - "rtsp://[username]:[password]@[ip_address]/axis-media/media.amp" - "rtsp://[username]:[password]@[ip_address]/mpeg4/media.amp" Bosch: mjpeg: - "http://[username]:[password]@[ip_address]/rtsp_tunnel?h26x=4&line=1&inst=2" h264: - "rtsp://[username]:[password]@[ip_address]/rtsp_tunnel" Bowya: mjpeg: - "http://[username]:[password]@[ip_address]/video.cgi" Canon: mjpeg: - "http://[username]:[password]@[ip_address]/-wvhttp-01-/" - "http://[username]:[password]@[ip_address]/-wvhttp-01-/GetOneShot" - "http://[username]:[password]@[ip_address]/-wvhttp-01-/GetOneShot?frame_count=no_limit" - "http://[username]:[password]@[ip_address]/-wvhttp-01-/GetStillImage" Cisco: h264: - "rtsp://[username]:[password]@[ip_address]/" Convision: mjpeg: - "http://[username]:[password]@[ip_address]/fullsize.push?camera=1&sleep=15" CNB: h264: - "rtsp://[username]:[password]@[ip_address]/" D-Link: mjpeg: - "http://[username]:[password]@[ip_address]/video/mjpg.cgi" - "http://[username]:[password]@[ip_address]/video.cgi" - "http://[username]:[password]@[ip_address]/mjpeg.cgi" - "http://[username]:[password]@[ip_address]/cgi-bin/video.jpg" - "http://[username]:[password]@[ip_address]/IMAGE.jpg" - "http://[username]:[password]@[ip_address]/cgi-bin/video.vam" - "http://[username]:[password]@[ip_address]/_gCVimage.jpg" Digicom: mjpeg: - "http://[username]:[password]@[ip_address]/mjpeg.cgi" Easyn: mjpeg: - "http://[username]:[password]@[ip_address]/videostream.cgi?user=username&pwd=password" Edimax: mjpeg: - "http://[username]:[password]@[ip_address]/jpg/image.jpg" - "http://[username]:[password]@[ip_address]/mjpg/video.mjpg" - "http://[username]:[password]@[ip_address]/snapshot.cgi" Ego: mjpeg: - "http://[username]:[password]@[ip_address]/cgi-bin/sf.cgi" Foscam: mjpeg: - "http://[username]:[password]@[ip_address]/videostream.cgi" - "http://[username]:[password]@[ip_address]/snapshot.cgi" Fulicom: mjpeg: - "http://[username]:[password]@[ip_address]/cgi-bin/sf.cgi" Gadspot: mjpeg: - "http://[username]:[password]@[ip_address]/Jpeg/CamImg.jpg" - "http://[username]:[password]@[ip_address]/GetData.cgi?Status=0" Goscam: mjpeg: - "http://[ip_address]/cgi-bin/Stream?Video?Acc=[username]?Pwd=[password]?webcamPWD=RootCookies00000" Hamlet: mjpeg: - "http://[username]:[password]@[ip_address]/mjpeg.cgi" Hikvision: mjpeg: - "http://[username]:[password]@[ip_address]/cgi-bin/video.jpg?cam=1&quality=3&size=2" h264: - "rtsp://[username]:[password]@[ip_address]/h264/ch1/sub/" - "rtsp://[username]:[password]@[ip_address]:554/Streaming/Channels/1" - "rtsp://[username]:[password]@[ip_address]:554/ch0_0.h264" IQeye: mjpeg: - "http://[username]:[password]@[ip_address]/now.jpg?snap=spush" Intellinet: mjpeg: - "http://[username]:[password]@[ip_address]/jpg/image.jpg" JVC: mjpeg: - "http://[username]:[password]@[ip_address]/api/video?encode=jpeg&framerate=15&boundary=on" Kingnow: mjpeg: - "http://[username]:[password]@[ip_address]/cgi-bin/sf.cgi" Linksys: mjpeg: - "http://[username]:[password]@[ip_address]/img/video.mjpeg" - "http://[username]:[password]@[ip_address]/img/mjpeg.cgi" - "http://[username]:[password]@[ip_address]/img/snapshot.cgi?size=2" - "http://[username]:[password]@[ip_address]/adm/file.cgi?h_videotype=mjpeg&todo=save" Linudix: mjpeg: - "http://[username]:[password]@[ip_address]/cgi-bin/nph-update_4ch.cgi?ch=1" Lumenera: mjpeg: - "http://[username]:[password]@[ip_address]/cgi-bin/nph-video" Lumens: h264: - "rtsp://[username]:[password]@[ip_address]:8557/h264" Marmitek: mjpeg: - "http://[username]:[password]@[ip_address]/cgi/mjpg/mjpeg.cgi" Mobotix: mjpeg: - "http://[username]:[password]@[ip_address]/record/current.jpg" - "http://[username]:[password]@[ip_address]/control/faststream.jpg?stream=full" - "http://[username]:[password]@[ip_address]/faststream.jpg?stream=full&fps=1.0 (1 fps)" - "http://[username]:[password]@[ip_address]/faststream.jpg?stream=full&fps=3.0 (1 fps)" - "http://[username]:[password]@[ip_address]/faststream.jpg?stream=full&fps=0 (max frame rate)" Moxa: mjpeg: - "http://[username]:[password]@[ip_address]/cgi-bin/video.jpg" PLANET: mjpeg: - "http://[username]:[password]@[ip_address]/jpg/image.jpg" Panasonic: mjpeg: - "http://[username]:[password]@[ip_address]/nphMotionJpeg?Resolution=640x480&Quality=Clarity" - "http://[username]:[password]@[ip_address]/cgi-bin/nphContinuousServerPush" - "http://[username]:[password]@[ip_address]/SnapshotJPEG?mode=Refresh" - "http://[username]:[password]@[ip_address]/cgi-bin/camera" h264: - "rtsp://[username]:[password]@[ip_address]/MediaInput/h264/stream_1" Pixord: mjpeg: - "http://[username]:[password]@[ip_address]/Getimage.cgi" - "http://[username]:[password]@[ip_address]/Getimage?camera=1&fmt=full" - "http://[username]:[password]@[ip_address]/Getimage?camera=1&fmt=qsif" Qnap: mjpeg: - "http://[username]:[password]@[ip_address]/cgi/mjpg/mjpeg.cgi" Samsung_SNB: mjpeg: - "http://[username]:[password]@[ip_address]/video?submenu=mjpg" - "http://[username]:[password]@[ip_address]/video?submenu=jpg" Sanyo: mjpeg: - "http://[username]:[password]@[ip_address]/liveimg.cgi?serverpush=1" Sharkx: mjpeg: - "http://[username]:[password]@[ip_address]/stream.jpg" Shenzen_Sunsky: mjpeg: - "http://[username]:[password]@[ip_address]/cgi-bin/sf.cgi" Skyway_Security: mjpeg: - "http://[username]:[password]@[ip_address]/GetData.cgi?Status=0" - "http://[username]:[password]@[ip_address]/Jpeg/CamImg.jpg" Sony: mjpeg: - "http://[username]:[password]@[ip_address]/image" - "http://[username]:[password]@[ip_address]/image?speed=0" - "http://[username]:[password]@[ip_address]/oneshotimage.jpg" Surecom: mjpeg: - "http://[username]:[password]@[ip_address]/mjpeg.cgi" Swann: mjpeg: - "http://[username]:[password]@[ip_address]/cgi/jpg/image.cgi" TP-Link: mjpeg: - "http://[username]:[password]@[ip_address]/jpg/image.jpg" - "http://[username]:[password]@[ip_address]/video.mjpg" Topcom: mjpeg: - "http://[username]:[password]@[ip_address]/mjpeg.cgi" Toshiba: mjpeg: - "http://[username]:[password]@[ip_address]/__live.jpg?&&&" - "http://[username]:[password]@[ip_address]getstream.cgi?10&10&&&10&0&0&0&0" Trendnet: mjpeg: - "http://[username]:[password]@[ip_address]/goform/video" - "http://[username]:[password]@[ip_address]/goform/video2" - "http://[username]:[password]@[ip_address]/cgi/mjpg/mjpg.cgi" - "http://[username]:[password]@[ip_address]/GetData.cgi" - "http://[username]:[password]@[ip_address]/image.jpg" Vilar: mjpeg: - "http://[username]:[password]@[ip_address]/cgi-bin/sf.cgi" Vivotek: mjpeg: - "http://[username]:[password]@[ip_address]/video.mjpg" - "http://[username]:[password]@[ip_address]/cgi-bin/video.jpg" - "http://[username]:[password]@[ip_address]/cgi-bin/viewer/video.jpg" h264: - "rtsp://[username]:[password]@[ip_address]/live.sdp" Y-Cam: mjpeg: - "http://[username]:[password]@[ip_address]/stream.jpg" Zavio: mjpeg: - "http://[username]:[password]@[ip_address]/jpg/image.jpg" openalpr_2.2.4.orig/runtime_data/config/000077500000000000000000000000001266464252400203145ustar00rootroot00000000000000openalpr_2.2.4.orig/runtime_data/config/au.conf000066400000000000000000000022761266464252400215770ustar00rootroot00000000000000; 35-50; 45-60, 55-70, 65-80, 75-90 char_analysis_min_pct = 0.35 char_analysis_height_range = 0.15 char_analysis_height_step_size = 0.10 char_analysis_height_num_steps = 5 segmentation_min_speckle_height_percent = 0.3 segmentation_min_box_width_px = 4 segmentation_min_charheight_percent = 0.4; segmentation_max_segment_width_percent_vs_average = 1.6; plate_width_mm = 372 plate_height_mm = 135 multiline = 0 char_height_mm = 82 char_width_mm = 45 char_whitespace_top_mm = 18 char_whitespace_bot_mm = 30 template_max_width_px = 165 template_max_height_px = 60 ; Higher sensitivity means less lines plateline_sensitivity_vertical = 22 plateline_sensitivity_horizontal = 50 ; Regions smaller than this will be disqualified min_plate_size_width_px = 85 min_plate_size_height_px = 28 ; Results with fewer or more characters will be discarded postprocess_min_characters = 4 postprocess_max_characters = 8 ocr_language = lau ; Override for postprocess letters/numbers regex. postprocess_regex_letters = [A-Z] postprocess_regex_numbers = [0-9] ; Whether the plate is always dark letters on light background, light letters on dark background, or both ; value can be either always, never, or auto invert = autoopenalpr_2.2.4.orig/runtime_data/config/auwide.conf000066400000000000000000000022521266464252400224420ustar00rootroot00000000000000 ; 35-50; 45-60, 55-70, 65-80, 75-90 char_analysis_min_pct = 0.35 char_analysis_height_range = 0.15 char_analysis_height_step_size = 0.10 char_analysis_height_num_steps = 5 segmentation_min_box_width_px = 5 segmentation_min_charheight_percent = 0.4; segmentation_max_segment_width_percent_vs_average = 2.0; plate_width_mm = 520 plate_height_mm = 110 multiline = 0 char_height_mm = 80 char_width_mm = 53 char_whitespace_top_mm = 10 char_whitespace_bot_mm = 10 template_max_width_px = 184 template_max_height_px = 46 ; Higher sensitivity means less lines plateline_sensitivity_vertical = 18 plateline_sensitivity_horizontal = 55 ; Regions smaller than this will be disqualified min_plate_size_width_px = 100 min_plate_size_height_px = 20 ; Results with fewer or more characters will be discarded postprocess_min_characters = 4 postprocess_max_characters = 8 detector_file = eu.xml ocr_language = lau ; Override for postprocess letters/numbers regex. postprocess_regex_letters = [A-Z] postprocess_regex_numbers = [0-9] ; Whether the plate is always dark letters on light background, light letters on dark background, or both ; value can be either always, never, or auto invert = autoopenalpr_2.2.4.orig/runtime_data/config/eu.conf000066400000000000000000000023401266464252400215730ustar00rootroot00000000000000; One-line European style plates ; 35-50; 45-60, 55-70, 65-80, 75-90 char_analysis_min_pct = 0.35 char_analysis_height_range = 0.15 char_analysis_height_step_size = 0.10 char_analysis_height_num_steps = 5 segmentation_min_speckle_height_percent = 0.2 segmentation_min_box_width_px = 5 segmentation_min_charheight_percent = 0.4; segmentation_max_segment_width_percent_vs_average = 2.0; plate_width_mm = 520 plate_height_mm = 110 multiline = 0 char_height_mm = 80 char_width_mm = 53 char_whitespace_top_mm = 10 char_whitespace_bot_mm = 10 template_max_width_px = 184 template_max_height_px = 46 ; Higher sensitivity means less lines plateline_sensitivity_vertical = 18 plateline_sensitivity_horizontal = 55 ; Regions smaller than this will be disqualified min_plate_size_width_px = 65 min_plate_size_height_px = 18 ; Results with fewer or more characters will be discarded postprocess_min_characters = 5 postprocess_max_characters = 8 ocr_language = leu ; Override for postprocess letters/numbers regex. postprocess_regex_letters = [A-Z] postprocess_regex_numbers = [0-9] ; Whether the plate is always dark letters on light background, light letters on dark background, or both ; value can be either always, never, or auto invert = autoopenalpr_2.2.4.orig/runtime_data/config/gb.conf000066400000000000000000000023711266464252400215560ustar00rootroot00000000000000; One-line European style plates ; 35-50; 45-60, 55-70, 65-80, 75-90 char_analysis_min_pct = 0.35 char_analysis_height_range = 0.15 char_analysis_height_step_size = 0.10 char_analysis_height_num_steps = 5 segmentation_min_speckle_height_percent = 0.2 segmentation_min_box_width_px = 5 segmentation_min_charheight_percent = 0.4; segmentation_max_segment_width_percent_vs_average = 2.0; plate_width_mm = 520 plate_height_mm = 110 multiline = 0 char_height_mm = 80 char_width_mm = 53 char_whitespace_top_mm = 10 char_whitespace_bot_mm = 10 template_max_width_px = 184 template_max_height_px = 46 ; Higher sensitivity means less lines plateline_sensitivity_vertical = 18 plateline_sensitivity_horizontal = 55 ; Regions smaller than this will be disqualified min_plate_size_width_px = 65 min_plate_size_height_px = 18 ; Results with fewer or more characters will be discarded postprocess_min_characters = 5 postprocess_max_characters = 8 detector_file = eu.xml ocr_language = lgb ; Override for postprocess letters/numbers regex. postprocess_regex_letters = [A-Z] postprocess_regex_numbers = [0-9] ; Whether the plate is always dark letters on light background, light letters on dark background, or both ; value can be either always, never, or auto invert = auto openalpr_2.2.4.orig/runtime_data/config/kr.conf000066400000000000000000000023241266464252400216000ustar00rootroot00000000000000 ; 35-50; 45-60, 55-70, 65-80, 75-90 char_analysis_min_pct = 0.35 char_analysis_height_range = 0.15 char_analysis_height_step_size = 0.10 char_analysis_height_num_steps = 5 segmentation_min_speckle_height_percent = 0.15 segmentation_min_box_width_px = 4 segmentation_min_charheight_percent = 0.2 segmentation_max_segment_width_percent_vs_average = 2.0 plate_width_mm = 520 plate_height_mm = 110 multiline = 0 char_height_mm = 80 char_width_mm = 43 char_whitespace_top_mm = 10 char_whitespace_bot_mm = 10 template_max_width_px = 184 template_max_height_px = 46 ; Higher sensitivity means less lines plateline_sensitivity_vertical = 18 plateline_sensitivity_horizontal = 55 ; Regions smaller than this will be disqualified min_plate_size_width_px = 100 min_plate_size_height_px = 20 ; Results with fewer or more characters will be discarded postprocess_min_characters = 7 postprocess_max_characters = 7 detector_file = eu.xml ocr_language = lkr ; Override for postprocess letters/numbers regex. postprocess_regex_letters = \pL postprocess_regex_numbers = [0-9] ; Whether the plate is always dark letters on light background, light letters on dark background, or both ; value can be either always, never, or auto invert = autoopenalpr_2.2.4.orig/runtime_data/config/mx.conf000066400000000000000000000023241266464252400216100ustar00rootroot00000000000000; 30-50, 40-60, 50-70, 60-80 char_analysis_min_pct = 0.30 char_analysis_height_range = 0.20 char_analysis_height_step_size = 0.10 char_analysis_height_num_steps = 4 segmentation_min_speckle_height_percent = 0.3 segmentation_min_box_width_px = 4 segmentation_min_charheight_percent = 0.5; segmentation_max_segment_width_percent_vs_average = 1.35; plate_width_mm = 304.8 plate_height_mm = 152.4 multiline = 0 char_height_mm = 70 char_width_mm = 35 char_whitespace_top_mm = 38 char_whitespace_bot_mm = 38 template_max_width_px = 120 template_max_height_px = 60 ; Higher sensitivity means less lines plateline_sensitivity_vertical = 25 plateline_sensitivity_horizontal = 45 ; Regions smaller than this will be disqualified min_plate_size_width_px = 70 min_plate_size_height_px = 35 ; Results with fewer or more characters will be discarded postprocess_min_characters = 5 postprocess_max_characters = 7 detector_file = us.xml ocr_language = lus ; Override for postprocess letters/numbers regex. postprocess_regex_letters = [A-Z] postprocess_regex_numbers = [0-9] ; Whether the plate is always dark letters on light background, light letters on dark background, or both ; value can be either always, never, or auto invert = auto openalpr_2.2.4.orig/runtime_data/config/sg.conf000066400000000000000000000021301266464252400215700ustar00rootroot00000000000000; One-line European style plates ; 35-50; 45-60, 55-70, 65-80, 75-90 char_analysis_min_pct = 0.35 char_analysis_height_range = 0.15 char_analysis_height_step_size = 0.10 char_analysis_height_num_steps = 5 segmentation_min_box_width_px = 5 segmentation_min_charheight_percent = 0.4; segmentation_max_segment_width_percent_vs_average = 2.0; plate_width_mm = 520 plate_height_mm = 110 multiline = 0 char_height_mm = 70 char_width_mm = 52 char_whitespace_top_mm = 10 char_whitespace_bot_mm = 10 template_max_width_px = 226 template_max_height_px = 48 ; Higher sensitivity means less lines plateline_sensitivity_vertical = 25 plateline_sensitivity_horizontal = 70 ; Regions smaller than this will be disqualified min_plate_size_width_px = 65 min_plate_size_height_px = 18 detector_file = eu.xml ocr_language = lsg ; Override for postprocess letters/numbers regex. postprocess_regex_letters = [A-HJ-NP-Z] postprocess_regex_numbers = [0-9] ; Whether the plate is always dark letters on light background, light letters on dark background, or both ; value can be either always, never, or auto invert = auto openalpr_2.2.4.orig/runtime_data/config/us.conf000066400000000000000000000022731266464252400216160ustar00rootroot00000000000000; 30-50, 40-60, 50-70, 60-80 char_analysis_min_pct = 0.30 char_analysis_height_range = 0.20 char_analysis_height_step_size = 0.10 char_analysis_height_num_steps = 4 segmentation_min_speckle_height_percent = 0.3 segmentation_min_box_width_px = 4 segmentation_min_charheight_percent = 0.5; segmentation_max_segment_width_percent_vs_average = 1.35; plate_width_mm = 304.8 plate_height_mm = 152.4 multiline = 0 char_height_mm = 70 char_width_mm = 35 char_whitespace_top_mm = 38 char_whitespace_bot_mm = 38 template_max_width_px = 120 template_max_height_px = 60 ; Higher sensitivity means less lines plateline_sensitivity_vertical = 25 plateline_sensitivity_horizontal = 45 ; Regions smaller than this will be disqualified min_plate_size_width_px = 70 min_plate_size_height_px = 35 ; Results with fewer or more characters will be discarded postprocess_min_characters = 4 postprocess_max_characters = 8 ocr_language = lus ; Override for postprocess letters/numbers regex. postprocess_regex_letters = [A-Z] postprocess_regex_numbers = [0-9] ; Whether the plate is always dark letters on light background, light letters on dark background, or both ; value can be either always, never, or auto invert = autoopenalpr_2.2.4.orig/runtime_data/keypoints/000077500000000000000000000000001266464252400210745ustar00rootroot00000000000000openalpr_2.2.4.orig/runtime_data/keypoints/us/000077500000000000000000000000001266464252400215235ustar00rootroot00000000000000openalpr_2.2.4.orig/runtime_data/keypoints/us/ak2008.jpg000066400000000000000000000246661266464252400231500ustar00rootroot00000000000000JFIFC  !"$"$C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?fg*u5w<16?[?fm>|iׯ1Zޘ/o'yc=~ >Ѽ4.1 @ns-ԧHy$zt] Vrm$v}ML՟,L/^? p~N=L8x\K_`?//_8Cw?n^? qwzʚq?*f[ 4?*_ /PUYH WIs +v $jCYMo>?x*ucIעG_5dV W¯!;7[@oʁEK"W*/1x|*tUC?sh?*= ;?v z~]&a\yS+N.\OHx*tUC?sjmD{v@l G;)oo1V޻hf}* H' 8Zz'yҟbӌ*EI4"~':B?t/ Z?M_z:i$~7 YeE,M_z:>ZL& O麍ehl|MyՋO:i5V:RmDE,A_+x#,Yoz.w1Im5A=?isEؗT,v0kŭ7^[6.#Yb>#^PUsZ.5c(M<З{-y\ztg.I9GP=w_|sj>*kZ&㋯ Z$66 9@nP\I9㎞^JԵj&r[i?*Đs^iBTtv̆ib%dHP$wZ_&_5)ffHMY=UXv㫊7#T)RqQk-4im^,k4v};5ЧWԬ>-VXqܼq:CF=}TqejoHBe{`pHUj^42T=R@-̿ -^.w:2~6DN} Lx z 0XzKu[h;{ۭTYԬ; VH9;*z+x{ 𪌓 ݗS'AѣzԲ[_@b~ҾGSh1^<Ӓg$dݣ%&Bya7jIym'$eO,5)?{T3\Rm>Tn~{0q_-yo>{{WeGiegBo{Yg t0CӁX}R`i!\POUKG7;?~ qbZZy/cej/k) h?|.<o_7d/pBW;)zhKRHkoK2<_A6-_Ήf r(f.a-A'||*FMAGHaq*.ݕpԊjm]r]*ΑڵZԖ/2Mg Šǚ-'o2SثF5W{B:o녿1 qbZZy'&*կWM-hR?|u><oo1WoZ脯i!&vS|Lr _E?<y)B)#A|Սr)<=陫ejV׏imxJ{9uS޾mjx}DdmFM6,LLD TƝ.z-fcKa"adGb ?L]*]έrN3HoJGVr}J'~e}O[wVwtKӵz%>k&5%:ث&5⌭?G>ۇ>li4=XU{ cQ1cxME#F=yɛF"H=@]4Eg wSlJM2sP3 % `Oz_)p==^ *EzTD]WQJ2=rԢf]xިTpԦc(ѪU5N&VQs&7w&*կW| qb[Z[Pv>._WSL 7sJb&񷅭|OFx:mu[YH LD5;);L:ιY6wk$6Gd,y8>Ov宊yF^sz?;~'+?o:'#!Z*2~Sֆ- uoU[hu9.䶖WA| 놯KdK>7ԫp$k{$B%cPb |y.MD6cG{=FKkM)JFȨ8EA9Q$^2JZ9-׿M j;Z4ŰW_xZ j>Jgw1M#="?3d/-B K&~ς@AĬ9f9=UjznmegLR_kookQVw \!#~|Qx~H?mutlf6bDYಕ3Ic0Ҝg+%.{Nr>˖7IoW}^=_S?/|uFC0nq|t3L/\ҭ4ݤxɭ/n{kE(E]QNu3F[YiBINII5_AnT61?Hω1"0/JI{Hݟ?XL_F_ܭUl$:M&RXy5:&ZFwi^[em#;Q+yx?XJ:}a/gը}~y:=wIˣܛG'%`xV+ ᛟɀpaN-:vz1jT7VMޒ"~DzEAYwRW/f&Q&LT04Mk ve*Am^3*}č!PU|O9u\)[+kyd`パM_V>ԕc$$ rz cC2sn5[v)%}wItb֯N>?Aw1|ak}>K:CF#ZMocArJ2MszW&D$Owj0K6ᔃkkpWNVJ+ڵ=y"-ȣJ싏1ieov)_sᕲl i@CٝuY-)uS}4dW^|E|npqއ&6yCSUnk+nww=ںBG@EHd7ڬ/Ǥ ue|L.q$ev;vW?|EiQ9v0hT诖*KVbUISRu[wn}" E^~WxxFDΤڅ}#v4QibbʌrH^"WhpͦFnI[VQ `0>X.01^j*хWCHo*VGSz{߮0} 8+_ƣ.wXm(y>V5 +BŶo:ܐ. Oc`rXUWrfUru .}tk-QOڂ ys\Y[*1Yy`-f . c/'6\j*j }@rq?f6?;<5Y(njj~krӎ~u'Ǩm ~?|yCj1>1rǟ1{Oƨm ~9 rK =T6??{LG'/=Z?9 =T6??{LG'O=Z?: =T6??{LG'/=Z?: =T6?b??9 ֏f?ǟ1{Oƨb??9Ifx;nS72[bW2Fs?C6?;<5Q7..j!}GBյ_G \d /<}99 =T6?*_ࢪ-7~@G'/=Z?|yCjf?=~!{LG'/=Z?9 =UkLX{\j%L{U&J~!1zp׾NA+u)VߺWE~kLl;Bک \wbrr!+QvUZҜvgB0I*+̠WchXy1w4y1w4QE`chchɏɏ.&?&?(X<<(`chchɏɏ.&?&?(X<<(`chchɏɏ.&?&?(X<<(`chch ¨(Qopenalpr_2.2.4.orig/runtime_data/keypoints/us/al2002.jpg000066400000000000000000000243761266464252400231410ustar00rootroot00000000000000JFIF,,C  !"$"$C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Z]>YkڇKErJ drGz?'ZNJ3'[5 ˘Y$Vgpݰߌwum4 9 35_CgUQu;U[P9R2GZ>)fXJZOSgJ#/xoZ4wm9Y¾]T~RAVXxwFT_z_ݿZW_v|)bf񍩷6y \dٓzTIH>{ 5_=.[(ěA9ExWϋgĝ?I~{{#i~1#2U{Uk~𭵆/Q.n@d;%r XIR8NNr[w_L]X~]|HO_G~ G,sA{ڶ6soq,V;n"\(=W瑀xndXnvFCqcI9] &:>]w&xT[שEx!mDmRA,ƮA!Kg ZiMhnvQK %^ 4dmhD;v6Il;6yiZݝ-5dBVCDw o;Eˆ2襤V}|wKAF;7zki J?h'_+kn|WhjZVՉxaͻa9> Gr愥.gfṟ#q gh_+Mc='|^Is+_' R[i/Б9?h/O]N?l-J-R[Yfy =xӢqo^Mc ymdm9 6@s\_.QJ[cEOKOC31ݟsA:;3vk61/GZV{P o.cRJ]i^h}rߴ??qW~, xA-ͨ$YoE|H?+wB~,ZPP ͖lICn L)y9)X\ګYwo=Mcb w|scqR#ܸ scqW~&f ƞ tYR-FMiqYg/;FE@c'*X  r)aK-A5i9X4>ټ"S,3G>tSO$oucM-z_juH8/8>@]A譁|=< -g[t=g,n8#[wyb< Wo|)Xo>|Z9e)Xciˇ2{U :z7+ZXRn*yDMƷ<3VXN H1u ҟXsQ&j5JW+ 5ur W nE5ٷyjC;ɻgJc=j `^tΣ7Gu)f5>?ĿxP R<,r!T Gv8 f3nl[KN8k7ETu?__#8?ҾS7Gt{Yoka:ԟS;|qLӳ|>\KiOMohg=a2mؠT;W7?gƗ5m!qUb8Sq_:5 wn@8_p3W_=w8t:#{#˾$j&CStmIjmcFe(UuH5k,7e/]Eyoke@qm#6t`*X~|Uey`u=+۰6{:Mвapp|ErhmNuBq7X:$7 &2$F9'덾ūhZ&yi̚}+G4wX-c`v mO~*ui0hv#G@JE:UPFYA;~|3'ξ#c爐V(dbPȡ% $R1\+W QNܽ_q=H.֋ov|G&[*fA >pCURA JÍ{ >5;ijZv[]jvƅne9i1]p jWq42Y4ÇB 5K2Bg=k|UQG]O&*9/.T8cD@"(+=N?X[GMu+L[\0ݰmăMKšot+, ޵Ǒt$#k!ᄌpKU'Wƭ.G~]֌>ru#ԺgϏEZVVJf }|q ޹TèNq\wm苌*]9SzyyO Aĺ| s\Zrā[v#jF9=ks߇<i]̱Pj\\g) v,2)Eb>b~6AO3<ѧHY,7a$NNI5ߏ +cX"ŋlQM$7-mצ )Fצr#Y-Yd5o?h ZkYYZNۤ{wdN@CCN̬֝wONʌfxP7.gK@ stP&~Fd7fYO.xwai'=-VہYZzѺtBlMRԪ < Pl_%ԝ층h,] P0=0 gF D{ڥ͖wnS(JO/3|I=jRiOwo-Ʋ$z>Vl4⺔\$ny,A\qӒ:ױ@I}x˥j:CotRFl4_* #{g815k Qo2 F;XLΆ'$[r/GF4QFmu'^ 5&vx{SZ2Fh6Mx#r'qתxn>#tDknX̡v\_2(oOaTDzZ30G`Lon2V.8o4?zvn4(KX+4MHY02G*O?0sqj[ۧv4i 5c%9O.xSѵoCi0Pc *8o"! 5k/|M}bo\iJPF ʢWTpݕ&FVŚ*7Ig~ySu;B anۗ/qqM> #RH/*YPc ;9 5sBD$[z5ĨTm65=G_֦5 "l";ԅ 8רx᷅4O&.MGQgk3"qفYy(nƟ ,oRl[ƬDLIcI|%Yr+#-WM }ت?{vʕ<rYQPvm+i}%N2N<ͭw/}Es&! 9A 0;z˿ßG@MDxv)93vUsھ#ӯNtUdzcxGD(taϯV xO3~nqlRg|:3.tM&ylEܑJF0"? A{m[pc#9=qu"#jb,plN~mDR+GQGTG) 9\C([1Ew+t>D\JYl:.kx9"hy*3hN0rH銻+׀M+hw`vJuI'"@@8M Kc?177'C3ך#0#/㎴ M`@+Iч}iO ^{Py$hߧ1ʀB)zzf#9 1L =YA=:1FOc(^he7dD ܰRu9l&ciz%1 |_0x9YpbGOJw}_O~i&||bJ 8BA}69~,[eޝk3Xb <] =oœs>iwb)Ymg">VF+"]G9| 0&K墑HnsFy=@()ZڴiYdw6vRTawfF2!:S~qJ^Z"Z_>*zkU~RՌ?Voksǃtg[-l?x4 :suy:SXr:  Άazf98? oSn0z `q^H9#qօ9'<9h- ~a=HGAL~;4u@({Pyg)x H:dZc;) d JE'j:r(N)O '_ŒB{)uC cu$w#gKc<֌|_Zvdמ(׵83qHvT p>s׵(L@_GǩLmzVl0#Zh~Aڦ@/ tg[$< CxCHug4ޣ:Jy=ۨEX_{g$`t=;9,vS? ~/ZS}O4jRAnGO֌ bp}[G8(s(P}i/*\eL9);:HO@SЫIڀFFn=W4g 1iO'{P`gژ @(o NIlHsϽ d܊h8 Ii}I'1N'ր΃'ғ9mFsLp@Ih{kUpT<)Ѝj+|2ax1A]6,E[nyVGFq_.!ZΉ'[# dZQ2LCVNyfn9dqu'}{Uoß7Asl]q+@c(\n>)0<Jq:>LԌҶqOy#c{?4.9Kr3;uG>Wox4d=iQ֗=!ry'N m9Jo>=Hq@G;zZE)C0 si_^? 9@|==E5N@Ǧ8~Z.cSWB1te;¤Q?t!(bY~}Od|k:=SZѼ;-;7uY2Ŝff9<}GΟ7ivcO!k%gypCof;c\O=+pռdO`NTNqX j嘙 zWoO$ZέLju"e9fn;'NNt5۫mfDnH%<[A~2Hщ<6H#h䵜M?X//cG#q^ϧg[[cZ -gz$gF@VoeBq,A,6ƬSV3X S콜Y1|d'v4{]BKd[Am`nyלsߴV3j܂Kv1ƹ gn jxÞj!nƒO,±_7^>Դ{y6&.y\'krz=> K'KGOI% Csn5622EAfۯ8F{54sO6$Vh3TM5rZ>=$ vϧҐ ҂Zb9vǽ8@4?<ϯ_U'h{V[o֢9JCFdτt`N3[vkY\+ʰ#9_vSo[Ǟ0JNOFdr{|Ơ OmY_2SpO A5=~ܼM>F9mh:XeǙv[hAb +LJK{eeI%s$O`8+~5|Xu K~(Y_uԆg $Ddp98!>i.!ƔiAt~HsFv:ѪT:]>`Njuӵ [d^R͌DA8H Emo>xoƖR,Iwc$>%lC`PĮ[硹ok-5, 7=\K^UrnG{i4sq=1K CI8!?0tɯxYZC_}[]H4m51/W-)/3&n?}zO|9>uJn2s硛ѡN8PvG. j:lڳ 6ChG'͈K`ʂꟳu2S}+!/^-Ȉ$1۰2:3O[ixܷTJ=No}rU{?gb|+(ьnTu3# xW_O >m#N_6{U4sXvWpCcg{g?%ƚtÜ =BGEaKZ44Neg)jrK8I.끵A _5f m~{KVL(s<$}H{~iosrLי-4S*n]-м_T*QG߳~6_ox\ڳL Z%fKY9a b.!Ao&;)fl+.A#*5#i#KNbgq^#u}Ǿ׿}%H4pN4;r22>ٮv[mjzNJ귒HMXO ;flܟ{zo{MxG|IZnlnj*7>Isŷ\> odv oq9u dH-P͌yKIGT!{.mhwVe+M%կIOZ,98r1@28灇M.jTVmSwZ񞍤i \K1uf?7+A5Pկh\{kgWHL6 8az]Ǭ-6$spBrndX7SxʋEi˫Ťi>ᄤӌ|?l5/&I@F,@ 9u|w=XGԵS E@Hr \zTg禽ƍZ:7n/HIn?ſןxˤHīl3JP%p6&ߊ c[7uncROEݒ5qB8ǝA$s8{؟TWxǞ. lC)|Q n{Xu+\³*C 0\dmzjn nRܱz>)mw+LMKGىR*êUsSk ״}h"ְ486Gxm#ӏAq)?#q+McĞW&Hٴxa0x)n<ꌹǚ->QnS*8XҎ>`ݼ` =lWJi,'9k5M3SѓYNd/Q@븜mFA2+⯁c&Ppe՚3 9=Ijz,z6>lN-__Ƴ$+pH`0s}^瞹]j;9Бy*ۻqD(PRz U)7 kv8SZ!Z8OZJo땻i"ӓ\:YBYsz^.'7I *2(x: ]>YVՕk#ʑp6Jr?Cѷ-=œKɷ,8ZsOV x.O&c2=rn]m,V?$[L'WM [?XGm׍xKG]>o.'E;Q4t*h^9Mzf'd\ԓ+Q)|76pkt6s5?~usXmFh);J@ Qrg7VDKI[{O~7]vFOvűoYcRoitkR(];P῏gNB3}3Y u/,W!чKf$! s]yZxj[g-Ng8F. F@I񬿎`fkiKY\塑@%} *=N5V67[?=t;mb4kԵn̽7rJ|ĀJ t8tHlĖJIVpIn3ָ/*ul~y(V 'REk\E7w(jIl+cں*)ԃPV8}l-x:m9h].P_:04gmv6Iа\o ͮi&짓PIW2Wj?gto<hـZXems䃰ar;q^w_0\YEĞY$a<UUbR2oF}Ki^kRDۊ(''5=G@?wj;^j {E[zEWeV>@/YsRq>Eӫ(?yMW|h٭u0)y-Z>|5hh1y03m/ڋ*]G^I#arf}Ti$<+2F|I(C5c[m3<5qpWgn/^7c!bݭc8Q[/砂N<~xVբ=Jt-c*ap>;Oa P;jxG+ঃOkvZs=pd(C2F,nh=n@GonA^C0(8t(fY6ocT@pI(H\yv򥽰! wR%Drp\UFxdyu[hx6M,!jQG)~5Du[xC Us##FMPm 7ͩ~&'MODl#JJsw0?|$VmO &l%IYuDY0T~nHs^ʹvқIeOiSR1jfAcc [@8Iq$lW?WJ6J;h9?sIv6[}(})rQ efţdۑP}Ƣ9aq~+|P$?&zV4yMyo# nqRΈ]GC<oKfxT*Q6{/րOҴn66gFҶ1 hj}=J9  OgG8Xaa{l( l5ˤ1vP{de9ߥSG(1r\I4ޥwX#!Bqҍb zT=JBu( ׈Gv{k|WrYBF@r+ 0[W_M.i\o[N{E? iVo_UfZkMYU])H9c X0r)Ҁ9i>(Q<}(❏AF4!ڌ{P- `7Od/ozuۘ$T0*˕#z—O-n_o =S%%8 ;QhKFe@z#wNjԂ[cp}>YʹlbSBݭІ߂ᏩjxZ$dvP1י1gU'$\j,B;!F FjlEEQ K;hmڼ}jlsӊhr4qƌsLQj1@rF? v=>+i0yS}(ǵ7=~qӚ1@rzi+Ƽa$9ټUבoj#'7sTzm_/qҳa=~ZY5pz 3F[ڗ&d 3G4dRQ\jNi4fh4fir}R4p9oLZp-yǯұI" y#Wrcg}+/QyZFl'3F2lf0̒tz~`Xh99`(HTToj)\(.M&O;dѓEѓFM 4 4sE3Fh'RfFO>-'=&7_^{(vfj_}\k/Oğ*nY|/SAZmcᆛ^I-_8}3WP;3iӊvсުbQ6=BJ!b;u\ʀeڬVieuw39fs ֠R ǽҠ>إuU s!{yhPnF0pM| 6ũbӐI:oA(*r=rk9:!Z2rvw 0zޘNIیu_>|7xK6iZ\~}Vͭq9nK H9z7ŸrG=b+Kme w _axJ5uZ1Hlc)21nEx^5f 5=CGHԷٮD&ރ"Lq 5oSxd,I`nރdKArI'KO+{~]{T =F@ck]!p}?כoOI{ދv xC_'&|/O^GLDFq>c'=>Qɣ%uVMlw 3J@TX~x΅;W m7t?vUd ;14>dy}R + _07O*)Nxj6Fv$}K.@Kpߌ㑜uqLPA8j[׋0OGU;~'$n}c~?zЮfS=o9:w+t_xwԣv/zV\jike6ċigKIGO^&=/PեD NI0A1Œ_?Œ ãǪ{):>03S\Ԝm)6{>"we=s[3>2F:k&.VE6=2b( @g]6N>jX5hqq5#ɣ{&}ӷ5󕸀&yz՝|.6woy|YlH LtU'~c:kG[9O7$V@\3,~> B>*E\ƜO6&c+wS%ÿCkmq:NbMLs~0>|l2HUt)qJJпv*_e]Jkإ&IeKl7Fc.z?o;Qv(;#-xȞQmJUx V:)5]% мw!X$\ǕZs@qv/F4?vjϦ7iWWs d$K]^B[p+LZBO'O47uG;}m C]"dg5K̤%'W1~0)xsMjѾ3ȿؾn.T}tgT9?"O+AS$7z/[ ᮿU{Õ\- c΋j\>l| qѼ89Oj?|])9H%-ug^!kw]MĒvI1ln9_'<{5& FN lWw 3*N2 {6VpđEk FT (-Yopenalpr_2.2.4.orig/runtime_data/keypoints/us/az1996.jpg000066400000000000000000000274271266464252400232040ustar00rootroot00000000000000JFIFHHC  !"$"$C}" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?4hEwqs,&[hW| lp .."vb8LשGzfT񿌵dHKHѠhlr-U ^ >b|T*CDыBֈߞ$8 Tk*{hOk3<  bИ}y] =_q@0qcɏz A5v+r{ξy:3 |Lح$ӷ=Njwxob#_${Gǿc&YZ7 =&mb6(_g/.b19?t >E)\n08y7S4ӧJ/K GW-Y[ͨj!E?iV5=ªmLO6%[)`tL# 3Y1 %5k)N@uyO8垝h {ycexUE"+9$tHVDТâc5B`[o|$D+.2]{|wWsvO_SIԇاk13ͼ6lW)`pP Úw=_\ڵf R24(32|n:ϢY/KoRo9:{S||>,/'Ú@Gѭ -%DcvL?rxcF%_C{;Kx&7y0BVS?"lRϣf+_tOuO?v)~_O##9^ XwGZڀ&-nbS)n[,Wu='J{ "[FΚm,J2ϋueQۖ0kq=~:>Glr~x\{3WǠhgP O.G}hH%|c9W*ʭAJ+=.Wh$ⱙgp p0Oo-cE+V>SσyׁGh6Z3ܙt\68yGʁ$U t]?OUՑ~̟8XyjCcN]M/=:ŧ>?~V'1w>J~:x%?|u1Rpm%YD%!L#' 6pc.|̅rq)3ɖyO_qo/♆O{ҟϊ&IyW> ~+iY۲#0sp Q-Cu$?H n*C쟗.GQQ JOCZ>c9AZ[Jn??ϥE??%$s\cC9 V<!@ d|qhv˥9ʏ% Y* ~@|7kF ֜4*pzw{gߵ/[bD}|qu,e$ 2 {TR$$2eu9 Bq_zuχ&O*<(s-n!9: ՟ »י;;df9$+eS#=|[̪acI<SNttR@%=pTw==fkMKpz 4M$6jrg$8\ZR}2Nrv3:~+.oO?fc$s#0*NǐHc>=q6nn` O^[F_Tg.rGRf.UUO$: xC)?tzgA;w8K.IZ)0-\:R4R;(a펇ss#d~_aӭwCFA&& ''k&}:9T*[I7oqvzpz[y&&FVVQNxTLX;^u[d^uI ūL𩑙KD\P8Gۻ!7gIǑҧ g85_Zoum9^j)4ȵ<' ̈ݬT;U tWZaP")>#=/ǧ=iNb*:=OUrgt;fiKgjVluas[Ȭ;Z1d=ڈD@ )޹+տ^]Hqø^&usou .skww[vMJͱ82RZN,{i.R8!\8Ѵ|OFHzsR3]T(~>"악iY#C?.by9|ۆX~'7ª#h''%mT:J7C[۪0f{6dʂ>`gUnta#Gl?מYՆުW#$8={wV}[p:^5=[WH$7>x#cx<应Я>!tu;cp)pK)r3׷Ni XM랙':iSy :/ aYnėBI'U?QЎݫO.}3]Od:\\ "~z`WAvxOGO@9_N+^wcw}5ִM my/s!_jގ޲li?#f$by#EjqO#j28S<^G;،QYfw%=O;ŤdzxFp |tO'ە>?nrO#yKO.~">tn9'!yWaoçP|['z=WMeq$$wO66&iAZM -g=sH "v#ӌoʏo >[|36SǤ!b+ϩ =3kBO;HnO֕K2F:=3N̲NY#?K0<8#y푠d09=sNp=Ƿ˸}^biQ44y)N9V"ÑzuFIPۃlskBC'Ojypo>F0\WNc?bx*3?=3gϘF؎GP#~F>^FQف G~NK"$sqGp>nDFF['#{OHrTqp@cQ,$e# O=IRy(*Lx#p=:g?ϧ4Xާ g ?='b9[x?ŵt& _8lֶIqtVwvl|NO_yg T 2voGJD9?c}ew2$H2`㑞w-S F@? _n4=gbO㜎#Hwq$QE+'r#h˃LcPCUY~nA`_!,z# Ԃ:ϱ@-6ف){~}ƗU9$ǩ T=A/oO>LFF(G9goހ~RxhE<?J 7J6rs{Cn'oX$:瞟րX"!Ct<vU /OƂXdGׯM!Ct:ycvNp}9(r}9\KAT rxy7lzEd;:G@qGa#T0@plPF@++@ gn?OsM;>O΀1Tݟ+U:cd1@=KI$``JKRʼn'9LF Uʅ]>6e*0qs4Rv}8WO8A=~zs@ sdGIPNG!V21?n>l;sH݄, /,p_,Zb>X6{ۏ 8'?>ߧ[^?-qOWVǟH3by砫M ȈL8+e6s|_@ZX9b`qF+N`|m =9l[x]wO:.,zuËe!F4E݃,'x_|+i__Ғs%pd֑1oJٻyrWz4];W0Kymǖϻh` x'~4JU9 cX#RI6rޝX(HlqL2&Rn@Ps1u$@9~] W*d˗>fr8p?.}y7(^K7=8ddpG<]F 'JA@sQ+w( ڕ$,v21is!ُ Ld =<ӕ9b׌i([?G5df/!eݜ@>]ɴF_Rs$;*rI?R GEN7 m~=~s!Y.:N ?Ae*0R#~VaO#9M0[`#ON_,8pr8$@__hztdwoB78>R+@*A]̑ O, ϧ?_8={4vF Izq00 p`nr8'n0X|89p\'4#?tc\oi)NB0Y;v^j¢C:w.{Ȑ`cۜ42$qy\ FsϩNyF3 :.~C@'X?^)l8 l/4ђ9$?/jVQracjA#l@88V_og:eJttW=`v<u_κ'JO!?]Xf<7[x-hy:]M?_ӡ$Nyi1ϸۍo'0&3>Q}{q Oz E#}?U}[H.@" SOIMV߆(YᶸNUBLJ-lK ̍ `.'PV~,6,2E3bF<0bx{tTeG_OxyF\O56k[i8kx^OʿU<$ EA= l\kڕ j1'r~6v8G9'^)ۛp1Q!Fyt{vۧJ`H3__ڳ]B7 *8F;[>GJԢN@$[$ qwecaEK\ME#<'~}I{g:}+]Bna~q6NGNq}3Y3RZjCGp$ixvQNQmlIWQ" 8۶i8 Ax湃uJd BG5"_2c~Ti2MKd$=O1mOǠ=Ok]ivIN2,1\/\ 4M{M]FT՜"#,[hPpJz֯;35tvPp zϯqQǯץr-;(|6f N$uO1,NաK+9be[du{gxA&u2j'Pz~~Tj/ ?ן?E=:j>\8}봎qTaRaĄ+븫cv4f/}Q˩0:ק?4 Z5Lqw>3~B^a-&O>M!һm h$g22ywZ:urZ'<>׮x`֖ǩソ̢)ߩc*i5nagps?E\؂y:rO_ۭQ68}9"_ĺ>. Gn<No*5pT`c$d)GwZV Ū$̮ڠ\yGƏ^'Cb UeuVQ{.O'Ubݲd<;kgCxF^1ũSo7sa_Lb06>/,gg,wc$K␂Nz|5,wggq|6I$0]KϱId?>)xR<5j:<`Hm'1>W u>)[o?8eI­YYI* F}=TWџbI0hѾ$m9|>A ?PNExu )Socz,ǻcspzůZx]ۢ "|Ux$`czl\tKus ̲D$G$=:'INn١JT\J1z*p\qzDx挷R%d\X7޵G](QiL?O'FVPqxYG쵫BZ6y/nי%JX{^2H|ᎧkO z%AdWFWV ?ĩyuֱt*wfpӗ3*MCO2@>4R)6M؞|jkk6xh :ᤑ9#IP]-/\:2MgO2V3ߥ -"8֬%/‡kԩ*{o)!J[gVw\KajlږorX~%#1< k_->O PrIsu?W'}ro|O-}fK>|Uwk-?S泊^΍-nᏉmZ/byt~s D0c7}?U( t@^S6c"'rZVL~}aӾ1l~[;Ss$4&UJTz֟-N߉_]cie}~RŢN4сf3qמѧ3)(=By%/zuu:bF$fr̮c,x)}*gJmtm6y+;LجHI1}:OWZU7?openalpr_2.2.4.orig/runtime_data/keypoints/us/ca1993.jpg000066400000000000000000000123641266464252400231440ustar00rootroot00000000000000JFIFHHC  !"$"$C}" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?k̭u$il<rvU#o @XʡM7DCIJBLⷯ#Y/eIb2G`xKaEErq![A lW}McHfpq?![X(6!XWMo־q`Pc{sI<=hjۅ&c GR$n?th.mkC_j^{u;ǡ5ij:mjK[BDX`e 2;pk82vL魃B*Ucd̼sl p&<?·h89O8Wn}-4{[?Pueb @*m(Kf\Nկ}*?¤T!pmO?›irއ]F-mB2K U'$zPh%Jqo\@)Lo"[T>AD?I\l}MElٰc)$ݑlAI4eP3+ƿK e `1ԕ['1oUx>(0?+Ӆ>ae\?S(*bc_S>1q~[Wjt~T0̠s_R46HHJ0zS]A(\g!K 估rKNB~G8y:kma,2xNC){\T~nPG9TO';@< THf}<cV|:?m ܿ|:OCEQR E9fQYF^3׈!oU$sY мN½3Zw7EC3_ň.+ԼmW aeŶ."R(rpJxEw-;4{kiiBv0#9 !qNqD#AT bOؼ;j1ZE<9886Mдm=n$v2NnQNw{ pT[oV]|H,%}oTf9Oψx񬎈oj$1&\r9>Q3[K%4dKvxzR`y%8'O[gG/+*jut +x/Il$xHl;{lv '[m-SSrz+/ibt}Bv`}o5V%ZB8ӭSx|^*fwQZ|9 b)[5/j4῀JKycN91<;x2wܳ*@rOAse~% WW]m;\P1]*o[nx%}-ՈSPI(}lw=X2V drҼI|8i5Ҵ[SՙTKz˜Gp6R8<;'ӮnSi2[Y-5y;[}B'@'_)8<}k:^j7~"յ>#y]rex2Wc^; ths$4<; 䌜aSF&lmᄣ nZ7hRV*5Q2}񅢊(Rޖ?5p5 +B~ϵMSzL'dw;=(qMэ[r#M<]n͜ձ7??έ졔QڒB7J([ⶏwjEo%%#B^okqGJ_7z1yw H-H$ԞsqV(s˙;1TppWZ~i> DžuGO/^ZH_ɂ12@Ϸ"Νu{Mf[HRkZ~ik1i+.tjW`ea~U1c\V =x 5X Ca$_D>Gnۤ 63yTKW2L}VISMw-j׺yvv$ !?upqޝ|= {(5H|5yrI*#Gh2 zW{@-t}2#m@NKOv'jt 0u i<%6R["ASא Gtzίo(GXPp N=+tv88)hr?|x6S {\ʠ; מO'x.^ut!ky$#M 91@0y(Jfaj_Z[%=1«)rI-~Mx_DWw,7:Ɵ6 (]SNFF~f{Ok[D·Ntrw. Է|tC'4k aQxR܆A^i9T]qMTn~4q0_ .T1]6>sdeִm>y ^xہ[jNET UӨj>[(9Ҋ S Uz5kQkм>G|D^#^c[iB52z.#lM]'Nj-0O3M$9Z1=(AEPEPKEQEp PEPEPEPEPEPEPEPHz@&7 u(ٯSG'I J͡4?^}GipΫjįTǎ33H Hoŷa]}U?#TW\Tg\B>:jWcZ=Aո쿆k95߃d?ZR }=D_ֹjƨd9UI<@o9@mkN?O4Gk>)E%zzM>-? >ZҍZ}~k\ϰ?L-/cVY?Gѩқ4^8(V?{N s?/'QrĬu>A5(?Rn4r2?uV=mT}sA?#\x"dja⸛ag4 /8׹ALmig5"x<;6jEuSˎBPN>ڷ sn=s>7# _Ʀf?#Kqι$j%9\ow3zBYY{SkpV8#?\rgf?*Qq o7I9ϵ>ST>? 3Gk9?p1 WDCeu鶺೰(nxL;R5}L| K''4Zhr|Sty)+%un?+"jpݷ=1_qAokp6O"C2>УҵJvƿ#U3JH ƿJG}*nz%|QHHlVQA`=irfO_S?֕~:#:0xYN}Z !O 5㷁Եq08k^¿t%Z6!PX2'py+i&ZҼ9xzU[yA T-YsJJiGgǀܓSǏtVרӹ5Qۥ2i#T}N~?xquZ|zEsy+J#F> ujFCG{Ρ'}Txgƥc Q?rTI 2IM}ei[*KMvÂ6?|mCYqV]}09sy|/<[j)1ҹND<`՝:RNCJYN+O_[V]9,3`ݛͺxn_E*s Rt u~~gA9V?i&Tu)Tkg\0l3+Z\a`WSͣ<ݱR> @8=cJ$ۙqz]<%gHg^tgVtsqyw* CO` "׋d{5ՙ6G p}r8  ^յ/- Cs NKycK<Jhնz-L>@sOmϷJkx.і#MUHumB`bEpasn U/^WzDMmqi;Gsk$k&Juuy\Ʋ|c"4s<2)YцOLOjXƟ&cGxm{VdV8v%I+dszg篊 _vml[.dqx= Iӣ(k?cuk-7:؞J!_~]J?U:۠; HO @.nv?z<ikoXDS5ܟ4NczBk4nm=ФxF*?Kc%;VXV59&glj>?(>aYd]&Asw+Pdrx\=VKؾifmߵp0A=*wu2Vn_4LĀ9$dNItܩ5Yb/"& MpxOִl/&XYѧX6>T4 )zbˇ+z5LiB(ȑ0ʹF{s>#-EfD81$ d-Ҽ'W"FʹsiYX<~ׯ~)_q sG|vMS$1g_WY[lmmù1ҔP۟qZi>›I5 :d22=OxԖd/]PS Ii[%]eO8s—ۢ)=GQUx5$X- !`8k>Θ償z )oN}Acm ?fr^ ?z\q5[iܓ7?=s sz3*] [r*^O9\q[sZ4qX6=;{ <D쌲<a?xcckXa7@~{/KZ#er31yJkVXL(ˆ dIcEmJ\FvE*y8g"Ҷ\O.흑@8:u5iPjOL\&| ēG^ϥEAu$K5,o9P]Xt-a>X,7ׅ͐=:jGȬ0Xds؀ǯj>8`<T=UVT,-+/xQ9 .3*I#KmFȡh2xzN1mK淸mn^Hi;8tC좴hHW, 'O|[%D蒳;|VH|/w88arv14S[ VbQѡN>.:zBO 9*oI~^o!ˈԶp aݪi:,DFR2Fzzz@y|ii"UbhV,h$ۓ98$k]0ĺ۟b|Hni/7lUN1Kl#_AiO'.I< kqաv|<{Lal!ש 3~xڌCxit2'PqW :s hdzdО꾵0j6#Nӥk$R FT9H:kCKʿrwc` v ghkBT^z=㟮jrn*`{gn{~"ڀ*I޲ g&CBKKtI\A+GW[D y tG`|V@cԗq,{׎fG%gtWFIKmhN0y^$^"@ׯ 8<dǩ> {zAsgfyo,îF9ZBKu2pYYq9@<~5,qFew# x<2BbC+eF98߭ qC+dg O]{w["4hܟ_sW͉RUF3vzDD Q+cO=Cil-fx0ĄX9p+_o q*IR{V3:E)퓒3Ϯ=+N$ppI9x}?҆(+#44FW`V`@89yUYȁ"D"$l`qڬ {V*jN0}9@Elf9&̊*ZBw;Oy]$e!Y rKHFb6:W z=UDYhwtuF1Z{!ڱ)r9>w=)%ᔻh_GYŭ=BF3 ضۋ9$lS;tzS ېp:Ԑx=Cl4GP_Dii _Wja d*4cNS ɟ~xb3>OZcF%_~.le_$Z,Zc ֛ʗG;v9~/˓ @b8CH)6'st';TH4^x#隋P崌W$8N o'DyT+ Vrw`rF? 6p%F@8?_VmF7jSB8bzFk1H==RSJpX m?N+?t݌pQFt$T#d` sy-׾w˼.zaclcqKH'9<"f'9(~_Η/dWhG{x)ԟ_x뎿K!:P@fey篿#=4y ǞΚ agz c5lVll'yohܝ rL6>`;-ټm'=;w@{c';&Zdu?OVp6Xzz{o"&FFpx4XHIX[A1;R,#X n=}sLPqu#$gr2<,Bl?{̮Ч' 7 N`N;w9`}:P cďpv$Ndn[9m c$|`:p9Pla玝0?h??g\y1|xӭ2_+_IJsŽE֯|1k8ϒ.qo\H ͚=ja[ Q_>`?gPQs*=@ [N=^r88 s3\ uzn-GL~d{怺 :s0r1߯#wMBRV$ノ@ӽL'93J$  :7 0qa;ӏZq ӝOcI߷qE ޣ"̥ErӞ~4 aAOG?4mFٹsOuMuTbzNy'8>U,9>936x G\*Ǟqz5vp?L?M W;8'.2Fy=Ο'7`)GM]7 OBwK.0TGLȠ[m~# '?꣯|Hζܧ < Of??ǫݴ"}"{ޚu~L|7OΩy 'LUgi rx뎧ǥirY1:MI]r hއ5/V -٬5 Fo&Nɖ^:ppT9e~"|Ej-q3MazMlsfnbFj/ ߊ} Kg4k9 2v{ğ|Z1:(KeQNOhg,ܝb!]i6\?툎$ۊ_/,o:]܂%D@0͏d KT hc}:H$ 8sVhh7>Οwk;yRv2HzK&|G;+7l>jBqoUݿᛋ 3[IeE'%T͞pq}oMSHE8_ i,fe2qJXn@p]7ŋm"\UFINt#8ԚOOxQltQ@v>av&]H #m^?~><G8@@"DrI#ֿr~" WpI=Oֺj:vLvUK=@䮮|AդRx?>B'g,2-‚HV' oV+h#]&ɲ D dlH r+$ I> Nxvwu0ĹB#&08%]VGO{e:lN\,G<|=W(7k_Ki?)V7%8׭{qt_H 203Prp?{b:hW9v9z^?6 d2Бg׻g[m<+lּ'2—\̒<6ҹ3χxj1{{1nu]69Sx=Oöwj ow ,cC}x^$I5#Ng[e>l~5m>3(;İ~ N?0бy)>.> <AoE8V?5N qKPSj7o#'#4YICA8Z_#GCvW߃mO h@OFqޕ^7H9Ң$cr OFO"<hI\&x.6nq)،A>S?٠v?>|(< bpFkxHd+rA>0|+@MAݎ{'+gm{Wnfp(뷧Z''vmW4U . IRp ]1uŷiDž1:,q9'{pz.[æyf)9$Ơ!g¦_MoiMdyf4W6xێ1Z<5 R ᠧOcPb>i^$m졄C m7/?>> &c.?O}S2Hu:Mu%s߆: B=5x B1cmЭ•j[[/zxJ2$i7)?s=?ľ*KOE'?-%rʤ '>o Wo d߶٨_Qo ?on5cFA6z>c6>_19=*B9[4F7,|dWE<:φD|2O ߏvB~02Kab8\s63\CKȩoi0g=dUP=V/<-Qs ;&YA +&dqM?tbx/[{jV|dm&=jPi70Z^LʊtșUIby$qΑ8ijGx{~ ?f3?8 Ym\pN0 jsXF\?}mϱ7cO ѢJa<%+brx cq>͎?4}1:j}EѴUx@䯅bd.eyeky:E5v>cG\r;(rPGB9o:'d?j r H}2:K }|?i`amϱ֯M\?)4xh#?4oIHuJAw3m4Qn õru8Wmz8Υ(>eCڟF^Y>SWfGc}:卌@Ơ=p`97N]=͝d-lu/GyCj~o4od@9?:>>ؐ uuϯK"u-Evh*vtڸ >28bvG b\x1|=h2*i4 Qcnvʊع}<zW7{qrd[`ɿ19oK oPѵlm!hn֭e>%mնX:=fKؙaD+rU@k<2YWw1"V&YnY\'Y6!rj7G,xt@T*J8oLtcG٥~5\Aftkh\tn4O757,*8tL$1'hzE$9u$篯~{gcy9>OoϟOZr!9K8#9, }?&-q#ⓨ$8Ϸ_޶:t8H>8ڽ\ch'\^]bt xR]B̳3#?⾎?ae|4FŞcGQds`89b1Ol$d#GӟʜPpOKڞw)q?otzR-#硭T:;m?ϰhG=xӁԞS% zRT\%aZ( }ܜThʌ>RH9'~9 -y\SˏjOBr2s<V6;A,p9S:#댁{zQŒǪ<})~őHI_Ҷ K)Tuznߐ҂2C19AGDd %`ne;{ڑT嶒 #=s[/sی2}zhcԙTpGz~C)T1 =߯Z$~' qzϰ,,UPT6OA@[g=_ƢkL'?^nݼOqԂ?V7$r9'8<旵C Y) r288#}rXs?3Ҷ"UCg90:#O >AԎ_Ck,wgns#ˁJN^A=)U2〹$rxrf,T~^N5J3yZRʎ;!K0#qA?=Kڇ!-IRspx$y%qԟڵ& ~APW+N1GS$Z`$G$}88sZ1$@FHNNOAӞzZX@FGC_8J|ql+Ys1=(db8?hռ7(xЕCrz篓Cu΄>lڎ3={VlMܟvaeʏ+f``YI=ӿ"B60\[$.V Ǧ9xR݄jvizϧo8$u*B̤/µ YrpWo:RC@9wP{(A#.đT#׿ڹ_`q2:zkee\p@l`*b}O{}ˁ1:CؙʜqP)b9㿷֣@rfs&+nFGnCؙB9qS k vHK`'KԶ2 g9:g?jř ru #~ pdbKg8ZFgͷ9#z?3.1:LqZ^=mF0K`284g85vOoÏ*3nTG}q={#;vX0G'= .H$)wU 6:zʥKb.7aG_{#/3d|9w>B6g⵾'$G@rzҔ)O-w*x=;~?z=)@Y:gz Hq9?inVD.\u~i + G84|Itm[@jmJRgn񑞽::EVMw© ;[6JĜdygʾ%gbrR=89iϕ#p1C@>/zB$''>ί;QsׯOe=;gקAWL&f29_N%YO|s߮{Ӓ"Lq=}*"U?tדG3CpNùn}nwA99lI^ւ1Pb@=1?(p9>왎-Wh!U>:=Ih{t=kPF[ zxBۅ_wGb̦# k61ss{Dؑ iMnY\_Q%;8ڇ3ܪ?.n$sMVq$'sT=9翷\E$8r^{g~3Gdf>asNO4j:ǧ`8^>UqzCx^?GH6"9uzg4$F,Shz~4lw[;zlvZX˧LNb~ǯ3 $b#I:R v|:qO"vO>~$sq =*4`sH'rd(GH8#ںo,{z,Xea0>8j#x_AϿW\?}\Nh]O+xe[i{~enso=Z߆Ŗ2eJ9'8+2=J[#+܉:I=G?sNF<Oi9SG>Tru2S̟9M{G_#9NK9A|F'rA|SY6Gv?V<yOz,gr rqH84"d`4tZ9Ȫ$n'S.>4#l 1n[(rM$֓ N?QsPnqoҋs(?W8 js;@SפG L.(_,g$JTt~}(b AFiUAM?~N>iĒ=8x=3EÕ 1}3\U]:1638ڵsӟ*e\&'{;&x3 +`H*T:R|wmMKmu{ Ih̯̥b0T/fgf Fd ާeXkNךtpr^0&Wkq}3:߄tJԑsskf hyYj.uu|kѭCw{gck)wBy`FI,ST{9AΨ@or;pf@VcVo Ti-VW8 }~][ u=ėuݵڋÅ?%`k݆y/0FjZۥSGH4k*b6 ;s:hBC&+Y񮯧k~>\ӴoEID!tR#w2‡/WJ]:ah6׿l=쓺#1G۸]C'OP[0=19?k6eeuqً`1&r2̎gH'-]C]SJ嶫\\F1Z0|jW#ośM֭"xLo"2+q Mك}bZHXa+W#.NlmҴ*\vRky-h[,t* (!gBėF}/Qu]7TuxBڝ鈀#7d曧{mT9?>4d_9|> <9"kSC(<_K#XD36\`y!wns^)J[Ԧ%%D$HM;| V%JǦM}6v[8=v?\VoLj #nyxO#ގsS'=c"D{j bރ?IRY⟲BTŁ'_)Ծ%>?'=׽UaGGk5kST). ءex=ٟ_nkCKTMep1A$dd8&>+q;zox_Ui?h)N[39BQn3_ A>4кt1֐|jjW'ƺ8jom=qDaj/TlvLQxiw3Ə̇>27tCʅԐ:'>h¥\/69ufᏁg U#ӿ[x+`S ʝ*o.9˯ 'BB3$p#إ"qED,N=[do QO}}c=O,~3'^-Ж!ϖj?|aGx`qѯ3o 307/+?<(y8L:Z,f_ cB,њ2xUCs.c9R?Mx?ϡk? < <0J < π'oEګ0-V GL+?ЗW]GAnP.#~&о+Iq?u#xK.VoXH#?_Zo/?SoC$x_?o9ti>Y\?~n|4V nGIDI'Gs fu t3?8|; Zp-p %2zG ICm~_]\!jS׶㥻K BHOM1QVGopenalpr_2.2.4.orig/runtime_data/keypoints/us/dc2003.jpg000066400000000000000000000211241266464252400231200ustar00rootroot00000000000000JFIFHHC  !"$"$C~" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?%'_Z4?FHz,lOJƼzuyqo 2\gX[]D]d _:_TGP/5rT) |oxo-ke1q#֎_\1q)gZ>Q.~Wm"1l&-KRKf&A4)] qJÖ:ٔ]#=?jO>Pqg™vA,Of+rG*I XR@0 0FFyKZ.?ٿ3 OKWϹWx;-^kmG:;Z]|8g+ӖS]S[ocus'(fN?yψZk _x.U}Ft8YU}vߊ?kūAa.|9kZվiZkz~D a/Eܝ+zX/?Z~Md{q,(3"ArSFkݷ/ UOcڦ5O8r/̧i=*]cZ}[JzbH՜eN2={ҎU9IN:{lsGOQG{8y42ž-͕rjf VX:@Tk^eGxHAW$FtzVkMVګ7\dz _>^*?xl׀[xΫ㘯Jn"O{`<1͵u sڰ`e#J߫{$9MG\ǬZ(WWx>-W?wtt:wneɜ *C=ýCZn|Ufj@a\(G֕lSWK꯳~О\gz~?ҡ]T-Ŝ&LpH:zTuge\]DC$\(e˫H&ˤ y?ۭVSi~[KFђCgkFv?R[M>~g*_2ki#<$V7,WThsi)2ڴ\{k-kM*_EG.Z܌X7:gaG$޼2G/@Y-m?q__%ߴkadnN k0Q긭%5 ^|J(mLœwxv'8[pN=R^V^8AWKc*n)Z{<7?3Ey\i_#(Z$u,f@ޠ=A_7fa᫝ k.#D4'*'Ez. q ?Ư x@ȵNzGiR+٭?|UV7(jz.6rH?rXY>IJx;N1pTWB/$r9-L|b؜xk8kie}]?0į=xnN~xMQg[hbT _t\x2_5,6Já b$xk__* I o!Zˈ;*gl?+h_rzڽ4;6ֽ xӵt%<cqu+5)w)P$9 ?q2J>1XDŽQkg0kFEdn_k:iv ig"s+Tp0Gֱ/|3yᦩf4zM"{~T J;s^~2驄>:=J>3雇R~('~̟UuSavֵZݷ|'+yq-¥-r&H G'+tEğl4FZ{ j3FK{ -~q])i&@Wqik"~&cE⫓ aKֶ[trqRk歵 O?ċsh0{M"hmG5/!|#"B86ĹIr\XcXK*TSd\ze3"?Uֳt1NQע3Ҧ9h]SѾӎ-k%\ɦl*oN@?24R~ʧ'5 E>m_S1_:֗ZfΣi% Q;eCnBkͅZq,bXmE Qߴʤt_ڼ!yN$ӣm:0p}?ZG1O SMB*Ib[ "T"J߉NKF+0+i'ZH5|Mawx|Fcy]*%-Fpyovr_['{[] ǰxVk% qzկW59)+ ҝ)rYYGyvVb_8ϗ&zP*Of~-"e9=sQϷEvRVf!Pzj@ihL?*9.4eMf,zMsK(ywh(iw{xtoΒ︻?y:7?󤢀}'?Z77%3)f?:8#Q@s>sFQ@]sy:7>>RQ@]:]:m,O4I觮?kk>?կU 6*#ފNƖQ@ f ( (@4m[.c%Y0}E/M̍{e*Ğ$t'sK6> ⺎EuQ8@*22=3BA:#Nm7t;EŠ(((((((kXAr~ cG7zVAWK3M lSQE(())i#珊_ |aV4'Z9|\=|z]OĿ0wS3?oTL^hQI(hhϽbњE%ZKx]RтM7V_o~*lњVEsu!VcD?ŸF8،eP~hɢ)2hɦHRd (&iRdѓL&Z)3Fi\&h.MsZ??нvկW觮?kqO&o$U~km&Sy"Cp 5fq9+A&cוҾL&7,Ml$'чjv,UJ)a!N½Pf(%\_=:_xԥM\姸/))cۏA[ ֯joz_oO} ŮF㔣cv8^s_zJQ?XhQG0?9of>={ߎK_䚄npc{7Jݖnh=$RϏ!xG? u"{v+ V/򣝦RɃ(⯆|}঻}O*[YԶ^2hc(Ww[o_/+@mbg\~ p>CۿK Ǚn3?YdV~#@]QH,kly;W߮ë qHmlSym?`'=NVMMn"8ZںC|׮+'NkI$gS{mqviI/}>C| $ R2fb@]ӷ^e zrxřWN!`'$,~4W? SjZ@:BCbsRi]z,qQ9G,cIuRf~?mV>!񗈭úk$7[g39 FS 5i~VŅ˼&F,Tg 6+sY-Nm>G|>:{LErڑ9 .⟂V.W+%*/z~~O#57m5/m-koj3H6s1P˾_hvC7+vIOځ p}> xG?oSů-rMЯ. Qku fL3ш +3>ymGNq^]:YcJ.pugc uK,sYIb||{W~\Dn3l"zW ;Yrceͻ\~.@sҹ?V_N-B",nlL$t»bYP񃑜M΍c+HqZNC&0_Cǟ 4_3U%ajZLyC7_ `IxFҼG7:"iL3_,*)mtBoZXIM?'٦xOxG5Γpoaƒ=*${B+Դ/PNJ-XVPYv"ys7uVcdXwISN>Y95QVeIb\s ~C;AxN84^J㏡}hx?hZx֦ _q> h6nx'Oo)hVpRr%Q(5 xJK CRҢ׬(K= \dtcCk5_Zx4{Q/mc7Kø /^t IMY jҒ\M_/> lxlF|,u(4gXrHP\bF Gt49o~)x{~)~&6aH:{Yۇݰ->x]ÿ Q:5>sguK #Hl$gG4N?(?6[R K37tk~5<=dbr>eeO`Ml|INZ A:TW&eCG!y0k?%G?4}z75!rWNuS)c׶֐ syy<^YuLd =iWu?? DF4]Tf7͂r23W?a74ޛ( $r:;ZRGU%k[iamm2]v `*+;hXUU_5 zjicopenalpr_2.2.4.orig/runtime_data/keypoints/us/de1970.jpg000066400000000000000000000155701266464252400231460ustar00rootroot00000000000000JFIFHHC  !"$"$C~" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?3k^EY[Oq4j/z&vTDTeBIϧZhχo=GY%2ɫqym,Ez*SoV6+R(BVKc|s>I>9 $z\g@|<7?|s>Iy5? O|s>I>9 $z/~j>2[khSmH\7 gfd/*I60gGrPvz,v*;͞ MǨxo&Ձ 6ՇK]=3K3v9 8$Tw=SpZ&si\\NŻJprqtKTֱƭ@|<7?|s>Idh~ѧu j%n[CjIHpywᴪ?`Ɠ^N*(+tu:?hχo=G4 7s߇!돬kM~>Ɛ= cLc'~/̆ 1f~]8q޻Z2m{KT=[?Q Mǫ#ZwZȴݡҭix'`s8:Hq޹=K5Fc%[jt_>9 $zhχo=\/.?jWznlV8n_`MqګxfKYIrlk0YˀzsҜW^om8ƢW4 7?V/%m{>$ f0RV]<1\nogk[o$n_nE>0Σz" 2I7xk]4##h@.(͟Jp5aؚ_P^J.lrp~޳>a?Y=y5zY%2ɫ>(Q_@qQ@TGFMRt$d[<-HҪQE$Sl~9厡us+ J9'*sǥtW.zeiV6sI7[ 3^wEy2-Uj;ӱq#֞"&2Z]F:n z;r+坝no'EV,*y$KKRuiLJ|Ooks{iPۥ)!-Wړ)H4?AwukNF,'<ך<9Nmk7_wV$x+J}Կ$6BI7`NV7x{wωla iF`G s8(gay*7Jն>r]Kſ^'Ӭog,kzm嵴0DO+@I##Dt 8Zt%)Cdc)+3Y>:M69ѕt⸄rcpu>S4oi2^-͎ 1`P@G\eS)VJsVz][E^QI.sxKi_j]ڂR"?-8\YqE]ˆ:J=F$A#$*KsK>i&Sˢk!bvKw? |WLjlamd  < n 2zsŷ@ ͫ:uU7{Mn:$ieH}IM?8yVJg{P?3^odVr~?_B|qVwnY7>@} |yc08|,rMq'ѫ_]N-sQ7f#tXJ+h5xÙ"cM{t㜊~NM$E/3l*XOM4a"~O;Cf]Wyoko6#Oω^"nF_mx~ 3 U T}:q9mz6=NJTa)K[6VÙxٮ(8p>W/~URIs=^?hwھhM$ tPI'+2U'۴: RzZ'/T͝+0:|?Յ.0Q?{n1|zc=bώ-,_Nhv;v}Z4u-K/F4-UyE0Upt [4$J<%L;fCXFvzrINzR0Mɯ7>w_Iht˖P7U\w*(n&Thݕ }3V| _Na6g.WSO @8u?~kp%ƕwQ#p0ģg\OY4eYuyt=ɻ2/#8\m|ůjF.I]Y7COqf<ǟ Ok7c{oTy/LFH^0457e 3uů i>`jX\H_!oil!Mr7pG} Tx3-'RsSmF\NST_COxj> D嵖8Fkw,q2@i^c.N>E%N*nڻZ-m4}?MŘLO Zv||Nu꺰j^|}E} ^?|?^,eV>>`JF } ^?|?G,Un#53ƺvZr4֟`,JY \v@RA_|?G3/y'+ZҼYi-V5i^W3:nɝ^ ax[pJg_-(Sӟ̾ ܼ#FZǗBX RK~3:k]i&lD ]$}+;8--V8"XFNA^ ^?|?X>Jvܵw{wԨw|?j._Z֞h/űBA^_`2;2"f_rOWK0Ti-Q䌞Y]s^[ھ2ɧ&/]8P:F!\ͧv#?K1RkaRQ8-k=ǝ9pEi 15w5֝.7?³$_"HKnq^3h%Whi#\Qq$5^ugع]T젱$2G4egVHZK ]WP~Xm r{W^0߅tӭ#h5`cw7)r-SvwU?(? 6MWo?izn*kwg |$0Hܠg<ZW:^+ޯM_[qLBn`my\}H*O/jw6 :0S_?μ_/k77V:2KC@a|9ZKgy̚(Zَr d;杌)TiukN竢KN? ɰA똀_?hٿ[ޏ*P0=aiSi?W:7.AuN8D3.u~M9yzCi`el#kOyv[-he%v/cOJৈQR.>|$2`̭ p2MTq48=צo08!@@&ql4m* зt6FM ,G1V=RZ}\BAYNXTPl장X6fуY\ߓ5qd|{8AFB:rsp>*c+U9T,6heod5s?w?4}_&ܪ.|ke df_& !i|FH G^=3oMQN^o\=W0H$PAkՉ]r/;j ēN~ZmMsЕic;i_|6˥OS8;rp SՋ*Gė:ƪ]Yn%mP~@1ZxV/J7*Vfg8 G#%:^pru 'Q6_p~rUWlFno=oh:4 yݤgE٤a)Ă8-WVژ+㎤5ծ:d~ƓV; UڗƬy}yo<-YxhWږxผ4)(#;C8':';~?o閚it"hCE#/ Cl'P$4?->/qn--pi,.~t~oK$xnY| <'_T\4e7'Y=^(>Я4RؐۤawŤ,W0NHU73OfӮ-'X *y ϯEbth7n^_`ivi^Y^%\\Cw7)q\gƚ^Egtnfomv6s;4/좻vhdRA.a;d^Pg۵[%RbIu4}Ee~Bl[x=6rjFAy2T A{y̖b;"# \~rv>^BoiPi7J*IZ܆Oι9$# So|Y&a3G"(B`S|.^/5 lV8FgtD`>QĖfLmơlɫZƭyy(WDUrGNIi?z7)#]o]ghi-lLR2 M*s}v𮻨k^n-K*BӇ!YC랕=}[K$M J#T`*I1ũiR{VJ( Z&Ko-JoHn] W%ʀ zcL-chf*IԶ w%A >g_]n9m;PWD77wS6x;yӰfу\'vp I4k4I3mk'r?['1^K\MUY7*] }>^^iꏞs(rMYF(tAb-g9c_26u#$:Z}&q@l(MO?œ$Qʳ.ܳװh ]gCv~1t-.Ưl}!nU^dFÃ$nE̞2d1}S}suRm袊Q@Q@Q@Q@MgyWҮb#BGy?hZp&QȔ'p W|Um < e9y#P:>㢚ar|]7nd)wȈYC =.٣S\\I!v ~Fh,x6{#bC#hIx N-zrg[!Q™G0$cY i0!c 7$dq`z+s Mm_-ϡ2:8J3ݴy_GWoZ4O@,iɵXTtBK~@,q؂* 6G8: ԪQ՚?mץђ^3KS_Mg3~u(}& B.q?ҾtfmZ/!XbC7儯_BF2yL|0#+-`ivOx?}*QEr8(((sk[Č:{UjǏמꁿ?|-lzN%x?5f?ΪxINf7Sd*(((((+agV,uK=Nqypkt:?.5v`O08KrxM$ӌnۃ$ k.@{p}+89Vf?Ta~%Iأ{iLw#edW8Pq]%/ω-uIi"*qwT`N b]sP[xI 2@X{+5X>2htntͫIҼ+`iHA(H-vd8g)$ۭy|a,*4hy4z-my)M5/ZVE,p€TnַG+ M6S4Ļg71U `Y{+` FD#E55rZ8bg K%."ŻOܳLxQVn͜Q^S߳VwZg仏;8R|U{N{jA0JYOр\Ű5"`ULJ\6 Š@ 5IӫLs:X~ԛUռ8yJ'ܮ[XʀMaml}=9IuE`u ( ( (0;q5Aނ܋}tZA?3\Nj̬ܶSAkc<#:1͛Cjwo >L?Ss{QEQEQEQEQExǫ (aoqnl@vȅ{nW5x2z~%ѬA\i:0u9WSٔE|mU~ϸoUq zs |#CO T꛿a܌wU4jW:5A8|q8N}QWJWmcv靫۠$Wbds踫/e1|N𧌮<]{J-J_3TpGHV1Ӿu!kVsum cc$kKƥ`2@U#W'MeDu]}D 1C9-/:4Q|%I5frq70;W>q {{FoYܼK!wVf<}jZF4t0u_ 'H-  %[?^8WxY_vc675JrJ+){dŠQEQEQEak fӟdru͸s#xC6ſks)g¤i&C{n#x5Y?q'd*(), 8=1\ֹOPt='N}UEϵ# $ע &ۻHO~GWxB_ƚxčj-\,rFTvParTֺ_WFjzn#CvX2A}\|TֲxYyzԶȆNcqs>ku_~Z:Ni4)9p8jolnn> K{{9U2x<ϧz5i=ӷM ƑW)*/4Lw`;jW:_Eх֧ C!H̏,W uz4k}?_ic 8޸ω6>yCk5gx :X~{+k;*b;<~SޥS"h7Rxo7٦b+y%I5mTףkMkuo"-Y['%C7kGm6Srwz7ˁ*ȸw/ҨJM{oߩi{:3^7k$ܪ<\#'=:?S4΂Ao<$ssF:_Xynt𤼗ޭxW;5|>U͙[4oP3E#é.nv_M/FZYpey;݁Uo|.xE{9B 4? i"Mq6n,iVutt>tw|Kwi}byʥdnkoK~]F|P$Vi032 9!H?LVuՓq2?JG"(((?fʊesj ֬&Yoz G] K%X+,NyjWtHx}k~+s a\.q;du@};TV[Gzۃ՞I=M.~u Q *ݕwmA:77Oڠ Ԃ@o#fZ&5[TXOpq!PNVhizknggg#hc ާn9sTt]ºFu ;Cvj<05F8 Ҳ\M2+;H#͟FIp~_Դ].MCB :RΏjj4z~siVWY^1` Wf#dgc+Ё;'taяKڠT%eUI sp"k7UtZ=fc%+p@a}z?SM?j÷`x=GK& Pŷdκ5LZF<}U@ڊ8zq*J_ hz5tdg#۩KNukU$Xjq ФE 5(E -6tٓ/ 40iW_˗lOhƌΏj:P}ũho.K ^YA'LX s%ޙ Hr8p:1oΟDJqSEźTUD*(UPN[#zTK70QJ ?lwۚ_AVn~toG]o-<ϘHSYŘcvv _5vO =#ˀvۥiL F3k7mopenalpr_2.2.4.orig/runtime_data/keypoints/us/ga2007.jpg000066400000000000000000000145521266464252400231340ustar00rootroot00000000000000JFIFHHC  !"$"$C{" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?\00=?`\[{9pNB~y5f<L3ÁY3HH1bh$Sp~GiC]Zh |G?vMO֑☬R/Ef\ʤ+t408ʛ|z'Y7 ;1key"vN{J]JWz!i <y'ǭ0>6Ci7V7SbmAGUfj?چ}yPL(`=Ȯs⎿?,Z0K ӚǠ|8bjZjӜq4y,ǓB@(3_B 蚖,-a e>ʟ[]񆗠ȱz摦3pjN3^}Jq/:Vwg~{ndO#5quGJW.+1W=M5+Wn$"w1 pOڢ?Ÿuuo4fiF:vqںOj:SK^%u./U|HS涿m.5OLӣ>FrV}+Di]6$ ڸi_Dƹ\0ȳdx4 gU1]RD'T@\McV=wXC"I.R=rN(&v۸BDdF}Tmz}Kp 7n[GI]&K 66|w~cgO?~8~Ɠ~<}h.RwH4&HIt^t\{mAm.b*(C^#*#^Q_>݀,2kl?/d:g_WЫ 7F B T4)`?JvG.~Z?h i<ב$i g%@N@v%AI nd?4RD@#T=k4,s%tpI!tb=5W|>coiM ƦĆFa1 ӣFj~Vtn}*abWmLzH&\ztVYAwk.7pS_T(~?.8_CG=ϟS>8v4駹lq#2cIǯ5h^ խ7i-} 8/+Y 5̓Mʱ?ݕ5{-aɘ.||O]< wY] j=ON6 z=0ý3Zm|Ix^1Ar H}N=@fxs Ol&d|l/cN%/"H-&g4KM;P֭CƷ!t^B[s?p>9玤o^)ՏۍV.9u 0zV?-R5ۻ7;.Y@Ρ=sҍ|1?^h^!]?U4FŸH!v7Tu/x"Kf@ڍ䐕 "8]F??H]n]A,/$ӧwd1A8 ;Gt 7-#y{N}r~lJa_A}JQ 3"diF_xcQ{o'm?@!dWxJVgS ffo4F8gR%8`x~5N[=J32}S7Z]Ia-Sᮥu U8 `Vwb-(̥-b#1TحA<;ֱ؄ZQT0((((=))M &mנǍr_^}^a6+Xq~vЫ)~[O#zrBE$Q@Q@Q@Q@ #hTu5F.$@F(JxAdMiHrW+nX[-#B*i~E a調j[y%ױ inc+K8qA2kn/IVfЭ{;`ᗳH'Soi{`n#x墷8?Q^] ﮥ̕NO=N!{nCɷe=Ry>exm5yq]x—cg!7o>x/\h󧚪>#pd6}kS⦘jY6}P޼f Cݒϡs֨}MRѓx'kJ3}Gߥxw-1Y!8 c^$dPFk*1r`Seӳ:sEWxAEPEPҔ84/J}ZF4 9x:Kϼ[l?/򠢭FЫ-~VVЫ-~S…*I ( ( ( (9o<13݋RO@H֛>y|.`eSX῁׺0ᔏPk8ٺ}ywgk%֙g?U:W!t6w,2GB~灾&Y7 [rk)&F ,J$OCQP)T/J8hQQ=)1d  -#4iϴGɧG"ɝu%H?z x%U-,?/򠢾# i!Y+Ekiϓդ00{qC(B ((&T(`tQE0 >'5Z ]^a6yyX㵊72@S(YK=EokI?_^O@ёʟoQ]u # )CWĈ E!3N?CG~Q4E!?Ȧ"~1տ.?ȣˏy) 2^ZH*eI = E}=7=q޴".?Ȣe$*l(·VOQ<Gs/·QCkSˏy(i|9B_6~U<yq4Es/·|CkSˏy(i|9BXGO.?ȣˏy( ~t?o:Z\?GOQ2ߕt?oʵ<".?ȣ.e}gB+67dsOV".?ȣW(}o֣uOSQEr??j m,csOX.Vss5&GAVf (($A RQ@~@ORO$f4'Ur>X,A\o[sd$_ҝzx,+cw:߉} sr *귲4ZMzolw:9;Ֆ JD2O^V+9aSgG+۝94gUs.{3d\q^_4MmilJx&[N7ܞjh\ZuQR_1#r:a˖\3`W> >SK.v}B6L8 1Ok_M~OYh֧A+9pX۳ 㱯}:VuH5Y46?.4BMŕt;kA4YDofrO5p̳LDoNۮ׾򧉧ϭ|7Gj%R^}=.2,lGUp_tKh^) -w )$J_\_UcA{50Eu8j$ M:Oy vb!s]jjֻ5se;%} -[K]R6庪kn. ;?kм=|04oK5 &UQ6 #4VZ6p%|+TlʎGT?ƃzYy9Jq{e}_2I6|'YciZ.DG'*+{mO/w`ɪWʒmg¼m\bK떉O"(85cY.-PnX&`cJ+lwn;j3LP-?N^ 33E*9O{[ϊFԿOCKIb uSOod5n"N?@?tQUKvrmoF8;?֡[|Â9ʢ=~4)8|29@v)Etr;ږuuPy'} ^6e Z^Z3JXY6F12:U;5%#*y&ؤf3z_3~҄o )8}kBW2,*j΅Z]G|Mqsڱ-6 t <A׳5v6. OU*%-sZ/[Y$]HJR5,NdדxT^I*0Ԯ>w&Ԫʮȴ=7p?Ǯ>]5 (=} [s$V2~\9֩8Ňvnr{}e?Ψᠷ9b[&?0+9~PPu>FS*w{ ,`B\'ZIݝ1 (((簢N3@ E"Z(( (#"1Jɦ98OuɢIc1ȁWIjjSשgpNGPݳQ@NyjVӦ2.M=;OU%ǃ?_+:tjpXS${SK |b|/D\lȏ<ֺz#6C&\5KQ ZuUdw eiʸ\O>z3?W#⣹KEq\뇚 ڕ8ǚ&sv.nZ(p_V`u ڪݴ§s#GSj )u'_l~T^[Wʰג~*["Ɵg8'njW)9;IYQR0(((#<斊E4Q@Q@Q@Q@-%ɣIchP~Q=Et wv >T^Ƶ>uuf6H5kg6 fd*:VdĈBc֯[ډVFD;+\2|"3Is˱6Dۑ]z*`Weiʾq)hn?gVy2 d >1k~>qeej.F798 Br3Tfo@9Dwf|R_~ 51/C]7I$cܮ]wG >'qxebccczc@Od8ּK_AWvl5`XE$VI=9])i7;v6~ a@uڮi6M{z}Xs(QӧPQQ O|u|-{:G :l4ֶBH 3"TFpO"|$>xw4[oa5 Ͼ YX$ H'ñeTpj|*]!$zTsjIjP-ˍ D9.rG5\Ww|c<\}D!bҸx>~[i6QyށRm0jV0JKr## jZa1қW=2s%qV񽦍ɬ\=GcxT {G  _tԥ-M7FqYyfwRgbԬfGrժ^m|sC ZzmhPaνtS;[/:/k~ )t+Qlgwaq{&Ju!UԂ=ppAE(/l"9 _Y5%ڣ4)7*);*Ut??vVq'J U>#m?T#H5+ituܤv?>՗],nmf&7!zRzhP8j `SL+dg_^MBdhgizQAwD}?ZEMBE1@6[=6,촻`BG1fbI,IMlQJ{k Zw[j^ E#vyKd`.jB[ľB%/t,spqȯT\|<߃3m|SlR^BسroxcY֮>xt7YOao,8ۓae'wKπF CPMKRup,K d$|9DY俰֡FQ'cX`]j+W/ j;<]LKmmw`vƤ_'xҴxRw"M(#Vp,WkNEz3ǃ[[m<= Vb9%H'Lz YkH<{k-Xt7ڝj1QN@Esko Ӯ/5+Ektk$nr3+EK?M[64C&qa&Fc> ^hZ瀬5v odzV甉 #_o>sI|#|uk;i ς2)Wz΋Zj7<}b7lP {]Pˍ֍ƯoUB㌘Fk(jnIcgh3bdzbIM(?4 `7}8q >< RʶJ}1+1DGGop˰$;뱵LN*R9}?=(?,:@5Fٶ~σH=Gz&?bdzf:>iiK-.njW⼽m8Qk3W͞~xi mč}G|:u㑖#ҪY=:&[ "׵! ;MQw6@J5$fHG;(C1zRJ~?J_YV͎c-y9Ƞێ09MOV#=)%Y{q@?7dqyB?֗??w/wk'Oǁ-'fݠeF~ܶqy}ǢpF9#$~h { /\ҵ>/lOz9f=F?iiƮe!{ "xDv0ǝz8ȮyXrɨ 6oO3J2 9uC})g9o֤7:kl'hn sݒHkq=| S]sQ 60zv5fF}ϧNHH U֣;}~ʭx]x'#J@? }=$#)Ѕ?_qP= g$ #v?Z>=Kۣ1G_;p`>Z;Gپmc$?K[JO;KbF{:J8eJ[p:F!>=4TE:RfJ[Qrxߊ4϶\%qw2Ͻt:$~{wK uXžsj9|w[[I;x Ѵr#T׬DM CV\ʀ202;q\MhvO)Q>"/i>!t pBܠ(\(rP1ּ$~wĕxgKӿh}N+s ÇI  \\.gw'<[X^Lzdom0]^G$sڲu-gZ>([qpry0Ì.N@89ߥ]j6e{@<`9?[vv>$~\9}LY¤}*f[ֵ^ɤJ0&2d1\d럻j%KGDg/oMNX!@W̐(F\scxޡnP֠Y4j$+ެ~_jzLVC2jĝiY=UTz)9-ͨacRJ,vV9%[%Ÿ9ǀ>LQ6"{ן|anOhO]K-a;%w@ [;M9S}kns[p0neAZfKrF܌c,VAe^}qM;7/?N*ɑ <|?B&Q[;Ʈq֯˚g`Eg@:.?k~=}zC d%/D5aP29*yFX.r1 }}sqk& p?8a(##9UfDg'uQ:t?ϿJ,Fr$9q~!$⏬0Ю1JGg@F{t31P@*Z;QyhYGFusⶊ@ gL!ܠRɆߧ\q՟TPs}}ae^Ks1fĒUԆS1VR =?*@ U1JåهgM&rxz 6@u+!8TU' |'xF팖0LCJi@vbU~Q ҐW#sVNi΅$ ,@zMqi,gvӆݏpx'לc5'cG99hd~a V6F1=1"`(0)*2Tg6W8!rr9W9Ҧ3%wT/׌Q_EfGFr >xdg=ĺ< Yg>vԨH.Vk8 psiG@?/LZnUuXaBN| A0f$k^ky/-\ qF:JxS/U81B~279= ?i$"Ȓ gk(?U*v)x^[u*JqF+B3i 0;S4>)8kOL tRW g?AX;Tbֻ8' qpō ĒK{0.Tc^2t{*jf4e׾֭}4(ӭyK9v`mV ] pƪ@gQ$hϏ+L TuJx29~N8@)ٸqp8ҙ-EP1:?SR??ѕ-Sӷ&p@ Ue`suR~_9 ܌=ڀ'ΐ # d2HNR#n9Ҝ.3?҂)( Fq J:PI *Lqt89j--8;H6$f'?q3۟Xx'U\psI 0['?E:`q \$9=2F OB>8b3׌ R#OKsu)cW|r=z; l>=1L03"x#| ޽j)dd)@^Rt ])+r=xu`Ҽ9a+/`S'=>k3ZjzRfwA] [{[hZߑ)$|H=VKv92]&O~l3e JAp&2U,-]%7#NUst 1|Hl|<Rxj}XN+k}1gA Y.9=~{?kJgS##QϷ20$t /?κ`#$601-\g 0qןQt^9q~T0' 3=*$  d s 9UOˑ=8B9P;}-#^L'ke'zo$rz~t? y'q~$ŰC i8 H<瑃S)$4v'&Cq֑yi,PI0:Ԗwh?ytbe_Fxs\gxUYf{0'$d8̂Chj1bC(h9sR&KVpkͬK4)t{da|qG=zsfm3ɺ/[I08>F1ITV=1씝Vgw$66Z1x&5Aңeu Q%d=}+;V.nt!$*N[9oybI޲m_9uOK\zK^U.gvhɬgK K )ߘg?ftدZP-eUpF$yXRɬD#x `d*MTnCDwҸI?Xq+ FQW\gYn❧$H2u,)#ړ~TnA?p {v7Xqsۭwfԇ qɂ߼RW uZ)&MIobQnv<`F?rT!bP[93Y4cHf;Jfirϵ7~T0<Hj>GZ,Ar==kR GnWNBLƫIqBz~=r>%A1/ngFܢFbb #F<֗4Ν= HĎ2NSC< afg<9wM:{W-&q'8ߎbAݛ1ygpܸ|9Ǡ[Zuּh&8ݣpR[:[_YԈT*p c{]4 ѳ ̣wO~*ω3]2aӿ O.X珽 _׊j˨ηAhg r2=gL9mO1OV&"ˤ0l#s+oN}<\V[ @ەn6`$gƹ\GH{˲1t`R8to=*1S88{q8*9=L7-:?/d qϿH woZc " O9ϥ H'OId} 1W3/uTqij7:yVvm'[/MY$P3>yR{y~Qcw$OV1n3e;5I#6Y|snC[z aO" ٶ[rB랾Ê{q(Ϯ=3ǿ|'U[ Ip\W۹s'5tƙO=*m52X3Xc;trongdTRvN`ssrktҚܮxp9? [$zFfTX<`rIk]&gO6ȨE`|1'y:H)x<)]!e˥S[g>^>\IȬwVRds0GPzVx32q",cBu62W`A8$sQA+zgP䭠]8_]JdL|@"m㢩??ZqƳ#VPDK9pq2*# @zU0&Kwm fYX ̀Sk}OMW#șWs:1C-ch[oGNOS@02$YO_ U\p2v&R@F /DOdGt5--JmEZ"y!`XqkZ<"_<]#DӬh:#0XUʙa*r@O|OBGtH''4ѧj'RwVzwz ΐ\!m0py{UbѐT`rs'ԣN:*]9J a7Rirr 9ONҙ\ Sԯ^<пL]otq!Ɨ(jF98ShiSm!:˥]k>;E'rj|R/:Ly?O4i@Iܽ:W\=]&96 K.$&)Nߒv?H4{d/^nh:U2 Mv70Tu=H=UҮqi?myrcBI18\ ԭ?q8흭Lm&kkj|fIrxh.r Ŭ;XӆtsPNEyv=)|,NYTGe)"]6r6']V{ eјn>B]op$pOr10|#r~WdYR.w_SUjF $䑞zS f]v|Biת0-&,-F',H qz~"2ܑԜ(ӯ6k GO![|`{PcI.\0d_jݵxɑ(Y`wvkhQopenalpr_2.2.4.orig/runtime_data/keypoints/us/id2006.jpg000066400000000000000000000247021266464252400231360ustar00rootroot00000000000000JFIFHHC  !"$"$C}" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Ik+M>dq^$ڄsYZ'5lHAO2V:"9W t_99S9lzn c'9^i_c:|qL'&˱dDx[X۩D&~Ӫuo#9 xÃΨ3DcV- A8I+sjԃtyjm; Bu/ 6۳|U*Fl5Vv>'JzО#BH+=SG s1c͕QGggOc'd7ߍQ "zƩ#} O?eum+\Cn̂jDLgw%vU[mǪ'瓨_ce xxԯ@Y¼?{~ C,5ky#[v+ƫ֋ϼO8TP@֣dj\IHPÂ;x4X:6wG:>?YV"{6&YH SA1Z߳-3dz_KiF\+:A!q԰ k ప~OJ|u??Ҿ==jwSEo#%̰r *y?Z^ K MjV|AS?7WKK_" w{j{$_2b47W+>x'ixQ]EF2\2|Ol|=f.'2jzo9! x/Lɴ0ݭsig/,oIӯ٧+¾'{<ϵO?;a?OO >׃xޭxQ]F eF֦lnPA%+$Wr"}e*Xv$v\v==  %tkC3 E<|qhq"a?_ t9s隇cd_V} ?c_S65rxD -zzw%RR)mgu[OQu8K7qwtm2r:ӎHĒu AbB^j;ɷ2)Obp;}_PL @ֶϛQTj. 4-_š}ǃֳo^}ȫ7*#tkSڹ><'VM3\O3I_OC-jr1cx>/-R{M[4&+.@sX)zk-#‘ZHu bU7c!YFm72yCzjHˮ\88YYN%f;QA烽>UgO_SoJ\ڗЂ*Ep_ s![f ܜUc6g/[{mZwM9#mvꥊcV[i? +;+eRgG̒>aekFZFKhV,M3ؖ- Pweb3#YJ{[ XjXx5K=B( <ʠ, Nҹ mijV=PI7[Khp*rퟔ4O<1t?XKwu.g@dx#ɬ-Knzu 4&ori2I)Fʲ*PK| "RZP ++/*K644{1CK9*w`+|We'c+m2kY$,y{Ǔԕ8F2z׃\UH$=3iֲcE]Ž>._>>-`St$My$sDepF;An[:Gԏ`OҼYԵvHs$OkaWS{i# j=k2ܭspn>ts+%~άjTV={IM/H)6yJƲgꨉ z>0 x&HΝ Frd#n.IItO VTL5R)`/J6(Ic~Qzi,IoivPmsżq2`v.FWuG'bW\I)+?oˌ] k~/ %!##zb-4dr+\wjc/G'^p8;/[泸5rBTW ;R~x1QYZvj2;qɯ_ciW}⟲bY:!'<ۈhǥ}~ NLc.ɉ>> N Z#L^zGgvd}+t~;Q G3Z͡^f1 k }ĈGl.)}3"g6رjӱ"FjW,,}k]cPqL]c#(TY+5x^FL8kk ~X3iȧ.'/Z"0al{Tc y>ƾ AϘ-4.?;O; ܜ>yMM} Nxޔ~ٌtŖzv>>o p<9R0Z>=3_a9?:rH zt.'10'ӵ4jo1#aendN* Jidt'_1c8<ל=pMOào*[9.bK9;pF:Tp]ܕ$= t VXWSEKkt|L5D/TD>P]x\'] : ?XK'qjoy,apG?|J6Y!#c c{=\pͻN^e;'Kd!dS3@s#e4DM($Hܫ=pWBĒY,NIOֺ .+Nm^KR-pW}kSTdg)93[w^:`{%0D`@E j=O|IauefgRsr.T95^n⍯Ƚ^U^k[뷷omoYd`1$k&A$ f,2xkMnelp]# =NM}!RU'ܠzt#Xs w1[ݸ׋%35$}ǀsێk<*[YLr(*Gqרkqs=b[[K/`9b 3מ1]NGj.N%2 {>ݾ&YZ+`Xa}>09JXo+ܻ7wY")t SX=ْ-RLS #A$YaTOۓNaǝE9_l@H㧷5QllZoy-RȰ)v2@|dV_;̏!*ps1&Hwd4W}ѓGܬ[w6:gKgGy ]}n0W1UؔΦoߍ=,R :,!]Bq{t֬Bn.nyUa`-ŝ/@&ٯ ׆J,@.^2X\x)6å}H.s©\]pkF{m>xY~Up9c}Pxc;L>kFٮkڱ l8qI{ƦUF-V 3ZA8>A?־*u3C%Rhd ;ȭ0Rq9#!AZG2y`xa{γg]|onuw;^2$[G1$sb|Bg'ʕ@pdkvܜfr-z}qu"ukky-d3>$b"KO, V&X/7ʍp2:<_4/9)q~I \GL{Ky cHʋdn+89?_ݦ~EV4cIP38| x4uW nO< ddQ͏6F|0s39/>1iPچ8naU Ls('mIօ:M597oFv [AN/KiJ_+Α\m#y085^>8][8|,$]䜰X`dkAω Ya+LJ\7pEPO#rZ#YY,&o.$#`q8.>]Yn,o㷺XAv#q/JgZ֚Zr[Zۿ-&UTqBqN_AռgHZKx<ƳddX26#oދ+{gՠV%C_JDTT pl: 9`WMNh͆L>Q듰sXimo@Q>.^kMrm̿c8m`r )<=Jִ T?M'*_W]sG9qj #iO/UHm@ƿc< 8YEFxO O5f$ /kL>*ToaF[>ZGU㴿ϿukO;_zDyvHwb1]U;"}O}Nmk)=Vok_.]xbѰɾ<!OJJ|9gHxdAs5WO]A*#>¼zȋ7U Eˑ¾]9Xdge~Htm`>#h *F1. .aqa $0|rp9,k` P<O(\dO$O4M⦟{y&5^Bm s'`UHxϥcǍK+Aq=Y\CXm[|!V|BԼ]1kus9/ J E_ >'ouo}x{iZa8w`'#دWL?<\5(5?\v?>o7IϻTӝ݉8|BBqUQ)qXRHOl iK0`Wb@@%[>MRØ:~Iz8!/k> ۥwjYG"ti@ny +M<:=xwN6vgfNlΞJ^l,Vh+hg^m5$<--}  тqU 3ae>Uvw'R~8-gAQ\' HMBNfp෴nʳI&cLN; NZ^x Mx_/-JfӐu2lHkO+M7OUo~D Աܚ OʼV>hP6I]<(9%vFv#,I|uG;{:21 8 8*eRnJgB }hZ"}ק]dAn=[9L4  #gyҚ˓I0q8:z>ch#Ǻ7WIi֑HcTz ig|n9 +Ӑ{ WZ-_ӵƞ,ֳ1HVF$T̵Hk#[Yտ5][]VKs+9 $v嘦Vn8_?xeqSTRps"iZR>KqؐY} 㿥yG=3]Xcp_R ]X>*A85˦o3QAR1^Q?x7Iԑ4[uM/ⴗpQ8?"$ 4es:VvB}WV)U:Y IW @ۻv/gɧzU8v]ܨHВTSI9~3Ma+yc`rb0O nҾ?O8k+۝L-ԴH$ fjJj#(sr^38I7\3+/WOoɣ3Ԧ~Ep%oL܇Zk^ +%\[h9X=d@BFPMOZN/j>uSY⸁SvrvŔb}2_]Y^ږChE<'rF6dug^xĚ^<,<ћP} yK_LjkT̒Gڡ9T]|.19 8TW~~n.dezSZ& X ]kB2/*NDGhSmڡdBcWONռMVM3U[.eM/D$;*dl(KdrxK"j? ľl" *ݘb88 V/CEW}~HM-z˧xM-M̉V8{F+Uf *#s_S~#oghD۴}䂬DnFc82`Fk9<1ںdaH>y-q\ G|ƙkkmHl"XPNHrz6.T/\f!{OR#UXS$ϒ`:P{I'zMðu\Wi;Tj 1\&%ǯ9uAP <XU̸IIh _x4,Xq*!ʨiȬo}]3HNA=& izGnG%z}{Re8y4;2xKP; _3 6tޝӜOB#M1)E8@yqy ͼyOqKwܓ֤s#4 qքˏjS\dJ8=:5tg;N01ZG܁F逭羚t n{Ɏy1$Rbz|c ߧ4qcJ^qH8t4) 4hpHqIѓcOzq?Jh'~y!q> ԤxbĬH͑v&˄Lw6++*^Av2y2%sIua`rfu/k#=-33սW6Iˌ|q=GTs,!p\YkPpbq!PRmTTR"J C"I΄䣀:}pQ/ IpeF$w YW\;tJ.PnB\vGH)(ddRg3>X;Q2=)8uF=i0ʑHNg뎴{n b( p₠g8#'J p{{FOhs tpI88 [ߧ4SۿN=O.Pa#7mo7$> kaɯ3E#|uy֎߅dʰ([~;&1~u҃pODLBqAox`u4w֍:SEdho g=)w՞=iEsG}}8^&~4xi{7\Ɔ㜆MU&9`sӚE@p~t>dho =h ۨ{7OzO`֏fĤWFzt-jS^Vbdk̼askf'_j:!7bMn>v \3Dc͚A3YFLF9cHu^z:\r3I`dzWcq恪8ϽpZK(pwTcwͪ 9A~3B<|}UsrGjS H?Mo#J`꯰wS2EڀX$0qNMlnݿ>#.>l~)P<oAו;KXR59h+ռ+ UY01p\XSQ\56Xde&9# :2ű3d.J`$g]ևORYX%{/!ŋ[ X}0rvj)Ս_ue^O^Z#i9<;H@whzݾЏsN;1$uڃƼM 7P}aNMOF>˭*}i_[?3T}bT|ǚhAGa\PuT^I=k_Zۀ_;IS} B_ 䀍 5/I!Q1W|wgVph[) R)kKvI<#3IWϋ~~}QRD6ul ڬFw}Ol}663Rl1\+_1FI8OLF:QkOASYn_-tI,F7G |PI*6ci&o۳~z^Iةҫ $_^iĞ[RCGʄcj2gaQ0p쮧*AX8qgYnnuk =]J›@ d;*Jௌn=z'rN>4X[6~;Z4ܓrU\t㞣mA#aՊZڑ[FZ!L]", (*Ns^{iF]Ψ֫(rSl j}˦Va)#@mv,0H+(nl|?vo{hoYRB2(o^{-BWKDٝ&K+"X܍c'OZ8UVl',gV5#Fh;\CUwQ pC*<`),9']隥ƙ*yk+C4amu8#G_Jt{7ʣ4vgW!xnO^i?+?sm}Yw$PuqakGަ=MVAi8>Wmgiwz|9yYJD(w\:\vSZ^\\!k{9"q kLsνEmv,D?_O?Zˊˉ>$|h. ȹb;~nY#~xv^_3Ċ-iu fM(́q$<7%?tyL1my{YF9Fwuc+ϣê|2aY~d_?@"2%OJ?xuxKb?J>'Żm,?!Y?0xgM2p2:G׋/#J\GiS]ܜe.#՜4~e,]Oh|;y>Kibxv8 "MN \ft'd)'p53uFM;s߃4}:[->Y.FPxĊ7nv(l>0wZeڄ}&hv mh c0+?ts@??B'eI;ã4J3m_k"O[tךIawmC-4e)éI^N*MVo ,WN}PK"y [GErv؟,oC:ztE?x;ÃP5~nW_+Y3[xZ ե=FM@ $*\A:s$x+b.|[᛻d&8bhyf %Q>;?Zx+s\?M9P?Y V,4|y}*JIP=_0ѓ\nK:%s#nkY|5ؙ@?Oz7x3Al (ס~nW>!m>Rn@2Y:Z[_ ¤6N?HX}-Nx! '>5ހ~-L-9c[?_4o~A}Y/uM\J5`$`deoz|W=C^%{yQ8Q*U$ rOڞ$ 0Cƪk~#z?ϕ\T\%δ)ZLaFGiU-[’4E0Tw^,\oBv`ǖ3OٜGqu8=0G{r=[;ָxvK }:K3/ȧdd_=M|f}X+)(openalpr_2.2.4.orig/runtime_data/keypoints/us/in2009.jpg000066400000000000000000000167251266464252400231610ustar00rootroot00000000000000JFIFC  !"$"$C}" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?=ƃm_hڄqZ[Ky][ 6dGZhfKLq>_?oڷᾓ'~h0zqxh߿!r_4__21!gh:UTb//_V?h[8\ys 9# [=kxF(h'>[{41>i>H!1jUÿ Ah6 2Gu*NJ81\\&xAտ6WF\}h0>\H?ڃ=֝mE]^\kk9n.&b(B+1 N_=MR?m< rm8ͥA;31y~ $}t6zPOWھsK)W#/n# x+`7}\9?o<~qgn8i@ ={9;?F\!(r?S# !jLd>+ʀ2zzn}GNԕz_}193?'>-0uT߿"xc#ś 0'(2zoߝ(9=brwqx{8vS*y|k'q a3@98^1ڏoO}/_?g/rԾ_=<'H? tI篚ڜ֒_7W/Vy05O zUDžtcmnNە}rD1!1ϥ)YmTki| ;[CI^-j~ƀm.5d܀T6Xl?/QjZxKHkX%}UY~E}ѴgppJnrtQy TMQEQEQEQEQEQEQEQEQEQEQEI۬x9+'2xI?m^4S4VX6$fe9Ծ_`yF=@O5|ygGD&:VO5xς'w|Oؼvϛ7$/gɄ4Y}kNѭ-汴Osj_=bCG-®visk4[$z̓cK'%FiUevMO `nFK׵k3OF# sPXvM*g<lF?]I^Vzx~k>8.!$Hgpkqǖ ,zpvzpCo $򬤻F0I$# rNOquͤ]\ x#/ ԐI~H_5yIBU@h.qf?BɃ%>X_m^7} ]xKmA--*"i;*N@zz~9k; NٯMnuv1VlnvU\q^bԇFt*Atu* w"~cֹ 8ѺvmSL:Nvk|%ono! 1l|r|3rNxg<<{fu ,>$HSD΄03z0a7m- f3?Nz)/<['‹%߼XYYo1FqZ5wҚG scX9 XݯI/fwہ<;O>ҤYo!.x72`}K8xȟZ?Vw|`l"i,-5ř?AOAIէ'n]u~VPIo<<K?,]19!B=~eHn0Fkh ti j8ܸ2ۙK(2apǨڻUt])Qm˩4*<z{gu)ElA:ujT IRc*-/wm|WㅇSNg]DXRD5xH~,4[2Òl` ޅOx>Kgt SYүo[hӾžU\4hU<_e5;qgQ6Qh5WdcuM+>WkMc:ZZ}̯7hbpN7xm~ZwLnd͆x F`r>v5 `I-ړsbq:v>ăqkh ybH,!8W_*9NJrPn2z;5?+I-R>xFŞ15kcqX4)ryg:C\hZݒX< 0ףx?Q|bT֮ۛN300u } _'Ml[j)$^TNڌ=|b%W𵧪#7M{3hP'_Ծ_=:.fYNE,(;)\L$|=~xAYjcbn@*@d`91r9d-{ \'s2g|?Q=}'AIa=3 _sg?yhHۺi W޿f0{Rg_'~*|O/-ty[{E1;U 1cɼ?Яrv:S洨&ҵu*k)__BQt9.!8#8>>NM+]zdk F}-~\V ?nm%o-T[]Xs}jbg-㰩xz QK '˩.|AⷽYǞp;nK@wu_6\iڏK{yJ!a۳ g-h߽JT]yZx;mw>-!_:4kL+`[)A_BB} /iv[2`8` 8F(c+EYKO-ݣφ6ՍΫ^Mc01Aſvs2Zmb0n:]3GiB#=8%ރ+]7>!ޝek˼7om;[ _>+Sm LI`0[ge )8_y{n* 㗌<6dkYa/-rY႑ -yV'YI'ͥO[p~Ќ}g/TC6NNQf`I%)l|C=SyT}ޖVErlz yVO!?µݏ~?Ηs:У·z+dzU-;Cx*9:}RTg?Ηs?eMPun ٗy?0/E$7zMݬu3C%2_7.5>X-4Hh6OL : xa($ hU ;W:\=9Oopenalpr_2.2.4.orig/runtime_data/keypoints/us/ks2007b.jpg000066400000000000000000000200171266464252400233150ustar00rootroot00000000000000JFIF``C  !"$"$C~" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?:ipH[ډFF# sW|U%~ŠIڦ9w[_?~/ֽ̿ Jt;VQ F'oV5}Jf%:?̟rJ5ߙ?cU"h-K[ rmݐ=x*)75^mI.-4(o2sPSz+v '\FfSؑ8 1RA|- u*v4QVlS~([WX+o׾ Zml\qx߈] %无XqIf;-'U*8w:۠枬Juߙ?߰2K{T[ˋQÿU_Efx[ov{amcyd;cL}NOXJ FS~([W| <15;Zjb8>oY}jm[.Br'XYQ<QSO 9U*F\%:?̟rJuߙ?|ޕ,mž<1R$_9Rweִ Mum5; "6(6 :PtvRU.VH[Q N'#:DŽ jZm)پR3R"gQygmxĻ5+UK[kFL-yp*Beȣv5)7߰2?)a~d|<xe(eR9]"fpB Fy3X񞸞֨bi\Gkw;c{+Oq#TL)a~dS~+~xQ.&vm n;[xOz>aqu4Aumʊx|5H)ReoS~([W-/!2kM GmnK!*|#˶wvm+}|K+E(D8d{yU&Ei趯G%?f4!JQvag)NgWzgCX+=k?lW(Ez'0QERPJpF?| #_%Ԯ>ѡ%| ?=W'W&E #5%8)ǰi<#utTnj7^ang^׊5>RIq*¸\Ts3wV2J/cup0nT +oZG4t ;F/'uIoG>ZRخF𮅣i]ͭĺ S5 qIe%R1fSH!]OR:EJrz.fq"A>X樚tZF ՅDz 7'{XwMXNA?xO*8Kc6t20cC;RP3ֵ H,>ţiY[.ʙ%ff%ML(J3Rw_Nqq4i3x9hF7(8K\=xأ+?:B@>oRFHcR Ss-ѡR sY_EzD?_?>G5CX+=k,DߖQEQQE(((*wBcN߀+B&&]I˷^?:橋OhF|uZ^Ni4si_v!'r8T渧?0Bg' |vZlY3obF;[՛c/]k,OPP4Ho|j3Upu?v,M;`uBO?zWL3?K %9в hH)RhW+㻌6м2eyԧYZ P~$7rx~te4E1c_*,#En A|G$#S |i1^`1jz+OEyφ??km?[WC?cic_$0Wz~Ysb1E +G0QEQE94 8TRmfԆ;u9ְv]:noC?MҮeTC;[;Vŝf|fH}qLyeX!.vuUҧ@082bp"sa.'rx7Bs@y5b',R B޸]-nDyt)У<z4!¸9"I7$|{znq*H"cAs`¬^Evϸm$~4EHi3#i嵚VI,TўloOޛI5vbbY!\1H1ľbkK5}HR /{SPVD%[W<ةٮ*28'NO56_ۭcZí>kxfL"f6E ~\/#𪃳^jS.Db}:_D:G_lVA ub{iͬC(~6@-Lu/=brUB{:05_^&6]OOEyφ??km?[WC?ŏzX s_?~/ֻ?DI\a]gQ͊P(EEPSIXB1QޑUh 3(uB"k9ldF+TckF +H =t:Ku IMK5P.ZI\ep]~Ꭼ{^ E%$Kyn'dر pjE1 0\c \ /܇Er 4I\CTpdeb[yhô~c S+fԦ J-$k4E\\~j,M[QqJ^Nq*ng֥F,,agy]߅Gm\򶞩9U'aRXE4v %A3CI+0נ%~F0}꽬Ot{u#o8SSiۼn2s#)fqX/͒9ʋl]J\[scj2Cg\ܩl!?Z g;d}r?Ԗ k#I6Cpܤ|dax*@NON6,pBS W>\Ifw=6ˆC$ֺ{=,o46/% zUݭ]Aq9IA-zԨ4ez' 4X8;T%,,J籥dgF*O,[F G=MCyrq('Z{uh׆ӸQ3EpB?+P{cNZlʉoX`ƹ_MOwַ" gE7~\Ԁİ4 ^o1]x|DK]jSSZbxcCEzI K>"N/[qwʢ@OҾzWRNrJVAp^POosVnY=ċX,{ 88r 14hpjJ"ӮTI{?H9_5nYd{E |ӑM=FX6w^Gԓ{k;ڂ`"֣Z+00leM<|{ScoT1pI8{~fq2)v$ǧjݯ;ہ_ OnqSqf0\O%uŶ!)hۛI|- `ɩ1eI 1$r:gIx GSi9G[FЭ-Qj kݐ~Q{c>8x.8pOVZ4jXIqM3F/vr8$鑌vCcjmey $ TV{_ڼ $}*+sab>:ْӾYrm}B!?\O˖ŀLr'^*->ıەHd? 5RM=m߈4j^dGlԞP*^`JPoChO{TM%pr}sSC`'aܖB7UKoXGTr r*̱RxX.G1ޫku8٩Ls Il&؊4OPE!\^DOYcͼpw'ڗVPDMrOJ7%YÀ1a.1<xMmA>sX91޸>ƶ>?aVM;3X%(_%qv?~/_Cf+Q@sqzIcٴ[J{?Ͻbivk`;( hY$QϗWԽ -CM)z5aogh G c}hWZzEPU<{qA NaK  thPYU(*zQӮii(LQP\[d?,6]iL.?Tf*~QאG QpÞQUX&QyQ+M#8*BEeeO`=_jI{͔W֑a{ZZ9? 7V(Zohܖsa?we]vIp`waejv)ۨ䎛oz~@n-CuQ&\7bO|ԃY*POQU-Ctaj? Y_di4%pKnPۤR\#UaǥX? 7GAiMZ|Kz[[Bdɬʹopenalpr_2.2.4.orig/runtime_data/keypoints/us/ky2005.jpg000066400000000000000000000177201266464252400231660ustar00rootroot00000000000000JFIFHHC  !"$"$C~" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?W%Vd ̒uб:<+c` o;t23gwՉ2.h ++#]Jd|FgU`q PSǵl1n&=Hˠ&Tdj=&I8zHH$sߊ6,Ǵi&=] '1&yi[XA56OY#@ߒ~S#yU^p;J-1ci/q͍G_daq5K OJyLjɁ =*Kf8v>7Hd aq}/ӼCޑ, XLxr2@d8yri|)䍿y櫓By_u]'j0l|YO_dP/,`wv{اAp~>N{uIjVݴ`ۧ[$H¶Wlur{P>1hn?`:$1j2_2]`Ȯ'+}=މoi\#&};Lv풷Fe##Ch//]Iq>ґR]>7sGx{Vu-WHpO'Ɍ_0I'Ż{-vJuHDϾ1"7ܖ7Y.8"X ˗BBM1i#\w#'.3Oʼmc⎷tk42PX %\oC{{;?i2ǩ~D| F\TwG<-(`8aCb(kLMk!ٛtρܧR1pz죬XeX=Er4ow1%caaH,5f$9zA&m36ω^{U*g:Fx8?NVgQR$9=ry)'$fqOb;Lڤ$=qҞ0恈zgc)ZuOҘ@f$zPUu*Ux8$*Bh-VKx+ekB-#IpA<?k^$f)&9y1W|,_| gn"ќNN&`yNpA2I2ԻWSҼV"ik'# bVڲLPm28eЦr wQxSSC\4K+2ۮJœ,>Uf3ο_!+ m'V hX$ꣿ+ٯOm?DX.i{ڤ#afUi8QFoV"Vew"2:6׶kiaXyD/$,Qf$~b.>xJ݌s6\$v ~8@w'DK:s3$!d f uăҜ(ЛN+FH鴯#7?^6ү[F^Y ciP"5 ;|C!wLw@4Ȏ#Wt)œ_p?žխ/Askkjj p8-Ӛ+}_*|ed,b핌b%e|&ɷ8-U5*9j׼D^t~g_T дSxjmZij67+QА+ƾ;_ |+h+kPkK;xǖeRJNǕ!to -Ƿ'#$W T;qm]lC_?ۭ>x<2,Zh) \XG̭ܚć^ښ>ir.]Db[䏳[DC| S%Ikt}Ҍmӷ2 ԢY qN ADH dIذеz~i# d[&DUR[U߀>aY[y:m{x q,mʂ4#>3MrVc6p]߄nY,+.+H.I`A 8m7gX?@@'_Zbs"X<jH#)Po qts?֣ݑ^*Uli2iČe})Tqp9;|0 .?Ͻu :WoouN:\^E{ekvV}ԎOޣ/g#caK uMI/9nJG9!#hh eN {VW7mZ:_6=[m ffqU,]WO,s;QDjуI1Wѱ?Zxb;q11ҳu`_[Ǽ\hAaJSsYwj?sTrώ#t1u#I ?#֟cw#G4}{Vq 8{Vsn''q8>9@=E`8n}c(ct\Q@+)r1.Gl#hV" N<ѓЫ wI$:#N 5ŭxd>Wҽ`?y]e} UcRR= .";Ż+6{0ȮF/uÁ^IGҽ dq]KKpLJGډL¨+4 {_1q⻬r?xƷi3`ݧֹ!_zr_o֑y /_ps>9 .GLOP>6 Xlq^pbA*0q?j__ps3W7?@c9RƯt?/X])( `!=k}N;+ ;|fI,1xMf G$WٺܢMPf tgO٫^7&F+ {SeqiÚڼ1Gl) 3c8דQk)崹$B3ЁkSzW$ZR\7g5I^H,89>v&l7TN+O"wxQD<r2)U oKNܡTJ+ǯ +[lb`˺ʀ7nݜwÜ:2$Zj1|2`^ѨYA}w#n׆$zH Eq߅7Q#̰H4ΣaO9'n*zHFTy*&3~jh!Oz+MP)*AM>K}"_ƻO827? }2}u+;ғEb@I$3$kge/+L?OvJ1$ƱeeAsaTcSX;q $\|~U|=*+䙤M*|:( ʍG?J҅co RV<,?|?`9 2|?s WViyկ9jf_Sx){KJ @9ֽ:ch?y3}JGxaiGU=|B}Č^ŏO,Cc?{HI<dpyĿ]B||0]g_@jC*t]5R<-f@GoKr%'&_ν? E9| zqP3؞ L ólsg>g I-x9nHҾ_"|hΛ:٩b_x-$1y09==T>tKU9!s{8Eǧ|b:v_&_MWO{dx5 mL_xaúVG9w/eGGh1ܵ~QSHS_¿ |HԎU4O)*W? u;sʤw|Tӕ*1࿊;? WӚUTvڥ'ҝ\3࿉T|Ges?v.m5B4壌@g8M4yx.]' hE}+r9>Xc˥/]>O7Ö{x,t}5sA#buNI;= @ƾ{)f]zm-T+(vfxKWzx_ {'k|r; _G-Gs =ͱ<;KsEp-1#p'q?/jMԚ4nmj7HalH'8gpqZsEǙjr:!W\Vg/>"n1^f?'HտLE8 0<ۓfՊZ{ԜNܼ=}MmEOU !ͱn gQoM_MuZ= l#)e,噏W-'T|T.nʒ+B-v}u5wxj3J-ӋI\X0|?i@/_u}OK]Vq&n|S<g2;ZnzMȹRѸR >JpdTWi뺺֫3z (_=HxZjzFMy)lpو5蚒jv&`Sn@)FpFpi*SS J5-[w8q3Э?”3@a~&jkѻR0ִ9n3xW'öMை.~OԯoװZR[yFKn=:RS '+xn-+E/$]^_jF?IU+u^ hq1)HsҚJ9bmcH(S ]?\iVLWd"O+1CRW]#J~鷍j?Au#tlm$8'<$eyuzcG$`|qxvSu[";M[G'vWz։v|Ir]BPY՜c7B+cO9:"ԏ8̵eTcjcAp=P'5-ԁVi.tv@YrA=*-Վ3Jf]O8 ~R<&*n֗oukEeշy7jIK{kA4XFۼ@Z[Hᛢ(u~ i)-ߣsܳ07@1fHzn85:<=-/N\D79 Z%oVQFcʊYI9'ӧ:޷'ĴXl5>X[K^wk߾Ϟxx:}ؼ9eӠcc rPĞX㓜UmD]:/^Ygb<7^1Ft"J ӭⶶ"q#?4.].V^;u np -ۊر4R3a0{H\淉o9`МWJU[կ x 7[QI1ȥ?s84R>PK?5 2At޶c"Ǻ"i+6Ƒf9tE9;FZF4-3 SݜcהZ?ZDQA?c)ؾ[G9ZhƂqQHG?j.Q'9.OO4gCv8Q))3?j\^:v^9GQ}xڏ~ɨ#]BV](#$Lǐ??ZRPj]--첃q ͪ#l;pG8 [-sPlQ<{XK=Nk|+x׾?%cu'u[jFJGlZ㎽:^8Z{SqMIڇ(;:oZ3ӥ/j>Qqngw=)Qr9"=颎1GQÂi3i)8ڇ)翵?/q4-W(eHP㢲j .i:Bj?J⒵W#CӇoIƽe^C3VhxueXV ⸱#tN yiz3<#LVnFiM9 `t֚$Q`$=sO)19 F7?J=r)ǡՎ搒G9/hIqQ8$4hJzެ5p'Xsz+aRP,q(D_@:W?+xm7Ƙ|ܶHq=]%= Fsto KHO46O'ȣ)6@)T{oSM܎y@'$ywK''rMrIA)خ 4rۊ v n94rgYHl.PJ|$y)*n$؏Z <sљ'O? ֽM['aV ;-<ӉkOiyt#xri!sH6'9zFnxQi 6)K|ȨF=JP[@/G).҆q>ҏh.RDn9Nz;ytˋ )3ǯjjm 1Z &ˏ\??ZxI11FgkKO4ۛ[79F0c&J$6(t*屓]t?D+X=r2poo"34a԰\E΀An|dF 5;I.XVZKM1ZhF  k|ve$8lFj"tr~ЮR`ԄHҜ=IU4CsJ_9I wS]DϽʏh۹<})CTL9@n(< ی'?< CL/›ܨ$$yIzWwVDqNд=zB7 lM9дWo:WU^waUțwG@on3m#(2qkλ:,Mڡ $.O\v۾lwߏ|R@>X{{T JH9G9pBG3 n8e *x֟9R;(cPѶ0k/f]FNO;z+w s\ 958%dُWV,_Re{a^S$ 3Ò( Tu_J.̚G_k} kR@q}kيHG=_^qmtlle g+}2;s2b$ueQIɧuybM(F[) sf%!q=S&RDh:ŐxN*4 Wf,vORS毚tlr*g`,YNYOH* UP Y&MW ťm|-QukimM+W :,#_)VIbGr D-oQ{VWƙ7|EbAkڽ[cY+R;/iYA7o<xѺ +v[lGOFG? } -4n I/XC+̤^6~A˷[X˵ WVjn] \KTΌYjY_F5Qeu+tk!|Q<F㔑!e?'tkOWg^lH1-m#ܬ0e%q|"i'X씉zl>*KIhQjͫrQ^E]kQVoekfSŏG:g. t9դ̩7x/ΧSik;q cw_W},K9YRͿ~?ug{ĿA6?C|04}uS?ן 4}N?_y?fU"9.(>]\)P6G?_C4Tz AEpFfU)VK;%f#`N0dN2@q. uBq HD Bq PF'ƾFeSeڡIvҧYђ+"01?Qj%5v] + Ԗ9>(foϧZ~qjO?8|?~&t52FZteIr-u 썌H- }LŸ|Zco7k j~U KFW?ƸQR>-|L\Ot?/ů<kU1đ"L3'%׈5BY|V2I':ۯſ\k%0|1 Y+|Ko֎205F\тjEr}Gg\0?'%93~&|MTZΤixV5a!AHAgָ37P~ `F `}RZ+3C~f.u[mV~kl@͎" - {GH#O*C@)~|4k4uI 7It2Jqd5(T`09Vopenalpr_2.2.4.orig/runtime_data/keypoints/us/ma1987.jpg000066400000000000000000000147441266464252400231650ustar00rootroot00000000000000JFIFHHC  !"$"$C~" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Y%h ~v^`-ki'?rԣ5%tiS V GU6@>~qֿ-™wshV$Ɏ n*~&ڏ,wږm >b9sIاhAwY oxPA,|1O$r63`Bg,m>}V9tb1],/֫ƍҿWV~.M 4nYy7e|&, y!\̍!=~Hsȫ֩hڮjp-c.諱\7|ßC]Z(ի9/r\c Te(-R3aƋMoDk;MkL\7rjM6wڶis>Ѭ20Fz vBD6[ԥ+sk?kD't]3#\] eeSֽMWsk^wg^g2;߱9(x LpUY+|-FsBsSi6z-amm_:[tXRD#C.}pq^5]cV!t]NV"$#Cj_hQl~¶d[?w>q~}8G8rNoUvG|(-7! <A"uF^[uZHkCEP~w| R%R,?Z#5 Q(u "簻ـEf 98a5s-nzNRNz%zww9ִxbKR -ȋ㍀ ?{R_[DQ+ˌ&R0AM_̚V[^x~^{2Wϭr0T̔^MԷFnq5W|q/.ڷumݩh(TJr{ⶭ#+]OO[-X%o-6#\gҷΩ8S{[<뜲sqTY^(߈f@]DUW [Fk?>KJ-'egnѭm/}+T>!*4Cc?-p8H􏆖+vײAEquc|;197H= $Wv >#̧F3_ xMtGͧY"pq]?(H}g%zRk[=RVۣR Zci_M9yc9 o-c|]o|}j+]^]Lvۜo\qj/ 0JkxFmb)'եiMp#?.r~T߄|?76hsc(\9$oUo)j=-}b9tZo~ҞW.c$k~kڅ5OHЧh.symE!lG<apW3;_xv~+CQw^[9ʁЧ S-kpxg],-@^}Sx>^XA<K/xI;{oT˧_u3]Nj׭RGaah3d34_:'pCW'YpO͒9qI⿆hz;5YaT `*GiBwMAkr]/k-5JJFM/N|m!峔$z@ u[j:=o&[[}J2cxFB ץ8q|Sw7Fı:Pr1P SO#a׈5GJQƼd 灓s1]9+ivNt[EgvOqc=S+#<l~⁌p0;Rq nŠ(4'+ z0M]szϏ-뤩@[ӿ?bi?Aҩ (Q@&)hPbR@ bLQZ(1F)h 1KE0Q@Q@!KHzP9~i y2t-ir5Ptzv6OUJ{!hDQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@!KE yRE-!r1Et_Bҏ ]-e<:U4 ?Z:PBEQEQEQEQEQEQEQEQEQEQEQEQERC넿WK\]3=>.?ޝ.?AVwjDQE ( ( ( ( ( ( ( ( ( ( ( ( CҖ5>"CokP/-?1ҮiEqFZ(B) -&j~Vwݵw2w s]Aa:* jsj #;cTSڠrI:gk*ь[=/4fk:|%vr&ep8,6P:)5_Y<;ywVj[6 o,O\,>Qd1Jzùfok iHco*$<71)Wu_xwKTHU ȶ eeRqc>Gr6FkwF8!YKgߝ9f8:Tx?QIٯ"Qq9B˃;dעgYo̲Ɨ{EiS>难i&S^ +wpc'޹xP=oaU%}^* E>qʮ81^g2M+N rڬz0?4C0eXn9yNKi> fdi5 WC:;<ەTufѩ7ZX>&|f-a \FU 5CN<1CRFwO@T_C<3[چrim.̭6뼐?6ui9}6iUws.# c Fd:At#,g.dy5}ZVg\xKRޓg;matQAqw,J+@T1?t+So fúG%GKC8-c^d}Ə#,?9xM[F^xx5c5UWiXtM6]> h!at#eQ\5KMs6=f4I11(x'<; ,"7gYcGKRPpiY4aNky`"07WPGTgץ<[q ?@t,ȍ4 6\Wf2[?Q GX4LƖП`3PfYHe`5 5;ZjDA qӔ#  #bȧi5&(5c [Mlp[sopenalpr_2.2.4.orig/runtime_data/keypoints/us/md2006.jpg000066400000000000000000000142311266464252400231360ustar00rootroot00000000000000JFIFHHC  !"$"$C{" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?P5;k.cQ9T-qr12 -`zliBwH`=4ͫ&bbϸ7ڗ鶜<`] Q71F8\}p&>[bmfvB֦E{5# g="O@{u<*f1瞴:M^s!Kܭ$O&)ѮG=㵑?^+d׵EQn$'۝z ?nsirZZƲ,Q3pO\TyhdKvľHK=nsߥ o[ʥm1@1!L|*< 7:)#m3/Uڊ< Doo* 9P ^Ơيjw?0Cl~9Pa P_{ߑW㾹o+Kk]G$FE',Ir"FYVe(A*zG* [1tk0u?[Qʂ-"1lIqCE.qWhcs:~Q!\z=F:V~0Fko6H n״vㅔsZĘկc#S{2'>SlIfI`1AjZЦQE("( >å_Y]]rfXʅ &FvdWo\xKVY]Ik(8߱ gFyu> EW6wyV&v0 {q@m-s~׮<-7[(L <{W/oIkc2ƣl,4HǘDRr%WO Exo!-`X]n1鎵1jV7b^K;E[YXˬr1$+zҀ<_R4݄wo‹ho$wggŘ*8jrDQ;nwyzrkρm=3J`͘M WLv*d\m]v!l/{ĦZotM| 49Uy<? xFQՂP1EDq=\ (-QLC"HQKv1Q\٧QEQEQEr0*5Wkچ7Rt{~A)׼2vhrƶ"ĦW:>E՛)(((()E:H$ExBє}| xF)օeyZwf9${gR7''2Z1O1K],{p1J~nIQ(Ӵ{}1ii,9Q[fx/էӦg R6b8ob p9pR*o}w+X%^g|L𞏬kO L".ƒu$3zKy,is@m巸+VPTc83Mvjϧ>gѓs$ v*W|-&hGmjZ)"$a/ U#+" 1SIK袊(((((|T3 teBo_F-8?gHaKxO/d;j$c[d} c߮|-CtXSVQE!Q@Q@Q@Q@6D#hT:^񞧮u> iR oKcsH9֞KlS>ƓFeIp25P<^)7V>i0A7X8mƺ4[^%CųsAakO]\~uhM/z]=zme8uѾAy7SlZ>Ն2퐐n##9$׺:eΓcvqRq 3VF5[1rxhןTr)kZ-đGoiI0SGw$ rTC^ I;O kMCΖt,1L@->/ ;_i^mss_4ҰFE;7 7:#2oKȅF\窨%NH.`4g"I9k}>^iY|1-R+:CfƖwm>(𞍥%^ #7yE8*YM7PIn7fdJ }0jմ [UHቺA&xCٺZˍ[IG4.,n7:l{yrf$TiT2Þ q׃ Zx[}9.WǏp2lXQ,lB73ΩH\U%&|"y֍݂x'=k}C,Snm|#Kh|-a5xW7 Ux/A|i$ha F[-TCk9 q0f"==_Hl!}Q8 u9⹝[//DKdд}2٥Ut,~r@ =|<3Q`ҵNURR⺞$jEB;cs6Zk4Po{>+d&0!Q#Cn1fH"VP?/%xÒu4i-eY%[!bUA` ԂJgaz e}irֲ.:= |nK> QgVEt dd)(F>O; M =on.4 ^\:vq VUr$P3ce8̫(lYs|e|9_~G{H[,s(cN mgZ6&isM(] v#8񟄞2冹]jMeu4+xD$h|Yۗ=je2,ݖq=bO 6þA)O8WWMTӌo,gUY1Gg}ј1AtoRv'8 05),--5 !mVXmOQ"aR>K0rzOV+gNu;{*/UhG <@3Ou.I\(o Bzb}E-Q@Q@`!\ WYA9}|^u#z!^j0.t3FFuYG%p>cV=WǯP? 6^aUr'2}.}p?q*4Hdm3in',:BA/?—1'NoɽƏ?lv_Ij1l)K?sO$>b󃑞{,βaDxkO!_G0ҹ p?qlS_ީ~(֧ۜUA}oq]tL>~(G]nvc.s1?u:pTQ#nmOso>eHvbTG=zSj9c(!7Hm&nnn7̆{܌@k}ߏp981]HTɺ?j ??P?170ܧ Z>YIgXZ΃g?vƓvy-`yqGce>gvư#NaOx6E>`⋘5?Hqɟdzr]u%q<߼dhRy +KñG?$Έ}`@ Zopenalpr_2.2.4.orig/runtime_data/keypoints/us/md2006b.jpg000066400000000000000000000224471266464252400233100ustar00rootroot00000000000000JFIFHHC  !"$"$C|" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R$R@Sqdp2mn91"Z| ʂ9ב]W̎1 ;䞝|6y4\X{^>gX(FV}W t3Zj1o { b7 BrF'8 ,gˊ`ܐzS#?ּ龋>pNR{q6Mո(󒣛f V"Z"9?N#*0?*ٿ`R޿9~2x*?q*D"÷۩xᇆ}{"^̒0lveI*{WI7U xºB%#VF;o*q9 <9v[FT֟<<5m~M%PL#@>Xf` #$gҹJ_f#VNVJ=q>2iO/ N j??pSOt/w[78wv.Qm1B08_٦tKk$d }7m}o濵$R{H8M,"3 ?FRp#`Ar1]C/x=sėJ\5ccE*HO 1cΖῴk.2BG h$1-fZOVS)bW+&ݗK CCmŁS{SAQe޹ MHJ }U[pbFPr pLJ5ԖZd"ofY@ڡc>a@'jj:c}g+5 -D(faT|m-"=^k1JGt[,r3 FT烏-ҴM50[Ap|D6$ DqrsQ-狯-IRM>x* R4DVbCSҳZNQ:4~&lO^x8o є㶒~5oZmr ,[9!kDn|F!w0Ӎڲx?U\M*g Ԓz@7>̨Yc* `m;Wj*NM_-Dc?Yڟ-IiEԮ uܰkk+[h;FFIdsȯT#G{tvXcb8$rBYL]+Ccɝ3Z!YXc|8׭uͧX̏(Gw9Zڻ/6zxc:*dpJ:*1o/ 8-ϰɦD "F6"=)O8BϧqGU~=燳χՃ^8498ҹ{iw_O#%-zE?|7Aek`8aN_˩/ w:ŇDYHNeYL֒J!"Vm;LHi2YMiwSkX.B,_b;$jBZ]ĀYtm‘ƩhaUGp:1#B9zRy#{G+k*mۡk27~37wq6R]iO" qwLY̘dljGNV3ZծxݡZh'D%Q8\TJІb #G9VK:QwH}Zks{V >6=E -+d%J3je{ GԴZEqwH!+汓s~0Hڠ OZS`Ӿކ^bohN ہP\mkmyZClx>~f]]X 9mgU%S29ңYnyY夒BՉֹt _cV-751*?"ڀn}m:z U6?]wbv}klڌgoړ Q;$>Tn9תb3޼Ź}Y3+brs_dssʨL%Ϛ֗ Zx${׹I-%G3:61_7Dzk[:v/69ت!}eؼu"G%am+8ɮKa [k=M~ǐ;Tτ%͌叵y`!cֽ_ C\%ʆkBȨ/S)+@B;҈jNi|W掵kڲh&=Ҳ ^*CO(G{;c2s)*TC@rs R 4SAv֮M&֦[@iDw[2O;wSUADB.G15*p)IzV3 XwcV@* 44rMyXOaf+esь&eu`6 F 󊊴nq94uS8䮬 M\gɬ_ѵBIm6Aڬn<%7*Hj *9?:*Қ%$"`>xoZ蚼7ךLUQ~28'mq{b0(Jq} #qNSE#cKp1ҲQjwPۑc֔ڃbAUy?<84sȣGXH[J6|hr2;QQݎ Tx$j]:Pw!X#gk+TQSv2D(/uҥ?+p=Tou++;f7 ,΢YeTŊ) î:B.XŶ'8P9H!R*۷Bz*H :ACqFjepF}EUd*x>V97f3?FIAo4*m256s OWcn2wh{U$ QELJty$[Wi$r|I9<xz" ѥn"7[_n5ot#{KLQd`H?硭o>oۭ]iT ) \b}` ^_I,qrG5ˏFk{Y.ṴƺwiF[}R[I4]:P΍ ,.)b1lҽO?|Q0='C ݣϗ!ּ"}Hxrojf7Wm%b1Ÿ+ifKK{s,U.XfI4o'|eM7,hT,M \82O21دet/k> tG&LwB95l?duW&W\o{rn8uڥJMn 'zۼlJδYWrs}9Y%^4We06\|'V2\dƯE5[( |w' /$A>~ k7 :~;ox'ĺFmoù.r:~t%8N;0#JKOϚ3<҅gJ$|KDQ=65oru#D$$j0A i*o Q]p@DVhhbgHc!^Z$ʨp8e3zj̶.e,2pzTki i[,(r@d#ᵯjڎq9ңY]}fF|1J8?S iJ94taq5#43c s0=N<*;д >r{?X韻Bp A?VJt;jgʉ5#;oi7:d!Ҏ2kXIz8#O+6%CyG kF?{#[9iͭX+ƃ_ ot) D` =+ooj\Zxv5uQ``ُv>ܨCǼ%Z:ZUupɮEɚg³G:Tmz=*ƿ'Z o!tuRI_ݜq%*>XUgsX\7W>N~W"C y"U9\ό?fz\xfH.s'?*UP?C|Ux:J(i= xӕ:ܶeǺSxjF}1꜐ޛzVv'D@mXn>_XF#[W`m49bm=]ά/fxHFqcʝc4},\Ձ8Q񵇞amܡr#C L,YD~):8w{]4P>wko;xOT.5eGmn * ccPj6\f3U050=ApWe*B)3_"\wc)$剢H7R PGqM^mP"hδҼk͢ZDdd;X ]YvSyv6v"۟@~chg 3D~){xv,Ͽ;nx}GZc\i vmMre p=@mtFOܨ_RGOxvIפmdb9$tl}hJV8QcUbʫn9jjәc_* 3?)=kXHH;,lP`D~HUbab UJ]JtT$\t?O0\C)7 *1)֑mbLhq1U۩.m. HVB2 ?·9Ss1QI lopenalpr_2.2.4.orig/runtime_data/keypoints/us/md2006c.jpg000066400000000000000000000252111266464252400233010ustar00rootroot00000000000000JFIFKKC  !"$"$C|" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Kң3 p:>1rM;#du5OOe}jc!q WPPi%urB2ZC'88 N(9j*ߌϭ!#hO ;"eo eLoOCG'7U$ &w n 8+hV9n1=)(ܩT30UGZ]#MSd҃9,Ag9e =x\#7 gJuci:An6YR]hA A+=DDcˈ㟻(0'^^Fýc\}GEV ku )_| E{x!K-vSa\JG0 fRM5vȿ.n[;s_G5zmMujE]^*{Qׯ޼\̬]p<J.۞N&H GKܚ H e}D|:(f.gcvBv3<OA4ds^6J<`=&cAd*,+[FEITd?$:##u5t`GqIJ19,qhU@n]Xp+ZgVgei'28N+Zdu>t׺gRV8ЏZ'})dy>ww=֮89M$v4;r9{Y2$ǭ;hۺdqQCvR,)$61\ś{>"I//4m2G9$0#~5%榺;s*[ fkFXIȄz8-|U};\iaL 0N0yk܂ =L61 ȭc%AI$5wȫB5lkvV<* <IaQkaC{2:1a,@ Td/:gլFV.nu XA 7 3:8FA<{[jϙ5oV}L6%;\ռ1G"7A"~m]ontM8Q3*?,ïj:%ŝϟjQWY4]DLfťM#;1fc$+WT֝Tܫ)<~7]Vm濨Nf%i3 `c6߽׮ g<_|!<+-0$]@w2V1YB!o,nTBIR9#W]_;zW?.MZNu ;}4mLFWN1+WsƊM0z Pr#=Hڶ90H9!@Ra>q;G?c 3N cU?֔`dm qHOێHGFOE)S׃^(9'ZV#3c*J)ʼrzr=4j !r9ߊUI= (\CXb ƙTc>,$8*09IgsߧX^?M6}hiӼ%lAsgP,-2O1::az}k=d)%jI0۳$q뚸AE&yHm-k=" ӣՔpY30Ny;4 c;9OFdQ#'$vgSEU,YE9ug%J]C?f\u{ӭ>1? lx0$H?tvG)`q 'ks=V&jOZR8dtdFO89<zo9۴sd8<==Tԅgoƞ܀9`Ά8arIF{F3Qdg(A<`rsP>'ism=slg;S$Q?o&HS؁vbRAi/O)w^bHyB p:ZZݭG g2EN0mm-|AmhsOO5Ѭ`뺓.4ՏW e}- ]Ak%wZq)8Iۑ]Ϡhڋ$ơ'u W~'T{?i7Kr˜t=OE_]qmו^ke4%:jql)=OrA۰I&ZӋM1ۂ*M\R+qŽQ)pF:_ 'c}* JNokk NvƊY YpqR!z:ѯ-ف Ž皸mOSuoqn~wwb[QceH7F}@Ir{çFLkCn/~_jV}u `0.Gp0k_giﯵRy&[[~:q6}Sj>p랟M$#QPFٳf'qcYnzQijNv#qk^03/Nu{w+*[sQFf!<يy?a?iKF5ݧcF=ZwN'{t$~V2z֤<|F2d4x99sD_2pXN:o&x280xzԵGk\Gs+ H^r<)|AYm{i-#m'9_yar 7&RSH.1E9֒6O>p['Ҽ]^U4nqM  |Ҍ4Oo“8;{}(CIM&0sҘ zza^ĎF!Jlwo*y'c}k~:Ũ|@'o%CDc<ò[$c$&pCvŸjD=F;5N57" cb8냀x&;IOR=JS˵/ꎥ[5W5iw Oڑa 3S_FU}vV!d*~902 )<.+\JUxZAPOz9$O*'6Hb;fPJQJ\d V۞+#r'uW[isaN?fb0sUiyW "oC j ˍ^l[G&I۳-FLԴC-&Cfnt1=O95G4WhV$r)$fOJ1S0L@`Xq}kֻEӡ$r;ӏPI;<`g#{^ ZfNq֑Hyh7c=)*fiwJhH2}JB=驒_ʟ>cP[ſ}1[m:d,rw Es t]#G5mgOMګjndˇl|ߏ77χ۝9FN|.Vszw|eAeѴKƷ+0Oyr]5K{WhԌpAȥC^iۘ<\};תo}nq  txeόt;kq1_I f=0>} 4p\(-fY_My]޿xN'e CsrBG"zȬ3qƟ3kXBATvN:1t pt:T1AE,QF#WQåtv} ?S.;GuYLH~}~¹hzd?ܵB?iwឭ4mĻF;zQ \«mS>&lYGqvϮKVWyD{￵`"a'<=+f Ǣ,xPNt\Ȭ>qȬWM\_ of!ؾ&P8ִY&x_N4 #x젎A%rjUŨ_ujr7t^;{_I&mp B@23%G |M.IϮxIRpY#'p%#W@$s^;'J-$ &XǮs޾{#_֌i]]H,qH#<k:$)IYG J=bϬQ.$9.6@~Xx략:Y6.ֺhN) %mG̓doDXK][_i2~Sn8XB.pp]bEvo2t<)o[zC^jqZhr3yl̓. gVmsCmsNZJבcF5*SkRk3`# u)}:;~w]"X;r98jK_GuiJ%r\';u uZJ2'$0sګ_ZIi{7q$R *Ã@~jAaY^Mg/v+2sS<5%a; :+T2+JcٌXte\(rԙN)]Ʊe* vLӴ#Mgm7JO,eRNR"iV V|Cz%{46]Z//̳&w G^՗IԦ,u;6hb|Ȍ_>7 Wv#FNqZIyFg\4M.@1$5y= ji<3x ۈ\FRut+2k}۞/=r|o\O5K)퐿h1 +ٴ$߁G/q#~9{Ff? 1WW? HiW0$&dp1g~#Ӭ!\]S|!>vTnIWw30?&1p[oחjvCFW$nQd9_NvOc oH@!wUS!𝎧O-jl.x#?1 f;gOZ6vegEM8'І;Ζ?h/gMt:zo}B3޺JbX1@> ͟,.ts[ݳΙ3q pjNJ>5JY3bny&H;BYAoZng}֣uKws`A 'R_9ܯ??ᖧs*-B.cKR>ث]H\ý14?:0M7=]UCNMtà~Q}i&tα2Ezd4cc^78 i7Hk5 ԰![ޘ=?i^YRfH_%H9\~x6W/Df2J܎Ogjmg&+ :R-$<_kOER66*Jߗ|G;/_J XIt?? Džl/o$UWF$( t+xz@]CL@v(m/9ĞjjSڹht'~~|{._sm:yL"xP+;m@2T?Z4 [Ò.+N}+6pyj֦Se}I#lCTJvGCux|S/;̨rX;8-Brsm>J%EƲJ͞pzM_-gia(ÍH*jb5ˈ-5S &Ȍ:hd*eBЎ?!hXsgjxI5x[+ĺF}xNxl# k$RK{L*';phh:ߏmYXɬǨ[hD2㡮"0bt?]G岎w^9Oz5z ZZt[]؄xf1?<4WY\j%+/ӻF1tJ6T)4\Y!RI:הk-9np.ޠ=5F$ҡg.-HYUbfTg+'=Y>OJ2ƪSsQIjV,;_Jdn~kyQuW+j)lW~ۋ{EIWpa }A5VcZb4Ͱ$~vcmcFfݹ|8,ڸXi7: hh"]QB@e_Ϛ6PLk߹9xރjf0,Xlnۻ8џ3V %(#fݾU0֙'cfXݠ=\W 䉼8 ,#νKKhm#(jFaU@{U=Wumqhv-G{u TQ2ˤ&v,6vo%ԩj٘"IZ;i ##)ea \,vT}$Xx+tRww=HG)openalpr_2.2.4.orig/runtime_data/keypoints/us/me1999.jpg000066400000000000000000000214721266464252400231700ustar00rootroot00000000000000JFIFHHC  !"$"$C|" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?_?-ծKպ[|G #`e`8R2Xm1o-,o ke[,iXϊiZo70JVT`m ȹob҃.5SD:;0v?V>f>ѢgK`:zZ9P{f|PlknZQeEt=_RMRJg10:l1'NxXl CG*!_~}%߲~ч$bzo+_0<5z{k)9`qGpUmF2X7쿴v@΁u3o iiW''q9O:k:c؁" N 0W=>r7 ;?4cW_KX%\tFK1־s>*xK@=95^BM.|+SʉܝRIç>*& SI5og^#oS"@ޟNCf~sxF}ź>J-m&b}[ 1{z9PoE hCR}ۻ?Y#⫤u?x@gg_]JkD0vp2 o>"xsŚ<> %^7R.FV)6gwNxb?h##S>D|,IGj)/u(U)\Vn0W6N9n~Ώ}n]sYkjKk7ZTvS~LH.w݈t8hL}#W:xpΌJ>xLmǞ udR 0hOJs@xZEЉ^{KyV51|r\,2l|7c㔦/!_r*᎔5x׋Oz&g ҎTGg[7dx#JO?vY̌g}^qt>r+3<'CqgMBB1HuO>r{V|~Ѓxgz)hxh:(GTdʃڳ<5Wm>!akF%`"]1*NrNxneTe# b+_ 2V\ʞ F~q~B)_O%C SePp$sJRܮnlj"~՞XQcڨfR}RQ+Mr7% 3<8z]C9=PA?4ʇ3NFz{8؇^[EWl |)P) H ի:Lkg#ӵ]TԠȷX2u#ѲN-o_hgH$;/%v;0:+7+t7//MJ6ˌ^OtFLJ<<?|Cjfk`Ҧ#rGJWx扩2֮GB B]kb˱ ӌO@x}6  'J>9gk}ryw"=ຕ'˙So^p+i}RMO=tėʹ[45FJT~©O5tGBhϟcuf:S3[6W>Сto>:ojmmgNӝ29=4dKy>xPzמ7jW 2Wj0P|G Э%r@"2;apv 3^]gN5ț&oxX/-v2OM]Ř(cwr"Mߵn.Whr DYxG\E5Z[]SrNfg4f٣/.36Trwt/ Wzi13nY6t$W +}$K>IxOxgŚ[`T5rvKZW_6;x?▭eg1涊 ^HAb$t ĢH:|"KH Ս\ ( ( ( ( ( ( ( ( ( ( (<'5?yƴ5џxB5i#@6]uA׵ ڥk:GeCW\3.^$L?+*H`߻!!Q˨ꗶiwDmDai"$qJ #.Hs>qէ+IS0ѵDFV29-s[Vv,mu?"6߮+Y:mΣ2Wo21{WhF^3F@6R5NnJޝG%QQwNŤ>t+S`((((((((((lAּkrH]?^d41k^+eS3YiYO<+fpiڨ0麄:MҘn֣W(0kCgBe=ҽZoi\]j!&MU&?0cAͫn[:iڞSUcIiQ{n8,#lzϊà5e[Rg:TW:I/mX31>^%d .1~oė,-`6@.*+̜j5컞_qu 'O4[5zmgڰ$2퐆39;^֧kY|Ak;#kE8CMD8.ݒc?=/^AoE %1&7 zAV|-axNNݷMΛ꛲<۪M$֛EsLB[q]PB;Z;B((((((((((1D+ H8W^`ԴV)pFvтHkk#w A2Pl-zezl/qq(HդRF[${|4M4wL.#Xr2{rkR<:sr~KmF>bUlU8'[BxL7Sdg7-6lWrGq߀s21Z5caQHAV;lA=8EPE-17Dy3c?Jui1'>I;QQ:Bb-dԂHH(VVPAVwQH]D23@@XብF,ǠQ@ maia~:63ր&th;ۻOsM*Ҷނ%8#gv ͏PMV(5>W˻sl?oC%yvmf7xצ֓%rcP|ujNtw/ѬodMFNOdWL=3i\O4jK#޼܋2`.^_ҮCCXb$G.6n3Ӽ(m~"Moc gr!ڥq^xH4HU^wzEx7gެ=,kK4Fc&w0IO?m붮|U{K kM ƤI>ӵ\__rџA渷,W [-kZ5_[No&l F֮:p<6֖fC Xd2K9 pry5*=PTGh"0_In#~+;[`˕`b@'+~ge7P-f$r$PS''9ݫ*R|,U'wP9qcR#*=?4oZ7X *}wrxOͫ#FdU$<'.zb7yv2\;9|B+SK[+ I"RWpaoRGyX(r^J9N)u^ GSE7I\q9J^"[iZ(Ӓl`pH\!vXl{f݌8>`*g D4I|Gȓ*D#fve9ö@'CIӮ"xe\HO#*x8mp#˧ xq2]!}AG=J^[{95 x$tRk㫁/<3&(Vu;Ԯ=g#] gzg][x;Q𦍭v+xhy;B:# xƓ+8i`0c'~5Y12\ eX9zjuyֺ#E4607;:էEUE|png?=7ނ[x}\ >2=?",|'aJ~3J;ozuiNzjݾ=POF|E/aO>L¿/esgAtۤۗUlw!2G\AX#-K#7ff'$WO~KOF|E'*(/ `$ϓGtPl?{ZOw4q!|Ny\ }?m% aLGsMSi.j_쵂-1bA\889T5O ɦ^t#eHڻq{_j?EIKF?GE?:>"$|{k躅0]wa Rz %<9 ɨ^^Ec!\I<_σCp"|7|ٰ(v/Bֻ>4熿]Jꨛjvn1֤j t of&&}ɸ9SI ؟gh.範4O~c{_H<0NeaΉ1l>Z]gÏiu;"68>_ls_glz?DuN7o}){8ahgƃ\ܗ֗7lmb0(:o201 "|6d:::M0f|&1W٣\ȟ"~7ʓ :"He}^=ܺނjW/}vUX6my8ϯAkxf16vv0=ON7sτ4_(-hARqhg ~MĠXM>9g~q&chǷ6/ȹ7){(Jo7]xZ Mn{x!62.> ؑ9, _M,mcO~Mb"O'| >xi4,#){ `1#:maĠೳq=,>LXۋO_>Sk/&>f\kWž |4K鶠7ܪ#EPsOrp+#ʏopenalpr_2.2.4.orig/runtime_data/keypoints/us/me1999b.jpg000066400000000000000000000311141266464252400233240ustar00rootroot00000000000000JFIFHHC  !"$"$C|" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ??ﴽ{_iW61q1r0z򫟈_ge5ҜN:dY^ͧ>*xUgPI _sq5gax4%ơ{gko,3|g8B@Q]P'xl_iWoH>-~ӹᣟRgHa2K$TO}+rZi9N8ue@|!/JNJ?$S8z5c^^ |3{’ ZT<9XA$˴J(YEE†d$dd;?ğ\0'-pI:_Ew_ōKğ54Oi(R2jʪHq-LlV58?3GiϥY>[i4_Z|5m:׬|KM@ElP6)̒d w5[Sּ9}S^GM.[den!Z7R16x/ZZ>agZ0>_Ac=>`=^C0/\4y"42/)7 X +n<|x׼{'<}i ˴zM͢JR,O޳Yт@,1>K}/Õ9c /0]su_j>6HLRFK{Ao1%,p@8 /ixJWeN2G&w照_?s??i:Vi6?FxoyZLӞR գ{ke]sG#Hq0 ެ16OQK\1Y>SOWjr #?!pmCWx"~ɖFK3`Oz[>W,)\j85ڕiUbGv$jD<7=ޥoaAs&3[̐pHrjIw弥HX*b$^>K&RQ\Z? o<¿ԱtiGOM_-z|4lgh4[/qHL8 ^P즷Tr]}Omf+2L1]o>8x 5]]j>VJY☎0MBI$*71җώvV=j^xIA7 }$W.\'uhnD@3qDH'e 9M{ϋY|}h4l9eydOd# ))O$S.7toW.r؇KmK#}Ɋ8(0gz`Χ SYxc2[ieu"tv{5?/W fh,rAKu<9|%*^|IwDaC<[ݐ0Cg'7&A-b0#ȹʌ3(.5˶MM1cP.ǺZ)nΟڤc60IQA99怹_@9=ɗb=|V^"դ[N0G>[`sXn 'Z' lԻZ(];;pÆr{Z<''41{4I DI#H2*˻8p\?BDŽ|eeu1 Qw>h(C{57e V3rY&ȗ3P q"!A,hm*i1JNB9?,<{M?\<];yLdv&%%,[(3~QE毇9h?^k f:7.^K1/TK kH=O$#ڈ@ ; Vn~xrIᑬm%1<{&BOv1y|ۿ9tmB;hyA #iYybb6R@yV4MstD-,n1 *Do˝ ~/˥kHء-mbRvo-A'Zm Zb(hY '@vȬFw08ץ襁GZ#\K˗k,k<dadrs Dr~4Z lH.H,0U-,I$rI=kQhq?waImŷ cqp!2ƀ BYX}I_>Zx"a%dYG![h W_'txn5h|~FR"N`ɬt<>Ml/u l#R̥b`X`+Oo?Cun _Ze3 NdI$X@{}|qOzn7<3Iwqh&٢D3#‚kz^6FG293 R;j·KKiȨY`p2Vc*yG8M+6G\h9iOS߭!v85TqA}E[.7c߷4 0J$p Rq~Q 19'cUo387y'$|[2w?U|tIc=8ţ $cJPqNW~=M1'1.r#2@:tL-$c#?|-I cY~Uf#?ВggO— sDw$HMVY#=C>J9C$9}A9U݌zFAs<܁}~#«'"?<} |<[% 㿔U'٢`v0Kj^oH~%ҫJ ғݟZkůffiOm0k$g !2GI"m?n'jK,FY˕mFl$8 g|K& g{Hb7m6P2 ;K?[:2 3$Ap8NҺ/!6+\aTyAbL*oJ|?V7vSm[oп 4 ލjYi1ul^XF>P[Sxðxqܽm TN v`wH_v+\g/jH"+ Z{1O8$[< ϠqNc~Ox^|җ sgCQf8wϧOCq8sߖS.1q;}=?ϥOsF3~N EW1װES 9EX Z<qzG<O 2IP,8O<#)򏜱掹O_q#>8>[ˀ{)p?K94U@~sSE?wO9^`#BUégO#R9cx13)<3wsy<|O^}+Ʌ j (i G#~~xp? GZ\r׿|A*j dw8{՚zanNb^Y>g^T-IGŚ~SOkKrynrAe'%/7pHyq\8cf$;o-r66~[/mli M -Å ~f^;Vo絾P{a0GI̢0叔a͂83y5IZ#r%)y/+kuS2'H;r#',UT2⵭Yy{Y716.G\]C+xN"":K[14VLזP6pL'fdiNj;/>6'y+Z{{ɻȻ d-i#8`OCW{ோ΅q[7nfXBcHYI#3.V!rk{ⵆ.xeaFN cܒ@4ze݌w V]cNӹ}O193BI]\63(PUnؾ״ibme*@dela /:ƟۖY)&m!Q8Qhp7gUmWOgI 2pA AW`QB~=+54|hԏO|r| OU@ ׎B?Aw8 i Q0Kfӟ [Y8989? /$`P0N8ﱧ&A9?S(NNPlO)sY2$Oz_ $cPۗ}ZBX6IyĿ)5QF:|iǏ9-.;{riP׳Ubnz^Q儕Hc߃қ㏭V/9ۃ֗("b)K|s~?sB9xQ|x;GLbK6{Wֺa?ٶ+_Gw/eix3m?'++'K;ztW.1izXGxTӼT 9EڋŢH! %x ck:~iY7Q䐛2fG%řnEw?zs6!qWjgWè$6y*y$e،=a9(W_Kk>+Alծ'!Ӎ܂GJShB` g/w}MF+X9dBX|7e۝-a:fIYnYBLwJmEF8"('NPmKr0O$Zv=A=_L\LdcF_'{-9'yLǰک, 9oBF{~^Խ$R8}j=9{1?ޏfԸ$z>3ӧa}*IoRpsha &nMY9xOsߏ'Z={B =D`xTLg=?p8ݖaKJ_3=?jKIz ҉վ\={B~H=sHϓϿ_&\9{&%;qBb=}} zc f 4.Igr9ê9%rzn;WؚXSZu;+?8$u~oWٺW8 nSN_. s+o>*uTERō٣L!mʨF1Yc`Qo:(٫tqhd\8@rd"dUd$:08#8!=|A$_ iZϨG#@D2ɉpG.K1֧< Ο 9E.$ Uݷv˜l|2b]'g+ 鵺4p}?J-'ͭ_隥 K PY^Tb aʆp:wva'Eݵƞn P@6Q;2 £.Wtܠ>^F-] [c/,ױ'CW(R"q7Y]k__Ah,iHgú[oR6pooz$h│$vg^5QZjwV[GHX209kr8?ς~n4k;9d_&VJlr3=oi:[{Vt=0?z=GI.4 xn,ƥpPʞb_*Ė,15h<9okK|u,bFhCFM9tu]ldis7ݜ O4a'ҼVcC"K圉5N dt#1mF?"0w]I;0#f4ΙhKS\Ge  z6/:+$2JѪT4J !I?u/ {u+Sö gݧ3gLjɻ۴g#waKPIPc1>qմ :O!!x50YJ5ueIFx^_˥x;\M1k2HT;Y®ݠ6B]EnҼ[;ْ" * av/\B$ʾiŠ$b$qҺVN?(Ϩ|9[ZLlWy4cVZ"]&W|k3< Gp71ɡp/X <-nB ʏ` ?˜|ÏҚ1[cw$3rOL{Q?(ƗM>Th#+c~<'<7x8%IťA69|Ba;*[yK&{Zk>uo k}OigDXvX}9`wq~,TxOkx!hnk*,#Wd"0g;NzSCJ0t<鬎Kirp{OۖE E妠M$SĮO.FʶB̠8>nb:l[DehL iG _`$1^O?ʰGq2 YzПԲSvs֤kt[7|5]Ad-rsp*oZ\լz^o-"Ƕ -ʒ\&KdbG&X?֤1A/0k"އ/ժs)+#;^ZMr5K"m3Odx-H2졝00=Nqkʺ85F_7 ;# dg<_:JH#Rj3\\̪D{({Z_rVTסUxUl|ϕ$M|9e] ubHGa˲<V򸽁ak))s_G4%7* r8.}\HeV̿/8Ŀԭ@ƾJ*rxncujMm+)| xwjOXfԃݭ&?_Z/bp~TZq*~W5{/ϽGw1;e>ġZKP1 ?bu* ..bD3M"ccdj%4a)Uv^Kѩtvljtڴ7O͡u͏:qkuM.Do:FnCe#FFFGp3^v]ø2 O#jغZS>?6FlǀBD υ~־ K 6iMa%BǖܿuweRMr>7._ď:!ܔ20쑩'ue¥gV^VWj7j}3 gKlCs i=_:7{_˵631;yݴ mrNsſiv-|XEU 2گ$ثGuU9NJQKJ[}2(0אGBzsO>w%(8&Vrh6<1.sL[%ơy="n'đioaw߇KBuM7Uc}k{}ܬAa'/<4Z+Y7KK.nŏmENu+a)@>'0j|@G5 Wv$YhBc%95?O|<+Ԥwy'd!#d pr7g'=,YPstMօZKM|oϡZa)':&Ҵ''~ h2|=wi`TI5Y]-L43n@77'oqI4i1\tp PInOKsYrhݿ-d)bk-F?dUtL0nmM(#y7Ok?jR/߄K|eއ j -H" Q AwIAv_pd.c,}<+Sy7]+Y's9>+x4f~ xoPjQo w#Xcl Ϯ9*Xx8=du4XΣesܤt-5>*|?2<tۯEeg_Ǟno&U'  SrPSb$.szg=k \Tu%eg{+;SUmcKKǯƛLΠ^cZYiۚ?_;Y6˦s$+!;w$7  &ѯ<;MGAՅ![iᇱ#< ~'<=:;^SqVgr4]=sבS>]ņIEd*|Y1':Ů=Ac.πkQgs78zFGC~No4/xTҲkIe$pw 5_UI|Kk[ +[nmǭ|6gX67%ϴϬ¾SeȪʬ0AUȴZU ȧKIASk'a/Ke4ѿ|QTέЪ*TI ͸xOFGg#ھ[${eNO,6)h*) n7`G|ydž}SS6:,Vdxnny t84pӴk_YYU " Vu~(Za˳nfߍ NyׄaփQVN4nNӓ~ մmoAOYg*nn"`)A׃Umx=݆ }x^c4'1/ -p 3}鷶_oa|@H*LNу#`{pye<,MU_].1jJ[|K Y"r}ɐIQIOQ֙˴쥢h?^_F$qgyկY.qo24r eHV A;Ѽ=2cKkiq2jJ?<'ε]4'Ufw>_uzώ|7JdX( X`û; 7A׼cE[3Y@Jܣ2BQbsKǮ[PVް[750 ZUb01),9ϵܜx=F:ӱpx;!\pfsۓF#OhOqEr#N@?AA9d@}wpi6<ԧ<Q$ZMM4N9ҝp27:5H$($֕ヹlo8=uۡQ>'$)3,ÞƝ>7'w@89aE7|7}sAzc884hBd-DHʱ ui:a})[[_L҇|9 <Ұ(^ '-Ϲ  &9ݎO+Wv3O/?8@RI^12ۆ9?5|[_kkP?WcЌl6gZt%;O_0ִ-l{\p/*;fۯ 8ڬ 鏮+4CwA#aFN9'<|#=õ)p3?13? ĊQܒy5xG84 qҁ'YNs@X0G@c4ǽ3"P 2( {SE<\I1+aMaN29ls) c'Iv@eF@;Rd`g_3}ߧ),^֗Bqh9ڗqqӵ4y˜GRSq’ 2?5xNJ >@`N0潒d`} qQZܜZGrx6\UU='Nwp~ƛ;Js8?Ͻd&[9qIT~P ?Ό q h<@[RE'|cz Ҍp<(y ?CJAUt A9#(3ҜqLcL$ӌ~x JN@ʌ]$HܑBZn`v5{l{;sǭn]sdJ~֭ ʎ)aH;١q1*{v:qOfփ('IRzUN \砥U$ub6;ց98{ҐXPp4)$qޗv~9jǸRR)š$R@k|ocwuNΆd@*Hpy/& cލרWlvH2z}k?{@|!cQ-Q2h2gmuh2a>SN^u=weXK; Gv{?1ۭ}b=.wG᷊ͬYj-ܓZNHT hz3ŏ[o.O?{lms`k&*ZROﯢ# 'w&N?o~z\ۡhw4gH?ZQ{?ʹ1b qNa9?*y(\0zF='$)ܖ!פJs@8 j02I^CMn1I21oh#|yٜ҆km#ImRdQ(R}#C{k4a9c~C)zZHXf1V ~lv#GZͰѴ jbXB_D&Tj͸u?CcYZ_[?ʗ- tm]]%<\8߿׎{VDMMB ~t𶂁X@n%zMcT^E.: `}F~'_WE-e⨼Vݖ`3AcaA7Cq3q3X:8*>EtoOR Ȃ*"+E0i\:^[ۄ"e2va!ݟ\}*IKn09_4H|:BY- NNOƒ@+oq<Ui>ϸ(#3 ;k4,cH!z1$s-Ǥ7썔Vmb[vJbݻv"۟nП * nOO7xn9"skp,KI`4a(St^d-n:?;w~jrO)m{w$M;W]۟ۿU)O=OPW[m[\y|G5}w?#½#G_n|- ҂y$g ג3f:nv:q EE[w:Nj^eo_>,-)WQY@o^/"\՛3W1εu|'GAC6v,fTKq ,rNKd{^&{wçŪA}Jvb5 ҞWiʭ{Vj=CW\M!u3u|X>P_ ɭ]Ov=ZhټW79r z?jO[]U*@gPN "y3E.p~QhZ[]{kwM/ ͚ )$}>RsRH0#~;mQ&Owe14εnuhW5$’h\g.֏B X[!?&[#k'Ym:]CS[[hYIqi4K0F )$\ytf?n."Sx y9j~@5|a6$_"fvPJ/#z-j\Ѹqn+r=ۃkO+F4ʤZ} jsǕ.% w3+]A6vS\*ߵT=zX?n?̧ZOm Ļ%sb6f qګOpܽڙ >q}x^_Ś5ƧӮy|ɷh8$YzwusP]izSBmTL[?媷9 ) 㑜U8~>X;X_iG}ًx̘3! Y,g088^3,=miV8~ 5!\Ǩ$[?4/ D!6r΋#v,dYI$9Z:Α3KXQw$Cbpv#-Kweswu!F2xC)]=Z7+˦Z|} 2ZRRqsMXΗ(tJկ!gM䡐ѓEh5wt>tƄrGj[bQJ>o亼N7wdgIT慭&XkzokɧY< p6jdž=g4E>\\5Ċ_2<0# 5':.N *@' l4MOMoilt 987 SGὪ^3NGo3ͱݷ>Wk+ 'Mmo"s%פF7sT'!!֬_:)RəĜvdpFGltJWz} rKA !pqJ5U*M #׳;Y-ȘE+){ֻ+s\([̛QfXfmMX|LvoQ3~s_^+MJm%ڢ݁~lg#29V-JW>IY\+dZ_Y^ˋ ہ . xg$~q_5«T]BtiY+NOxU1HqV3'9  a$w <mK?J(3}*_@QNqEwM~ֵg3"дrDOYojA\`j.r}_RM}Fe?ӥMZBGr-Ϲ^k6%6l>,X9?ʸVlc,9dl_Gӈ`-7xһc}m+ίRGjVڝųEew:Ba^GW簒jΎ)dkҤ۰? UCpr?8:9eMNk~"`ğ&ݐc[Z1鷌c(G #hwuABqȿ?͏o֞_F՝?9=icPO5[XhUe<{MF"LHJViת~u(ıPyVbYZ{KaFX;`98^V;<gJm6ӨF蒊(BŠ((Q` REQEeEj'Jgzp}([{N~Z՞kBٺ=?j+7:( ( ( ( ( ()޷6n#ݧ'ߕ~~€>WmV.l# C@g'3I>G@ei |s,}4*Fn&=[-"Ю5>($׵W|ӼuQm+1s4EIcߕ(#7cxZi҅l8Smn AKECKEQEQEQEQE@P; Z( ?*9QEQEQEQEQEQE iJXφiJY#c?V1HM6Bq~mYB٪qmhfijE*zgʎ7`tjNAQ1sq; Q IG[6Z=B;9%?N}xQob>[] QȬv7qJn@W'ڣD<`p3^?>"[=7MO#[`X!,źCslԣZgHb[qTYUix|iH1d7NtXN̼eL; 쫻ι[Oþ^T5+{8)#fa*aQ4ӍWDWCNԑG^z/ RƻMJTo.0L^:d`q#_ <Ij.L7f8mB8,~f`+zݯG@Ki􋈡-n|ȮPJ;FxVGuZc{=(+ͼ [iζ)-;\&=@LAVo¿ڗ ژdHw[BVV\.A휥b6ohN /+;j9݊>֧.IڣGT+[Kѭ4I2nVSg;55WxbW#iYRlpN+<\)>:g>IQh+PkI$ Xz??Ҵ/ҹ 4݂ajJM=J"2Zo˽pm5[oۡFM{Ncoq O̽ǡC?O<>??·Y(٣yJWLᏉ|?R@??N}Rgit ñե (1? ?'OWu>)?Y:cƖh֋a?Wo]~@?O?N}RQ^p}/”xL7g+ӿegt ~i1~'K/"E7|q(=}jh:MCYMƙ;xUY.  u$c5_w?;_]P*A(:۸.M ռL")3n`]2=kB ?òh:kOͬk@GB .У5w?“;NuPIoOMbRkR3ѯ_[[8@g 9';w ^:(ӿeR*QIA/O^CxY?xVҴ .(5Zxi T Ҭàh?.|R.4~Z%e}䑑(<׺gt ?Y:?jJj{/JMG:UǏ/-Xa[qn1};8x[C4Z~#Pkty-!nJ@\Ӝpӿggt |m(>֐L'q?]~@?O;NuW7b/8?gt ?Y:cuOw~wGʎ(ߩ~C$ zA1EӞ:Ҧ`r-Ȋ0zpzEjopenalpr_2.2.4.orig/runtime_data/keypoints/us/mo2006b.jpg000066400000000000000000000234401266464252400233150ustar00rootroot00000000000000JFIFHHC  !"$"$C}" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?-;WgjR}xס!fܼB<`pUztPj'}:)Y3\]h̘ɿP#sB}"^VF~Փ9צ<Pgz71 ,Mq9)'-y鑋<)qۓlKyh|р짐x88?~g5TvǧiICtZY { uc VQL8ynE j$T$3M+VvH #7#9i| yqBb~(j>!w 6d;p// kU{ťbEfy691Ee)jm2Г!\\?~#-M%ψ[.W퍳c`0WK(9ʒ1jz x-BQT{de'#ȯqG,z3XRj)'.W>+s%R7genm>XZ0đL4 XNO?ZZg<~h",rF\t劜'ԭi%'7=΃s8'air n&ی綼w'>6 #cjF_=mA-+GMu?j OJmFtmDМ{,ȯT˪BM)unej>]H}?P_Cu `X>OWn560O\}*$8<{Pхo+򎞼O,đ9,Oa1Ar7z_),9?NTjs V 7W, a݂'u F.N/m{"s۶ *wzx9w/`w5 ,ed|3kA^1WB9w7i#zvTず/xQuiQ5QOq\ֺ'ir>=u\M:6 ݕXvp:mr)Vt'Vna6ԡImn'OxLmkG_Nb5ݢb%FRwoBFt7߅@?d.#gVcNiχ7sKqk.$Fq.}"![? ѳюsRO] GEt*eLmg:V_|=⯊'x-b~hci*>?gZյ(u\۾7nf~9nl<-o^ >U+}CDFҗ[1S+r#⫭ºMᘬ2jڄ$g``iܨǖI?3o|mSZZ J7Iep<+U`6܍ֺ9{~,uoOuO>Wy]_70s>.1BRWRabaVwsiE}++vHỿkzc`11If:+de0>+`s"+yK6= {Ԫp>c3֥ue]I$(O?MW]yF.nOrrKi,zT- smX;yp Sln=q\? =4,7{i0>lQ AjwQʜ:Olֺ xkK+%\Gs]N|7y:(%eg¨ǒ99UFlc@:;8W'iN1@AK^x46[w7t6 G^>*ͦ7 :tnNMbN2̸#+Yy6\zk8emk#,m [8浥vDhҍ<_6֖Ȓy!O^I%E-#X7yo>_ srRx%{~0tk}(_h9ے}q@=d;&l=k=̅z x'ӋqMsd֥$w~dG?N{>j9by-rI˕r>Qc@%H\w&]ާd={œr8FNiA;p*@ܜ 8p9FHR=5(@8Pzᒠgr cS`l:jxĀs)n {tԌB?4ecތŀ8#S(=E(bH='"23I3uqמ G>pInz ?1ϧbx(Cu&c}ު0@^i\݅G\m$h\+JmOͽ3t<;-GS#HSxg#kU7ʃVvi O,s~7]͎>Qׄtxq41乙μmb.T}Tp&%dqqV{J%Өg13Fݨ@#qINzҕt{ H8pP2 n;''BI!{iܕGC1w sڍ dv㏭*eA$ 0 psÂN╆A߂X{KK`s$( 2)Wc@ q~ځgAF aeN8rXgz :2N2x_ƀ$iH3i!f9޿ 8?6O\4;@0;@TGBe3A\`v 1I8($0Vـ ΀[ͼٷ?pƹ=vfxZ活=c]OSޅyzjPApJc 6TcR=ÕPfR0*>hs8@qF?ZqRIЁPU4YpyzJRr@r+Pqw>?$3׃k`t<`Sd )Ce cs萜d^1TGM֭hnH\y1^ϦhVP#jVL1 RbPG<<-#IUtktϊ/=RI HPBB^&SuW2]<(;s#wnHXɥ29]Y%[Aԧ[lwyS컿G\2VS&k?MXG/~q'1@WR< 72y'_ q8'+ak׿_NQz-Z)RΠ[!>xP٭m0ʯ]K3SKo [PU$I7J01T3 ` |C#Zu݅ʮԻ: "J睤>tm: O6kJ`@@cI<__ WJٚNqSG!@$ ~!2}z2 מsިJ cBɴg!C@R">A,IcǶ> CNdW4dw)vc s@ yu6Ndэ)^?z sqȤ2>G(LNsQH]̾mN ?.'0jC{c(v `TŗrFbA =H0a9=8#Y m\q@$czҶd9>h1O˃ey#H[tI%@#4l, Q)h?{t]W;esW;uO]Dw`3*<1"ޗ!xwxIybpYL |p8nX0O]XsuIGCIJ|`]g*szLjз463Fݤ-9Ps{oR<2Kͻ]=LOW4:ir$WbHpB Fqx~5֡N_ %p񬱒Q91<_ƳxS>y}>{=^HF+23 :S~guG)5 0Fs^;g~,ב/JoWv 0dqgW4tʄVQSsO+l(IQ~75 CBm⹛N3ٕI$&9#׼Wޕq-H[]2I^^l&(Ihziwlt{N28ؐ;,#M}MFrF20X96]xY"`h̕*9'ǨoN<>5S7M+ZFbT H5ut84gږk/[gMɴ!O^r){^G)ݓ#y I4m*,; p{ +*B3ʼK{A\Co6lwT]ы[b9$ kZkh(]i$T$63dQqgZ'sI 3 Qm 2[\ $`Fi6C@ n +@v ;,K ň^:{Saڤ~9rx$hdxlw G^8)8 Fܞ;Ԅaģ䛘H9#wO_º җ9Ŕ#?\ύm1i*; 玕xXƔ (@`RtPJ Bk G`: t]A7ܖ6q+bgЃ؎Es#4;^RѬeI.RswFr2Nw݀uo^ 2V!LB8Sᯃб]d@֪*Sꍜn`vs#B?ÎJ?|qāL~u$_h#k=+|ʂdB8 mӧLЈl T6@?7מUς<,WvS6}͡i`?oj{}^"SƑmKhv F܎т g9 |<YxoZm')vCP$I5g$gE(/ѬW׭=կ8/'xo7 ]9VeP[|;x'C𮷤\ˋoH.l! VSۃ㯷7 (4Q TʆG+ chmTu\dԺuB6OMźt=̺?nxIbI ͕^;=Gf>OO0\t)+5}.X:lπ<&I/[}ANRg'*mg'}wԾ$`ec#!W+99G67{h e<+.D(>^Gt_ otRH| ROI,z*\J<'Ymfn|SC In5Yu9Q *YU A=v)#6閚'O MjRF6:F#j;Ϲ]1Wj='5? ]Ϟ?Zp'N }j5岷3 Lp j6͵i;n9<eݮmjn6PH#A| Eh 8x@ ٌ̎T1[/uķu]=c<ˉ .BRĒI𢏓AW#}<3 M9 Kk51 NgoSo_ UT`e;T,,6ͻJci#XU60H . ź#3ìF45ttmsYZ6ڝ1}f\MOD,LvP t0G M JIٱGؑn-9DE;W7Vn@ץPxAGv>Վ]"f('޴{&j%2_⠼4KY..5 4rK4ˏx] t[.:f3i߇Yn-[f}pI'}s\&_oW.HY |a8kUT*)B8$p1O$RP:#dopenalpr_2.2.4.orig/runtime_data/keypoints/us/mo2009.jpg000066400000000000000000000147061266464252400231630ustar00rootroot00000000000000JFIFHHC  !"$"$C}" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?-IVDQ -CrH?[7o܏&,?J|-A!%j?JV1[ȕ@S}74\Rp11h+7i>kV 8Ij$21r 3Ywv#zQ`c6KmIo_1jZ(LIo ;iwOF6{yVTO<&V|1ѤeڃVa_=.}x^l Ke½0#AݜIմ)^K\{d: 7[jOkGxKx;hDK(o1TRǎoiKZGNlKp9R+/ m4*w7 k_xQ}k=|Cku}FK4h 3|#W:A ZƯheiwz5ڼhȌ4Gjc.i@ּB=N_`Տ&jI| l#*|T>Ht6Y5mUL2`cEHǴxĚN_Nw:s켉 w ʑALX=_x[Yu(,RkI (BK3^FVoslP_$ժJ a>j'=Ú߶Ӗ[A!LkH4N#?Y?Μ]ƯtZ0jL9њČ}Gi&btrrvX~ =zVck $x敇~ iRC̽nZٯ5VےU.^AD6P\lj4#,w!Nsvހwxx[T`SZNiO҄H9<5;n =Gړ$ǒh<@'4lQEGw+k4rFΰ gjޅGo ſe|6<7Qijr { 49 h^2fiVPBb5h庝c*k5+/-D)HozzQ5  s wew t,mg^#xzKbW53ZFu^ +Q>.h"8F-w F÷s"{m. Dj#)cx<{|-hvw|7jW.=!F,G?/Y_;5|gq~޷K+u %[1U^&N Yi];R jut1b/;zkh&?SIo.j#@]ҹǿ5w9H<-OC߁;8((4~qJ0G{b}dtS8ϛƒ[1 R۞ ;(Îq[gEumm{lpG42:H2v=EfxU [^E,M)X˻srnA5|˘JX 2:w)>{9giL @W=fؼ[֢OA,K}/E,m1/by-P:4 rZԫ}ZQ_J$u` i w ;ݨ*ٸlH~w8*YyE4dOGFOLvLQ@)sȠ2sҗ'%9'L 2}4gNI4bdW86?O7'5H##-ls^+?>[:?w*蚎#EgGE?=ixGGEH;?J#EzaEae*Is³1HzV7GR?&k8 'd1?(O\̏y?iF5kGI~ѫoB2%Ko;x.Eʁ*|MBJ̮m-. >%<==,He2+WbdbTZ WUwHKX$w6GcO2'nl' ܐ$K$Q;f@LmnRz2;V6"^Z^]Bp (Y81#Un.cgZ*OƏTr9.zGGK*~.b:*@=3Z(QEm ^FOWW.dV՞瘑xsn.O| ;YzƜ%bEv+u'9j'|G}kNզQӵ8 ј\?4yF9S֗}E]]i^yyRksi zW$Bd*Zezs]?'gcp ωvh?Q^sG>ss2O4v4w(fIyG.fIyG>ygxAr1ыW@$ 7A<E9|H$;^7w~֑\̊i:)ȍWU [Ya0*Xn^9/pFn-Ñ~Yw7c8AKmP?SWΫeAIN*ѱ^}Uww9VƁ9?ml+Z c5(7r8>zz2qYˎR9?Z_9q5vuLV*F4~Ϣ%y\x 6T琳S+G%?-Wе[b^icW IEJh|rN̈]xZ>ߗ)#oKmF6Kh٘pent=jO4XuMOem3erǜ㠫5$_}&iEpKu)t`y=4icD4WR @$z*Jó}n]f!葶;xm)*ǩ*劊g4ysS^rq /җ_QZZgܢI"6cOo8e{.2m.SM4|'v_iwɤ@VGZ<ӎr ́K?a'^ ivEy]'תjɦڣa@A+˯VzgzHdcK[\Dc6f'ӷqcwec.A?z3f`F،>Y2HXޘ<WDfǹ9?ƤKIֹtۏ(`{i&`q/;Y|WfA`|~:cXbZΐhsD6RDay]];T-0bQbtckanɷ|Z`=iդ+5+(ꢋZѧ_ BC5X<2IHJq[Ա?)t0m_#i&= `@#5̣Y+oEཱྀ.b=җת>O+KFa'Q8aTGkoB+x4}Okrs:openalpr_2.2.4.orig/runtime_data/keypoints/us/ms2003.jpg000066400000000000000000000236401266464252400231560ustar00rootroot00000000000000JFIFHHC  !"$"$C}" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ɼeVGncʹe'M>=*ÞNș"#b'IYS>`T'i $ӎΦ_S[){WPwh8<Oz<ˌsy*%;= b m"3@~˟`q]֑oɠdѼAgOb~+ c`<VjGsIMx?Ұ|M⸼3e濬iuicJ% U 3]vK2|)4l)m/~o^fJ7 {$}v5LgcjNTz뗐=C:+;"ʑB xx{j:y2ҕY0ܕ8 ּ/~ 0KG, oz}jyioM%RPSFHU,I-ޫK-ZڌZV>ɞXŠe.&6wcNz|@ҵVJuyndKVP5I@_>^"ዛ-bԯݧnhɹ*'%r2223MSᦻa1[i̇D]()n#Q1.= m\iSOKkkrۧh-'9' ##k8 F+Jıj 喯\ja͂\?MP Q#ExŶ|=|7-O j$$:GFE@G9wiIk|^y"չhf[4ބ=y E$;u,mxreu]ῌ^! )I18b0Nc0vzω} [ѣn5^,tƸ# 4_h7V:\fKf0:1Ad%{$&-5gU 2FXuPH_z]#@ԎBB+G 7T8Ԛ2P|I{McɸF.A23q^]O_z>)4 ܴ LjF 0OW%`6 s;hsimXk^67z~S%%Rp#O[[ԱXmm(5| ?5( q72g(rWdSoC[^O?iEwNK;)fѦUA}=°{Ŧnc#c8#ןP5)7'cKy"Id)2]NzTk`kh}&k<3O<J.D^]LG1sO5=N(㖼Ⱥj'ðħv5KHX 'ҭh^!ִqi (Y\9:PzVvٝd݉qy_o\5\Ԗ:t>g;FYY1c<3^mMot@k 2:z?3h@ib$Xvs`L3XTGV o"/;r ۜ~W;u}ce<1!r$ GӯN28bj£q+x#F^#V񖟧\j{i##y6 Jn `EY^]~ %|+m0Gu7qr Y]~A5qpdo$nxddcbQ~< I$S֌mWE eZAjgo-V`{ dyCQAM]bB]]. icU2GF+A :sl:kt*1<#Uu<smSs\x[uxNq!vB̰A6>VHϚhsbTRzg_|M-i: ޕ{vX\*9T] dz OM'ȵ2[-nq+", ]C# /k^ "-|J&-K gysZG[ln#i Aq$j[ǙGPt漶Шԛm[m6|E}kj r+x,d w1nW5ށSğ]q6ɥy[HP|!;0Xm1K=VO hZoWL쉻Ԍż{8 \W߇zPƫ4+Zlm햞C0cvOĹ@qM|W4ng韴ơ?P u+Yf*JݎWkeZË Ao\|K1ۭw?ۆxU ODe͎5ǝys3Mw{)&X 1dړi{!ϔz{Tr)Qu}7W!#m&M=42Je .# 4^Hy.t=$%B$@RYs1L1i[9P6Nr9Q׭%QnxgcLGLֹc7̛y//j+;iIep3p*q\\VnC):Vf.Z!m<ޟN("UbyJ922?ºܕgi aIQLJ`*1߯N}+ jn2LWWn5e˖9'I5bO ̅v'yB@'+W]ދ,S2mPq{[S_O7$FpGG8Un-Ik*<\=77YY KKv>7].l䉇&\Q2#^/z}k8U v6 n; їND.a!p1D#$cK_#2_Z u!@V>xǧGt!NIvǫ0^["ag}IfwX?gsg)Q9:y7Cko;%tk|뗘>lsLX+,*.9ӷъʪ18b2=[PrG*1)>g3$T  Nl B  c+_18Pm1NL\Isyr뎿qlzR9ؙ[o1FSԁ4l%rry>Qn8>җx9LvS6q릥wc 9G_1GRKnRXo3p9 cדZkn[!uҚ ,(ןԧ]Z)|L 1ީh(28'wiVFq4'qJc?5Z)bX|JYz嶖$~ծ3l&DOǵ@9P-ʺ ` ~-~Q`R=rL/  0QG\~>e R=0H>*Վ 6[Pa@9)Q2~~$?F\F4$qVрs cA:&;y{S(aϿWAi#9KJ /ӹ铌ս:aœƯ9; _+dpNQ&* Z$3 ,N8֌27ԿP$v*'n}8 %p5=" vy:/C}|n3\d,V6cnmZ' 8}+$H~zcq֐" 0Oq9#C{朸93n3?A=E/. m$IzSL'ր7@9֖3j<=Hs/_Êrڐ4= )'#4A+.$Qc}k~au@$dX,5+OZͶSN>*?O\}s#NTߴWG!? /lm-oԄ1Defq#)wI v TW-: I O=tc l~Vc_S0q_^ 2#(_ NIῂ|a#hJPjփXCV.uy'8'M, ӱ4'HʺpvA\g)x?R02sM9P2:4=zsOO0ON? 9?jq#@B03'r8@\dc4pAt#n?8 2:)ss}?Ľ1V*0}($mÞ[Пl}kwcjeajۜuPv75 /f}xO\=GЃzx<jėq*خ#)hXf~Ͻ!a`ڳt?ƣ:8c5:}GjB{qȬZȒ%-pSo:OBB11ޜ nGM9x'=j̍`R3Ӧz{f=;Ra"?w޾$ H }=`$*۰ =n28?֗$q8с߃PѱXO2&FQZ;>V 뜜zҨ3lqR'mN; 'jQ?@ rK},`tHO^47׌ۃN9T׎(AmTu=WvxHlL?. :)>`pq۶s4U9!t@KsR;*boڶơd޴FW'CʀaR}x':S$&pCE+S!A59y ozBlk"msk/u4M}|z!`_kQeKbdFso]M0/+UUԞk'_"dlJI0'{ $npbg֧pڶce2vHh8c\<>lRE׿RGT%TPMF`y&;lmaҽeTr}a/z` '׷vzF7es{gٚ<U⦇.cL2[=ڊMe㊋v=eIuѳ#qR[6Qjϲ<68rpz |/C<93=?\.nJNƕ)9sޣ7?+pCOA6 :RoNsJZ։hrd?5@~+?t`?؟I)s*Gt :^9rH>+&}L|hz%RW(KL1gw}5 ?G־{">y1cP"kH 193_jY%oX>R.I# ;~4lFa׎ZQ𼫺O bO\雝]8Ii7Ebx?kݱ=uSʀvޕv/6xNI%xуUҮH[}WOǗtLpk e?ofv8+8H 8gN3a8?S ?SC:C8G&?^=m0H}syO{Yw:Lp9UI@"|M_;G\\ki7D䈇 \o%e0tb>NQ6rub}Nt;DNB`)i18?ӥs߽,7XsO [s[5'ЅV]Γ_*Ly!xQPsC'dӕp:_D U΃6g{ [Jю  wv_#X'GhS#/cߧzٜ~c1IENql\\61= ,Uz88'i `tɑu+o1L]A(@89jí(|9>Z09|% vfOG9H~!7wpE}A K&oOӇ7}qke??OEƈ@|r5sOŸn$*Ï+ 8Dz?x G.59/YE;DW4$V۷ p R}^R> F&vqz?}D;~*It 4rgb޻|J1WqK)|~ xא9J> ߠ9#k?@?R?hkiy}_4ϖOik?&'=S4sfiy}O>U?|r0DSG<'<}Z| a.fL ϵ_V/M??I~ixK 3NO_T/ sޗm0{q'9=kq~_wɧ|>d -8G)ظ禡V c{cwX?'X/~g)鞛5(f|>y8k+,co 6`\ |Q{?s1 ϛ#ƨspխBsV7!Dr|ˋ־' V}OY>" K,O<ž"irط4-EL=a'BJ7.yϦ|j[CKa71O~&z=1 }d.sߓ5̳h9*QGf&rvGM d;FW=0squB{_;6,-Lnkǀ,.GMBM_ުYԖЏ_POϜ|;XQ?:,}ķ*dWI%H !]z}:)+rڅ\?j|^A @e}ˈhDgݷ9x/ĺ7kmWQ%x3+O|8V$½|1\($)(ޤɃJ޳NvYg y븟f)%em-K?openalpr_2.2.4.orig/runtime_data/keypoints/us/mt2010.jpg000066400000000000000000000212641266464252400231550ustar00rootroot00000000000000JFIFHHC  !"$"$C}" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?YS?^WjקҼ5%oYr@Tۂ |U߰?sNGB"<ׅ̓+ېm)y+ Js/r׫(N1]N>(> x~*OZ#/0ÃaPJB*Q?& #ǧo?__/~(x+&a~!ৌ?чW 7M|{CMGKחz-ExkAlYno%@-,r Ƚ tݒk]z}fjzЯ_A%SH~+x|`Gx|+ :-7?b$Of~_ON?s{@Bu"8E@FVW@sҥivNUN*n~]>f d|Ͱ+|Tt'3nXokCģɢ\ vmu(=O_^3çYZ٨Z-pV. HBNix3V9{7ŝB]6F ?-CSzhX/:/Z#R |nQO%f#Y'߇5-kK{*DӍ2DR3 |_m\x9R>1jOmd+Vb@ ҇{_}b]/6⧈C Kƣ#[ SÞ3dexrO C^\Iqa:~"+ŚqHӏJ͸o,[M3⟈sKƄ xn~ N*8p+/f"2td09Uڼ`sI漹>$xp3ҧ9K޾);ʏ6 fZw‘Ir*Y>=OjV?&~_{">VⶺɃW }5WRuڭ֭z|06c8Hm5S־[R份VI+ `(('FVn7\rMǧ~F=ZY1*Hcr: =jTExW)OĔ73>X{djÀ@ WՔCGR9rX\K U9?d$"m+F[ ?Ҽl GD;_r⿋Lhq.:c>xRWҞ8QE ('o<07c8>֬4 -w<~GК䨬 oFU$giA>"I4=>_d5B 8RrsmƲhZ毡ZmͧW{: 8 98zrsQKV=^㛽Yt/-C:8tD{@!6! یR׺iiQ_ TQʄ2#@yzcPVnzF=B zf,8|* ќ ϲXi^Q&2.]+"qG~ :K I+rV7Zmi|@4@_B'Lp0Ó׶nl*cI#?1i lThA(NRVgm'Ik-tmDP,`Wng$,:0yFGb?EĒdY@?'8EOKMv;'▵Af RGFP$^q⫉<-xv=> KY3bbtmyzwHnڳgeZ2׾-lg{kɡ}d7\ܐw4i hF,)<(,I85p6ԙM$ǽ-UQEzG?gOuk}5ƽ+ďˎp|T3Z[KGS4:ޠh93U`'@1AZW?tD{8OD^ N}to?uO ym1b^Gw^-5rKg#ׯbT2)¤erc(v?'m5~ԆI6c'TW=5mGFi?i]H o%` s0 P}+NMJwqv#RP+((((((((((( zW?gOVץxg?jϟq??~ږQ~*_ú3O׺!!O߳C13IspNPH$getG N[ĸ6d;f2k'2e>_/IO%g8ihޭj k0#pRvH}z,o?-s[o0|h0 `d¼C*M xbR?z.n(UqVWzfqj5ݻ剺8 GEA^<-oih*Ҡ 5+ݯJ6@,W3W1kw%R[DdT)"B~P);h<3 xXӠtˋKP!"#},FvLs]qi57o?n7t;Hzk*` t\S:v,o&I1kSAWQ%Im/ZqsMi :#cV>16]oFMFIv<ڌs(F P3fvc0%PEPE((((((J\m?js^qǞ5Ksb>*-#@?8pH7t'y<zi i'{8o?'4=?tq~70<9x]Oj>+~,∴QE{G!{W|Z$s)ґF'UeqZxg[ 8s9ʕ8'F\ќUڔiE_Mu MN۽XȎ$f'⪎Fk׼1/EҼEh^m :Ay5_>0 X1$z+o Ej[ϳNSqB4~9A ITw'WIk}4=Vܐ6R'4CiSCenF*so '<`?Ljt |X4g8l~mlx~-I6W[b@t'ܚ#JI{wIt|hư4#P69yBT`~5X^>OG!‘k~%\>Idql1VprGMr[ԏZ_? j#no${$%bqƱIVVm2%J#õM3TҚ5t=.c8qtȫx.\L XJ@Uv屑tZ/⥎˙| w"[kZCJE,G9ԱGX/RMɴUU\&?f>u\`fu0rǔ+N/+1o 34 H]94m,$dU*GPAc^:jhTZY *(Q* rA5?Ë;+'Z7#kՇQ(b=j-SN? hs(vc] z/#1-Q^Q@Aaz2y>o<v1H72Pt/ z9oMmmCmqHDDc8횟m:f+Y@exfQ f+w_:x^M#(e*z^'~!ksx~m&[-E\0KU 39\HHʪnkKԇ#t:ӛ>9#A68#񗉓Mմm$Lvd00lV#bbN}=-m,L||Q.\}Yx[.͘5V #Y2rv$MoO!֬:eHܦ1+Ww/x;:^&^qdO(Xp9>=vr! 낤5 ZִWmO;_< C\-5}GĞ}F).m#_Z_$x{{s_ai)qA^=C<y$ERTV'V/tۦG+N&׼.~= .@1~`:r|z+kw Ԥ<ڼWȇy'(!I|K 4!\xgݮ'.o($jɍ NMP<;M׈I5;_$ rru8cȇy'(!I|iZ_?mgm?p\6'W'U6 7c$4Y<h~0N}{Kȇy'(!1"ֺWg|\[`9)qU^D??QC&> 0?yyU =H+FIۿE*3ӓ_o8sOgυ4B[Y.Co!t;0H÷Z")ʈ*}W)EwIo"I3٥I Pih\|ջfp?1'H럷O0‰zqkt_ZɽJ0W˷mԖE˾9+($I.Dh%U%s׾~>;&6xk*xֵ;ŝ?Խ?ԚkxKJ2|7ُc%dS@~<WQEϚ|L[hN3h?*_EO<9Nr}3&`A9\pjAXƑj?**>bA<7G"(?ONbA1SّB:p[?ҏ횟ʃ>ϘG z?PB k?!d4F?jtVT,=Ig5AtxcxsV'P|/p5|ל_Q%\uϥ qeIJx?튿ʃ>xG\ s@#kD񷄵-gϖ(wP.#\nnT+WƑcToM՜B iBj[J0i_ [-Kz_Ze-5^ϏR>aۍTAk GQ߹nR`2$O+^l{5ͭWvmIS Uƣbo8ҙ#)O3e%^jX #hkB]~$s֞5֖ڜ2@$1*|IyZ Yot5Նīn qvwqw#ʀo]2'⼚FV$Z/mXTfi>rpLVi7hvc3,Մ=1URíj]}Ň\D7:B.:^gl,~)x2Qu yXl 1jCx_O[!NkUbޒҖwGXN.. .)L|7cTvW]wo+SƒFvHeq?ҡiiz-3iB;ESZRr*Zdsw/He8ި蚵&O\%lyV7&w2J|b O[+T˪< q@3SD>_T5tyٮCXGf$$,\Y''V{\$v|?8f':]jqg4[tg.Aܕ3Q󤶱b2!ONzTa<CJ@Cr9֎UCY.􉌖UP{sO8k'9:H8##Ȭ[A;z}?߭hYl<ƽ~&Ckm);Ȥ?6szrz/ c"3Kb8JN:gP*R,FmVS8ﴀFzP1̚߃6]oEo7)3k{^c'|5e>uqqMйc#kel6A8Ozʼdzn"8lB-]fX2E`gc-!īc

Ҵ/ߘ4w&c=K PU;o}aN|Wiq{z%]JƤUI. r~2zb|Jq˙ rA1?)?<7im{k8﷚(g@VدYu-Nte&68; ԥN:駢] /U.%8̑BQvB<9|U[W3q%Q:N94Zfvm]+t].T$"0q&ms-KPLqx<%Tir{c+ξ!r[▩½]e jo8%}~h)"]F}bx3Ǻ5#'o<\m]o+L$9~SJE^.o.;.82񒤩/< 0ӥ=OZX${vIi1 )琫gs=ݥ-9 8*LrpAXYŢ2_-!hNr{8i"u"igN9 9k-A4{}'L5+ ݷ(UJdt9Wϲi::&֊%bʡrO<x7QGK2m_M+I妵j )Esmb1n ~j?: ̫=fl}_?k(4ڨui;}c_kGG%$lt.c(g$9! #JC&r UAl`zFzr ܟ:*rwZ^g\h+=LMHÛDo92đZ!B1NyFry z+ NRݚpsMd 9ONhOԌEF1Ɣ8?i}zoSޔ@J2x#4HUE/S"8`P??O=*PrO֛nր- n׫̀ō8%Uyhu2VA\Uu%Y0?x@V<ߴ8Fq vN8Tu︳95 8#`gހ8Sd !oŽˁG|TN9*3$٣8OZ2 91fOA2qA=)r89мcҀc4 sFxz14nOlsKyJxq&XuJ0 ?N9OjuJS=i; @(<◿}'zz?S?7J8sJ8ǭ j64K00 4k -Z<@qƼ~vI#غf|rqMkYކ$Ifsc،g8]x,.22{U?f;Y݊C#A zRjޔLM_#fh%7]AwU;!-$DO#Z^W-/K'Dg3D;3 żsHPް}R泺uEٕ@R8'j}?;,T԰O֪tZ|B3ORrU\6Qs=Js=$3V':͊@,vqч< ֳ4,vU  .y9sה{m (!xtꐗf`Yo ؼ u'V⠏UT g c.ܡC g$lwdݬ.{nj$ƢFc60Fz{^@V' 0WgH-ʾeFջ9'A$O̻FFy#=H#eq3\),.UɎDaF9AkYmn$yW9dlO_fL vQbX#SGoj<֯,)b+7a~hJϕZ䗲 5{#23OhnmHPѸ8V6I}%0F2&E H'<yH-iL3~D8n~U:./ i26b9iylph+ 2AA_3:+/YPcY,>YĿ #ye&]JLdnO}FQr]]gf\) x]0wF{?]FE,0Go#|EPr+2=AO*6ި;low8 "p''YKѹ#=? O<-$M#v2¡'rėi)HPToـ}'>߭SϳeЎz* 6ưeDS.y%G?O5t+m&vix:Q}GTXmfGP^;~$YY3ԯP>@s8f𺒡X9A0GLTir\kıNKǯkf?v~:c_m?`8{ ;Yh_C+ G[.tvsNGU5?>rH6s׿V|/%dP~0Xv9W̤*w&[I$lJGӨ 䵟zMp_q4Q_`u2HR̙@܁cyu4ZNkpp'y@)Thל/ct XZ61ݵܦK;YHe$@#s>9~\<4ZAU,vuχ/|5rO#$i 9}z7qiIl2?˕ ?WY}\2拻ӭEx1H6ۖnF$dxÚ&wi%zN:L0 +<}EfxÚ«bkF-UeU|zgӸ8Va?ձ~wʦQ;ܬ.pml#4d|!}cer2ѻrm8@ sߦcRxqi}Y$4@6.Bq>Ɨ:ޗkGEn3pH7>5[E=X-Ǚ\\8Fʹhف^\UѩR.x6` IZY31+\uhMpC;dqQ܁'𯎆 ͤp ziSc1 u^yC=6jih(A Hz}14m2SX^Mj2 ps?ޥuaeյ\_m3jׯl;>`qюs#ԍf8~d||{g=Y/i6&G$3#;dKƾIH28=dJYOƅQ]Uώt=Pa]TJ%Hzy[?w®xo4ҖݒX7>6$2̤=+δ;!R[ $bQpz|xXEql8 .${JӔ}= 14hTBs߈AZfGv!,\)$:; |W+^Rd3 [;#^iiFtulDy#cWp}5d~ۇvyRbA~R%(^,%.WkCƐu)<;m6+ypi?xVst?M>²kP+ ʀIWzvyůooF=_9:7Lm۟vqsqF`U :N1 ja9esJ<d~Ym']ˡH6Wl[`9V_]MU`m#>wr sSkU2°UTc8zq\ͩ>#-dk{t$Tu G}۽ێ:Y<\Soq= +$А1p?xo\[J݀.|G=$^ =<;ZVoT{?|So?]t}(I`bN29FI>ՙ鷺eoڿnU&9R 09O~&\ MR_ZF8!oYvY|WMGt,y~XIW|o St9)K gFu!Xgcznߗҡxm$IS̷ɝ۰GԌιZ2<&}@_`zqLL]N7fk\r?zeSkR1ln t?^±5]MhadY@#^ |gR )V{8^38 d{גOIv9kcG7"6pvLGOV.͚Fu%86DK{ ;5+F|·=N Z=7I^6#) &i8/ q_C*q?ߏӮ4e%}<ڽϜ<9Oxq.DZfhc`pxMfԢ5S,I-SrҾk9D'fM6A?- ~Q^{nK^i?hϟ'm[fYi"`Tp~һOeԙq` z1_R:}ϓXO4g=w'EN)JMYy>k~g?#dv@(F#''8g+d?XVh]2Fߘҟٗ;xL_U'm?ԟuԤߩ%ZPU|AwPGojmѦhPHNON}j{X~H^]At;ʺrO=_Bg\<M.t_?HmĚ~uZiU4rsnOLu׿9:3|+ڃf8XTqw59J3jfp˟lq֩]x^gӒU!{(UY}Xw# 4'1T1:?4\MEnFIxtzysJ@:Kb庮x4ײ͑ ua.eS_C_xI[3UedePO0ۑsɤGo5*ې$VDpppIC68MLU%\]ghu55U66v]k$ 0$Ҧ}^PI6K$ qhnJjY۩).#}fLno tUvrK 'wiZXeX`1]EEa,`cPӴX!*1:p?շ^wS;Vopenalpr_2.2.4.orig/runtime_data/keypoints/us/nd1993.jpg000066400000000000000000000232771266464252400231670ustar00rootroot00000000000000JFIFHHC  !"$"$C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?"ޓ-,n§=A~4ń1qY 'cGP^(O]㠏 {oU汨Ps0Ź9R9Y5lضI3.B#Wo(zx9sPݧLn>.^^^9s⼸1#\NG5+/5b!n`ܸ?~&Ҹow[wRV[.d;HeIYc!=3te'f2*m?#nW<x*R zbە}2;{oaA<:kURi Nd ֲx,4g(jj Sg2958(T VNȰˑ 1pzng>1Ӌ?lb^1f+nmܥż)>Kpo#U;H8/.LziȽFY^P傂F{iSFBEo2iK-]/Q5KE K R`A?rV m+GRBһ1 =ZC NS&ӴBq֕,Q_ .OeM?KA'Or?Fӆ}崋's1jSK_]Ay6vv[$|=dSC{4e|_߷ ˝}vۮ?V41yΝ-$& 2Ki2 bFO1|BIִݮڸ5[-lPZc|^QW {fc&nXY >oθoP, fjL\]KvF7H>/]u0]|jxrqZm$puVpqw<ӎWj=ՒQ՚ʤ[#chI%5Q]:-N:f)IG0wl!NrUAn0,UoZ!oRۛ-'kUg&ݑKQI+7; a7PԤhyύg|/7$]e,Cc8䟭q?%}ʟ(!|^TW/junﴻ$"Fqķ9:N&HRal1q/$:\tj[c{WqG9"Qc23GԬR}ѯbn"rCFpsqU=^Ԧp fE xZtr@'^N4yoDyLۊg O5k 9/+sۡ0^tlH> Ş%N,22^}X7y!.6ݝK*3#VbB`z~-ksũ'mG7)2lfBcQϹo;IAlTIqsKh%SNɷ!1>ݢGU?Ϯ)0'?O DazPKqm4p"50J KKҬIgIKi7*6psvOm,f"v79=,I4r2F<?BMm"2xqf tK4UW>̡8 22#X4OE:滰tfqWno|9k^YP 8F:5`u9ǧZ"$''89m?ڵq[9NI)=)NHiQ׿^*L}qɧ'3;=~IH>_=9ڃ){@=ࢪh<9r{+&IX(rdIHƔE |9320R>wzU䍠<~frŞFk Fqzq@i*Ōar@)?ZC!TZCNY /.Ŕ ?){A2xʜzTO }w>YJߞÐ!p܎ZcF@=+Aa9ߎsHm A{2wQۓG8 2BnIN.8!RzBb89 m8 BִzbxcOJjb+$!$rsKg&aRs8Cڏh/fUT5l`.9O\Sd\R!IH IՁx'GSR*0zztUs#!?jIb9<,xprpzsx8g߿;_q~ջ5~|59"y`W55;v?WYÁXz>-V|`h7J?yyBzAv?]tOR{v83SP02s}{!rt>Pu{=:_&P's=s78oLO |6i:'s{yM t0x(I3*ԑ( \ʠ~}a7kYa&0`׵f6,|Cv=|j% t ɭ(OsH|:iQ<8] si22?[{C|Yy=?8 =ZPNO/1:t Z=; w#SW1pǞy#<%/z^~#$/9ӊT{ :FA;BĤb9G#i<_1GQSLG$<?h/bSo8M=C$){PTJ~S74 jcQ}GKlw"ڇ),y=o•b0OqWB0[9ޅR"'OAy#~_޻?9x\MLH]htl|Gyn+ Sp sڹ Yqd|Ky}~1@p;c9d{@yiUNvQҤ6=&<#hB"8&%|gj可LgPmXJ`R gϭy@xf5=:؜%#pW^v]^X9FATi<4HF@$qE)U\ m9S>F}cJV'SVd{|{4\vpqȤ3dpsXRxÑIe'&8ٸ=S|3m텊?AX^\S#ӯZk먞>^T!Gcv=?`g»%H*yK'#hI |~i %E -a%Z~#m<ח@ebé䯦ry ׵[n/n0UZC{]ů oê"(n r9iuIk01"'IDpYܼQWKDuK}:cҺoΗiCw e ç0y  n(YR(ўI""bpV̾`lvW G6Ł Nbv7qRo |J׵o \\.eDi%Xa<9xVKѮ55n'BTSyg ž:INvo3%s$ϵst6z{hsLqG+991Qz_<D|Gk&7Iq%6  *ppyҋ]O!Z}'K{FT9!H-V{x|0#Э ֿ4B#2d`swAzMŞTլ4D\,#8q {*(6uox+ZCZ] Ֆ,,ʥ(qOe5GS|iAŚx[rڬzҍ]xgV};E"n,nn]F;AR@90Gb*Ajݧ-PX 46`6,<# k_x Lz}VA,ZcjU|x5+SVgc(,RW88Gҗ}/xKh6V5֫e L2)i ^(%L O xV>AcocgԮo$b ?)1Sƺ)H蚇mtJ%7VR ‡-I>e6e 6w\f$Ȑgq#Y^5 /I֠:=saHm+ 3t`ےN;Uj:?YS弱%XE:םx' п,j:Wf=*H!\rKVg=:P5“+<;w.G#=i=;þ#u 6oi ]kصM/Ex -AJ*m`ѵ߇-V)fK h.,̗'K>WSa}a.\`\Q [kKIu%6dX%F1zu5nHj= [H[Kh;K:qG#=kB:t44Dx{g¼V^$5Bkbqv⫞zEk6۹NX^5l{T(nP¿b?vE{l0Vx#G" g_~󩫗k8&X,l/OS4 a mvSGZ4fZd7cHϤr@-ƙ}mqҏDB *oN?³S6qb$k6ӷV YԈ^C$Gޣ=;W (Us?ɦ i72+} .߁2W^i`]BTHH3"( y+0C$Ho dz+/ְ?cGq 0ZB<¥z]u;_hw1szN#fQ^|"?y9)?'2 Lʴ Mtqx_mGKX[k->B?vpMQg5{4dR8`v$c96Gxv.4 hK 7cnF׼7c_u#4x%@Ǘ ⇗Vo_>O>sGO5E{C_.a ڽ6Gr .~xM\ AXb2M[Z)c)<3zrp![,:eXܡ zj̎m,?Z?~ 9) 6 )J A=yos kKGezКV[ӯ/susLЫ_}|ɰԇo=:P<R8oR Tx٥iqTi/aLD%ąX/#DV֓vK&lK}N%f#F8<潵 PE#l+5@G U08֬>8m:O bB.zrq֧S~ٟ9&_,?Zbx5zKמx*]y44H(^Ԫ12 zlxGNsOgX)6 ݢB)|Ui/YOwS,Ef9=.}GIմ_V6hDOLr3TO%QIqWeuRݩ;/Wc?-`ŚgL'>ڜ'Wl2'8i&5.nV㷺ŢH<*e?E`N*4E?`J^.s,OK?W#*(cUٖ~ *c?k]{*}UrUOq«|[q'K oJ54?5|N/ hvw6 on-c4eݒ@ExoOen-KԹČD;]XʒKueulC K:*6gnm ݤIR_mX)Wqf/uքm6k-I< !:JO [ZKh7m'r٢|prST㨯KqU=*r{)$魏2y)FiIwI\xIE?|0zxIE??gmsuZzܣI wڔpH`7+ '"~$`]ɲIX3ͧjATN/Tm~Gc*Ҏ{iGS- i]:F?/<Ag[ =t}N1Fߘ~_]TxgZ<ԫyYL8Cw_%5i||~ź3 Nx7Bj6lYd٣Ϛ1grѴHb%x%t#R#.VsTᾆseRB# /?|0{ؿƾ]P6r3 Mw{7ZԿGS?3A)2Zxa%݊+Y$ =WV7zlE :dz7jp$7nܟn@NϵhW|׵ SjS4׺slțw!c݆}Tn~٧[])Ēm'ae*RSJ5#8q ci﵏^L"ƦА?FaMI5 ^0FVMȗF7_9OR^յC.NщG'#KZ,ffۜnh>}֏~bH"WeMڛeܦ@a~Fmc*hd+?,Ch\O,׍-3F#ORqyiDyX: M#4B*&5m1v6?2 얗鼧hauH4$Oo?9-CڬBMHZ26BORX+[VH]iyz!f/ .mZ=փq.EaDʮX#;$Y0xDچ{&㩦On$a9wM5_(^ܗ2?Ѣ0H@Tj5AmaCcnV K}͸9ڊH0Y6SQ+DY%yUhsԕ7{]Y%-ީ|۱xR񵯇;;^/7`1NIfp:U O>@t;Z ngFpA+V=Qanô1!T ŀHv\Fk? Zkzl1xbOIm3H>?`s9AޫWsp7CJ2%vғ[KEʬdKio g;nYe٧}8JfIu]JvijG > CfU䝒W 8>ψhkS i}^TRꧫOt/اtbM&VhJsU񞌼9T,jX<}GB!_]+^-w\3! $613×zcgiw  -Y 1,rqF8n_WMƤ͏0ƌxJʥ(4SZmyʋp:sҁf0F1-@rFk B׵?飔c=={tvvnsn?lujmL)i6.aBn?NyP:S,]ܥDZN;χN[4\c[kѼ<4zX_˛'lW.+A}Y\?5殘is_קN>F.b?k8'WS áil=I7Y9RIiy1FU,@$9ظ$ Wt,ew OQ,!vq+N0S} 85VJSo% ]iQZVWjCy,[00#.1ۿq}^oBKmB_2Vk9O{sT܁yidJRH2UO<3>xGCuqkVծu"havrI=[\<w6,I|/2| w/ yNi`8Q$:|.3eOzJ)zhn4+Ö>x<5aBg,p7n`sKi?|Cm#N21Nϸ%pz ⺨2J8Fp^YaalֺǎsxԌ5x[WnzG f{;]oIXc򱓼9<zזKeu.5S팚cN=iCQSFJJ6Gxٖ&XfhlsJaxQVA8i =:_8=d`L{m#f6 5-㞹R2zpj$ZV.@K+ƗgrkGXajO gx>r k){zZi(Ꟛ=x}^.{#6[{-UF T^AGEGzdW3cͤ]?0{y o}*0 q |.x,]L=ڿ? <`ҏ֑1ߚ۶pp:;Vp0I8@Bn8=%b v<qҵZoQAO99X=6@9[dO0CB1'~؟bd cvsQ5V`^|99PXGl5D62}kfv1VqN? JNG_IЯbq~fo(V"vvq[g5-x_O]55~{?XBRb]x3 `WYm%ek{w0dsȡgJ$^CІ*0ҕԒVOtL;qiF1 jw W폜$"u>6) W_4@:A.'QRq_M]uD5FgZiv0Й_5M~:ğ'x p \#w0gjўI8Qy'#|# OGE_M?s'B_h_a>gnpP<%;q⦊|~d8Z?. lK`mՑX)#sdàh:vR&C9i-Y$$EdPabTr_!x?,hWGO8٤_g[?~xƓz@wEfqW=WwstXU&TsPJsˏqU9LnaB1@w60F(L☬@gԹۃhA#0C ![;on@Oy+F3o LI%yO8+- `A:XW>2U#^I!#9xgL:9iacLyzcJ]B5.a&88$?>S<%dG#SOZoN+X^1}C!%u'9T{w.܃֩Uv4=c8rAQl$=*x@7tW8Qgc#U.@:c%dN`9A8$sW Ѽ # 2r1hP6c$sHr1=>gETq҇$7^`GFs Lv ^qex<3{Yn#\iB1HX*T5r4!.^ KF>c#XPO8<⼼v+٤y֫RM +/C^~Ui.HQߚ;d>_#9Yɸn1x?J5UD60`8.p3H7S<=y>k7]lF;UU-vpyz@V+ާ$N/C1啯g> sx_SEǓ=4pEAFz׎|c|Gx_M9Z4I; Q 9[/?wZuK3$d*I%F$J&kGOnImQ@m'}"NUi[>R"n3 x5xu H9̂D 5n;-5+e[y"Uc? _Mϼz| ->piA5?=;NAV=Sm=JKoF158c6{dL;P2q{RVů/kC>1~BӴn/_y>a>'Pn^i׷Iwi2[C\aO5 ugRleF$_(bKid`I.#3`LY'\x$[(FBX"Z32%Dhl}ͽW?7O?.UfBCufǁ.<&|SkirPFdZJV69`7NB':|sSցxiʾ;hʞ]ڧNNnϳh[`$,I^e f a \屽K tYNzw x8aw+h*OxdqCa:"@}+9p{F3B_ #+P}]÷AOoU IA'$EМzzn'>$߈}cm☗Pm֝"N WQ0&>>k|'!k%@c.=\ew xjuqn2m4Th^iGC]5z*I-ɮ=p*Cs"XVHEēzҩGҵn-P$\{-ѓxJ' ß΄sr,9;Ñrzcńp |tPln8.?1 ]"< QGZK[/qMhEc'D#{A\6P ˠC#mH>dbW[_ Qep;}ܩ_oF";MY%"E\vA+`7J%'םh?ċMroH4 0Zi4,r m]AdF+eqn"P-.39!WM cJI~F5\ϒc5/y/"{@' T\$暾ԛ⏆~YZ.~=dre>\}1ԆﯗI! .Aiwy=[57Vp{ӮĜƏ 1ǿ5RH^b#F Ŵ@`WKGϫ6Wdcd_){h K.̢? x( 8EXwY=Yz1gv||v//kAf&uYl,,@O8B9TUE 1 =)f D!;#5q=~hyѢ`F @ @5Z@IdGS֭mqopenalpr_2.2.4.orig/runtime_data/keypoints/us/nh1999.jpg000066400000000000000000000245341266464252400231760ustar00rootroot00000000000000JFIFHHC  !"$"$C~" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?LC"9/oAXW>!-/${i2,q;-ï/5LB@WO\PW5G2NYܞI>P0YJp|QrC_=x?ʔxNT8|'⸷#I{RReF:q]Qs;9í(QqĶmڷ<׭nxn>^5P{MX-38.IeR?ִ-'_]%L(<81*38==[,,y92}T69r s=s^/~mf33 *>wNs{W#+^ƭ$pj;$`;FTn]*)ėgv7=kRA8=H^w~yB֯n-Hml|gQK]r{iE71R'-=H#k=_/%inlL{YX~P`t*Df+53 ї H y+nZ3XVg{2g >X'?ic'V_6ݢR7 I>cki淼 fyay%d(±c5)XG4o3|(8$p21N^l&2qz`qKsNV)IRdc 뱨5̎Ġmā<NJW[7 ~_S`,x??ʡU2@t\s4y@'zYrz&`͞8M*#Og8{u?jMؤYmWS8 ^*lcvz4׹Hĩ06@˒9 =zB,m,HqHB H'=?46Vr Sjz 7LI re8u8^6{oM 6;#:ZC $(-,-NV]֥{&w/*^v={-WQ;`n'֑+m”u'ZJb4XMqo%KGV`"@Ay1RX։jMƧ1OĬ<{tSC321$W,¡]\?uFH zBʖArɉ$ g>߁4$ڵ͕gLv+(8R}kyAy:s~bib qY(oCυmt{n"ԵxPr@*9sZZYjZtu+-mGPGB={嶍 `L>m"1gll,~{{U1b}G-;O58u /MFkD1H-v`qOL>vmnyUe!# 59"2&AzJIyhLT2A<#&Uk ?q΢IrR(:R<6aFM 3ǽ` ,]ۙJi8 wѤR\ w}dF$Gҹ 'q5MVST>ya}k=` H~?  88ZNYӸ5@U,')ۙTq ,JU@:1"BOW9!P\$R!* ĐGsnip0I'O~Y@@n{q@L Z(9ϟl#1W'O*f[8?N@g@e[XsGQ\XKnoF9˫^K\D̍6c8?IjʕRI *IA#N15당H$6c=JֲYE:h{[;@%C3@.-?dd}O1ܗѬr]ч$t5y9ERvl|`~k˛嶳ehv1? 룱g|4[#nRCa$Ջ߯cܰ73 dlsOҺ?8{ '.q sʚ9©<}:bH\g{ʸbGCzWp!m1L#yR6㧯AޕUC}ɥ s dV:8 lAJb 29,AG-=1{0$BpMsTJ ޝ$i˪QpzL,۳=G 8+ab@YB}qTOdgQrs|~uʪKYc%K n<؇F3pNXd#q'1 ]u\t۰#8!X PA+cҘP~<n_w29 m.1\o#0zO"?uQ:@~^PFekcu<,.LdW$$ry݋^:1꒏2kX w8\F!*ONyӯ׽f\%u֭g j3*@}r3vUtͱܧIg;q3ed`a?#16!\:t=9 0l'_^Qm?P~ޘ:`^GiX(PǍon.bǜZ֐,$#~)Xs xש'>僀98M=?0 ۵1p@:͂>cvw+&b3ip[!@`~41c1率2;dQ'^ïJqr~a^I,9 g8=G@K+BmĮHN?Z?.?*< D"( 2+g8{{WU'ضGxs2XI.H8ӟ"@o3 bX*]nֻ0?<^a۸䞝qȡ/r?†YTc94Qx4Y#I%Ⱥݕ]O=k7Ş0Vl59Is-]0Lz>e`~&7BS#M;Ga9z#ǩ}')w&fI z ͤCq:o/)Z5^FX.I pv yk_]kSFcK#;6KxvmsH+k,Jƍ bXg[08=qe|ř{ qvZo7^6T6LXg{$x1n:GT+aH!lgqNpï^r^,(*B $r$S$#2s1Y3xgk-hԞqo|q"7^:{Y6`cR=J74 h,aYQw%3T,u/ Kk^ ||0p?h<4>#xCI!FyIܓX~,Ty=v\l<]åWvװݜ%܈WHV%FA;gKĢĐhBId]ѺJ~nOEqW_]I{v"R/sHie_:"Z4+*ۅO5 c1ӟӡv>fvzuW\Kj2ٟpH t0}G}00ps}<+@۵9ckIm@7?socvRidH;hϦ}ٗbM ;Us\ec1+_s6ieMg\K=Ԗ7471VF`p<19c7(J;5(* yϡ:p})s@l`CJ.z{ql*!NmL0':@Jnn{ރHKtzg\ 둊8G)(T\> ч!MKs;#I_WKIhcp ][%vFJ?}tױ] IxsqYiHΓ,/3HwWE:jU6 }==)`vz`3lƽ)v m m-`/[[}r}~fT#@?_J4SѳFNA۟;`=2x)b;^3c˰.++":c?ɪA y_YfbXĒI$I{Լw=jM1b:[(QՕ9(u*U\H<'?+`/q=h:? `Ү:}hL=<Wӯ5vO̒9dP$F=\ʆ3 P|@l'Թi\vۏ֏d#-0x\}ߟ}G ;p^{Ou";P88 cߏG'!-qк='"=bn<#g7!׏Y1`78ҫ Mg>|VaoQO''=qȯt@cC;NU$ n8Kذ(}?O[滾yQ@_yi9$tC+w1(TqA^}\܎IrZQ]>[|l*=SHyeY x9g&r9%I#9iM|= 46qG[ԗ9T7{ƲK40O׎88)P~ׁs^4k|4z0A/f0man{Ԗ d_2;C5쉤8'y$Ev۟ҏg Hrǡ1O30 ,ŏ 0S 1F0qUl*)*9Oaw*uSYopenalpr_2.2.4.orig/runtime_data/keypoints/us/nj1993.jpg000066400000000000000000000175251266464252400231740ustar00rootroot00000000000000JFIFKKC  !"$"$C~" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?_ھkGw#ڲ\&ϫ)&asbܟWTt8gKQ:ѵH yf+ KE+ݦw졅Vvz-A!ivW78qGOdd5X:zkkc 7^w_MNέ{m_ l'=ڶ@er62tš=̆I۫3DI8N-^5eԛvYtH?M͟gp~=Bwii͑.N8kݒ?iQSvЪ΍u{R+JIe#qgP%iZ~l'{.}*Av#|k8S/K[6L\#ϗ(YO 9˸0J5wBjjjϝ;Lɠէ׉oxm2c7\${APOtedž%3ON 0/.3)s$yincis]r- _Et= uM2L&͡8M} pvIψtxd%A [c)nKKm.u&˖)o#WG*[ 8ȬΡ;ғ5'O%O!b^F2: d sFmai#b<@݂Ӟ+?i{Vi6ksnT޼11ؼGnZvW2D\n⹷gTԌR:)[SӰv~*䵿zeu2v.p =Tvb]}E,|bI :2V{h_Ct#[קk.?tI-_ڦlQ#0?{5!>q]X(0jC{_CxjǕ'?SƩbC);yckǼ!IuGPuFRW(8]UGVu?2xn4f6%R7_(O=jx84=qx,?^q9> 0vG7jmx@+Vvx>r~$Vkiwzl70?ORHYs־;#TrvmǡM9cUs:l`fh% 2+Ws)->k⏂Wz֓lϩxOvsFYe #_w]k4̷Ot)m(fdC ec*w%kQ+ޅ٭Սlu?yOgSٗ?|/ ~!q6k y^i$…n `}sUjy%{3$cþ"|0վ-5d4MEhcŕh6T:5y+㏅?O1$di/!ϓӨqo<=q zMp>@V% I}2xN[3}ۧb #g M׋N}z~?iM|B XƖ= dbǸ)#ؔ&36_0lQ՘\G^q>v1sgzm!TGӞ1F> ž,.<;jK,}yꃘCsp#&'X"Imgen"ݜ^;oE{eBʭAGn xO? 4Z]NSx`Ci>"YlNx{^~*CoiZ4]anXNg<677!XN[yp;BI$ kJ8V-*{g% m ጃOVMR )rX._Onxw=K">v/ۄ#R+sz}Mn4<;n\#(XFʒ`AȦu[}FPP8m }Сpy\L~uת~NoZMM&E < #f*W+Ʊf u0 ;ecgdclcxcw:]o5歝Ρ,o(, VX5+&[{OA$}u=_:DaU]*V=5:wfZmflxsG4 Ëk(:f%5j@R85u ')jجCUMi>W=y=p=UOV]#V1|o+zׯ3O𯊴g^jW7 Ɓ4fi v W?<~[hLIGZ-F14u$qs҂o_jnX d|Ш1Yf})U@؇1X|mZ9) 3{!DO8C)9ϭ4)3oB!Ϸҭ۾}i9?1yͻqjrv8ȧ |/ƙ|R0ZMڇ஝rիDy_01g֫8k9?JX6t?1TÊ861$u`x`C׌cIIg+'MƁ8cxz=\  Ҳ}h۞q=s.>ztSayt<;u&*{#+!*b98Rmj~9q q~4`b``J1E=T\R9~9:ۥIv?NyqyGb>lҁp2O֤})?b+x#Rc``T=OQdGqqR1FG1ӥ&r:`{m9ƝQJߥ/b.dmhx pX|lZFRt!r[UI_sEmyյ#S6}p?S8547FDsR֞W8=JחMxjr2}1@^:QEDTu0*P{Qb%_nx8 p~$f8qiJ(Prh!DIP)\` `StCo9 n.3ޅ^rG4sr;PF1J>@9@byc nF⍝9F{`sN5"=?J6Gs)wYr29o ~\d|1Sm9h1K؂^i=9v~.y*:1L¥+}h+>5t5V՜QHI&5U)K9,K }^_M4R8KfF?bqsF?zRWcO˥/t4c#9o\>Ә '4OR'8E#jns?evGǩTCnӌtZH@sB.b5Sױnix#'/ȧGFT /J]>tE0/r)~5#c y~0iF2Ii!4!@^r@AR1 #U )R;zzyRiqpi# `p=J`}i~\mht0\~TsKzh+9AHA<5#zcByaH2g*Lc')f9Ǝ3̸㷯5.dq{1a/^IPA|׿sOY1.=j/G~ 9BEc8"9_jt491Olqm:ԁXnNO|/ t.1 W#;okH:UȖ0ff+`3v /5_~կk˭yo$ cNMx.◄<|u+[k8Ivo59r~0zq_|1|7eys6kLAǰᔡБZc !̭cnec ͚/xRmSo9Hxw)to쮺|AGYDrk".y.2EX.}$~c/6>- Bm'/_:x+#2|?|zt¿j+'ەQlwBA}: G\p3aQ<ڒ.k?w1SLP&d+8&X’㚑W9VK>OMEA<T2pF;t>QZ)e!c8n:UTY\+ +gPmIy+)77t[/@! dls^үj&BR吹MrO#>3\%*Ӳɞ(b־;[wu#隦Jd{ֹ5lڢmi ={'Mى>ޫaqoH1<7&8>q IZ_¯^iV: QXjP_[VdF';̹;BjMzOPOǦ?ƩBB2~ |8JTӯ}&R˪NV@Tt_cG; iSY Z_,p^UzVa=\~QYO9o_?S^uy# qU?'VGhڜuյ TH *e#W\A4II1=O-?5eմ)8y/$kx" pd ->nGR#|"+' |zR\EBO~8HήRu*8QbyFׯBTq~i{9w6=)F=X<` ?JUOҏg!]Ol ( @'G4&#?͓VF:ԚNBfFsӥo TTj{-,W'1openalpr_2.2.4.orig/runtime_data/keypoints/us/nm2010.jpg000066400000000000000000000233421266464252400231460ustar00rootroot00000000000000JFIFHHC  !"$"$C~" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?mxDϕℑŻb82zIj_ښI?5GTI^VS,֌kM>i+&۱y5rJХS;FxO\ٚ|CcN?u+S|]s䎁EϗxzY0-;w1ܓӛ3XX8kC}4=V3\*O.BR}=39S>6?t9(P-/f d٦]fyxc,fM1-Q}\=kt-&PԕK;+y.-妱-};f{rJe sG"OÚS|CuE/t\?~/;%ڷ%hJ}pZ,|#\UmzkvE_Y"`  *~."64$Kbk&Glni[4[ȿV3@Lkmg֍/[i7ٵ-sks8a=WK|MO>@6v{HN&;chyZjL7<&HfaKV-FծeeEݸ\*~}v63.`j`"޻Llͫ~=݊=-@WdYo$:hECKS&9hT1(>x+]YG P#ԩmEb*klʜ\FMCRiz_> jwͩ\Gi>"j0iօsr,m|2fЎiS|^1s䍋-GB.i{[RS^oF-Il`g-W*-?1]+ GjF_G(ڽo䏬101ՂRZ_̿J8(Lڲ[^5g#k8>\#?>3?bpGS”O\~ }IAI҆;yp8zq@Q|=B[գ2-Wvo$m!OJp}y85)qq{3j5gJ^`U!b٬~Х)+v-=qQA▵b,Cr Z0GV䃸=QR;W4ptO,oD4kXD^5Fz֛- 2CK䌛#屜##ֹzNF~Sk(,fX&ܵw_Z*, ^xK{JY F*,OqsaJ=I=9JimHm>\C<;3X,T]:f{WQfE?܃G9F$Pԟ$Є|㎤dg:20Blژv#_?sW繭lJh~E}UO ?5޿wzoQ]r={{HRCJ|Gn 1>\~^(&.BkE،I+X\ט[xSO7b2"QG5XcN[C9oߑIMե ѪKezzV$ō(ڕxka5r7ppH^~Ӯ;[(,d:WVO:QURkKݻmm8~~(McՖI%fs]鷐2}&A-֟ .ɍ+qǨ}7G,Tl~J7?)cױ~7ſ+r=;ѕ8qJ3ϵ46j64!恁sKcw ❁JBHBHM 21E ֔x4&qv/9zQ\=؝~@G&rry }sG?&DXPNGa#G?&~ß)/&6a_#~Ms 7nMP(GzFQ|3F+19+֞~#cxQہJ(- B*n($KRXjdӥ(9Ro[jqaj/`|wrQO f7Vn,d1 cQa=~eX sTZGfjrzz;|xkRztT몿zZZ ^w,r9UVxZs_E{i ( 1}n O#Ҿ!ü>ٸr)mZ>K:Թ]; lUG@8S)Oݜӹ9j7<~p=N8H`GJsK r 4>ԪIړ7Hh\|֕~nA@ps94r0ߍ;#zR*r1`1<T_{+<IKڽ"ZW癊qոsL8ߊ1=5<:W[I/t('98#k?e_*L>0 ! ~M)8%p=i:ix0:*!9=ڕzX1ǥ.Wqx@ ,v6B6`qW|7xUͰis3\ g<;EsK`vx$N8'\?jx\w2CKg A>աx/Xn5K{{>?ۓopLKAӎJ_؊0w[uZF x loO>4&Zȥ,F mSj-[L`|(ێ8ϥ(ק/EO^o3փ?xОWX\yr8ƨU΋ͦ޼/q-A g+YLҫMK-[GL,Hbί&Gqk"ًD,͖N>c^m<9o9c0I :H8S|?W]N & 㓁z; C4}:03gE5)YT ⤉8޵h~׵"V m1`lXbF8YNe6ɉT627p=kW~Nes_kML3$E n[q+m'tO:C~gc\Tou;;yl}-7ܕr0GIզ5v{nՋ>0@z4?ΕhB} mb#=9/I}Nte atx|ܜڗ#{=t3=84)z +jPkʲGKA{qHc.vx㈥7&ESyiwh o^q֫Co͎oKt&:PgsJ039pyB5F=+μ1H@K5z5;FvxwL335ʌnsWQADQ(2X?kSKgv?>]nT}!}ʶ!$N R2R0=He( gXDgf *ܒ@[vDD;%8ՙ-&).s?=^\Kkeuu$7<hj!=h7ڞZ_2]G6_a~;JjMY䖝Abq*Yr^jBGxm3̗.P1#v>t #WL%;*GמYh6IwsEp;ܓdAT]Cq{mc ]K 3 $): piTݞtܯ$֗WF4:BVMj$VM7~O \kv}HV rXG̷|sC5hI,m=K_dʉZ_30,cjҬ$zPXv;(.$g^ 2Vp#~m۳s2&)BPޖhW}|;)aَ'85.4"º[ i5<8,s6Fچ"WȖfheڲ vNک <3_=F-`qlJB7@- Fҕ+ghZKNVr}k[ۥ}q! @^_~̓D2O#==s իZxB})o&81$sTz_[[ԥXR%c v`ܘr\Rv:W)<]eu<%$F ?yX޳ hϜʁzHrH;A=WƵ&L[L%}ʬ۲Ɏ9JF5/%%Z2vIhk[lVW6SqZ7&u_ށmٽvLtHGXM mBvYm=Ib@}TD,l%YOMbeS!R=$ZY\&c-ޯ]}`uH yۃYΰU&N47Y;}U|ӧ$ nKk7/_[ֵ{ /K4Q1$zk/:3X@#[;$ sTx̵OS|K#ˤ-0DRzR*K뛫32\ƑK,w T%Uky>zy?4_r6\3Q\͜忊.Mԗ6-bC0K2ydh{u)07>ֺqsY^G$w0s$kY/1aGj\+rv+ 9ah, /0\d;`U}tF13 Tr]D+4>z4?zZrr 6kՓ>RŶt]G.$-ir" cڠgi((f# $eH ;XƊIy^@}y0Ǻm8`Iˇ+M5,dO9F^Iksi4rEBxP4/jwoqs^DU%cp8wHp2d_oC8m}ljLS{}׷f~$ZWtOq=ACp( R2^έ&-$+o *wd2rۦbN3*3$^y>hcj7::/HW@}Zkhz:|]ÌH0_o?38Ժ  \xi@gRquDK{-ϸ|Ih_e??c,V@ ΄w˶Pxv&52G \K5FEXgGZKOQZQ""r 9lgZ|^**9UeAO+ sY{[?openalpr_2.2.4.orig/runtime_data/keypoints/us/nv2001.jpg000066400000000000000000000212541266464252400231570ustar00rootroot00000000000000JFIFHHC  !"$"$C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?cTg&Hlכz=rzS[ݵ_P=X!{p66 K'I)Q?|~{ԲGvO6e̍w!AB\S^eJO3?'.^mKm|Ƿ?ziv?_QךÚ~evSMu 2mqSU&qS7VVwpwC[$w-oy:2܂=I#^kzƗ}watbI$HVP98w}KH x/ƚꎯew+3ZN gE + 8$e,o{n*Rt#wppv\^y?h'Ś7--4UPP,oB:sWi QGWD+ҥj,`Y<ߐ+s=+J3t:OG]}>q{5-Ny?_'xZ8f-六su$L&eV(.=ɦCө '+%M3÷i.9nqyp}6Wڳַs\K\A, sjd@UdF@!|3S3+)~pu$#'iM{|{exجRx%i|iG˼ .Ns/e-QOOOӥ\# 6 !uGӣV|7Ɣ]|x?Nl"|zxwOYΙhw P3l;oV*2>aA'̾-M{R]a[UN|BE=wsMZxgr$arhϯz޹ ߋ"{%>T$gGCʰ 4j2_[jJNqҰS6M̤קsvi 5--Î[<ֶqsi,kkŐѧ^pf,] d[0u5;XOӜ@[eFgF/$ge w# ͞.8\ԋ\"zlP:P\GՃSH厇R \vz|^)9#M,ݼ-A}vm%sW'qniטo íx!o =ę ևf3ZtRv>\pA=3pWuKdU;U]ml0ژ}s^n'#bY:/7cӧp8UF xLgTmQc\?jxOŚ?:x@T<GY*֛5r:u16zxwF^VPB?: XM AR6\`2d0?Ӛ$.fVfMhi淥XKcc\ g?㩭e.[)+k RZ$HHK}I,9uHX71^cV7b}so+.y}b2}?\⵼U|IE6pFr XlIrEZv<⺊_3%:Fqr^?Zƿ+hR)[QXU{ܽ@"B+~EIiU dr)}0H:hV1tsg$gY P6@1J]<ɄwmwOtzTs B2><@y!uz'a0>=7AE#&S>jʗ  aO6)fGBkeaqSY~zZ|WxSZ2t9<霟"@YkK< 3Fo閟;3Kn`f.5 |ɧ%S1u(\zFeìbOԬ4=ّmø6g$pt{Fk9.kY7Ÿɪ|'.mثiW>,ɮk&1ڸo +WYm3_xsT?,́.1`G^N֞ Շ7ܲ#=?q :E{w}NWࡂ~Ng/-I6r cҽBu|Y<9 X-3M+Y.$PQs׎qX04sip5I2F > CQT]>XG|-)S&uSnָK岟NUJ?MUңP'gmШ9kwD-WL2X75#C)~:Ⱥ֦A7}GQT+_]iږn} ?9N1܂#\5. k5R qugx{JQO8 |[F= ^|7ؽa'է?1翭s$XjN኷dt;#泸{,uH~8GEӆ ok܀2N+~/F_/ȥ 8+puyiXToQ-A'L2@\[8nq]^fGFx=n}5G~[H~ۙ%~j۞5!9sU**KTMud˭݌g &oi%N;eR\bt>4-H%[kY\--h-AE\Ҫ݆v:X!v=K1`v M2p)SJMtwn.d!敜Mu񾩦CmF6U3YB+A"r5$s/k7RǧDGH/v?\q5໖y.AN"rcsl~qE8 hޥ=fY& y _Ҩ}#Ӹʟ'jY%dK0~B};WK ?5$T31|c)EVݷ4Wga6"Aeyr7y0xe@5yl|$zuLa}`|4ϲ>Gp9'n)wvOE^IxLяn #&j:հD6 ;D@;v1B[t>[ SZ[2xn\ryO9|]#KIrx.6eacopqMk~)[cS۵ vElҴ< _#ٳc\ *µͷ8x= oO۠l$m*E@sʺ}Aoѱ@z1Z=fWMχwK=#K$bĆ#7?㿽xS|ǯ JqNN#&,׌?V]?Ief?SUO(dϥLj8 .`֜,Fq[@ C' cҫ2=l)> q[zQq0H[%<A{V瓜@hF84{f *@#>`v 摡Q =dAd4߰z`!큌160p3jpZG14{f/da5OҔYq" <هF ؅8X2^5!pCqهF1[HHbl# Y F[H@Ga쑈, Cd,kwHa=dO)zzBŎceU=Ns++^)׏l7犭G\++j[$:zgNH9Ӱu=)Q\OX'NPFi97_΋0$ Aq4J.qCLsF9< g .Tct1J_#$;GC8 yHG'|zSCH]wap1җQ]FJ,1'v┊cH;b `1P2x#Lg^iLGNƋ3F98\ }Mr8YsHF2xz2:EǁRq:S<1==Ͻ?`5[C7AzM\;Hޘק< %,y >֠FS\ްrzc^/`?ltx9[5Ӵg bN}){c7j;vZN}kN8ս#8ڗ:3zon(k9\^פOn9dOp?S~cxS'&ϷM[e:W4,:|43Q^qs/n:9uۜז׫J睍Bҏ095@K$T&ls]~7O8;cYoSzK! ڳC JO0[~!hԾn_ F2MS&c2 瓐j֛=3Ky o(B䣓 6&?Lzr!_ϭ3M[2{190Ol!YAۏ階OKVI>gp@ yq1KnbAr۱Ԣ<'k8V" bԑ3LK~зv9'# Ú9"bcvnj rwbӦDV;0FF̧ۚ;2HҎHM۳v5UWV-G46$:?)?NiD,wLn%1Cn?rG^]Sc$I ι"'P(OF@ Ң$TNmx=>~cM9fd"ڜ;z.2Nzӯ4cs(st\* ~p;޳|--9&\SHݿ\֚.X%,&:ViP.AF3 x`2:sҙxNwXe#G up?s!Gޒ?ӢrRѕdQWŇdkRo(Ni|2Fp9mx+᫸cQ.^JyT5Kx. w^m`qhKC >wޗ<+t!GA{L9R ZWOO_7$Eaz,=s+Bﳟ eMf2]< }YkqxZdPkɣfGWRI1r8w$610XO _^= %!sy`g}<|~\? X_7$h<9b{x?5!Z?AD??Z&%ng?x?=ћ)zz!kS|ׁو'6}X68vJPqBtw$M}<<'+Si֡|-/";lQг6o iz:69)Cjҵ/AӍ}?*ہ O݃.W_d_k'0~m:{IV,tZeMV<(~_x$&_𧯅A1 'yik?BYZU|=-7Dxԧ|eUܜ.}&X|=`!O974xv %Jn.Hb0;zۯ&u:JHopenalpr_2.2.4.orig/runtime_data/keypoints/us/ny2010.jpg000066400000000000000000000212141266464252400231560ustar00rootroot00000000000000JFIFHHC  !"$"$C}" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?/DhcQSF[+=A8Wx׶iˑzr.ƚޣ!A#U {|^]j7^e0eZv=5|I Y$w8RC9}nӯ)c6S.j_=Lj4"N-if M'7Yg0$u28t9改bݮis@'G-l59fAo<mgNDw #ٶ*~WF)YmPTqYө _MJ=OE_h)ĺ4?$'X5kr }xA]]Xi<&&, u΃px_T`]zZj";-"}JH C7=F1x8*jւvNqŪ} I|K?|]8*1x{PO^lj~~%ƱΒ aTHD4LTW<* ^]Xk:sBQC:0Re}J5+N)^F뷝DK V%i+mx}HMR~QRC= ľ$nK;Z^>o|45yywwnrD#qJA>0յVy NՐ̨ߔ8?ky8g|\Etבp8W'96I5LV4DlC]_h^I~xsA,Z.rW | z'c-jQ}c<fddY?ud*F;#z1JIӌnk身s?i7qK %ZsTzr])~*|<ޙN$`2\1F)cl=i JOxcN7OMCX{l$3@F M89|3\ *ֲNU˽K~GD>N|﵇X3;3r 1 L5Z=;1mKFS ) mDJ7lV'q8kg_I4 mB@׌eKyQrTR ѐu*YQ\Ē;0$zފ3n?A ow۾ֶOgg@ Ts dg'8~mݾWך|b3o&F6s:R`j-cknO >#ʔ|B Io7?I|6'f |Pjwk($B A$dr"-l<&y .D(}"EN{ `>3 cN-EKmEJi-~Gb>"AT\ҟ>=~{g_K_5`jHfK{c(q 33qr''~ 4iMZTMǴARv#KJ\jbҽʋxRGA}9uVŸby=lFN:>mֆ Q 3mVrue{ltc!ICoC~)|6AajZ},$eer3(! ݌c=0km: 5݂:0@`FGK8b!W4#(+4$ɹ ҮKkk?Vм\xzi6꠬sc3`hҝ8b"ku$|{tzԣ^)SzJ[U{ԗA߆0-~6"+#{bB;|Ww/m$1̙bpA^_\6lR+*J)*r9G__ ousFkSC@wN@r\m UT1i$ڼvvtЫWpjImn5o> JNJtuHEcԨ[nO'n-?u$|S]Qbƽ¢ xRƵFO>1-SDov"⾢9eR+Oĭ]ɽȾ ~Vi5{,u72Q?FTkfM#Lӫ>,O _^RWwKGO~r|9 x_~,&>c" Zy7x~ЃM [W1:k?p؉RU7}_vzkN3qWve bbaž{5䧾u*~z6;Y~V;e.Op jŖ6'O>Z\?%gR_{4TCq6LiԓIEr6Y!6J0=)hqQKEW݅~4QECQE((()tfSRkfdE)6nmI-GR?&vPp?JEtW{3P`C1Ar&xܑÏ ]p۶UQ]x~!pW~&SPDାxb[- Oo"Xwԩg 8jK8`@޼.6Ѧu%AIsͶm3SèJ׿s׺E%e֞14㿗|ҙ`E +N((((URAf'w Eq|L׉,>{<,)4KضP4A\ \g+26aGJ`+7 ( ( ( \QFQEQE>//sjܽԣx2ae%mR,;H5«O7I=TD?$Z˭Mk?ldZ˯?_)1_ (x ( ( ( (+Wnqv`M=5'ͯ]ȷ =0=<{|es-U 8_KYv]L SRt5Š((((((dmWuVuKWL R\ڮwE?7}ƿRU~GoMT~+O[7Ve|o;Jg}䂊(:B(((((((((((((eKnw I?Ve]JFӉ}~7??5ы@g^\RAn;ϯ 7՞Q_QEUNU!A$kSV4+Ny'8ӋEwNp?*⦍qBUs zyC"ʞڧ6s1-RSUVZvqs$q28$`gS?Ze>q=jͷl;3%mN8yr1yo|ETBɥnsF{|-^LONK8Riꓦު۽qo\_Ζ^iG?AYmgF_ˉ]I@ۜ8OOeG[O7LNdB7Lש82}֍}kԕ/4Vq&-n 5Ն|&k8ɧ{rgS2-w_ez_:j*ͬ)\g!$U-OREO ˤɫG>9Or_aK~c֪ECf:| k[YZ]ŷ#"tp}{WXoUzzσɰXE ӌNrvv&mOt^vH}{C-Ru:)SѓpsҖMoHQNmJ3-lȃ>e#ָȼW~ӼEowfBw9_-O8?1C?MQ*̸qCqLrz<kɫSsm&$M=lzo{5-Z_OPK+L9V ~VWmgku pBO-[fG#v8,-K#H3??N|מjgn<uYeIn3X=Rӿbźd7zMuWգ/kCnpgV=ɧ^شrc%)z\gI.*FO.郢Wnxڻm*l.oe v#Îhԥznm~QVֲK]nU*vI?kj3U[9u!l&HF2F*r G;_ц2'NZ3*( (ܔk}̮aY\g9kV2 u[>;#~xJ֧% /~#g}ug3Gkuj P83+=?,_a1ե)N{[gsΥTME=ljGi'iuKA]_9i14͉\ _ryt>۩SKuXt.r*0)X}j~aiƳMuvwOd՝;~zݩ.F3(?.nzégN?%|9v/>щb;mwoʗF,^mfxI?{jU$,浞PKKai]&䇃~=+o&/(1}C[i.!ҒGЖio~%cF 3?[_֫694]>M\'D+Ty\ZbtQG<be߮vA!_LzĖ?*|z\ZhWK Ka8 W<zW(H!#4?:~a|~?@9oM}J;,}dnlˌS.-)q=%̗st 2A0[=|X!1^Jѯ%_:_O8m5 kh#=UA98_ynd}7Nd_K䳦ͮO\5gbI|ugw|Dޞ}{I-/`mtzS{UhcIe:d$5]"e& k_"(aI|#+Uv~OAntڂxU罒ًid+?(9ҝhWƺ:]9ݸ7c8Q(ؙZG*g|[>|Cwg>Ԭ?]wx~Kˈlrd "p u?>=/L 3F%" g )cu¨|CG<kP?({7Wvq 2#:Zi'GQͬX@J:iW`?j?~ %l`| yP0Cޕi[kݕ?=W1]2;-!5ÐHYè9%~PʤץEÓDv:.eES(l(''CO~XZ^̛.5 <ˉr8WʁGO5Wd24o՜XƦ-Ւ?openalpr_2.2.4.orig/runtime_data/keypoints/us/oh2004.jpg000066400000000000000000000226331266464252400231470ustar00rootroot00000000000000JFIFKKC  !"$"$C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?qV}^ УYkMRL7w+c(!K>?|.<29ß1O16 ?ku)edgW'fo#qj[G$v-|R@&ӎ3ԁn+jjI9AY~reJb$?ڿ9~(Bg__*/ʑEI\D]F&f>rk8g`]݂W|;tgE|O[7a& ݎIl˱iE$쏙^tRR_ZH6i)murj _X$gM&e?6s nb$ 3hRXjZƧk:ZfjuRnUԀCt|WV<pia>*1 YJА۟v]VH=`mUmC 7QU4ӑxg`l Bڥm,j1u޳ǎvrqhZku9uqٮ*<1,q&A <57<Oϭ>)WRM> R{O@ẇ.3IF}Ӧ)Uq}/=xH+ 4_5xU•pp}AKiX~$Tꒋ~麌WJf\_گgڼK/|Aᨾ!7xCJϩ׵KVPO^eN;3\^xJSxLw!@Ψc\+') .|Gm.R7ť2{*"{/ xsGi3ͦI}{ B$ItxVpZxDM$ 9PvJdL ]_<9jwic#{q#'pni#h#gNcϟRR&_qgᏌκMX61mvFT+x?vX[Ri>{ ]I+Pe$R=|o{0[HK`nD p7ݑY:>"humL,D$HH-ѕg'18$%LLi>yZ,vJU*ܣ_~$PkZs)^7UW$jRMyl$NQdG]R! sJ#O?)[a&?| ?a'!Vo#wގS3MP9/. $G<$I+P`+eq I (Dm!'%)-?sa#RJQrwvΣ9)>`q'{Gb)Y?OjxHQf<^Β<)"VD ?z)kE? c_4<3G1֟ɾxpV+-e_)-41b*RQ-?-qtʕ'uq8l8HҵG-ÏaA PF? d3  &zaHJ: Z]pjR*L0HڦYCeN/q$V&Y˽v5!r9M4JiE]p:IӇWoaP,QPkR5천1;83985&?e x{O[pPH|JɭUsqYQ:t*d4jC)PAꆯ2KktějlF1#Hpr3ɨn`!>kW2舧"rj-sE+#qֱKŦlm?NEk\Z䇴~ 'HtvR>|sbIR<` ҘH$I{CYծ/C-\FN F 7I4Hu&"})2cHzqF*DLt9F -%s:=Lmmg9+Zjyhaϩg7tFXomJˢ]?3Sԣ/ip_?T`#U"-m!/Bl}&VotRlM"4W+.";e_ 2ŵX.4!H}GOsMc/~%ͼٮOh OcԷPdXiɅ}>qi"X G, IYj([TS*yDʀWǰcެq(s>zS3 "8!idߵF D,'Fbp$I$h E}qKG$皷Ace%:&O3Ο!+/ޠ-l[K9/&,cN>NN7rAwcsXVsNWR#Pw?zc_Z/-VcGO^:pVU#Oֵ-.Wu oUUִ oaS5avȞuZe|/mbв=ETT6з淑G<Κk,"Q3#H:vv3ZlBly6)m2͎zp^51A 8%I#8 ʭ)iq41' Zi{ɖ$rU!T2O ]x7!bQ7DM.ob9E7b?_J2s.d1Pihv2N=^p{-ʉT8ӭ5R,uD )>ƴWr[Kq7fȨAk + 8>X'd]KsfR䏴O)Ư ]Z YbާHʲp}2AzpCJ0^!JH6>uG&׵ɨe $%SGQմ9@Do~f> ʗTuۤiL܀2=WIB#g?Uo[Arx CKꚌR;W_nkUT*\}K.KbO7 7 w3S3Je݆*rx!0=E-2STRd˖k8*㯁^:XlʕNDFHŻ PI<*pFV֟3w백#]Ɇۦݰ+0VUR居YmKO 1{ 864kYImLnܙ7ڥv|bp= :~+I|IoZGs1}GneSRo)mÏ:˩YKwO71+N:J/Vh?Lշ([Eܷ%x̴ϖY%bz|SvH]j[;& -cHB.-nn*EYmRI/,WpCn޽+;Ė$  cLnGqs}:V&aEAk b@t)?Tu{sPfh+; w۹ X6˄ ?V&έhe  :z]_viPxQ+=/R1_ .htՖ[chbgNӤӭMaDY@ k„o'ªM_#5C;>v)*64{qzg8+:+Y俟ip| h2f5Cim8}1Z4 `VDxV Q":jmoCx fY?6uԮfgև}r(o 'e|nG(9S伽HlCvSvWyܭų 3]ֺ)|FnlƛZf=p-SHo?.Ca&y?ҙqsw᲻FO|I<"K%kJU-=]IDAPy{L,|m??%F?:ֲG-#d$O[?1S>?ۓJ? 65~%lJNFaҠ? jq)| ̓ZzxZ恜֯(=K!3_?EK.YVgCVŷh|Vc< 'er;zpOjubD!>?,"B0qsqϹ͡q'/`$L'Ğ e7 jN1߯M-ˁͮkezesQ?M˨?}-ۮ[Eó`s{+j5\[Rl^0ߝ543_?A9,xwrgg5ΐBYƤzSrqGEi%2kPA4PXGsȢ2ox7C3m^TA(|3K~K!.!0clFvOoM£ռk?޼<ޙ"z?ȵϡmC @"[Z0"~i'#2yyuG;9qv^䟻k|?%vm?ig3|"357_heWuƖN??vޜH5I~so?Ϥa0]dQJ<{qр_I_4 ȩ08fCY_?jx^-f[b + ) Ns]?S^K6!I^ e/cZTӽ_q*{ZqusTHp1²J¾uOk A9\Gs:7BGPN }q MIAX;msrx\sY`d{6i٣͍M з/ cGK'l6@kZ!Y4 ma+^ƺ$.cFH>d{WK__қ7-%pOEg.'?f̐|q"]_شYgvc ⼙@ĩxWc%-'!@7&l?7k贈\k{I_Or|'?} ?MIv:XiO$>y|Qœ}{v?>s-4z|)0O_ؒcc4GZ} pH'|g)Lc+Qg(o֘'97cΌ! H'͌q_s yA`!#m D9dj݇`ʴ4#8UG嶹hg\3Jwz3kT-ICdxHB#+?GN4v#I?X!AϘЩkB%+9 )`4F9VtZXmXfzTˊaO_Q `|)DžhW$vvP{3!ܐI˪ [֬`WV[~)SvGopenalpr_2.2.4.orig/runtime_data/keypoints/us/ok2009.jpg000066400000000000000000000207411266464252400231550ustar00rootroot00000000000000JFIFHHC  !"$"$Cy" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?YIm"V ѱ  Gv0ۣ=ˢiy1یj=RLOSV3¹;^if勐3=zcV)y?-!%=-qxQIயǝ/NR+o7c\Kml;J֟z`Y俓;fvJeIsW3u׷^[Ƨ+ǿ^8xcFCBd{k8`)J*Ng8B3pVM ;H?)S;6]+&n^\id(;@N@N synֱlގ6uR]ojS=kg\?x{֐ځ1隍~u [&)}e/^_^J+kOH$86W O5λ+5<O?:|ST&=ՌkL KI$n1994\׭w% qg QZiywPfcR:mgX"qFT{TI=+ÖwK-.YA|UtE5?/_W_Ə:uCX>8m!68VIº68S荞tĊ isǸ_coϿxǏ֤?ZF$k"0d`H9ھyiw{yc/v̰?8ڤT[MQ5J&cjf[Nj;)<06a+(OT6ܧjcJfei.;=[G#^9_N=, 31PH9ǧjv\3m /պ(<NJg*P=f\v`OТdzmGIm3IT+x]l-.c "PFyKvdg+ǘYeϮ ט#h X|u\񁽿uz|j뗺܀1%{;{ *6(~ӖѿVnl G_,r~!%׆~-W|= 4LWN{vP[YI@-t;93L16+aI g` axSQK ,jrb$#הӕ{ɨΣI:,ξtwܗ-ܓ5j^> D?uFA'ۍO^w29>eZKoQR!yW0ӯX{ˈ$ 2y08`:{֊I;XVc>2'4XZ}n4PrEm#!cTx^gky?e#Ú]ᨴٮ-4 ,8,K5X|1 :~iI+e$'/'$2IߩNIt $5mI؆/8=+tGN~!7yuq |#G| }kӼޞ.@%(2neC9E\i3>*@֞5+ Pk]ZIdtY 6nb> {(k:--xtƋuk5ױ\FIAK`A >S 64q $bA pF1WhD6dt((.[E=x$T;ѯyصѿLWܺA!V8<[>.5O{ҹ[V rF~FahkϞ8!kr>)eӠy -mr~^ٯN#v RQU~ڷDT|s]wkkԓFѭ-HB]q ߕ7Kyň.bYgk7]>\1nQqx8~],$֠\no.V(:{׾kdb#*w_jRJ1ؙbЏc#§'㏋4 ->YoO1K[Z.S5dc%usװWjnN=e^ (6 ( ( ( ( ( ( ( ( (И4;].CUORq3{PcJ%̎zԣV.'u'a~V*Y}ORVxY[jɯ8EEixHYm=7;uU>~fXHPcIo;nDiI("8#ڼN0Ov?jt].rn.]56r|qTN3x,4m1iV\nqKSHg|t}q6@굘^q{G =2KP85vIfI,^QclדZ.vzjQM2nQEbtQ@Q@Q@Q@Q@Q@Q@Q@Q@m斫X=Ҿ; 斐 /s\#z{@xtDZ|>НUI46[CLŜo` ޚ}Ŵ|`ȹk 8GҰ8ZS" (4 ( ( ( ( ( ( ( ( (Vb]RI! 掻 %AqOZ٣+ _wq [`L1rK۔m-SC[ߵw1\E>Am=f8fEYV)(NVd~5w1+׊<' ]Vbcl'KdOQ[V8M\oڊ@H2OMM3D'?5FJZvZ$,qk))%lR1 0H?|_Ԗ . 4q®'@'zeIŇ4E18C2D?/k)~$iN,%G;T߀H=*AxvSjK[V9|PG `qGFԯ#5oKmP.Gĸɾ2ji}ggAk-*؈<@\8u<1//K95 wB&sŽzQ*4o%)7kR?$du4RK9?FxWx"tgWb0eIYbIaYI\?x[HU-ˡ^]Y+{E¤!lppT=տUc<Gkr53OBO䗅d!W*|3:ӴIo;=BPK^nv/rbقK)en(^ʼn^1[~ ZOk7-/b4zc9Fk~¢?J5dV>w i-X-#5/:lPi:Kx.fx)& W9:´W%QொI7G]yw0:U{ΓgOHig\M1~P~'κjVW7KXc   8I xoK/2hwp0 }~W:i>L!ܱIeO :[[#ZabB ӟj)>Q|| w<k뢉FA)9;4ߵ$_jk+b8_,pLq0@CL~.詣iNDtHJ%F࿌-Fm|":37iK\msm9䃜щڝRz">p> OưIk5_«\22pF=0+R^If錌l2c#zi>ZyhX!bRZC[BTqPs'> +ق&6KP ¾*Qҗ{8-G$d; ܜxO$CsK}M*4"cGtW-8ᗏհ_P#Z@:׿?ҏL>[{sq!Y'19$Q'^ӕ38w/ƏL^3wbhts,vc!Iofbzv d2y1{F16openalpr_2.2.4.orig/runtime_data/keypoints/us/or1990.jpg000066400000000000000000000172201266464252400231720ustar00rootroot00000000000000JFIFHHC  !"$"$C|" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GkyK|rG|w=p:WNAbkz$9e Y~QkSQ[v0N>v?pߡy~T$qY\E v:qWcUdʾv?pߡy~WOq4Ta+>t=϶ǯ\7^I*tEf6k_??.N\DVXd9WV^ ,IPi C85iS2V.͜ޫo^Oyx5=䎑HB8z'=_VBcvPy1G7FF=<;Zmn<uyWM.yh 7~^4i-9-=vRޥ <4gG+as84Wsn Hėz҅@OOzhԯ n6_0]qW~ְZ[hEo+̛B֟/ <[b7[>#7;N;xв}Բwoey7m._i(˖F`zmFg5D:K]01O><2@y/^r[k`nIg㷇G@>b qW5/xC֕.4Nm9I&! v^w諎W{i#Mr&ѼEjq!e}P9S4Mv{[vd@bUHg,80w84fSg{dbpYޘ+ 8+7ЧEihk{B:`m@玕po[jZ 7~:v?pߡy~TQa>S~^D\![nq<ֻ.~3מq]7\U-GvaK^en3φ/MsՆNl_A,zHPI<;Ջ t HN[JVnc9Enw䷰I#_*2IIJJl)lҊ(,}۶m1:Uj(I,dFe`qY{{(+3u߃1ϊ?x5baVG, O(waxdWixPGT%,`Ur0dm3s+В>*:[Hc"S <`mG vўYk^*ܶ͢VI$$򼒖 3߭txך*Ŕ'Apn+ ЏZeRV9[kNǎCo.iytXw:]LvɜÁB~'uW^L0Kۦp}qOip(XQ͸rrT׊czbLڼ%P*I2ɨ]:P|IGcf[ߋY3F%2NQ%@bʅ99štk['O Y)8AR$PU#j[6^xu{SGR{Um$[@S惤sMndŘYf<㩭Zw:VW:v&MF[ 1o5$ 02x]fei%L9P>#j*UI-U$~!+L eX.&e%rd)Mk}Kg6yg`T7kw (Eg`H ?2dc!G,i/e\ ʣ*3}($W"c5Td\]QEgjzytjoӞ+e?LW<e vo굒 !(~]Å_Z֌a3AʢHsI0.r/Y3HJŝMwy$i%r$Jm1QVAEUQEQEQE^ x4?>뵴^ktU9'ҳꖹh6Rƌ5~QˊMC)_9 zs_i-!Y7L(7 +.`)A;LqRܨֻm>ïCƳwky-HKcvp{n^2mP%$쯩xYTݾO!>눼SHƲJ[ k>ȱeq=9Wj߅<3\UkoxYFUTw{_Eic?5[O%6e*R I;rE t;/5~gY^]1#9gNGUaJٷIܪcEl$⬥wXg=)]Z؂M?B>՝=12 x^RC/d{QΫ0`(MgQ]N1I&+M4pr)(آ6t#VQg"Hg=ia$+Ǟ[pH#5eՑoB0i*HY8 9tˏ-?yi99kcV8]9\ȇI:R+=kNkŨj^M F76-}rnLv03POJGJ nb浸QEHQEQEQEQEVFķo-nEԍSpQp;zvz>o W\Η6Qp2)?{jc1T)Uite/oxv ʋ4aӥywč%tN[E/'q/ʸg=9S6mCQfSvLr gOAVfoE>؍#@Km;TdGlW!OKXxУ`+S((((z@?N>ּٖ#XlM?>rJ.94]zWF~p+{Ҏ((((Zwt%M2]A.6'iVG4/ItHexd2)18Uc=;uSq(syw"IۭwM=jYH<1ȱJ"#BwA K!;һ2y1K_x IngY7(Rp^L8GOy/S˳aۗ[ytw=N ډo/'C})+`ۊl(eQ@Q@Q@Q@o?k=k1'ZOZ O??_ 'JEta~)QEtQEQEQEQE(2@=񗏤ެT,!7/ h1b6w$gdk#>źUky#'ְ:Am~wvW6JkVU3PÏ<}q?iScЀAR܃Z o:J~lG1>IFBRLrI$qh b?6b"HRBx=cgMtMj6ō+4E* V-EJ+>JKk90w |YwunwtGm5H n}qt g:mvrF޸}7oýfQq[FS,[I),XI'>Wڕٕ| QԢYwVf[XGrYщ0x8j ]O"eL{@w6>R98$|;ӭ=ϋ,nt*/Fq<-ͥIj`ű ;T`p: 8V>K,e|;~+躟4]*yZNb$#A>g¾8O;Z$Ӗ&#Vݞ=ȯE ͬx_LӮmI @PaeVR2FZ4MW6ۍ.VYCI,!U`H`=StN(Fvi&i:I- Xȧ ґ6ut)SkWa2 {? nd̨#NA+ZnO rO >Mu)B$4 E@K;+ +>;A3QՅx?t]NH nd`$pAG+diU嶒w.pO'S9Gb`n9w响X<~u׀ z=[Wͬ ƿ+]@~<+ܼMhީu*qDYd?wqȎYNcNG̿<oǶՇgV9qnANp3֕2[I #b5':(Q@?JZ䯆n֞+S.3 [x\dR]N麝Qї=+1DnE:ancd?lCc袗ɋy'+_G1?*Uj0 <"ȏok~үR[a."!0l7$ GIyZM6Q%F2w%WZ)7%;rIG,KڗsGz>QC#gӼvϧXJ,' f9IJ[9b("NӦm>Y9WY>H* Aд\-nd*88؟xź43OյSMVX$o[o 3ֽ+3G[$4D|%+&N5 U~$6LsfEff@$qң#[y`M8'̌۸Zd_ hj I|嫈QЧM5fpj4גkY-v=4d'r1ҼoG-g(1eAY׾]^{HYwl 2x#%< LJaͭvzDmf𮔬!m.%Hã5ROjދ۽ Fd-Q m,l Ԏ(ª@1DzƇ*ʔaV*VIw>k{LkxĭT\11J_|1hu20CS;Cgh~+2{h72}ݸ,W'kK=c<4)ABGVux{ەXO-y=xW|zm-i=:+1 ܪNQ3699<1KoqܓEc:Pr*曦{:s@ ddӌtC=ρ\pA7tQ*u7m2I_- ? b@og7u1&sΉ֯5Ox}Ϛ?g?W^EQڙ; /_!vz }5K{n\ *qZg=|3ի 'J՗Olaon5W˙F̥d?' :FˏV_Y}G1?*>]?"Q9Cm% ۘc=~WV,bdh0OeO~s'&%openalpr_2.2.4.orig/runtime_data/keypoints/us/pa2004.jpg000066400000000000000000000212071266464252400231350ustar00rootroot00000000000000JFIFHHC  !"$"$C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Zԍooy0&(ہd{~3xi).5R0<+|op![fGSBӷ51T~X~-_Mqq%M4^I$bNKy$Y&Os;(1QD}iexϪv5'5µ9Ml?|aE}'}Y~zAZx>" ~Pt7o x0uZMWF(.gHΪF]InNmG. I-%Yf96wMrK&MzN;k_Kҏepu |=Bo WΓͧfY.||5]3VSIZͦEdar857?՞}I<5[Ko 2l8Hd<ɾ_F^7 ^%A|5S׎_>ǾtWX9bA=[Myޡ x}*]'Rc'Q ?tn 3LY^SjIhߟ=|KOHk_.|N{bWvZƏmO!C$f3gַE6k//}7UN֒MG)i$ۜ$.[A]y.(~g`#+ᵮ$1*Nݝq9n}kOFH??| zsC+?g/_[ĺ' &.=|?`܃WƴW_{Yg/[+SonnxI},o%մ 8aHq4N>8h `j-5VG.qovv'e$Z0 e (q('+NCo|_?N/%cl]l~Ү}FUso} VvȪ`@=S*I]:[,;& b9§5_lq8`x8(`gWd#M}OhaOIF9Eh|E} ??i᏾(c?{8;Q_DyDŽ63K }?8˱ϛ/-C;Cq8e}?cߊbǿ3 lfp ^rnΓ_ <=/kVK]ޗ{ܗUHAcWb~&5=JƣGH[I 3 I˒6H{pr3 Ա?yxM_t\GQ?Yem[v7|-i#ž-?PZ˕hn1p@9#hmz8վ|YQo_o[#_ Awuw<7ʿ)o S~c8UX~9τ23[eԽɸ!57}oGO:-XbhP3UZ>[Uz5Y5W0 0eۋO!?**cω]k,Ѭ3S&|?y;,cz&Qʾ(q^ľMdpWo<ԟWދ/'$"A(d9u/0?cnݮxEp1y9SH6?&erX]w}Z;/^>M\_i/WDkyBN@GV? iڿ>G#ѵw#10s('_yc^Ocωlfdg=b/u_+MSNo IŔ7zZc ty'n2MWrH #{~ǿ<$!<lxGg5р`mWuԙ)ˡW1 Hf'IA۩xVBE^~}jOf4lg.Wj~?/>*n28>zu1Og?P{9v=?igI௜>0Z\JׯN_Q?^ռ O7}{_"|ixkPԩƾa]Q߆v=&ӏ~4\H/ܿhH&Im_ |kc 77Q%{J7$U-?iU,I8KtsF3dp?LW5wCRg/JxG0uFiyH0}{!>.1ދzwz IS!<$8#1Lgx"RfѼK-.bIL#A Az+2~?xoZ^U%kgegUِ޽L-XΛgR| W?xž!զA$S9`+z+9|\}'WO:(X(Pgkw+ Yfx5:*WN|p+(TN)CK8ր>4m;Nw` T@~T894\1T"< 92HP܀s!M* ɣi1PG*?[Wğtd9_ap8^_ňSiy;woIO~H >oٜ+8P֚X,A^)kh~Ϻt2(dtH4W|91A>SLK׌CdR)czMR81Ҟ1}3Mڎ8  .2 M.{ғ05:oQ. ;!3vx=ksуҺJғ"QRVg4񦣭5.K2^Nh֜FH*bj!URA'#=kwȥ 3dG$F3kzSrisy'Hӯ9 @ (?.^ϵ 9zPz=3N'qH0uJOj'@-Mnj<~=9ϋc8߱o_1Qage#n{3K,X?'l9+~#/>Z5zPg$]6z&q>tkYk֩* \a*GvݏjJ0ܒkY퇳?iu/&u 5(oqs1?Vk-{Wpqw8'OV[]3ҩ+5N d[mB9e4mws[jIR=j{'K *H'OQTƷ=Nh]^븡ٖnOo헝?W4p+f; տƳ?ǦuO[3G ǭyj *kOJ+}~t$/b8nehH5V[.Ew cgm˟$sڽ>]RPY|M^-4suzo7XM%4mKXWM62ʲCnXGח3j7lf}GRV7E{G`x.x+[UG v񝼵$ p{Wm Jͦv]WC9bW+q‚ɵAMvrJF֓k{fyrI pyu: j7pr7LRPN?J񭗉t 'MͤY.F 圠?RBU_<~v^QeWס ֗?o/.dp{?qk>].Z1<;h:vmz#-ͪ`r||e~15[{Mc WӒ@5όgg9*nHv:oU#0i&ԬG \<w{]vnUQ@i{{R9'ם|;<-f3Cr80xy>6|2+)=v+,Wq[^yc;Q#r0Aj[`BMLRUPG)=[i5 P(:dםdT3PY8J6.2xdWL>nx}R"/zUH.5g6ZkC.W?(tĿg҆>wpm)IWq}..ơwO%_ۏ}?[׮t9D:LNV&}2Ia8  57g!Ij%\I 7?]Wl[*6_<JsirzsWFbMTRk!?'$?t J8lHfYYT0I^^ZE4֒LOn_C!d ؽ|3z.ƴrط}Qm'. 𭎶uAn,'HV=H'=@{6eey")#YԒ>Ք~ |9r\п~ ;8|VN'}6}: fJʗV^l{m"8K%?gc՗' ja}-7ZJ&i>T}$:~ sPR4ne%oe7ot>2떅bHHd(*%q :1Wt7HQWQniYBܘƭɼ9,7·5wtBM0"'F{ZúhqAKƑX_N?R|35O >E쿯՟@'mm^-I!^.oƗ k+Lj]3WE/wImo4Ww'$V_?}Z]XX'%GLN@^~΢yq,H"UeRT7S᝾N-Qp9oֻ*UZJ.˓kp͡ 9F}evHd7ZT|gτAi8de>4`H9jEmcmM Q UW}6-i?j+S^d2uw%=FUimO:u1 r2%m!-|m^]?RAE]nmD[[a8S^Idm̕.MCjc{b8SVJz-xrr{openalpr_2.2.4.orig/runtime_data/keypoints/us/ri1996.jpg000066400000000000000000000146061266464252400231770ustar00rootroot00000000000000JFIFHHC  !"$"$C~" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{acҲ}b?YUuZ/ֳ4[iml1g8:׏ˠ|}/.[i.,wmH[R=ZI'~)YG2k/~ ۵ȬGޙvE r@]Rld.w= ¼$|&??>:ҌUI&}R ya21>խg6Z>ac2m##ą]#k>*:w!+×j:̶D/31zƹᲓs"EY['UtAI_?3ĺu2ݭ[I 8޹3;ӯ|E[Ivnn#ȽjB!z 2@ÕψLJLu6X^E .dzV5MgNӬm&KH]Uc9=+ҋP'[4DzH\ AT OeLZ|OXIx&i+(EhX<p+iX2RXB*Zkp9CźF&y|_H[ʃo'ffy\>?|>[ڻKY魅ܱ&ZyHI#Ӏ2NB>u=zLԬt 0HVY8PmZ? =7[g-ο1 c)77{Xúǻn84l ?}bhI_Py;Ixawa`d^=̺dZ7sɥ[gD]$یpBO]O2k2_c J| {I<֭m2=Ԇ+kxt$EpFHVh?$r27ONM@zO2hI_^nTE#p<lY_?'~5'~PjkmA,Tr} doz髃M[kz `qbU@'p+7 UuZUuZr?h麿M6ܿ(i,!K^oBx_,󾕩\j,1<n Rs8'1&ܚڊ#!v>͌E*e6[&xNLd:Ma;$9 hdҰ~F$%g(i^{HH7 b_^T-/L KN`dۜtU)(MI埴 &d2̶XD A/yIftԺ[g&la=6ymm{lז\A *VT$Z.-u܄ăiɶߟ<>; \q4.CT:W]-֛Sj 9̂0.yy椿4oK%3 O^G7pJǘx~[[܎?v}*4]SX7q7ukHQY#0'70'  swiK-3HK}7β^wBg=U{]y}wi$kY'XTN t{;5"#>${9$6,T[6*˨i*W\GGDu)Q,/%v۫@Hҏhk J ~j.ըɝ)wП¹}O_pcr ޽#ItvAn̹|Pxx}Yy?cMq֊R9G2oIgm+ ՔqKGj xn(C>writzX[5l!pr0: F:T6o4AiKn6"[ xI8YVA,ZeVC ( (3u{N zvo"?U-?GB@sKLaEPEPEAwwkh#{olg\c%lsWM&rKs. M p#ծ@b\~k)٤r3;MROBHIvHHgI(坌W!E>TC&txp=ى.7gʵǦ5ԖOu8&VO.>_Ub#9bjEpTylsNŠ(jQE'lREQEQEQYΫkv!$k@f +Wp'{ןk9NczW.9ˇ܌}>FQ:4Qh9m^^k4iEzUeS5𗬿q]V,q]V$*I=k^bLOs0x_xKjq/+ܓ<{=̧R"֧]3r#W*V (Z)-QEM)HyPT&ffxb: XgJӬYRRAAEPEPE$KTlt~ѯ, \Y3~<Jj-)o(0m_5\Mu;O<$ՍGJJ+ES$qg ҫ4__- 4v,zA"^}2ze ֐I AKH:RՙQ@i)OZJ)å6>QE!<XN؊vxWk#(0*YhL9#؎ZG9FvT60][L/6jb7 (.mn'!Z+UDR5D8xpROq7ME]H5 ;α+`G5[zVO״;^-OB~UT7\K=R['$Pp{qG^wjkZvA$EU)Pr6 ]o5}7IY м{b4gI$*N@kRL;kW7~Gbq] Y)r1>o|Sdmo`̥cv/')r;7Ɗ>u][ىt/2VF]0tWC`$cJQqj6LB@?oi~ֵ+ /LXn.r:u-O3:ͻ١?G?/T֊!}}jmm^e:v*{@vZM+?`ܿףWXxK@zz¦vf7O!II>lv #$w-J|a?8{x5WaѡEA#ےv0vG+8 ujQ wg ϑ*֏{%kq=3;^|y-J<4ғiLn|y YNO班{uȩ( +'^ҴZȠ]ߗJk9;ZpZopenalpr_2.2.4.orig/runtime_data/keypoints/us/sc2008.jpg000066400000000000000000000244221266464252400231500ustar00rootroot00000000000000JFIFC  !"$"$C{" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Ě wm!F 8#9=xDʰMy gߏj4{'%nkkc\ Ӛpt)HNj^:lm=L^qco=ų`4-$䁞{j4{{xb8>3\l9Z0l٤sdxQ(;86:S-.ͤ>_e,*B*d;X %H+%3iIa z0|r#97kx)?"Q/Z61 F[<r쪫XqncشVsa9I L8'9v hxM!sҼVV#LjtKA梶 2˦䏾xyȭ=NT~ klŽוI9nBr gFޣ秿5L4q;hLXYRp1^'|@>j~ [햨mQVL(9R^_^żw #J 28 NRGVН]G?dacPE_>7Զ! G9ud׌6rlmoU (e\l {YդG̫ǹB#g ͌t*鞗IQp$ y=x+6O[5ݵլC^K<8'g5=' yS-#I&I|ƪ{WzlWeq JDQ$sq8=B樒muڄn[1#<~4֢1G??/:}KON!XfO22 NF*zͷ Sn)!BLRL_)c'9{ʁ:ϩ }@a g# #wvѝos~s;Wx5-JPӮ:@<9ghPX%Λ^|P>[g[gt]nU*vd>QUv K~O}#2 'X@;vO_oS[ku"d3;^Zêe9FdR xXm5Y_$1J8{dVH#Qi)M[3W0Dsy;gO\T,3=3)jax5=CGbDYF QoVn@-\> ˂r`^ƗbyO  OK |mLc=G=ti\(giZ`2`` 9Cx^.%0L~a1X䐛''5}˷SI9sno5J{(J<^O,Ɉ氾,q`t}~׽58Bi (}_)R^3O ǿ)3'>h[rs卤'sO=s4mVvxL v义7##8=k:l.[ CsO?utץIzsFcߤfuVrIǹIc6Ppx;D¢jvHHm_ ~UѮ@9ZXrwU23ЕryȦcmV#8?6I9q_ZjKWyt=r\{we[IKb2Q\c1=Œ($`1ϯjFs>b\_ y[P9ؾ c?V8Tw¦\O\{(uD->mYm=uEH?ٗ"PH2:dZd[!kpȶvecn1@'`Rzv:cMjCqar."raٳY4WD{m,- sǐ;n#U{KmM]KڇW+h1i=ڣ5P!rPdC{N0yh$嶠].ēOp+!;6FLvr? qJji<+o ˢj$\O['b^-m[ 19$$ ]P9^cNHYI<1:#m>^ []^-0]q+yr1xI*ŧpPEgxV%>lOz@8=sGG,ue-+ӟIQm wH 9Tl>_>>t"5*H|ͼ~>q{g󧵶[ }>* Fgґm@?ըm6s&Q䑒<OhLFבM3'{`P+GJ˕%x_AMTFfIӖs_~TǷS gOҵA~Rn.q i!CzQ䎤 vhy@_jC|Qp(y88Fօwe ,|yTU9W㌀Gz|;r=3CE⋋?k 4;/l9l&\!28*8;o>CYuNԍrFOG!s]6mVobIa*U;rp |j:έl'"O3h>uؕՑ׃!ڡs9q3'oEY}JSNTY`F n92kKVAs̤*(mqpN,N X4c>sjׅͦB>tx5%`ߧHЀpy#'?Y$s2sN0yQq#1J"^8;{U>V1Ga{RB7L=?': G>b<QpqԪ#~Q*0*C(sޑQp:dwQpQ* ?.W,miheTj'$D|a_Y{KQog1;P01V%VW>m<-_3QNaVYx((A;J d̯kX: I8 n= vMzmm pIn!@[<ߒܶKzf0$EkpLCNUO'<k%2ZW8xヂ9b ˩p |8h qZl'}LVQgh^&Eu6 )*z`m'{v_4ys{ݻYdߗu>Xo֏0Ǿ(>tFr3GہkrU:?g8+xJLkyeM-"@pqOAJzܜcw~)!i .ss+weI[AEyUut >E*O =F=\Au#^kIσBHb7 f$cos*d$g0I>@~R|xwO9rrd'n`;\ݫ,r \IT H9UQW[$M<Fy:kSXm"YOBFbF2IIby292Gҧ[uyqNtG ʍU$ZV@#zVڔ RlQڃsnؠ_@3k 2, @P9={QXj8*^)$6{~}Tr@֔]c.[Ec{$=v/ q4j# GՍz3G׎qXjR@R ^:Jngۿ^jV:"5 {uz<,89 ^jyE7+L[r{ xv_2k&i̻w9Mgw;մ5Ḿ[/p|Qqڱϻszci2l2p1ˊQvAGG׵Ԏ&H.;OZ빾lsj8=8✷_(>~K3Ib:8ƔɌ*lykP`>`H;AyW?Tt 'Ns摥_,zt{Vژ G~;J_\@^|Ƕ|x#2'?SݞHdl{We9'|cO%xay~ptս )s)Kl6 }:jƭ3OtkpK>cON|1*g.\,GT7+^IzPq-󶧢|R1H!ҭS–G#9i{sEGps 6aC(N9 yqxƚ5g×]Aoq)\e+FY2?k ]NngzӎE8O3Ir(یzgڳd񷊦x[Xcj:ePA<9m$i,MmoK"Y$P@8'^U} @%>}%嶟s iHC;`ʜ˰ty&rm͜,ꅂGeAs83>"弋$˿ :v$_Zwht;o\YkM7"Z[מvF  b>x7 0Bҩ*xl%hQV327u'>"xI!l$+FMҼkFIcRHG) }}j#+#$h6  /M~ky 4gKf+,<,>  W$qQk ss G<Hiַsq4{?r7 K3=k mc4qI%2dwg8*>2 Iš/uψN州k(Ǿ Cnr0rO%>SE'<R(z54 Ggv"2&Rdf'zֶz/k&p]qsG 8Kv IuD4}oN+7ܮJ,[hB}3> |/wi>&5 X/nd-5%bg 3] \odNI=naO;RQQB}T^lgc}xT>"LHPɑ#cĮN=Z _VKӠ9[86 ؂\1N߆궚G.+B+kkM@Fi.Xcfp;ߐ:#VkC ɶޥg񯊌d&yI䍸'ߚt9bݫNwE H~f<k<=)<ۮ KH"F`2^ Rr~'n.c&K;'ۛ[ x 27$9jx;M}/Fg\^w 7͜?4(38Dkn@8'=ş h4PJPk-U $qX$\⇗`*8J*=f[>RE6G\UtW4-%[9U%TnNWω$jV~͜a.n1CB.UY0 =+':__xw]֏DVܬ29Rxyr ~Gm,K|ާ\@b, wrD N q:ĩdk@gqk̄TK83ֶØvn5sdӳ|DM:S=zF[ 3>Ն9 r67gA\gq=BqWUJA;嫛$nEާkۦ AQ(%${\F!2qd Y[8~5@9jjS98Yn)οho0uPQҮOn ek$gu|' lbDXýg$^8&PW>^67|B`ن?JBGf?>ds4֖Mo5\Oiָ3:o>Poı`T6:{q_pL(o}}mY<-u'IȈ}q~}#V.'PY1qzUu?C]\$O&GO?fSSPF9ci!#Akurǂ}L6tO%Ƒ5nz&|xL:춲 -e>do,I L&ݞwiZ^SPƕz\\(9\n%Wki>߶;xYU&ڋ [VՔ1HS2d@w]DžD*Mjifɽ.HVuqAI52nP|YUè<$4jE ThNwJ *r*x"0} R P][e/!Rm9dCyZlZld7Yy㚇[X$h^3{;N wZ4۬r>bT.U%,1_Śm#TjYYGez2$!CT0 +ۻS'M BJN;ϭ\k+!'Zo X?Uiza'y 2UXqyf.+tOO}gy'lPCbL,CIdp\q<O(~*d|kZƉ𯈭?E,0ǧ'1T:lndޞkf2|qSv9ۿخ>߈+{;sn^:yZoV!rmq]Ƈ9%[PW?Û 䘗$+3Ա2{<-bFV_?{o-'|'7~|ueݗsUhC~#[ tOv9YUÎP=WFeMA*UjE5e<'ǿ (}&bsq޼E0E}DA^S}Ox~;{ɒE>KHy$0<}VMx|߇\W/UG'WC?1ֆF( =ÃIHjxV:hO57B2C=+- ~`!`{w%-&ʧM#9G}.iqVE'}sjaE:|VWS$A@9^т2ps_Qa!h"8y;?~5*H"Bw${ǧMH/"$A$c*x?x?7 ׍w_ˉWj(ZIݫwa5pY7ov:h uK vs<\nOcfٶ:|0 ˼1LE|jW'+)P8JeWIukg89c-4cF}pI%HX ՜ I*{TSI:A(s;#UTsTyO66h݉g^}v>+kt$vZ#d/8ޭz޷C2.RyE>[˭,-ǚ1ѦHۊs&ݝUQpΥ#bwʪO#tc~T%|zF؅QH]<,qaGqJ򵞛xt]U֍5eHڛ?o3D-IpB,y?1j='1rNpWۋ51p v/];{OJQ]Z!A魝~;%2x 'w1ܜjMgvkxqU/+N-Fy&JbWMvRqNI皹sR"iGtBqSZ\ܭ܎(˻}5.j:Uْc9 o܏3r2|9¿ <=mLcs1ǮNJqyg+-Ok q2Pz_NjpF O W_ t;/-4kqofeX3m$oI_Bo\ ,kX5G mECsKa+c͍ 9+f~Llu_^̑#lKdGh1Q{dxTU6Jz>jukio4 0vQ֡tĥ*T9nDc.j\xZ+dmő?*4cTLW[+37=Dm(~@\7@8%4Com̈́'׊M\j44Z:w> bYmv80KU͆9# u>ڮMSQt+ڪI$5hD#>-^r)G۵=CIq\Mf3iFOʬx}k(T_ Y#B}cuvdH{j}:hCHr0G@gb|gw^YW1Zp߻@{g>?ΣU޽ (/knVqf@1N߳7 LA#!@8ٜ|*'śWJxWkp_΄ҐPfqcrs ~F~}O4iŒJE<}1lܰ)VIBn+w /!DRlxZ$v XiSU=+]ϦVyo/jQ5.>QyE$瀭{R|m-־[hd`;]XAro q>5Vb+E^(cYB 睼fQ=ÄbxAjΧ Hb| z#]aXZͱ'*{N4>H^?-l%rɁIcƧ$몈 tRC5[tU;i"?l "na38[Lz9O<jZeܬJFG4I$"mϐm8pvTp:v"m-Gp"F$'3޺k iI.f#CH`O-$p֚V zNuVRt9#cY\麔w:*yg F nr[=\uéy+#nkEc+~' (o)&<'9)8#߂ē⨙>B$eSm1IWo-\zO W5{gO Wv=Zq #L/>#[ us8bz=N>{-9 >̂ c/x_ӣT5k{Wut(r dNa)Ŝr*Z@801\\/)qs"èJ v$#u0 0펔+e/6aNwqg7(7O#&tj9v/+c2F"g%@/ӯ4ȴ`1 (,􉮡Wsar=W2%FH\:+duuJ6{X{DaX.:ލ*>6qV&O.i⍶5k;Rmh ӭߏCҸt $O\N:[VԑN+oxiؑS'بK/= cs[SBq5qUwmqm:#ȀlH\v鞕k:LhHݢ,jJ_7Pǥ%؜lSqg5ZY1p1'60z;zW)]M%90ĖQv݂ݎ9`xȮF+?UO?Jc->u|3oSjlS"@=1C`ԣayr1b$䟭mͯXj.&tp=l56uq/.pq޺ i>gM\;d>о[dFcگïZۙ^!̋A8 +/ jW,/#Ve :6|;Яqbխr,eXSPK+a3QEJR62Τv{Wc8N8@u;Żk!'ZF QIgs<6{_Yu+tKxWl:ĺϜx[%BG1l/?/|DO>Ka0 p_j?hN~-ݱ#1 GRk ;a$ZW/E@ WK,aqHۆ>^}  叽N"iilSyZg(U,hFt"c z;3ө?(Fx40PN9zuP-ZH`rH> "e֦Y7sqH"NH>\1'ٲgo{+մ A DyU,k;6Jn935-&k<{W 1t; u}$>tS : ȬcE-nt󩓸╬tȭdrۉSz XcR#IjȥluCTn;FJR.Xµ+0srG\>sim -I=\rֶtC|t-1X&8M:u[URK z{M^0v?֥2j)!w۶n֜p;}x*ޤf`D'̲/H ZL}Fs]TGCT0=2Gz6+DAN{TYn=)7+> B(WzRvAg59CJ?<-!^pXr,l=Es/ݘ?s`/??~"~-Fʗ9*WB`|3h Y2QO/x_obIlϧ,n#5V#H6 6Yxrk#;c$^RLlꤖH*>4t3tZ$c 4XVs\֕堔1r= F'[?6O"ֶHS<v#&3&jRBFpKjkM4VeQAnQ+k@&aJ5sS)5 }^#T޸J|РǾ+CYUD=P,vKRYaQԸwp~)_xn(^)ZxRYkΦV956+pFubyϣu[L9z|>cstTxmbYe"(>+ u%vGi-Qh"ij.KƬg:Vv[:i3#1Y(+ myjױzFz1-n.8?Nq֗z}76Ҷw'#ҹ铏 x^;\OY[e<}ޞ =,CC<~^g`*ZwO޽k$B *׈M=C0}rZo.\hG,i21Z0695C5׊ìjb2[+rUc޲}m #]Kd*|Reuq(##g'i:MH.8zvL04ɔ2T2?g> ӣ:Cīrqz#ϒ4{XF}TԦJpI8$U"508Ԍ|=Zo0{6C s<< Y,O&l U @z̆ ,LzG?9!Q/qhVw[ɜzqҙ><#bW]Nݨ0Nh{XV8>e-{?|zG!m,k{Z35R bԣ*οype#X+p&k6\x^Ŷ#Ԡ!y#t:A9O5ƒE d?՟aG*qm&5AIݧyK#vxtI|!+ӧ-?ƽ1lV4bg?Z}b??IGM+P#?֑k<H'I9VfN?>m<Ɵ#>}^_yxW cNGTVUfz n4W/izhZ8m<Ə[3} ՝(?T(ԃI'#.|g5XZ}Fi|JTH|H L+HUoVGi?kO}G2߷?ĖOн*v[џ77^4RGB(@N׳}y'حu1/I|do`XJf\ n'x_~8:@[߿(m۷x9$޽VGh?^֝O۽K#0~.Ѓ_MdI;F3x `r[<GКO[3}VGhUiu޴<|Bxd3nȒ[xԈ>7Pj;[(@%o;?7;I$g]?>m<ƏmP/n|my+'-<׭K,Hԗ~oSU09FuN@ -|xbg?GmiϨ?Wom/4Ɵb@!,6_ZzOح_4.ޣ?v"’i/ğ)xν3VGh?YMb?L>'/)%>T'(#)Ķ^lm}y/}G&1 şU`|GiTW>'<ekga,-ֻo4Qʍy'ح?. 57ښpnu1z*.N:u=[uAY ThEbunjTIvtRaopenalpr_2.2.4.orig/runtime_data/keypoints/us/tn2007.jpg000066400000000000000000000167431266464252400231720ustar00rootroot00000000000000JFIFHHC  !"$"$C|" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Q%wNZz 'wX$<ֿ%6͜bW+Ź,$k-JmŪ_݊O.p-+C}ɥUP8'=z03+zv`8{(nRq ׵.wz ~=?lcJ8lp(kW `mCǺgm }#j>E .X|AGAouzW 4m8:d vS)՗q4Y-C]Ilͺ ]=k* ١#F`+H@$LQr3T`#l?Mh7.|@E,5HZL$ŤvG/fAt4'')D Z~[e18ی(xlP:ƈ~C+ /zrZVaki@Ugc3T3Y5>k'\m>y/$U+)W F܃ԁ`u>O2}12yIu_x0<4DB g$5:.s=A#rle: ˓p@$dEȅ[i^)!<{Wygpgv4LRuG 47g,g[F8'Mٝ'zk?Hwڝ5لvqy(+ 5r=( w?Z#>&p={)CJ2veWbN¾J \ПxcWR~K`}v$_ljhӮ=ruHP 3PF'ڵx'M_촋.^U\#畽z 2@<{^u|2vx~#Q[F,'$壅A1f#vg͟-wjĬm%{s^uO+Zyu7s<)\hm9<0OZMź>&4:Q"i- aCuyj0<'23lɤ#9#!{ ix'>*xPNH?eAf!m  h=*OC(__^o1c%)HUT Wk⯈ŤZNi;]_Y QDqv6@3߫sRK3ADlZ)n4G%,ϽgH/ǖ%5 `HvY%b܁Qi| &hkR~+vxp,4ϕl%N@6sz׋]|]#~7Ҽ3bQm&H@7-I$' N9 Q^_ZnPF*hVwh6vؕXRχ<' P-.J:5ډf*뵂 y#$- <^6Қn?nj[deR7)̐ lN{ /LkNi2ܪK'ndUlkҴO_ C햾Myt\B؈b*O|6i6iϙxB=V#}.E"eE!Kku?Ǚgw6~MVO%R%cøְ P(*/ S=#Rv lJEV=9?vpy5=qFH R1+}ޜd@2i(t =yzV[bnARw:fsJH4O`y~A mFqݑm!@'>ԴPw-֞*Fj=9?@N4'v20?> ( B@5L)x溾?[Uşh gOERL[V7D~k{: l5R!sTxj- V!9 {tMsxNe&K;=@GVHta̢:')Zc47qu(hK6t}GّnA=1Re8k-G 9I41"п+k}Toh,sn=nFԠ f.vՠoZEOG} dqGt:R90+*^*?^kR//‰u"&UbPyp泧W,{.H]F9-eN{(/p{*ܐivWds塩ހ?$[3gـ5Oh10RqF4Þ]͘Ađj-joKqi9bT 5XMZEif@_b D+F0h&i"y]3oo6{,Ģ 2T3XlsK<ܻqZ\Ad>bddʭyUNQwEuqhk溝W픟i?+8̱l=ɓUس, sU -cݗhhϹ\xUI$#.*)#GkNƸ.)T+;`gu5K!*,.WFqP?赮2ߥޜ ^߻'( esf>!u{k nTd$6r=+ۉ%F=u+GAW*|'=wd zOms Ep}:@#OGtEDk:;r@@1\zhn@'"YjdT cֱW9Va<~f;-=GaϤl?ύXNijR}-t=x?]ETPs8 (]"V"3\NF1r <{?ΑrϘ1ʕt7Go5“کͬDAcp$Әd4 jWss*WǶzFg"nk'87Sɢ/GL7jj\/yYw-S6,AcϘ>ԚtQ8٠ʤb?*Z) E-:O-V 6:lO3 珲\| 2h@[赭foM_&- TA}ZyGF&;H dyXgHxDRIK D#0c 1Ͼi[h8(-aQ܆W)0UE3ldAR>ģk:WPP~M$mҴBYc!Y?ZDIm:du3z#ⶅY;k94u'lp5hYǷ^~=uذ+np1]Lk.NJڧӤ_©ZxB.D)ǰ_ĚSÔN}5g4QqdFaiVr^%if1с ?3V4x.}&vjDQv-ۨx+] ${r:q}OҁȽ|?-uݨGa{88Cbn^6>Cy92qA¢TշFrLj>ΗX.q<nh|:߷3Z54.\ܡZξWiOq-=].KHnu].R$Glw\d`5*_iڍ11H7 -GM`:0> EA8j*]jk8)n|RUkx ʛ=)n##Ϝv4Ѣ[|e]Y' O6;:cjX|xt"WDmLWK~&kIJӬkg,c 5lѝ 0aܜ3x/ҹM>iسJ=I$M})jy)Ni[A~>'>#RopxZ7 h2"xpp?ޮ 8(µqϺiG )6g24 DR}kf⫦m d,z¶tO"&ק5A> G#ϟ_ö+O?Ϲg*4ㇰUL?ܷ]%u5ϜĠ!@宆8X.sAR;D0}%$WUUG?Z#qHOtP(r VVrrGlj0JpNнVϛ-UnVoo 8@ڳd{Zk; nt(̒U@2X3!2"TEuw#srsTVTi\ MRAZE}Jȱ]_s|s*._3h͑ :']gx4|D r,c`Xv5os-y0̦( dN ~'O׆<[0Dfħg`~fSp9Z.ԝ7[/>q]Ɨ:_ $YY3x=pW x?[oouM͖)ծmUQ`1󿅶Sx֚n5Զq:GpU_28a7[/"*JSݔ`Ab,jf{H^;SWy{אk̴kʽ΢/~x,3Lӥu¥enwc!ZwfdGmS:Lڧ{Ԍ(i.e<T,@ #yxfE*},2pIT*t(=$ ,3͹ŋSM#Gs$xz7x&9kմ'6Ŭ#r']XfPUi##p# 1[TŴi_vyEvz,IDž'6qֶ;oqB] w8 'O3KbȄNѭ֧CV9E-6i9<ןAҼ5_&Y2Ps+yK_8u۱[Kk5Ιz$2%)y 6G~Ou)ENvjjZcgĚO&:ou;ǟ O' 1sQ*Ue=T z%j7OZ8{uI=7cr^dƝmq{au,(Zr97Ǖ{,=rx#@+Ƽ[>+wjqxJuZ=y4I澲P"".Uc~|mM6Q+C}@2?4"4F%I?,9@<{˼h|q}gߊ!fa'c~ IfF-%+4-8j:֫sR_]>e::1[hkIo3=I"#SGay&F@’8V= ^rV#tOi-#ϙ&;ŒW^4zx]}1ol؄(鏽z]­l"ׂhblI;OК֚U7m %vV!u«c '+ F4R5żmIMۻ}Ϥ|h8 ҥ(~%ڧë/&m!O-sӝo_HxK jZdN岒gd>]!dùr+ZnOH<,<;gJԿ st?&|9c]vJvܫd<9ii%;)B/LoiNy#)V"5<9$UQ9=;Z<6x}6}{p0tek \zm־.jZ[_\[ʃ͏0{ny6[c:\nȱDD`D񏋮t_wE ?lfvm=1޺5)Z++Z/lPŧCyI*Xcկ:5ꢷ@Iwq=a!$ŋ!^}J(JY$5 |O}oΛ! B&SY <+N4sofORrz|^@}2(bZtuE8N-k:$ ](k+yS#8 +~~.Midd?[$`^<Hrp+s"*=F2+J<۽„bQ*?#l&$;Wc(4 ?Y86Z5\ݱ尸 }qUk K<+2OAi~R:r9@~l&9BK7^ ca?u`=<+,o"l<*{߮kctdenAUz5vy8m(AE`" o.u%2ܗ7 44uaOy?Z˹lJGtӫ}9yܐ3_zW Kp.̓8c>~w]ZQM,{[p{`qAf}i2-.dA% $U)"5F>ƟˬXYigj *=:ִZq dV>^ʾ T`C{cs5Ŗ[MpKO$w,f$X9b EQu-F#$w7D {[dbJKov5 Z1l$,Sʰ[XGqGHCԯ°eK&}B?tQYdFJ)eỴVꖿ#XOF8M#qyJg!"3) >3Z s{vY_i@ C1Ij.rO?^a .m#DHXwҢZO&Yj9]JȤqW "]O E]} qlj*W2I-YH[1~i@+g9 @<;z aĂ;s\唓KJl8OaZaj3Np~5`1鵰ψd\˱?W?T.GEjL!NkòQ:ARGb@1If 98?ʨq'գ@ɉ I)pqk>{XiƟoC"BS3ޥKEy[C)9$ dǒN \K uwX#LXz aH cn*y_!Ya(9-Ph#XƪσO{W>!m<h.kٯ]%H8S/a^FxԌ2+/ox0YѢyR07F* z+ V5c^X<~r0\mZѦɄH BK zotK-/SFL^,6A]Ē9BpOҾZo|dGcծiశgvۜ }G~t ةks+uq3kW`h˝^VZsu5@p ֒7_!|I'/x__#ğ*I?|Ka{ԀDoc]p'׭s@fPE (\L Z)3K("$bN;Fy IHXZ2(h.hdPFE3Fhy4\3Fhњ. _$E/sW/y@%Ŀ@<M6umKVu8^ ̒8ʸ`U5e0UL,i\'FQB[NJr~+VmGRĚ<ʀf6K1\\&Te?"qYc6Şt$L:q̫qB7({RG_,Gv/ h3Sb 7nץh{K/:tߋt~r?3|y}e6m7b¨ȕ7[F ]"=^M Ҕ9AAS늜6YpTH۴U1V{EDZ wT_ j"2%Y{:sָMڇlt?-/5;]80C I7omycd#[^,#\JbHSrV9F)‚H ZSmv_>5]s̹ 8>@93YV MVpZhɔ7 ld+ Byu~8KY9zI9gj+g Lzo(Q 7m185a{x":q䢘Amlvgk[*>j/ೋog :¼: xi)m!R?_j^&fukJct 

UGnW|eX jd鰸6sUx yX/d-|TrWZsC[,$!!fO:Q9ޟxΡCx+UԣscskH|¢ ɑ>nAh񮏬kڔ)5ƕXX$_ߨÌmg cn+>E+QX?ⷁ_ퟳ18.HufP kFPug (;)!YgJ>ࠂ*v6 sjg2Gv3 X87?F.'3%ƒ;5'tTpF~ >j>  #je,2q 8995ῴKfKny#i, {\thө>KNI]#H}qJ9雅O%qOvaYTxW{Z*L|a|WGԩ{Ic, ] uὺ&,2CVeg^5g\Z t_ִx y2}N_nƣ}$Bءe%=G|rSysW&m[8iX\Ip[iT޸Mb$4qS,-e*ytۍ*< 6A=jH= }}P5=KzN>eb2ݠJR`|V«0*I:¤٩Ł=ǩ$Uk9oD+wP!W9*Nh"B0\l(+lhՕβ;֗C~YޙwpDcq0}meKǩtTZ _vSN2K\k!–HU  "X˭.?տZk bsƺUNE3;BqS)iX%J0@<܎jYћ8PnTڟB[im!n+ V65MeB 7s4[GaxzB11hyy8bι;EgCT,.mt HҦykIQͳDF-~flr,;s-YMzݦ9?Z?thw|gu[Y:7W]7GI#7̴gƺoԢnT ơx<x#Һ?㖵kiWOCi\^B\ANI#vF2mS_q7ZޢWGx➩-i]M*iM"~hDJiH261) صϩYi-ևs,\"1A"qGd%A$ ^>Kgœs6sҟeHzςq5(OXJwt]?I6}wgrc2ZRp$U*?59 /ctc˲|^6j.+j_\@+bbXiNUrE|'^4byZt1.v xv? c-S KM =@gdP &$v?^ Vڻ,x[sտ&8:F~K 3}0?^V///u/߽E{sϜeYL=+Sڱ[h?$9RvTþ:ӖqR쫥VTxo5Ka۟ǡ4=t-/W_ܨf4wdMF65>(ej5|5c5]*nF`zo֠-4k'<377<0>q3y[JZrc]ȯWF]_H7?LJFעnO4w4WWf3۰GRrۧ"{@_qYaK bpH`A6ifa~!z_O ̈́X ebtd1N?$@HD`2?K$5;|9Cz;IO;bثWuG`qN+s+)=^ćw!?>E}&Of4?6@biI[-5zq /i UfA_B iqEK`m“xo]64p3i+߃^"Ӽ9≮ui+[6Ն@&?^/h\jvhaPA9s <lPC~9+BJ1jxyS߽>#D"bKyl4&)^"F`;WϮiڮZA48Pѱ1= bVYvڻeF):|m54#lYxS5~:Z"QCshQ+Jkm-ƙ 1vQ =Hd^a"8w|oģI7>}WEE=^c}QӬt}2[e7]t3Q7$ny[=fGuO0 gS\p1J9m#tUdX!7oIO(GB܃c_> ^փ>CZ\˿l~ b maom#E uˑ>>ɬ|C{ly0Ox$"UqsV~/{6,{zֶjzyF,,[h3IĜ9lWe m(G6'tg.tYB`\?}ngu=M-XMGEm[6ݮ`Xԕ85+fgcx_=Ξ"+GNl)$\K 0a";E,yPqN?*^Z;\ıg: v^ףI %ݔ}=G+x0S֓>Y8bo~Z+4|>RQ|VԹaы_G rhړTI,?S]GB7yu6? mO*3OyJa)W8(g33*1PMi<]Ĕcʴ+[C "lD pX&ElndӮR!Ԕ<}GQ^DQ0J  }kyCS̴.RmcRrj;$pm,/ Ay-ҽASPۈbḍeԫ+wrsK).S5jM2B1OC ?ú-ͺwi_ҽ7:<e.W3tuXYJ2e8e#q]x|UB Z^yq}2Dnct0DR~2*U&v?Eҍ; EXeYyW;Spqq~ѯ?'dur|%=SHEIw{ˏ "3t Is'4m$97v)u/xPgƶ?7Gyׄov @b0k3#}\pjg$zyTIGҤ?/{?ƢOJ*@(d(0%RwQ@k;V ADoiRJu%N\vdbL1j q mA9 ֹ܊(]–vytx/+My8E_c\W?B2z]B@P"zֿt C'XEHʨ+O xPv7ռ[PQaL#>WQG4E Ww aƧ֦(;'s D(, Q׿FqN`Ter1^qi3ΓIed9>P}S3kr'G~M8V~G=[gwk% "vO]N^olZ_ƷoL¢?T>&i\6/ 1\yAT)϶y&.*\v¿\W-N1][`A&Ue![==F8,ߨ7 4VU~3M"Rx2}OK^ եWjzR?gm>4;vFm׎AY\JjR VmK!Yc1/%'Xz yQqqo J·ޠUBQt/ʹA^F/'ȣsZjM"BWS{wb8$UZlM%#5hSkSGtY9ɤrR~7_G>>U( vSF%WQOgO{goRRJˏڴtNi PpXkѾ&Jn n oO,f#:Uv21==F;מjK i>Y$X˲Oq*d<;Xrj Y`<beIZn`{Q3BK+u;(;Tz}Ir׌|1WZ\C=Ch+YG]Eo_/FuQ]O311_/\ċU +A?P fxyE! ,<J FQx -P(>EbB8Z&a}Wp+ݖO Q\oݹ0^PԺ?ɿ!X7toVu o8HEHXZvsqmcV*QNT#JS1\4LЫ"LZG ((Vca~Tm3tGjj,ERꁫi{ 'T5+ab$'J#԰U\'17Z>M8*:(xM(4hVo4u*}OR"Y/nIo\5}nH-tבV}qc8S\I*>~hS)#;mQRIcm;{T[MK(x>RLm/Pӯ՗]xd6z^g:]YAw'd\x9v0Y.a`7c,RGs4EUQu{מM2LIp _º8*#IngЯ)F.-^η"!ǚd⤱W/y$ MhGx*4k ӏ$$iUBI'LOM<Ii<}F1G%#g,U-V#fmn=F=: la*Mt.`tjl~"@/rא5Jۭ0D xj%7r,ѬBEH!MHf?1.JK޺JѢn?tU39-. Ip dW`MncM9tqBNsʚ짊f|;*כ:mֶ; xv9ۑּV5#gp"z9յ?:ϰ!+Zc{/f8elWY' wdi)o)s>ۨ+Z%J)ԽPJqk][)!X&JץE[j^ w7uKcE M9"{{ۋi2# u`km9LZG%Y%9V.Uu4Fy19ǀ23bI>j;{Knt,څų|ƓcyNO5IgMO?˜fXk??¢O'8TeU#RI#RR"J0>A'4!f{y㸻Wt|k7m5EвV)d1,&j6</.6eʟۜcv^q׌Vqgp qs>JUrn JoۻozC1Lq[?x L):ʎuQU-m+[6[X(UUsa2@ TbåⷆoguP@gNmAS5 ;C3JmuMRw{yzOM8!G ,I8I=:;(QubDrJ:zgzS8¶?1>­exQUtcu'x_}=ͬocXnbIPFƒocVQF1(UAc[ ڣ6 c: ֢WR< Nsֵ{+9l;{yFq8TP Ѣ@g;Ht] Ħ sY1IJwƔ<ksEzATa g:d?#aUdmVyxhMfo{AK26ڻ]Z_uondK:+쳞ck+w7X|8  9dܨMo(yaj߃}cNyQ"BYƸVʫBor a[4openalpr_2.2.4.orig/runtime_data/keypoints/us/va2003.jpg000066400000000000000000000177301266464252400231500ustar00rootroot00000000000000JFIFKKC  !"$"$C~" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?wa fap99#5eVr펻p_Ν35rE.?JG<`gZLΑ ?s;y̷$\Olv>\& pKyweb;b$?1ȭ_ʗ$S|m7`Κno:;?Jq9(A8~v+9 n3~}j7=1_ K{C[ 1!U`~r0'ɪ)Uk{ C${r! }7&ǧ)E2]sN-G^6zaMbn`(' >+c^ YZkF^H)?->NEi,%x&MWsMI}B$y*OָHh:Z3I py=+aŸY5o ߽ps c# :ppx5UXsIo^R~z:cu[.og9>O>'{:*$Ӽ?֨z4*ۂwr9S_tO꺶xovZdnl`2Iv{=tgRbkҍN| z5>/xO~jK\X)|wavǦwtBf7#R c;/rSW7t9ΓFx1õ \n}z h|Aefy~|0l*(IH g$㟋N~(ӵ:R^)FRD2m$C>W|Kۃvlh^k@M]BIzMVOr^ͷ[Jѓܜap t3O xŚ75at0" f1\NȚTjUOMNoo _^Ə^2nn>cuI:ӊ*GjrK{v?1nLԋ{vq}c]7+܎_/JڼKGhnq8KM94u;s֓f}i{E|&I%0+))>yihߨay'?6]hH{Ei>Mռ;ۅ- t:s#\WNV`OֽY>Qgq֢vܸݕlwDp > s| ,Lu ~QF8t {zIy(# 7B)SG44y'mZ{ jZbv+>i?dE-u6$3V ~;B{}讌*+ ;3_/Of?F5xM,mg9b3c1H ѭ$SW궔WI5+ #U<+iM2HH=79vk E ߳|~K[iڎyiXZq$* dZ]-nK9tka37 N.HHS\ru"w{ס/čWKZRMװ\ƑC1$yO)0qH~Ͼ*akIY,~BKFćwߐ0~\|j΃so|g{LUI^;6x#HlCؐyg-|WO[YԬ.X3)I@"mn 6N=tTF-tg{ +xy$uFP2M3l,f'.ǩѾ$|-־%Za{w'<wNY%V ɻs[x5?x!tWi5 .Y*s%>xSŗuߍև/t+&H,om@+1H ּ NXEyi4׽{Z6* ##QE~0UԦ}~߅s>^wōgB{ 8X dEȓVR2r#{a]gH{k'ҬOs4tz=q13 WbC !sOtZe+o,(I R3ՈUcעT%^I8'ğxH+}Z0@ໄ adp=/cA\O}+1S,;hZ!(lbE J,} E}ITh,& E*fE*3pj O .IJRj)KM~DZ|8o<64AR84 gögej6$o2i v̞0=0֭f; xg~$J%B6;[ͷi 13OcX[^AiKyvC%X9FA:/Ĵ河Kgw}E6s߄z>q1Q_},OGץ/ {G330+[dӯ'n Y@ eUmpv \L >.^o mj&$nqp)/$f="=iqMT#"akHظ#Y?u(ZJM$|^=8{":`OVk& 3MWdKqi01s@)F2ZDMx 0{QsA>hi1A(=@A恌ccQހޓh23N(ېy=h (JQҀsGp=ۚx\v# sUREisӈ=`_L )Qd8tB(4$-LW,tǟ[V@3GO b&h( Z_ʐtO/?ZJP=I~ցӽ#>}i/8Luo#H=o}1!\7_8?ڷ[Lf&'SOӍDgtAX Vo]ɖ֏ӊ? BHO)G^4 9g#ii9^}hhzg'<#K}9Mw9f8<ёZ:4n(9@u֊23@P@֏ JSIa߭/9PE/jB1?*cW6:ϟ(L"u 5G<ĶR/y9_7n_7kkO#;f p})4P*P{#o\=Z8|s4Zjb5XLP19?>yxv_X[ yوPΎѪ-3OQkKUdNU Q1#wsKڼwՃb[G@u] I]Z}JѢx%]5߆c#t5kfZuKaxֺ|6#D̲ȭe/S,`je&Fѧ)q]ݩ$ZuNgOu2-#ky̵I>JdEejyڑGS?կl-&%Gu+7je:>fLyĺey>u 1vP;Ht7ւ+$T|g鐟6+W?SҌՙPQT]q煵KDN%AmWJ1UāG 0>F1Yo[kb- ͩi-ڵzc4g`'+3sb9 `8O]ӇSjb$_r v}r\WHVzzWú`kSӧӼ絓O伉8bFW F-%;[lF+9թBjk/>Z$2Xeyky-{#(򇗍x2q̞lnߝ U2u=;hgΞ ꮊz5_q:փ^<'6jBPXp1+~XI">k>hk"s_4iO c$qH&Ss򑴎M둂~[Ә#{VG3~wnŞڙ>/Nm h$7#omÓOGí;ZAtk=0fwwv$cw^9,xR}}TN[?oe<>_sH8{m^i4;Yc Ddea+5ODk *wvG= }G,7ݖl(9'?ƔxUya4 34oیwok_nǜFkl<3n"WTzɟ1v9՗m>&xƑ}賶 ">I&8GxZQ/q4g?j5Jj gb&̬/A`0O\L9?L/]˽?G]ypЌF@F@9y3/i`9͑j6sP>>'I}=?Z~OOI3Ι|woN?:?5uԼoqbFp2 #W* v?z]/Zuo`U*$,IVù+04&S*ǚ}ƭeoon"3A>t1۽ y8O=r;V * ^J ֵX]&Mt El( O9Լ5sh}`4{[+jU/d$ Hjwg?/]{jrQG24 CZiW(*AF1|1Yj:4Kb˴g#G;/gW[o3O{6&=g?<ę^>>\^ӯR[_y]ⱞPc5;@ 16Dv+XrL`1Z\:_gyy'?59~ x &z/:ޥgmn UYїY>bYH1^)ozg]n- &u spy㓝¯Y~'?88IƏX>>''"Uz/>72RxXxZ`$I]R###n 9_Q}Hޓ{}n[V1)]3!639pUoxvhXrB+# x{l%j[& 1"n\ p W!DZ__çiڅڝBWa\lu¤c':+{*|y3 ω@]rOEzN39s>iӷ|wML:c'P<8ljs\u7?ީY83;oL+Xdd^Uzd}*w4 &>:81'7U0gg!*~lւķ?O3g><xqӇnIru?@=zrN)0|n{~+m4OzMѬ^;MsldG$LH;#i 5o xmncyg%',t=2޵uRweg87?Sղ@?mh9jutuygGv_>\X;c\5&^h v2+m@J&98<rW2ms3ޟ״xZ;_ikiIoo&rN=T噐%Nf[chN4y,^3 RHtV ҽ,;N+^a/̗YgFUP>PWqJw61?2yq/J\d?JMy.xTJ A襁 zp;bgy'gi%V,9$1W%aM_~ wSY2{R]-$A`q"ey.zg  hrֺEGfu+Ro,fYc(',G4ψz~*ͥkw43c`%}es<j%ltNB뿳`?W)&67@;i;U,jUT;`7RI靣_~㯃5_Ri֖.nElx'zԞ:Ro\tq &k,tCE(D/i#^4wo0u,F:/8/8onM =Awo`aHWğ }H'L)?$ݰя<Ī踥$k]O#PheR Hq+7v{Ħ7O&3a|w2GNZ6<4\s7v}UڡG`_A~塎ȟlj?gBZDo&tڂ3ɠ{u?Ҿ| ^ɘ^iW$i sIe8* Gd prq>_@3Bt?:>[iI'EI2L| dM_H [oo}\D> |#G$w "cR A"zCsǼB˰ {s>.&>&k$*}Ꮎ(gz>lG?f''Ӭ1,~6WsΗx?9NA9e8cKEbi #c֋Ɉ8&}ZN `|tc<`֗8(b-ڜxn?Tz;㑑Kɦ#@q@n$JB:Q(@ XFfI8zf~4\Җ)H,O s1IqjqsU9jΜ֪IO:.G r* WJ?=y4sf`?>__~FH)$sp:_|Yz`)\X:V"jbeR󴁓 W ?,Pq x~tx>[{Q=?:Wc"\A/\iNg ր'?F~R;vdΚ@- t9\Zh R?:`&=px?L06_G|'njM|psȿSX}%)e'ײT8 "$:ka BXbt# @9viOKR~_8㯿JCq*\Hh n~~z߷| ̞>do"s%T$2HXԚOͣAɩDβZϘ/9SCT}5A<<;6(IqcQ5%VKdm3h8c|y ^ͬ\Xi\ۥ3$mN3c^w+p@wI&TRoxvV F'9Z8=_;t$q_j^p{R)GЭ Rvhd#W_5.mJgrQ=_69|@#9쏳+,F]Hfó 1O9z_?T+nJ+S^U|/Do\1xs*ҧҾ(xPľVI};7>/oFC˲!?yYNNty+w4.f8 8V`19^ox|E$Vב-ܤxdTQ1n'tņlj2۟M/XO/9ڛ\y[z^k[G prODRJU|ߓà[_fi{+Vmqohn?)6ݟCjO?mh+[f? '-?->ΥsV7Nf[Kl&̬PK>bP1iy}-4kuYlHEe ˀrO&W8> <{oE+T ?mir>/W/Jt&1P 28QO~>퐺 |V> O[9%9d:uo{tfVfxKF) 0vd}ݿŚaft4%1 j!w\rs?~*u /E^?2qmȴr>s1no4]Gmix٭$T,IC~<$ppAvV0|֛w6o-" hL:Kh(;ȫ#P~ T~o 3&O$Ze^Df7uɬCeux%[X4b= W⺿Rs@z{oh_Bu}ȴ`u-gwgZ84 e omS)m95~$xwGcXc*n  `F`9v7o2-ȴrK{9$?_aqsɧb?1 h7 7Px-v6Hnp+< |:dRrNk-W8.{•j =.U;?_×Z5Z<"ݠ$i#.@'>2:uDӲZUQmN/>E1{oz)'R5[:9J@+ !</-;E sZIv'ϳq܏]|I&gӇyg~/eSYYxEp +"d5(:1_8nD2֚\LA^g\*<%+_GWM8SFTܺopenalpr_2.2.4.orig/runtime_data/keypoints/us/wa1998.jpg000066400000000000000000000231251266464252400231720ustar00rootroot00000000000000JFIFHHC  !"$"$C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?P9E~m T{?9VBIP%ȼj$#;@RoGkRyqw|] ;~:tgoG'}y?*OEg<Ĕz;Bx@P/[-|zH8=Ar{c/ywCjOhM2g*Z]C68֚ϭ\q7GX~cir^>3Afb`r<kg<cJ d3%\ox<s"h7eqJqnwZ%>>4$۟/ֱ< $ߚ!s"9\N7֨#AT'zⵗN=)%PP4rRkr>sN>j)` 2a?ٮrahԗ<J]MTa\K)"V2 E&w&RwEWUt$Z\>|\O6Vl,.H.N IdU''8\G 6X%Du]kGcWY{x2S:E(ÊUqvzX\ kJ-Vte5?5m| uAsVڜ:UΧe muYd 99 >x?~1*whveq?:o_6sn8 &41tΣGѓGs=ZZ7HzxSGO!ҵ8g }q=+g&&~ +&xJII"+SM 5?떳)$Ha$#rW%W!'>vb)`cR;nosڭu=>QVޠckzHֱ2ء?&twH3'ZVh_o.C^yZ;L sΪ7:)ϡHZNy c =;wk?3w}-m{T##\앶 9#g A㊱Vju Fe|.25ƭb:S-qldvnq,׮?xwK։]wD$:b$p>rڷեէO=Wt:icmKV0[d}7G}j4V(4{:,Nr v!we*ZYy;2H2H b×_⵳nNβO17Q<`z.5$j`PZm$_[}OG0#*iC=ZzwiŻ̸$˃yɯ/ < X趖R%HT4@ϣέ|W4[Jt#W[Ƞ$!_SSڧ]:ؿї'$-zúٴ2|.UԀHn]SMSJPKW|V: yP|yg_:7ο:Lcs"7-[Ŀ|={9V éڅ_lIr_d 5wտ Oz4#R[ʲI*v5s#+|;-O>KR:鞣{XOR`.OMyjWQVc7vB[8cxӕ8QTH.p Ꮗ7=ͣkfy~$o,Jjb)ax৆"-`yQ `p8:sz׫ymIҋ,gYwoc-|<~ Y/k9Ț2wA 0ï|/:H-?ifan'h=: c88*)(?*ݻ+뭵W_ j_?kFՌ~Bd]wojxR>O4涴_1#e\dW;Ҍg/e[!c9ui/,&@ݑ3t3oN=@>.ӼEMi$nIݐH#`Qh9`2〾魂I9? =RcZ2Ok ˏ6JltO,ő,qךu翀 ;VI^_]7{rkHm\zf-inv`ק⾕[VLҍ>bm q ڷ".2W'h Cy&g ⇆̧.UdՖ<݇+&ehIF u# !B)>G@Ўs}j~:nL"vG{+O#ޙK&2M'zf})~aj\ d7= sF9A'47ؠR?V>N2 6X`u/,rrM+w@G @zo+i\AȱNjl]nsYyOW=}R5",zUIg՛ڠy>\WL)QeRI>Ӿi͑~<(Gb{Y rYexnJʢ%6Xr[>OM̍k{ܐ;0=' 54M=P d~<62h-;G1*r^_~ṏ^X dɏR9ǵRid#ZCsL "⭴?տcRXm!K_o%D{3ңa]akty Uc޸1j1ك@`6Le7DA[cۯ@xӮo:A5Osd'8IJ 1Z TgN9'ڳu]kL~[DIO\rQWeד)xq\ν~I>\f*98=@JnCxjZBkl .1dtO$ۭtG ''F2E5:]S:N]eYfS ?3~5/u=VSiڬvuf zghzT[5iLGQTt+[^S731P zҮ{]5VO}<S[?̧,U{hZ(#y"I]H2yB9片-GlV.uf, UO`=҅i7(Ct̒#֖U}X-OAg"4wRubx+Pi僒g-<}rk8?U"L(+4jKީtε$iW0i܉xBhR9{:)2ȞVi~a_j}Y!oEotL`QgsW/+*]k:qQeӍԠ^$MӅ\?HT_A4ۈ匃}ϵfquyy{tdTa`4mJPF$ȪDC&~d{-tcCԢ:U-|sS-Y =Wmyn{Ybh䴓3ZݛyV3.oA޴?0Fvp;WСywNQ};ֱSNNr[sps{}jTmQ3Ua>~wGw)<Ga:UFcHUـ(~pxV$ cG=1A^.GW5t=Kx<͏XӼY6z4x>ym|C2gpKt5$d@\g$tzsrȬ}qI 8R.zS\q0f $xF5*>ϟnƳdcrk>H?-6|ǃNO?IkwEŭr24\^N'G$tV9}_荥5u&sH-cިs=6i,*Տ2LnI?¬4p,:|'TDrJΟp=_$U:^~l~XhU $ 3~A#PM67VJhMҰL{}HVYq#e;:exq\AV-\j?0pNyf-)T>kg]xUk0CkDPl~:p]$E;@>m CKIWȾju3k|ڽ%u'U;I|)2G==M5Z1 Vt뫋;=nҠ {U* IBUsS;YaB\į,r0WbBz^X2խVi--N>\?c\Z.n{aepFzq.mG\žf &a2 F:yWZfu%aՒ1ysDr׃c#ۚ]JB} 1jH|i#L~b{yOۃ*uMV tEdbҠp':Tj/^?ԦyfvmT2ᔎX6[I3NAA+lb \ d zV(SŹA+jzXdՒ(889 gŽ)6P]zNz?KF l6q{ԭ%o/W<2#lW4[ۺ:UUZVc-wYDvH '~b oֺM Z6"I#|\g$v$pwm,,lCO{U &1Cq/cMZ{Op8F  pH)z|3 |ffڰBLHH%T/3t>+L sMvpVzaKKg֤L}cQ!i{>Ɩ}wJRFʲ1=/[{X`9?jCkzПS(fQRFy i2N_@uOVvEqn 7 :+)u/1 C*6ԖE;1?Ѹ⹥qFnEڜqW.=}id7ʲU#qA޼Ζ~ZZ5kW{]ƽJn?a'h[W FfDpu=10 vUoZyEDJ=K%+IWkG< zbJ̠.Zce;sqM2\kfFBvn.]\K;erLixwWT2ZX6ԁڲi90y3۳M+^ 7v Rs#z p+.{,C{Lص MFaE.GNSw߯}ԁ L`Jo=WziHNᤌI%So ωnH$Z-R\R\! |Nx+Y_u%ĵ}^v8d5k0Dteqq3MD5POsEukȢҙM\ AZxKFT.ݧ#!v65O>#Kߥ/'wz~b²'y*]gRFQ'0(P8FO]FYe@9?$}VK~ѷ.KqQQ;˷F).V_{;tOc'V>(!IT6o6*OmK⸶HZuk"y2]y# ƺ!:nvG-L:ѣ-Ơq4_5\jZ5֐Dii42۟j0(U<*֯_iޗʐʛJdF؅y~`O~)}zWi~eni79lupG+Kɦ-mWWfw}:> ׮tXHZI,uN?N88&AAvӥSٺOKyu jO"rO>Q[XdC!#m֗pw0!{={X?(mw¡ɛKX!s䜞I=Ԭ$qWh;=kź}  ܜ`Ӧ+f2!bqbrI=갾)'o_x-+I^|^Xjp64+r~+`/\[NČ!ZpY붓uk"^}jyeg{c]!N k,u-JʵP~ڪ%س1䟭y_5ܴ:;jt ˪O ~RHdgᡶMKkm"mjZV]싳,<]cL }W$z\de;H>IWjs/]s3ٱ߾=8˺I$n4W9f6x &!RSCګ3%UP;z!b]C5]Kr}A>!0z~tz[&}oii/Cbi1EO,PO``+\v-bL*kӛN6g$bL⁧ǝ$c)m;O RJR%TuhWlxsfjq`bo'kڗO9"R op߯N\MUT'<g{$#PK5>NycFmBP{Ȋ!2c½ɷ##Ɓm9vW}Mܱ2{+)ƒAZ)w{߷sm|#e Ηroi(o,~]{8?_^555u("Tx&Yedr $}k#!&SqZGF1qPN8NS[igCt"t˨.4̋$ JV~jWwW @8<ڽ;lǦƠ&AEKa\4K?O4DyikuWgǖ?>zzU/hLVscoĶkH>apNzeH1"tu^d{:Z?%Zn<0#ZMa߈ Ҝ!Dk J?P} h(Ʒ |<͠I;g_nʃ1wpY,qԞ?gn.}J)@+ XT'8巑&ޠZ(<7?Jo[l?HDx;{~&Qs2y)_nsw 뒟DvxP/K v[N,{#qUۃe*6F*eSw wߊ g-y]6?openalpr_2.2.4.orig/runtime_data/keypoints/us/wi2007.jpg000066400000000000000000000152471266464252400231660ustar00rootroot00000000000000JFIFHHC  !"$"$C}" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?K`Imh<jRɭxœ"Kq*J ծMu.nݻY(^L2>?)E~>>+\};W`mH?Z~ \](?q|>˱,抚4-&-7}FI➗cka5D%$ B=iƮ"ye#ɱa?›JQkV1u60(w ONZ{ -,]ϵb8<ӟ+3E8籦n(?¼o_,oc '/.$Rcx=#➻D:}Şwy Kex2y{?g*M. )dorqQō¯_|le}i-\[̻dKUǡ*Asl=eF*?%&xWX4m"T= ElϺ4RS% й'b >*|ۭ:k{+DGa$`g*]])!!V O5rک}':kE9?Go/"|xw?'|MjkaM<pl1~Sΰ+J1֍>zyuKе}Jo,w9Vyl8/?!#WX,{7-aO~ g'j횦%s#i1't\=YTJoImku} >7ux~2I^8RƼᶷm?Q,fC 8^|g[Z=8^Z@"86O~/O^"n5 Y9#M1ƎqgjrV_S5ܿ~'& FWv y<}(1$-4Fg1<ά4ߧJbQ|]k Sǚt13;AV|CG|5|si.HepCzcl|F U[I- >|{?7m,.etvXgYmtjABU'-7Vwf>AwO/anRG#dtSf׮mWSz |A" z}Zt̯,Qٲ qw5R?.t8tI,uA3̑,s&rIe\E׼MEi?JeG#s?z'쵦^յF\}aCQ~>÷i,pПw <}jo|[Q)4Vֈ>Ě֞_Vg/n/4ZY9B-#` #/P=3חQv*.©vnI2+?⟍[ZiC-—\r}=ai>Xegq0Tb9ɮ_ƾ8YQ/rHDAcp =^k0:/DͫO:m|٩⿈[:Ͷ`dsG#'Zi$MPћjϭq_XxUcG皀 .rx[Wΐ05-Ot%zg_D@1`Z8jj7]j$OOGoƧiׂ{o:`wQwcO|AL_9 *M>u[i&Lȷ6P1'xiwK+^|-CѳK^}o*z<<֟fk0Fը|g}uq.c7~d00=:H0*X˱D1ړ5>.մfkڬ@Ԯ[,$([-z 0`9UιL~[-Ύ;W ZGnXCc'G񵧇KI ?- V!*8{T*zurʰiE;?}Nʊ<]. y 6V֦,mr['hj~8GLmgGk&PZ(eS>^&o/tK\MxNY[Xa_^Y$d6 f 5+/\\ibjeK]HbE;1<k^*_;\?~ DmiXiɨ34)10'x[Ś|inl.(tVV?8]ғ=TBeTu=kCZ( (2&?Vqk+"cmWpx:VR)S٪ϏVCcf9aI2 <@nQ+[[ kmyT7c^7N69?*_[ ?*aKgcsVN=DEƜ5[岳{Cgb07Ast?c Ώ[JΦɫ2ԝ9)A٣NgI"+#T#'G'WJ#q(w6x :؛Ka,؏ T8:?y=N?JŖɵ<@E/w>\xr|muRie}mϧֺkmQ[nq*q],mi4ܶx6ݕޗy4i!ƛp.L9EVoun{F\[,RB&f6FTzנ}؏ SmyA*"N2m([Y+,7{bE >S "$Sk%<|RXߛkN44goZ}ռ`څټc rw'<[Alj DX&ݦ\h:J?T`R2mnS>m]5ϮjI}wnċ;oxzxIi.o. dB"x,J=9Jl7N6"걟n_XjW%}m"mf;4(0mA9$ⱬ>|C`fmၤD* OB ~m_k}M_'z { vyO>< o#qN|9z_2[s&T6?6:'I@o/v޿wnGLbpf} Ϸ1Gœ?i_OhtX>܉_<b8e{Կ 4  .gEΧ0H*NJ3eA9nURԣkZJ E/y6[FO!o_OD۫|eE RP㹻Yⷳ&Öʃ #݀9a\[][I\@w&BGr+`pNWmZDkqT{kI|4R8W Wņ"Yuܬa`p29#kk>4L/-nk9TnhOCcGu˱JOZ3?^>'"qo7OOmns81t~4A'~Y~>b4@@'Q܏νdFP#.@:<HLξsoNwW9' |b sڿ9$_`gmIHDHг1(F+%||DZ׻N1;ja_V~{o xs232{֟taB34mÞ #CA)?•<9ts^R?kRƗz4oF`u5/ulsNR4k`9KҌhmIt5cEV;kGϩP?]χ-%z !T$[m'_'RhdY#JvA^'o5I.>ҋ m =sY:g$:|}_1 j vAM"PP4Sdc'q/4K:-x|CkCyn6)HιzgV^/R:qnoOUC[w[:\҄IV} ^U.ũTc7+ѻ65k,ҝk7OH6x7^Z-ޝznj+!R8#!N<;(.no湋ww^4䶸FIɸ/'4ۍkWiӳ2Gl}8elvFe=W%Qkes,=ZSM6^߫gxVNnȝEwttMCR[ Ȉ~/5K ay}~U+kj]JlmnE9lrgͨc)tdy1qTeug/jB`@JSD嶛o#&sgj4#9ٹ@Oh)M>H`GV r;szӯu{_&ܫZOnݳFdu ֎%֯J. vO_z:[xw1Mn۞j[Su;? lm_f9'd: PFٷ$Ƨ=v9VߎdqkwZn\rSa%񓍽{_jyDPxoFKK);~mۏ˝ʼn?xvQJeGX6\c}6ۈѥZWN[[o뚝? ֗+k0.cTPe lt-@VW$޲|.avZ$VKyKPP84Eu<e'#} ҇JcuͷeFSrwݵucQO4b:må6^ᙰYh `ǺF:FwvEt43*X+k7nspۤ`ПzrNkxk(`qξ!%םinØbhգN^__IW Zi7ՔW ͸m@Y-SiO~𮎊UA/ϲWӼU'c9ç ?w𮎊?_/Kc9,O3AGl?9}j_fxyQONQB`9.KG2_Jwo-a"t9}n_3 M'_9˚tQ?_[_sCK1?6cGJW0qiٯWIF9`r_f? #,k_۽Ť֯!Ny {W]Y_7Y׳կ]ti*QMWs ݓ[}?55&CCZQEQEQEQEQEQEQEQEQEQEQEQEQEQEd]l;?Zu#mg٫^%[(j*((((((((((((((ȺfzȺӳկ@%?*?$ΠQEQEQEQEQEQEQEQEQEQEQEQEQEQEu#}z[u#eկ@`$uR ՆåޛGB%HW(~W*4xXHWʥڭXԒbߢ=GO_?E4&mZ>}Wo=㎂%!_/<<?a_}=ŠK l7cs!s:w$gԌN0ҬZU+7:`+G o-옆d1#c޸*Y/Oc&k]eTqs3zmhXhIMizj)V~g^wk6ikt΢m\K)L;sg7 _ .,T0->˫JjR&;4dcq g+e<:`;sYA>)w ?ө,5+sׂv(ӫ? mKoO~6xd{y<0_~¿qW?2{g_+?[?IbpObaa_}=bF/o>yu(LjYi{llhAhuJQfm8IY6hN2,O!X@dr2/Ÿȹ]|Y0}ul|*pEcmnK L`沞 UVW5j& |R+¯ 4R#wټSׯv%[ߥ ko~*~-? [?m ѼL>Uq_?׻KC% OYϝ_,j}o?(xZ&hmty/BUҔcV'a*Wp_@}|t+бoM]Gh:'Oh>J @% s坣F1/&|I{ k:uomDhVe>V”ٓglgV`Ghn~+LVt/ᧃoum^u L\|ݜD ߚSedzZߕ 6{WʶK`G6O{}ScYji->[h\I Mo|M^2+{Mլ-^$+l # F95_ej>R|RI%mD#7Ş/$\á4=l~{$kwFۿgۚ O-|A)ě\Ur'+{=lt+Rio>TyoK<|9@i#,eHNFG9 kh,-ˮ]%oBU)@=+Yϝ_V_m~W(4[ʏ!}/Ė5ZOm*;B8bB2#q##p>x)$I|NƽYَv/Q;?K24@~_> JOk;w7ï={جQ+/+[?%[oc N.bOX_f5=as^s 2;M(ՑWD a.XEEvZ$dۓopenalpr_2.2.4.orig/runtime_data/keypoints/us/wy2000.jpg000066400000000000000000000274671266464252400232060ustar00rootroot00000000000000JFIFHHC  !"$"$C~" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?s.̣q z^e uly `j;j?$)_&JS]9ӊr|I=|A(Y/_;^OP}~yn׵M!q]1+[v #N HgsO9@^ߡWvmynbEfxYG㞕 O!=KOҔ|WxnFa<|-(LrMAsnU|+cNV y&Kt¼w\r g% ?wFmW0SQrK]vo^۴Oj٨ׄWo[ڞfM82U=l4RD)RuS0sI|B'@wyZc?ӭ*jL9ډ"RnHE-L]?|jnڈgGS[#\mzkw>ζԫ8;{fkmƛ[ UU˨z[ђQKWUQU$>-q9284_?qrqxھכof[k]@N} eIl$ڇ#w|):l5]SOxJ;2A3 {yϹ.cPl_qA9Sm-i''C|Q^K !"HBMp[Q,n~"8LXF\][/4YD1\̢WU'rx+)k:vi ,(lNy$~W3/z; Sᖗob։+m_Z4գKMJ.$hؑ$eCN ǥ{~6Rk7c\l|H@qr8 A@ :n^pFN!-N:0O*8?SԶy`sQp7c9bhV%؞?TJI]XY]_*)| (Q^KD7j*0Icׯx/.Iq ey1SCwխ}iss`"iWF#[v9;T}k1I(MƂM.TU/<1 gN2pp0~ynKG%Y1`qҡEĉMoGv2$rO HpqSkvC@V{!A^85W4n|gOBQqoWDHAL'F ӷjǢŲiv"ēprr3ǭQq Ei&ԞX<}q#zt5XF~1hl\Uqԥ#jIYEu#K Hc|k é+atPk>klg`tr20+nAQLʑlg. Iݎ;}JX+"%+.!CsV8TNө5pj RPى.1V n\{:Y;C=gh}s^o}2sDK6 Hqn}X4"?$58ގ|mS##@(/.3= 8Z 271E뷒};㮯.vo:2I'C6t;bi[ɃLҽK JfxZE@ J+/K{hZT`LG|sVv }FRͽs5C<˺g:Λj~ϳd-+pv)߂鷚vhV99l''w=kІ.vIs$WB_F-1#\H5xmln-I[UV9|7_j׃L] P*OV>_w' x9L(b_N|v#6Vt]ХVQF+KLAe:`:s:{z8KɳUd s⾫]~0?|#ϷÚL%39aI9klIfISm;-N5ukKLv%ܑ!vnkկ qadvV.9ZC#?LguofZʣ9|㯧m,22FJ#8?u5oT8%q MöVy>mAuĘ̂ᠧ'{0+ϖ(ao9n+W^%=-ld6ʤ! P q^ݧor40p`?ƛvAIy` lw^;>eJ=|&TՌ2aӧ l7lr9%XVSI^y*,_;aZme6Є?vPm ;7?qQ_ZB,u14ݫmT_КI]}jb_5P Hܢ'0n?Iww)U0Qw 0}6ӼZ7H[:ѕq䀠5"iLz)ooVb=*ՎRyn #ܘ`vFsהedB8 zq]nj-muF)<~z,}1ӚnTOYJy5UY03JV5#C/2˅\3RΞlp^"6C 9ҝ$Le>$.)1FGQ5_͝l#Hl[$Jc˟|5W}?t${1LxkGG rW#4vaʖC a񜎹es`Π׷ֽZoJLdkt70>C\~Fy| WNè ҫym t¶@6PQJ;٫q usKA)0; ]'N=+{ WNRG0`u$^ߨXM~݈qq>ב~:WI#v 8'~:mxrV6?|gk{4|ay skoxwÓh:E-qJĒ7$ʞkտjIx#DDx,"1N<2Ǟ'ƾr"YG.JnR_j I׼+R5̸#, 7CԌ8 ֯\*2 q}==+)*N zF!.d=cyawc4*&ve2BF3^NxkѼ35t.bI#>`ڻ`dN95ؼ+ k1D7D5oS@ r{MZJv< x]jY/ ? WQ on#D0qӵ2Mx&x$c#>r"E-zj#R: 8K{W:{ʯ"|2=1kam$ᶢʸ-FJᬯI+ `ut[@#\:׃բ{8Z)GM)J [AY/Rx4A$qsܰPWs(M3PX+ g;ʷ bh\{Fc\6*#Q=l=Nte(|]69# wtU}~'RF<Ş9sBw?Uۈ2HG= ejwqm&|-ew`.1[Q]MxVEPF sB`׸>PI cAIcM<2AmO9TV6@vwjk ?*tۻG2D ֣N[͹ʰ!F0$z~2~Q{ [i7r̤Xqi>& d9qqQg~QMp/$ A}kiuS(O &ߖFcLOp/ÚbV.c,q@\yҞx?}JNh0qs 3ai}E㏄ CK'y679ے"'9IW5M5MFɣ"m'S88Ȯiq5lҖ9&E 9PGF 9/|Vzh-NE׼M ui^L13z6#)xY^OL`QfR yH5i5 {İJpqy5qQl5s%@UAJcPxׯ-i@S X0p9'l5E<fϮ |}~^?]k%. w|{.}Ս†Mg}9ȭx#Ss}W{x:~Β0D ]Օi0e`2CvȮsŗ1/!Pv^$qFz[k2>e@31?Sz}cGȣy$O , 19<;6z:9vQq9LWDpDe9cǶdRid6kb8+=x;e2,KX`~~UZOgK8<LH;#v(f,IGf= Fqq^/.cYch!$V;'|^Q8L^"$F|ذb+t:(M(}Q!8]*7.BoȂAjVEw@Fq}XT=+ YT. Ƭw 1k] !p{@GQa>Ч8a)*,%v3*H1޺yC.w~ـh -}[#e^2d1qy (}9for]f+Uqf8eZ2{8 r0Gkpۜ8{z}e;sb4ab\{V8 6ϲQbglC hE~znl*jH5Fe]TImy`P2}O_UYԈo98-ˎy·ӧ0ǥkB|2fldur1۾)@^;dG/'֏'qd2y}kla@[cq(#?5mFA9G/T`ʗ9R}J={&s4jL9ƾf߂<9O7G]7W/~K9+ f8< >Aʹ`pRUH##?aYCjIŜx|[|/4fEB}lev[5j8I",Ch~2H*X d: -%#fKhJў3sUgZipۯ#2B9q=ZN\,ҵeRC*p\aإ17ߦ' ^*T`ֹ-fḵ=2OCsgnY[PAQHb,=?:aU,u[Z[k,Ip!lۀŸ2s:z[ Et3 'C\ Z Ўs8˟i"׉<$^xdF+"{sjhW 01io f[xf@JfKp%MO³31ɊA*ESKGP񜖲kvRX_ͨim%!u?>@)(WsK~4[/Ydlj%+Da-#scb5V[Mct XE߆+{M}<)x'S,0b@F[}5L/M!{tZGw i٦S v6 ccg@R+?|TYkݴbQGr ͷxDZ>xᖍ^xN??v"KFueሬַm}aMFmFK4C$y%[grF~?0!-Sxv;߄^ 8}COor9edC ◹R^<=qhz$N˕!U`R]$Co xZNm|Eiab隍7e8u|2H楿|ws,iw/2]kZt\mWՃL9SI[ {vg9AǦ~1e-歾JmBVYѣ `+ŷZZp.䙜)L@|ky(Ns֮xoGm=.Tմe,`;'Kx!Xgtyd*Iok7{+"ʩf,uܞI>``b_cXټ^[px+ש5Oʸ,8e?*#2Ψ$UbP$zu#5 2Iu?k0->(D8Ґ5wd:g"Z}GSjеaX↶rA jLz3.$<.%hu ?ol{SM uz>)()6AixHzopMk1G_'ԅvPk5 3E1nѻf'?_үOH4I2H'S-6pڵW&HT/~uSy^,;sycX8Iݳ@?Nȸqס}N:>)c? ,Oi @U}0}jSڧw5ݛ8>bw}Pxt8+4 z?Y\vJIbi((}Fcd nG5$zᷞ"e/R韥 | @W{Z|@UgZh@TVU)T%@TUE$T!@hH@((!@|DL@,}=@8{Z}=4gZi9TVUyU&TUE5U2hH@8(2@|DL@<0) @@DD!D@aAD@rvAAHfHU՗Dw{՗IUgNϽ[˽Ʃj@UQUEUU@@QIDU+ ? > ) @@DD""H@2rAH@3{AB &BRv;RIUf?bN?sϽ3˽"ƩH@QUEUD@IDD+@+@*@)@@D""@3wA @3gAC @&AB@QAa@;QAIUe@?aBN@?GqBϽ0B˽  ƩEUED@""@3gA @"V@ Q@"V@Q@U@Q@E@Q@D!@@D1@0@ @"@@"V PAAAA@@A@  @D @D @DDT@EET@EE@EE@EEEE@% 6 @Uw`@Tg`@TVTTT@EUEUT@FjJ()@G{K8-@G{K8-* ((UE ET %* 6/˼ @ WwoϼYgjUVUYT&TUEUT6hjJ(:@|G~O<=@|G~O<=h* ((TUSYSEU)*-:/˽=@){o߽=%g:UݖyU'T͕uU7hʩ(;@|G}O<=@|G}O<=h) ((TU4, $ UWYYWYUUU]m?@i?e;UyU'TuU7鉹(;@FmI(9@FmI(9) ((TU $ $4( t,0l0@]VY@]VYUUU]m?m?i;UiU'TeU7Յe7@EUEu%@EUEueTU  $04480t(0l0@]UTY@]UYUUU]]?]?Y;UUU'TUE'Ƅd"@EEDd!@EEDddTT 00  4 4480dQ4 l$ @TU@XUUU MMI@EDSUU@ESQE@@@@@@@ 00  4 4$4 TT4\$@TU@YUUU EAA@ADSUEU'@ASAD'@!@@@@ 00 $$4DD0 @TU@]UUUՎ UQ@SUEU6SAD6A1CAAA@@ @0@0  0DD  @DU@]UUU*U?Q?ʦ*@SUEUuSADuQpS`BPA@A@@@@@  DD@DT@]UUU*U?Q?Ʀ*@RUEUuRQEDuQpQ`P@@@TAQR@DR@UEUV*A?A?ƦƩ*@QUEUuQQEDuaa!!@Q@AP@UUADQUR]S]EUWj]Y֦Ʃj@QUEUe@@QIDu+`+p+0) @Q@@QAP@UeQEEEEwIUgjnYƩj@UQUEUe@@QIDe+ ?0 >0) @@EU!E@aQE@~zQIjUEwjIUgnNjnYƩj@UQUEUU@@QIDU+ ? > ) @@ED*" @>vQ @{aO8j(UݗEw{ٗIUgNZu]Y4]$ƩX@QUEUT@IDT*P*P*P)@@@ED*" (@?wa8@?kqO<*f(VEvVIUfeN@GuC 4C $ƩEUED@ED*" (@?gq<@&VqK0@-`@P@E@AA@AA@AA@AA@AA@QAT@ETU!%ET@eQ%ET@~zRy倪EUwIUgJYYƪ@UQUEUU@@QIDU+ ?! @>!@)a@Q@E@AA@AA@AA@@A@AQAUEUT*")@>vR9@{sjUݗEUwYɛIUg]N]yY4 $ƪ@QUEUD@IDD*P*P@*U@)U@@U@@@@@@@@@QAUEUT*""*@?ws?@ksjgUWEUwUWIUg@eN@@FuC@4C $ƪEUEDAUEUT*""*@?gs?@&Zr[˿m&fjVEUfVEbe@@Du@4@$DD@UEUT&"h@&Vr˿l@EaVEk@EeVEj@EUUEU@EETE@DE@@DE@@@UEU!Ej@EaEk@@E@@E@@E@@E@@@@@EE@@E@@@@@@@PT@QT@Q!@Q!@Q!@Q!@QT@@ 0@ACp@@B`@PETDT@UUUET@eVeE@eWuE@eWuE@eeE@UUET@A!1P1" 1#0@qcp`bVTU&UUU&j*{.{.+*@UUUU@AB"U2ETD2"*F1#:Fԅ@uczFԕibFUݖUUgUݕUUwjꪪ;??j+*@UUUUU@AA B( B(@B(@B(AQAUBB@GDcYsEUTr"*Fq#:Fו@uczFוibFUUUgUUUwj險j;}?}?j)j*@UUUUU@AA B( B(@@DR(@DDR(@EDQ@FDU@FD@GD@4WCD,!$& wYwUUEUUfjFuzFו@uzFוiFUUUgUUUw隩*;m+;m+;-**@UUUU@AATBhBh@@XERh@DTERhPEaT%$PEeT%4AD$!4AD(!@tB@<1d&<0@ui@uUUUUU@ejJ@uzNי@uzNיiJUYUUWUIUUWIՅe@EUEu@EYEu@EEe@UEEU@EEATBhBhXRh@TRhPEPaT&` PuT6p A42t A@81tR<1ha<1@uTi@ uUUUUU@djJ@tzNי@tzNי@dJ@UTUUW@TEUEW@Ed@EGDd@EKDd DdTEMDT@EXEQT\bh\rhXrh@Tbh@PEPEaT'@AuT74381dQ(!\a(!@uTi@ uUUUUU@dj@Tz׉@Tzי@Tj@EDSUUUV@@ESQQEV@@EC@@DEG@@DEG@EE@EXEQXb(DXr(@Xr(@Ub(@PEQEa%@Au540(!TU4\e$@eTU@ dYUUUU@dj@Tz׉@Tzי@Tj@ADSUEUu@ASQATu@C@1DCG@1DCG@0 ąE @ąTEQdb(Ddr(@`r(@ab(@PEQEa$@Ae$$4DU1L @MTU@]YUUUU@fj@gUz׉@cUzי@"Ej@ERUEUuARADu1G1AG(0, U,@UEQ(fb$@fr$@fr @fb @@EREQ @EQ !1DU! @DU@]UUUU@j@gUzי@cUzי@"ŕj@QUEUuQADuQtWtFW$t[$dYU@dT@IUdHVT@HVT@ F`@IVp@@EQEp@AE A!A!DEPQDT@EDT@I]UUUU@Ij@gEzי@cEzי@"j@QUEUeQQEDuQtVdWVdUVdUUTDTT@TEUT@DfT&@DfT&@fB`&EVBp&@QEBp%@ER !R PR @DUA@@Q@TE@Q@\UDQ@M]UEUU@Mj@GEzי@CEzי@j@QUEUePQEDuau[5Y%YT@PT@TUP@fT&@fT&@fBD&FVBD&@QEAD%@E@q@p@A@`@UYAT@Q@]T@Q@]TQQ@MUEUU@Mj@GEzי@GEzי@Ej@UQUEUe@Q@QIDe+` +q n2AmcBYSR@E@G@TRA@@RBD&@RBD6@BRBD6BRBD&@QUAD@AUDQAD@@UA@P@UiQeETmET]֕EUVMޕEUVM@TEz@TEz@Ej@UQUEUU@Q@QIDU+` ?u @>6A-gBWR@E@vFREA@&FREBT&FREBT6BREBT6BREBT&@QUAT@QUETU@aeET@eQeET@~zRy怪՝EUgՍEUgF@UEzF@UEzF@EjF@UQUEUU@@QIDU+ ?%A @>&@@)f!@@V!E@i@F!A@)@E!EBT)@EEBT5@@EBT5@EB$UAU@QUEUU@*bi@>vRysEUwEUgF@UE]zF@UAzF @AjF@QUEUD@IDD*P*AP@*A@U@)UA@U@UAE@U@EAA@@EAAT@EAAT@@AX@AAY@QUEYU*"?ssjUݗEUwUɛIUg@J@@EYBJ@BJ FEUEDA@A@@A@@AE@@AA@@AA@@AA@@A@AAY@QUEYU*"?sgr˿offjUVEUWUZEC@Y@@@DY@@@@@@@@@U@QUEUU&"j'r˿o@EaVEkEeVEjEUUEUEETEDD@@DD@@@U@AUEU@A!VEj@EaVEk@DAE@DAE@DAE@D@E@@@AE@@AE@DAE@@@@@@@@@@@@@@PTE@QTE@Q!E@Q!E@Q!E@Q!E@QTE@  @     PDTU@UUUETU@eVeE@eWuEԕ@eWuEԕ@eWeE@UUUETU@QQ@@@@@@@!1P1 1 @1S` RPTUUUUUU{{k@UVUUUU@RQ@@@@@@@@EA!U1UTD1%A1%A@qSeAŀaRAUUUWUUUWjꪪj@UUVUUUU@ZQAT h | @@P| @DQh@EQQT@EQUT@EA@EA@$GSEbUrUUTr%Eq%E@uSeEՀiREUUUgUUUwj險jjj@UUYUUUU@YQAT h | @@HT| @DTUh@EPUQT@$F`UUT@$FaE@$GaE@xWSE,!h&, qiqQUUUUqReEqSeE@uSeEՀiREUUUgUUUg隩*k횹+{횹+{*j@UU]UUU@YQAT-h=| @@xV| @DDdVhXPaT&hpeT&xq4"xqJ8!@tWBD<1d&<2@qi@qQUUUV@qQeI@qPeM@uPeM@ePI@UUUUV@UEUUV@Ee@EUEu@EYEu@E]Ee@]E]EU@ EXUAT,h<ڃ| xړ| DdhXaT'lΐuT7|ρ43xAH81@tVE@<1d%<1@qTi@qUUUUU@`UeI@pTeM@tTeM@dTeI@UTUUUUU@TEUUUUU@EGDd@EGDd@EKDdMDd\EMDT@LEXUQTDh D̼ړ|@̸ړ|DhXaT'ΔuT7΅43E@81d@(@!\%(@!@1Ti@ 1UUUUU@ UeY@Te]@Ue]@UeY@QSUUYUPSQQUUPC@TAG@UTAK@i mIm@XUQhh D̼ړ|@̸ړ<@h@PEUaT'@ uT7 4@3 @(@!TQ@\a@@qTU@ pTUUUU@`TeY@PTe]@PUe]@UeY@QSUUYeQSATea%eGT5eGh8%|,u|@tERh h D̴ʓ|@̰ړ<@h@PEYUQd@QUd P @DQ@L`@@M`TU@]aTUUUU@faTeY@wQTe]@sQUe]@"UeY@QUU]uQATu1A45GAT4uGh8%|,y|@yYh d D̻ʓ|@ͷړ<@p@@ERUQp@AUQ`APA@tUA|T@mTDE@]UTUEUE@fUTeI@wUTeM@sUUeM@"QUeI@QQUEUuQQADu2At2WAtrWB(x%B8hѝiXBxX@iBh A@L@ A@IA@@EbUBt)@E#UB$)E#B(E"B(tUQATU@TDD@UXUEUE@UXeI@wUXeM@sUTeM@"TeI@PUEUePQEDu'QEt'VEdgVEdZEdYTTETT@IUATDB*@D?@C?@EB*@@bYBt)@@3MC$=P3 C<dbB(tUPATTTT@XTD@@I\UEUE@I\eIV@GE\eMW@CETeMW@ETeIV@UPUEUeQPQIDea u [vQ YgVYWTTg@TוT'@T*@ǷT?@GCd?FBt*@aYt)@1M$}0 |$`Bh@UYQTA@Q@]T@Q@]TQQ@MUEUE@MeIV@GE]eMW@CETeMW@ETeIV@UPUEUU@Q@QIDU+` /u@ @n6AmoFY_E@wTΗA@'@ƶڂT*@ǷڃT?@GڃT?FT*@aUT%@qUTqqTp@aB@`@UiQeET@meET@]UEUU@MUEUU@MeIV@DE]eMW@DETeMW@ETeIV@UPUEUU@Q@QIDU+` ?uQ @~6Q@mofY_wETwA/@ƶڂ*@ǷڃT?@CX?BZX*@QUAX%@QUEXu@QeET@QeET@~zyꀪ՝֕EUVՉޕEUVEV@DE]eEW@DETeEW@ETeEV@UPUEUE@Q@QIDE+` ?U @>Q@-b@rE@~rA@.bT*@aT=@@`X=@J(QUAY@UQUEYU@*fi@>vyEUgEUgEV@DE]eEW@DAeEW@AeEV@QUEUD@QIDD)P@P)QEP@)QE@e)aF@iaFE@iaFA@)aFT@QAT@@@h@,QAm@UQUEiU*?EUWEUGEV@@E]AEW@AEWEVEUAED@AE@AE@@AE@@AEE@@AEA@@AET@AAT@@h@,QAm@UQUEiU*?g޲˿ofjU]UEUUUIMEA@L@@@L@@@@@@@@@@@@@@@@@@@@@@ Q@]@UQUEYU&j'޲˿o@UIaFk@UIeFj@UIUUEU@UI TE@ @ @Q@U@EQUEU@EaFj@IaFk@DAE@DAE@DAE@D@E@@DAE@DAE@DAE@@@@@@@@@@@@@@PTE@QTE@Q!E@Q!E@Q!E@Q!E@QTE@  @ @ @@@PDTU@UQUETU@eReE@eSuEԕ@eSuEԕ@eSeE@USUETU@SQ@  @@@AAAP!P@!P@@ @@ @@ @@ @UPTUU@UQUUUUbssg@UWUUUU@WQ@  @@ @@!@A@@AQ@@AA@AA@EEEU!TA1T@@B1@C!@@C!BT@B B@UPTUUUUUUUj⪪j@UUWUUUU@[QAT  ü @@dü @De@EUQT@EUUT@EE@EE@$Ws#U!TA1TAPB1AC!A@CaBTAB`BAUPTUVUUUUVj嚪jjj@UUUUUUU@YQAT  ü @@Hdü @DTePVQT%$`VUT%$a!$q!@|Ws(!h&#(#U!T(A1QTASB0BQC BQ@CdBTQ@BdBQ@UTTUV@UUՕUUV嚩*k嚹+{隹+{*j@YU]UUU@YQAT-"=#ü @@xgü @DDdfXPQU&hpeU6xq42|q8!|Ws<1h&#<3@U!T(@E1QTASF0AUG A U@Cd@T Y@Bd@TY@UTTTYU@UEEUUU@EEe@EEu@EEu@EEe@]E]EU@ EXUAT,<ü xü DdXQU'leU7|43|q81|Vf<1h&&<2@1T(@1QTAR0AU A U @T ] @T]UQTU]UTEUTUUUE[DeE_DeUE_DeE]De\E]DU@LEXUQD DӼ@ӼDXaT'eT743e81@XUUE @1@X%A @1@1T$@1QTAQ0ATU @T\U@T\m@TXmQQTUmePEUQeePE[Q%TE_QU%TE_((-8,}8@xi Dӽ@ӽDXaU'܉aT'܉ @#؉YI @1@TTT@\d@]pT]pTAQV TU T\UT\mTXmQTUmdQEAdtq+B4u;BT4u;(8),ü,y,ӽ@|i i D}@=Di@TDZUQU@DUQT@DT@@DT@dU@le@mqT]pTA@f TU7TU3TmbT}QT}`QEdpq%B4u5CT4u5h8%|,y}@}Yi D@@@@EZUQ@EUQEEt7C@4|&@$maD]`TA@f TAwTAsTAbTAQTA`QU@pr%Btv5CTtv1hx%|hyX}X@}i ADM@ A@IA@@jyB)@@/yB=P/8B<d;C8t2C8|&(mUD]TTADfTAwTAsTA"TATA`U@p'%Bt'%BdgaB$UeB$U՝TBeU@ԍBeB*@E?@C?@EB*@@byBu)@@3}C$=P3糛@o-zEsA/rڂ*@qU=@Epi=EJ=(YQUA}%@YQUEie@*ji@>zy՝ՕUUVՉ͔UEBPA@@DLTPA@TAA@AA@Q@E)Rb[)rW_)r@o)b@jrEAnrAA.bVU@QQU@@@i@=UA}@UUUEiU*?]ՕUUUI͔UEAA@@L@A@@AA@A@A@AaEaFEaF@EaF@EaFEAEaFAAaFU@QAU@@i=UA}@UUUEiU*?g޾˿oj@]UUUUU@I TUA @ @@@@A@@A@@A@@A@@A@A@A@ U@]@UUUEYU&j'޾˿o@UImFk@UIiFj@UIUUUU@UI TU@ @ @U@U@EUUEU@EiFj@ImFk@ADAE@ADAE@ADAE@AD @E@ @ @@@DAE@DAE@DAE@@@@@@@@@@@@@@P@E@Q@E@Q!E@Q!E@Q!E@Q!E@Q@E@PD@U@UQUE@U@eReE@eSuE@eSuE@eSeE@USUE@U@SQ@ 00 @@@EA@A@P@@@@@@@@UPTAU@UQUUAUbssg@UWUUAU@WQ@ 0@Pp@Ta@UQ@@EQ@@EA@EA@FQE@E@U%D@E%DAA A AAUPTQV@UUQUUQWjj@UUWUUAU@WQAT 0 @PDt @TTePUAT`UATaEaE$[s#@U%D@E%DCA A @AA@@AA@@UPTTUV@UUUUUWjVGGjF@UUVUUEU@VQAT 0 @PDt @TTePAT&(pQT&(q"(q!,s # @UD @EuDCApA @AA@@AA@@PTTU@UUUUUꚥVj嚹Gz隹GzFj@YUYUUEU@YQAT%"53 @Ptw @TdfAU',ΰQU7<ϱ3zz@]UUTUU@I TTA@@P@@@P@@@@Q@(Vb (r[(ڲ/(ڢ:A?ʳA/ŢV@QQU@D@iD=UA}@UUUDiU*?󿀪@]UUTQU@I TTA P@ P@@@A@Ea@EaF@Ea@Ea@EQAAEAAA@F@@A@@)=UA}@UUUDiU*?g޾ȳoj@YUUTQU@I TTA @ @@@@@@@DA@DA@DA@DAD@@@)9U@y@UUUDiU&j'޾ȳo@UImDc@IiDb@EUUTQ@E TT @ @U@Q@EUUDQ@EiDb@ImDc@ADAD@ADAD@ADAD@AD@D@@@@@DAD@DAD@DAD@@@@@@@@@@@@@@@@@@@@@AP@A@AQ@A@AQ!A@AQ!A@AQ!A@AQ!A@AQ@A@UPPQ@UUQUPQ@eQe@eRu@eRu@eSe@UUSU@Q@SQ@ 00 @@@EA@UA@PQ@@@Q@PUPQR@UUQUQRass〚c@UeSUAQ@%SQ@  0 0$!@@Q@@EA@EA@QE@RE@URD@EURDAPAAAAUPQR@UUQUQS(,,󀪪(@UeSUAQ@%SQATD  D 0D@`@0D@dP!TPATd`VAT$aF aF!(˲8ڣ@uSD@eUSDBPEAEA EA EPUeV@UUUTeWhV|GUQUA}@UQUTiU@*fj@>vz@]UUTEU@ITUAa@a@!!@$@4Q@%8Vb;(r[?(ڲ/(ޢ:AA7MAA'V@@Q@@+:QUAy@UUUUTiUjÿ@]UUTEU@ITUAa@a@!!@@AP%Ea@&aF@&a@&a@@QAAIAAAD@D@D+:QUAy@UUUUTiUÿ޾oj@]UUTEU@I TTA @ @  @@@@DP@DP@DP@DP@DPD@D%%QUe@UUUUTeUj޾o@I]tC@IYdB@IUUTA@I TT @ @  QUE@UEUUTE@EYdF@I]tC@DAT@DAT@DAT@D@T@@A@ADAT@DAT@DAT@@@@@@@@@@@AAAAA@@APA@AQA@AQ!A@AQ!A@AQ!A@AQ!A@AQA@UPR@UUQUR@eQe@eRu@eRu@eRe@UURUQ@RQ@ 00 @@@A@@A@@PA@@PA@@  UP!V@UUQU!Wacc〚c@UeSUQ@5SQ@0 0000 @@P@@A@@A@aFbFTbFTEbFQ@AUAEA$EA$EUPU%V@UUQUT!Whl,󀪪(@UeSUQ@5SQAPD0 AD00AD00AD ADAPD@PTAP@FQD@QE(˲1(۳1dsG1dZcFQ@UUQEA$EA4EPU5V@UUUT%Wꪥh|<(@UeWUQ@%WQAPD  AD00ADp@0AD`@!ATAQd@ƐWQQ4ˡ0!l 2x߳ 2ts!2dZc!R@UAUQAQEP@A$YP@A4YPTU5Z@UUUTeVꚥhZfxKr~8Krj(Jb@UeWUEQ@%WQAPD !AD01AD@pPqAE@dPaAUARe$QR5x1|!l02x߷03tw13dZg!S@UAU@UAQU@UA$Y@UA4Y@EE5i@EVUee@EUdZ%@EUtO%@eUU4O@ieU$J@YuVUIQ@uVUU\D@`eUD@puUD@pPuEE@dPeEUERf$US6x2| "0C"0B#[1E#jW!ES@`eUA!U@ eAaU@`UA(]@`UA8]@EE9m@EWTeuE[eY5E_e%TU_eeYetUdUU@ tUeUD@`QeUD@pQuUD@pPuEE@dPeEU@UљERV@(IљUShJCl C@U^U0C@U^U0B@e^U1E@uVU!ER@uU1U@eqU@U(]@UA8]@`EA9m@`ETe|`EQY<dEQ(dU$e$eUtT@ aTeD@`TeED@PTuED@PTuEE@TTeEU@EUERV@EUUS@TEUC@TEYU CU_!e_!e_U!E@uVU!EU@u1U@eqU@U$]@UA4]@p*A4m@p+Td|p+BT<t:B8t:8\)(\UUxX@LUTiH@DTTUEZD@DTTEE[D@@PTUEU@DTUERU@DYeEV@D]eUTD^TE[U_)e)uUE@uU!EE@u1E@eqE@U$]@UA$]@p)A$m@p9Td|p5CT<t5C8t5샄8\%„,\UhI\@LUiYH@DUUEZD@DTUEE[D@@PUUEgU@@UUEbU@EZuEf@E_uE&PE_A&TE[Q*;S<;R,;UQp@&Ua`I@tpE@$pE@ $Q@UA$U@p-A$Y@9TdX5XBT(5\C@8tq\D4eXD$YYTEP@IIUE@@IaDV@@DIaDW@@@EaDgP@EaDrP@FuD`@EuE4P&B4d7S<t6C<|6C,|6USh@l&URlI@pa\E@ qE@1U@UA!U@PAU@ПUPUПeUPԟeU@TaUDeTDUYTET@E]UEED@D]eEVD@DDYeEWD@@@UeEkT@@UeE~T@euEh@5uE8P68d6C<41lCP|<1hTl<1UXY@l!U\I@pa\E@`qE@P1E@T]!TU@TX]APU@YYUPU@iUe@@yUe@@TuQe@@eUe@@UeAU@EeEAU@EeEVU@DEYeEWE@@AUeEkU@AUeE~T@aiE}h@!iEhx!hTx$1lCP|taXBPT|aXTT|QUXU@lUE@ddaE@ddqE@`P1E@dP]!TV@hT^BPV@UVPf@fj&@{z@&@X{z@&@Xjz@@YyAU@IiEU@EeEU@EYeEWE@EUeEkU@EUeE~T@YQU}d@]QUhtaXTt$aXBPd@eiQeE@@ieQeD@ieQUHU@YeUE@dda@ddq@`P1@dPQ!hbC|ѣP+|;h@?\@/\@*@ҽA&@MҩE&@EeE@EQeEW@EQeEk@EQeEz@UQUy@UQUhU@iQeT@%iQeE@@~zzX@jjj؅@UYQUIU@UYTAdTadTq@`P1@d@Q!hbC|ѳP/|?h?\/\AAMѦAADVA@DTUA@DTEA'DEA6UUAu@UUUTeU@*fjX@>vzX\@]UUIU@ITAaDq@@1@U!@ bC@4ѳP'4;(?/@AAMAA@AD@ADA'@A&QUQQe@UUUUTeUjX\|ÿh@]UUTEU@ITUAa@q@@1@!@A@P%a@%a@%a@%aE@@QEAAIAAADAAAAQUQU@UUUUUU(|ÿ~xCojhFj@]UUTEU@ITTA`@p@0 @A@@A@DQA@DQA@DQA@DQA@DQADADQUU@UUUUUUj(Vj~xCo@Ս]4C@Y$F@IUUE@IT @0@0 @@@@@@@@@@@@@@QUQ@UEUUQ@Y$R@Ս]4C@DA$@DA$@DA@D@@@A@ADA@DA$@DA$@@@@@@@AAAAAAAAPA@AQA@AQB@AQB@AQB@AQB@AQA@@@@UPDR@UUQEDS@eQED@eRED@eRE@eRE@UeQEQ@5QQ@0@ @@@@@@AA@AA@@AA@@AA@A@@ @0UPT1V@UUUET!WeX!gX1c1〚c!@UeREQ@@5RQ@@0@@0@@0@@ @@@@@P@@@@@AAEaEEaETEaETEaE@EABB( B8 UPU5Z@UUUET!Wh"l2,2󀪪("@UeSEQ@@5SQ@@A@0@@@0@@@0@@@ @@D@@@@DPT@@@DQD@EQE(!(!d!dJb@EQAR(8P5Z@UUE%Wꪥh&|7<7(&@UeWEQ@A5WQ@@AA0@@A0@@A0@@A @@E@A@@DPT@AHl2x2t2dZc@aA%ARА)@()@8PD5^@UYEeVꚥhjfxkr~8krj8jb@UuWE4UQ@E5WQp@A@E1P@@E1P@A0@@@A @@@E@AA@DT@AXH\l 2x 3t!3dZg!eA%AR)A()A8@EE5-@EZUe%@EUdZ%@EUt_%@eUU4_@eeU$Z@UuVU$YQ@E5VUdLAE!TLE1TH@Ap@TTD@A`@UTA@EPUTBB@DDUDCXH\ C20B31E3Zg!E`eA0%Rp)(`-8@EE5-@E[Te9E[UY9E_U)TU_UeYUtUdUIP tEdTM@ ATN0ATJ@pATTF@dPUTB@UUTBR@EUDCXIC\C@Z^V C@Z_V0B@Z_V1E@ZWV!Ee"&%r&)(.)(.@p*%-@p+e<`+RY<d/R(dUQ$eQ$uUtQIT qE$TMD ATNATJATTF@UTBAUTBV@QUDC@TDUU@C@TEUUPC@U_!@U_1@e_U1E@eVU!E%"&%r&)(.-(.@p.(-@p;ԃh<0;CT<4:B84:4)$eUTIP aDM@`DDPDD DDDDADVEQTEB@TEVA@TEWQU/)e/)e/U@u&U! %"X%r)-A@p-A-@9T\<5CT<5C8t54\%$\UUTIPLQDDM@DDD^DDDZ@DDfDDrEDqbETEa&PEA&TEA&;C=;C-;US@&Ub xad(qd dYAd@`-AP)@9UP85YCP85]C@(t1]D$\$XD$\UTTIPLQDDI@DDZDDZ@DfDrE4aEt5P&B5T7C=46C<<6C,<6USx@l&URl pbl rhP2dU^A"d@TmAP@ԞiUP@ԞeUP@ԞeU@@TaUDTeTDTUUTEPEAEDE@EDVEDVADjEzE$y`Edh4&B4$6C<45lC`|<5hdl<5Uh]@l%UlIblrh2d^"d@dY^BP@YZVP@iVf@@yVeA@@TuQeA@@TeUe@@TUYeAUEEI$ADEHVEHVADjDzEDy`UTEht%hTt$5lCP|4aXBPXvzY\j؆@UYUUIU@UITADb,Dr(@@@2$@@Q"$(bC,ѳPo,o(ooZY@U@AEMA@ADD@@@@@QYQPU@UUUUUUj\ÿ@]UUIU@ITAb,@r(@@@2$@@Q"$@aA@aPa@a@a@aE@@E@AE@A@@@@@QYPQ@UUUQU*ÿ~{CojjFj@]UUUIU@ITU A`@p@@0@@ @@PA@@QA@DQA@DQA@DQA@QA@A  @Q]Q@UUUQUj*Rj~{Co@Ս]uC@YeF@MUUUE@ITU`@p@0  Q]@@UEUUA@Y%B@Ս]uC@HA%@HA%@AHA@H@A@ADA@DA%@DA%@@@@@@@@AAAAAAA@F@AAF@AAF@AAF@AAF@AAF@AAE@U@V@UEQEW@EQE@ERE@ERE@URE@UeREU@%RQ  @@PA@@PA@@@PA@@@PA@@@PA@@@ @0 U@5Z@UEUE%WeZ)g[9c9c)@UeSEU@@5SQ@0@ @ @ @@@P@@P@@@PA@EQE@EQE@TEQE@TEQE@PEQA@C@, @8 U@5^@UEUE%Wj*o>/>**@UeSEU@@5SQA@0@ @ @ D@@DPT@DPD@EQE!!T!TJa@PEQA@C@,@8U@5^@UEYE%Vj*???:*@UfWF5U@AvWR1A@Aq@AaA A EA@DPTH,1(1d1dJb@PaA@5S@9,@-8U@D5m@UEYEeejjji~zky~:kyj:ji@UfWE5UU@AvWQ1DQ@Au@AeA A E@@DPTHl2x2t2dJg`eB%p5S5p9Ã,=`-(-@EE%-@E[Ue5@EUeV5@EUuW%@UUU5W@UeU5V@UuWU5YU@UuWQ5HQ@UuQ@UeQXA XA XETF@DPTHXHL\\lʥ C2x߷0B3t1E3dZg!E`eB%05S5p9Ó,<`-(,@tE%,@tE[e4dE[UU4TE^U TU]TeZ%U5VU%IQU5FU$MAU1 U!X  AX @X@TV@PTDAXH@B\PC@JY S"@[[0R#@[[1U#@ZW!UeB"X&5c79ã?-.@p.-@p+ԒY<`+RU<T.R(TU%e%I4TIQI0MAE  E X AAXA@XE@TV@PTDB@TDUT@B@TEUUPS@U^@U^@U^U@UVUeB"X&5c79ã?-¢.@p.-@p:ԃ\< :CT<:R8Y94 i)$M4TIPM0M@E  EX QQUf@EPTA&@TEUA&@TEUA&U%U%UU@TUU%B"X4c9ã-¢@p-)@p9ԃ\8 5CT85C8]54 i$$M$TIPM M@E  E\ QQQiETA9EA5EA%>=?-?U@T&U hB!d,Cat CatYBal@`-AR\%@`9UC\d 5YCDd5]CDdT]1]DdLY%XDdMTITMTMDEEA%A1A1aET`5%B55C=$6C<87C,87US@h&UR p&Bbd 7CctP;CctUnBbd@TJmBRP@NiVBPU@NeVB@@NeUB@@T]aUB@HYeT@ITATIATEEA!A1A1`ET`4%B4$5C<46lC`l<6hCdl<6USh]@l&URhI&bh 7cx;#tj"dhZCZWkVgVeA@TuQeA@TeUd@UTTAUUDATUDUDAD%A%A%`UTdd%hBTd$5lCPl4aXBPXϿ>ۻ*UwGeU@sC!Aa@aDAD@@DPD@EPETTEa@PEQA@C@, @ ( U@ %]@UEYE%U*??߻*ewugas1cuB@eAEPEDP@DH@I,1(1d1dJa@PaA@5S@8,@,(U@D%m@UEYEeejji~튺ky튺kyꛫjigFuUgcB1TcuB@eAQAAUPEEDPI@DHL@M\l2h2d2dJc`aB%`5S5`8Ã<`,,@UE-@UEYU5@UEeV5@UEUuW%@UUuW@eUuV@eWUuUV@uSQ5TR@uA)@eAm@m@i@UPUDDPI@XDHL@\M\L C2HB3DOE3TJgE`eBX% 5C5 9Ã8 -‚(@Eŕ(@[ՔQ4WTQ4@TEVT @TUUT@TeWt@UuWUtYQ@UuSU5YQ@Vu)@Vem@Rm@Ri@UUUD@IA@HHLB@LMLCT S"DR#OU#@TJeU`eBX&`4S7`9Ó;`-’*@p.)@p;Ԓ]4 7RU4T&R TUDe @U0T YA@U0%YA@0)@ m@@@ZDTAUTHBUUHS^^U^U@TUVU`eBX&`4S7`9S 7`-R &@p-R %@p9CL4 5CD4%R U e@]0TYA@]1]A@1)@am@Q@Q@UAnATTA:EUD6EUE&U%U$UU@PUU`%BX`4S`9S `-R @`-R %@`9CL4 5CD4%B$U e @]0TYL@]1]M@!)@am@U@U@aEm ETA9 E@5EA%V"8U#(U#U@TT"U TXBDCQP CQPYBQ@P)AR\@P(TB\T$XBDT$\BDTU\A@T,eX@\@}`TUL@}aUM@aU@QU@U%@U%@qF%a0FT`1 F@0FA4$"8$3,$3U@d"U T`" T7P;CPUjB@PIYBT@PIXVTTITVDITUEDUPUE@,UPT@@}QPTUX@}QPUY@P@P@U%@Y%@AK%a T`1O@0OA4$"lBh<2hCl<2US]@l"URId"R d7S`7df@XfC@HWkgeIUeI@(Ud@@iUPTUT@iUPUU@T@P@Q@Q@QWaTPa_h@Pa_lAPe@aXB@XqXCD\qUSD]@!URTI$"R $7S 7dfXfCLŷoLXH@TVѹLTVh̀@UUPTUU@UUQUU@Ti@Tm@P-@P )@QUMUQ@EUPQ@^UYPQ@^QYA@U@eE@e@aUXY@!UXI$"R $3S 3dbhbClolhH@TKѽLT h̀@U @TUQ@UAUQ@i@m@AA-U@AA)U@eU]UUU@%UUUETU@%ieE@ieE@~vz@jfjٔ@UUQUXU@ETE R,0S8@04@ad(bB,n,~(Hy@OLuhpQ @TEQ@QATEQ@@a@Aa@AA%AB%uU^UU&uUVUTfzf@~v⺙jڨ@UUUUXU@ETAR,S8@@4@@QdaAYYDY@NDUAXDPA @TUEQ@A@TUEQ@@a@ Aa@IA!IB!qQU/uwUT~zꚨꨪ@UYUUXU@UITA@R,@S8@@@4@@Qd@@QAP@AQUP@EQU@@IQUD@@MQUD@ ATD@A @TUEA@TUE@a Aa@IA!IB!q}QU/uuUTꪨ~|kjhj@U]UUXU@ITAP P@@@@@@@@@@@D@@D@@D@@A@A  @  @ a]QT%e]UUTejڪhj~|k@Ս]uh@Yeh@UMUUUT@ITUPP@@    @ Q]QT@UMUUT@Yeh@Ս]uh@HAe@HAe@ADAU@D@U@AAQ@ALAU@LAe@LAe@@@@@@@@@@@@@@A@@A@@A@@A@@A@@A@@A@@U@D@UETEE@ETEE@EUEE@EUEE@UUEE@UUUEE@UQAQQU@D@UETEEd)*e9/e9/v)*UvFU%@rB!U@U@@@@PD@@PD@@PD@@PD@@P@B  U@ @UEXE**Ͻ>?Ͻ>?߻ʚ*;wUwsbUB@UAUEEDEFD@E@@PDD@@PDD@I@I@E@QE@QAB  U@ @UEXE**????߻ʪ*:wews%ceB$@eAV$F$F$EPEFDPI@HH@IH !!E!Ea@aA5B8 , U@@UEXEUjj)~튚k9튚k9꟫j)guUgc5TceWB$@eUAV$PU@F$PT@F$UPTDEWDTI@HL@ MLL 1H1N1@JaaB%5B58Â8,‚(@UEE@UEXUU@UEUEV@UEUUW@UUUWZVuVjWVuUWzSR5TSuB*$@eA~$@TA$@TA$@UTUEXDUHHLLML\Ď2H 2L2@ITdB%4B58B4(B$@EE$@XDA4DDA4@TEUeT @UUeT@eWet@eWUtUS@uSU5TS@fuA*$@geA~$@cUA$@bUA$@UUE]UHLMLTTCDTB@UE@TEUUEPeBH&P4B6P9B6P)B&@PEA%@P@A4@A4@TuP$@Tu@T%%0@UuU0UF@UuU5TV@ua*@ea~@Ua@Ua@EUUmAUU)ATHB$ATHC TADAAAU@TEQUPaBX&P4B6P5B6P%B &@PE%A %@P5@L45@D4%|P$| ( @YuTM@YuU]@um*X@em~X@Um@Ue@EYUmETU9EUDB4EQDR PER%@ER$ERU@PERUPaBXP4P5P% @P% %@P5L$5D$%lB$UlA,e(@ @}uTPXM@}uUUX]@umjX@em~X@U)m@)e@iUmdU9U@A5EQAQ%[4U$UU@TTUTXDPCPYB@PJAX@PJTXPJXDPJ\BDTU\A@T,!@@L@}1TPTL@}1UUTM@%mT@emT@U-m@-e@q-U}0%U9 @@4KAP4 8 (!U@T!U TP" T6P6CPUfB@PEUA@@ETTTETXPETXETUQXE@T,aX@X@}aTTX@}aTY@% @d @U- @-@q-u1%U5 @P1OAP5@ \h0Xl1U]@X!UIT" T6P6Pf@PDUA@@ETdddHUdH@,d@@}PTT@}QU@ @P @U @Y@Q_U@_UU@_Y@Pa@_]@@e@^QX@@YAX@D\QU@D]@AUATI"B 2B2b@@RC@Anë¹HVѹL@Vh@@UUPTU@UUQU@Tj@P~@UA~U@YAjU@U^]UUUV@^UUE@V@^UY@@U@^QY@@U@eeE@eeE@UQUETU@AUETE"B 2B2bbBnH@[ѽL@Zh@@UYPTQ@UUQUU@UAj@QA~@QA>UQB*UeU]VUU'%UUVETf%efE@eeE@~vz@jfj@UUQUTU@ETA B$0B$@0$@adbAiH@OL@h@Q @TQ@UUQTU@U@f@QAv%@QA6U&UB&U'yoU+ygUzz@~v⺙jڨ@UUQUXU@ETAB(B(@@$@@Qd@@QAP@APU@F@U@JH@U@NH@UAXH@PA @TYQ@A@TUQ@@f@Av!@]A6&]B*'}YU/}wYT~ꚨꨪ@UUQUXU@ETAB,B(@@$@@Pd@@P@P@APTP@EPT@@IPTD@@MPTD@ @TD@A @TUA@TU@f Av@MA6MB&qUU/uwUTꪨr|kbhj@UUQUXU@ETA@ @@@@@@@A@A 0@LA0LB azQT/evUTovڪhnr|k@ՍQvh@Qfh@UEQUUT@ETU@@ 0@L@0@L@ @QYQT@UUUT@Ufh@ՍQvh@HAe@HAe@ADAU@D@U@AAQ@ALAU@LAe@LAe@@@@@@@@@@@@@@A@@A@@A@@A@@A@@A@@A@@@@@E@D@EDTEE@DTEE@EUEE@EUEE@EUEE@EEUEE@EQA@EA@EAE@D@EDTEEd)*e9/e9/e)+UeE'Pa"EB@EA@@@@@@@@@PPPPPAE@@EDTE**ͽ>?ͽ>?ߺʚ*;vUwrbUFV@UE[ UK FJGDEGDETDPDPIPIPEPEPAA E@ @EDXE**????߻ʚ*;wUwscUeF(W@UeE[fh^@}UU}dU9T@A4EP@Q PK4@E$EU@DQ@D@@A@A@AT@TTPXDP\@DTP\@@T,!@@L@}1TPTL@}1UUTM&njhJ&~{|J.~|Z>fh^@=UT}%U@9@@4K@@4T8D(U@Q @@@@@AT@PTTTPXDPPXEDTUQXE@T,aXD@\@}qTUT\@}qUT]&jhJפ&{lJӕ.lZ>hZ@q=Tuq%U@5`@@1TO@@5@_\@iXhUY@@QPI@@@@@@@@Q@@QA@ATTddDQdD@,d@@}aTX@}aTY%jhZ%{lZ-{lZ-jhZ@e_UTf@%_UE@f@$_Y@@e@_]@@e@^QT@UAT@PQU@Q@AUPE@@@@@@@@Q@@QA@ATHVиL@(h@@iPTU@iQTUfjhVf{lfnB{lfnBjhge^]VUTg%^UVE@V$^UVD@U@^QUD@U@edE%dEQUETU@AUETE@@A@@A@@@@Q@@QA@AhHWнL@Vh@@UUPTU@UUQUTUfFjhf{h&n;h6n*h7ynU+9fEj9E@eE@^vxJ&hEQTUTUETUA@A@A@@QT@QAPAPi@ H@L@h@U@TQ@UUQTTUfDjhf{h&j;h6j*h7}{Y/}wY~Ծ@^v⹙@_wJ&EQTTUTAAA@@@@QT@@QAP@AQPU@FQ@U@JQH@e@NQH@eAX@`A @TQ@A@TQDDj(D{("_Y;h6^Y*h7}{]/}w]~@_{@g&ꨪEQTTUTAAAPP@PTPT PTD PTD @TDA @TUA@TUDj({(@O;(N*(mw]T/mw]Tꪨ@o@Vb|kbhjEQUUTUTUA@@@@@@@@@@@P@P` p@LA0LB YvQT/YvUTovڪhn@^b|k@EQvhQfhEQUUTTU@@ 0@LA0@LA @QDUQT@UMUUT@MUfh@MQvhDAeAeAAU@U@@D@ADAQAHAUHAeHAe@@@@@@@@@@@@@@DDEDE@ETEE@ETEE@ETEE@ETEE@EPA@EA@EADDEH)*@XEYI9/@XEYI9/@XY)+@TUE@PQ@EA@EA@@ @ @D @D DDDDE**@\EM>?@\EM>?hʹʊ*;duE7dq"EJ @EIO  DD DD@@@@@@@@DHE**@\EM?>@EM?>ϻʚ*;wU7ϳ"EJ(@EI_<< h(EXDT DH@HL HPP@@@DTE*)@\EI+9@EI+:߻ʚ*:wU7߳#('@յ_<5йȏ<:h(/U@XDDTHLL LL H@PE@PA@A@@A@@A@@ED@TTEUEV@TEUUW@EUUW%߷eV5weU7߳%T#*('ݷo<5ܿ˯<:n(/@UD]EDUHLL LL H@PE@PA@A@A@A@ED@TTDAD$DA@TEUeD@UUeD!gD!gT#c#g&h'wg|7ç<;n(.@E]E-U)H% H XTHT@U@U@A@A@A@A@A@PA A@@Eq@Uq@UQb@eSR@eSRg&h'wg|7<;n(.@mU-eU9腔TD4ETD T@BD@B@UE@UE@AD@A@A@A@A@P@ @T1TP1T`! @UeU @UeU'&h'7g|7?<;>(.@}U=dU9TDB4EPDB P@@@@Q@Q@AD@A@A@A@A@PD`D`P`! @U3T @U3U'&h&7g|7?|;>h*@}UU9dQ9T@A4EP@A P@Q@Q@@@@@D@D@D@DDP@D(!L@y3TL@y3UUM'fh 7g|?g|>fh@q=UUT5p%QE@5`@@4PE@@ P@@U@Q@@@@@@@@@@T@@P@D@TQQD@T,aD@\@}rUT\@}rUT]& fh 7 g|? g|>fh@a=UT6a%UE@6`@@5PE@@@X@TXPU@@@Q@D@@@@@@@@@@@@A@@T$$D@Q$D@,$D@@}pUT\@}qUT]& fh 7 g|? g|.Ffh@U-VUT'@%VE@f@Z@@e@]Y@@U@YQT@U@T@PQU@@@AU@D@@@@@@@@@@@@A@@T(8HV8H,(@}`Y@}aTYffhZwg|o{C'|jB&h{iYZWk)YVWj(YVVi@YQU@e@edE%dEQET@@AETD@@@@@@@@@@@@A@@T(@R(&@}UU5dEE5TD4DPD P@@@@@@@P@`!@Ue#T@Ue#Ue'&u7S7q?S7a>R&@}UQU5dAA5T@4EP@ P@P@@@@@@@@@@@DP@D(!L@i3TL@i3UMe' u7Sq;Sa:R@a9UQT5`%QA@5`@4PE@@P@@@@@@@@@P@P@@@TQQ@@T,a@\@}qQ\@}qQ]e&R U7SQ;Q*@Q)T%%Q@%@$E@P@PP@P@@@@@@@@@@@P$$D@Q$D@,$D@@}pUT\@}qUT]e&R u7Sq7a&@QY%TY%R@VYR@QUQ@Q@AT@T@T@@T@T@@@@@@@@@P(8HR8H (@]`Y@]aTYifR}S}iYUk UVWZUVVY@UQU@U@edET!dE@E@DT@@@@@@@@P(@@AQfw~]TD@@@AQVw~mTD@w~mTD@wD@TTUwD @ TTTTUUUw  D @ UUUU Ug { D @ UU  Ug { D @ $ee  UF F D@UU  U@@@@@@@UU UUUUUUU%uueaa`P@@@Pdtu&vvP@@@Pdtu&ww P@tu&ww P@w{*[W @@ w{:[WVQQPQAAA@ w>]UEAA@AAAA@ w>@ w~]TD@@@AQfw~]TD@@@AQVw~]TD@@@QQVwD@%%$$goD@%%$$TTUgkD @ %%%%UTTTUUUg k D @ %eeeeeeU UW W D @ %eiiyu  UW W D @ %%))yu  UDDDDDD@!!!!uu  U@@@@@@@UU UUU@@@@@@@UUUUUUeaaaQ$$)*{wa A@@Pddi*{w PA@@Pddm.og PAem._W P@em.^VVQQQQQRR@ em-]UEAA@AABB@ emm]UEAA@AABB@ emm]TD@@ D@@D@&%$$@@QQD@&66&54$P`aaeUUD@%59)94$TTUUUD @ %uyiYTTTUUUUUD @ %u}mmeeU UVVD @ %u}  UVVVUTD@%u}  UP@@@@@@!11!uu  UP@@@@@@ ee UUUUUUUYUeaaaQ]UP.og  `!A@@PTT]_W PA@AQUU]^V PAU]]UUQQ SA U]]UEAA@Q Q VW@ U]@@@QQVW@ UYD@PPQQVW@ D@P D@*94$PD@&w{jytdPP`qqeeeD@&wn}tdPTde%%eeD @ &wn}tdTUUU!aa@ @ &wUU U!aaQQ@@@ &w  e%!aaQQ@@@&w  U&w  U ee UUUUUUU )mmiuppP*~~P* {{ p1& g g p1C BA@DDM  R R p1 C BAPTTYYYUUQQ c#   B TXP@ g'   A DD@P`q q fg'&&A DDD@P`qqfg+*)T D@ !!&';:)T D@g~}tdTP D@&gon}tdP 0 0 0 pp@@&gon}tdPP`p00pp@ @ &godPQaa`pppPP@@@ &goUQ qppppPP@@@ &go q0000&go a  &gk QPP QQQQQQQ)mmie``P*~~juppp1* {{ p1TXXYUf g g p1 TXXYUV R R p1   C BAPTTTTUUUQQ r2   B BAQUTTTPP v:   ITT$$$$PP v>>-I DDPP`qqff>>-X ``P`qqff>>-T @@ !!&f~~mTTT$ 4t @@V^dT  000 pp@@V^d`p ppPP@@@ V^dppppPP@@@ VZdp` `pppp0 VZd ` s bpppp0VVf s 3 "00 VVf s #"   TTTTTfc     N^Zedtt4TTTUUJ K[Z edtt4dddUUF G W t5 dddUUF B R p1     eeUQA q1    J JIYUeedPP u9    y}mYUUQ`PP u==-  %x|mYYYY%10 `P e}}mTTdhYYUap0`P` q q e e}}mT$ YU UUp` 1qee}}mT$ %ee eeUapp`PPQQUU]TT`  eeUQ@@@@@AQUUYd`p  eeUQ@@@ UYdpp e% UUdp` q1 UUd ` p `qqqq1UUd p p `qqaQUUeutdp  @@@PP``      tttUUJNtttUUJ K[Z UTTTttF G W V eddd$ ppB B B d%  uqQQQ e%   * **uq`PP aaQQ A Rf y}mieuq`PP emmmUUex|mimmi``P  emmmTTd8<-Y]]Yeqp0  1 q e eidhh i Y ] ]YUap0  1 q e eiiiTdT  YYUapp`PPAQUeeeeTTd  YYUa```PPAAUeeddp  U   P PAAUeedpp  U eudp` U aq` ` p UQQaqp`p p dUUEEQQaqp`p ``P@@@   `)--)pppUUj~>*ppj {;* ppf g g f UXXX ppR R R V UYYY  vrQQQTTTQARW & &&vr`PP TUUQARf 99))6vr`PP  TXHIEUe9=-)>~jp`$ $d e8<-)>~jfapp` ` dhlminn*!00 P dhx y i ^ ^ !   P tt$Td  ^ PP@@Tdt4$$dp  ^   P P@@P`p`pp  Z @ @@@P`pppp`p` Z `pppp`` p VPP`pp`p p V 00 p `dTDDD   `PPP@@@PUUimmiU   pppUUj~~jUpp  Upp V pp U]]]TTQABC  ccQQQUUYYYTTQARS  &6wc@@@DTXXXTTQARV )))):{k`P    TTe9=-)>kfapp` T e8<-)>kVapp`P  d(<=)>?+!00 P  dhxyino+&!   P T`t u i n o+& P `p000 `p  o+& @ `p000 pq  o+& @ @@@P`ppqqaqb  o+& @ @@@P`ppqqbb S o+& 11"S S k+& !!"S C @S AAAA@@QQQaaaeU    QQW/--``  W/--PP W]TTP@AA PP V]UUQ@AA  SSQQQUU]UA  &&gcRQP@AAUU]UU %6wgVQP@@]UT)6wgVQPPP@D U Y T):;+@  T 45):;+@  P`tuin;&P  P`t u i n ;&P eu5! Pdt uin ;&@ ee%!TTTU  ;& @aa!1ueU ;&@@AAQaa!1u ;&@@AAQQQ!!e ;&U @U UUUUUUUQQaaaeU555QQW;99QQ W;9yddP@AAPP W?=}ddP@AA PP W/=}uA RRRQQQQQVV.=}u@ RRRQP@AAEU--me@ VVVA@@AAEUmmmeD%&&&@DUmT%&&&@ P $%%@  P 45%&66&@  a``P`tuijz:&@  UUUQPPPdtuijz:&@ UUTTTeeUYn~:&@ UUUee ~:&@UUU  ~:&@@QQQQQU  ~:&@@QQQQQU UUU UUUUUUUPP`aaeU555PPV65uTT@@@@PP W75 u t d P@@@PP W;9 y t @ @@@PP W;:{w@ AAAQPPQQWW?>w@ A@@AAFV.>w@ ->w@@DUm~w@@DUm~wP@ wVQ@@P $%%%%%%@  wVQPPP%%%%@  jfTTUUee%%@  jfTTTeeUUee%%@ ffUee i)%@ ffU  i)%@VVU  UU!!!!@@QQRRRU  UU@@@@@@@U UUUUUUU@@@AAQQ!!aPP@@@@@@Q!!aPP@@@@@@ V65u t @@@ W75u t @ @@ W76w w @ @@@AAWW76w w @ @@@AAFV*:{w@ ):{w@->wfQ@@@@DUm~wfQ@@@@DUm~wVQ@@P@D U m ~ w@  wTTTT@  wTTTeeUUTT@   w  Uee UUTT@ { w  U  UU@ k g  U  UU@ c c  U  UU@@@@@@@U UUUUUUU@@@@@PP@@QQP@@@ Q!!a ` @@@ R"!a ` @ @@ W'&g g @ @@@AAGW&g g @ @@@@@DU%&gg@ %6wgfQ@@@):{wfQ@@@):{wfQ@@@@DUm~w@D U m ~ w@D Um~ wTTTT@  wTTTeeUU@  w Uee UU@  w U  UU@ g U  UU@ s c  U  UU@@@@@@@U UUUUUUU@@@@@PP@@Q@@QQ P @@@QQ P @ @@QQ Q @ @@@@@@QQ Q @ @@@@@@QQQQQ@@@ UUUQ@@@%%eeUQ@@@%%%%%%%%@DUiyye@D U i y yeTTTD@D Um} }eTTTddTT@  }e Tdd TT@  }e T  TT@ qa T  TT@ q a T  TTT TTTTTTTPP@QQ@AAAA@@@QQQQ@@@@DUUUUU@D U Y Y YU@D U i i i e  @ m e  @ a a  !!!!@@@@@@@@@@@@@@@DTTTTT@DTTTTTC<@PPPPPP@@@Dddddddttttttt    GGCOAOAOAOAJAHAHAHAHAHAHAHAHAHAHAHAHIHyHyXyXxp000008 8     ''&&&&&f~||||xxx>>>66667' 9@@S,$?bڿZӟـh~]%,' ** @@@@@@``1111133sssssPPPPPPPPPPPPPpxh(((((((((((((( '?y@%ww]40H@    `bbbbbb!b!j!k!k!k1k1k1k1k1k1 1 1999999 ˅˅˅˅˅˄˄˄˄BBBBB@ 8<<<< ojjjjjjbbbbbbb`%@%%5555======999;ZZZJJJJBB ׾lI}(."zH+4J}.!8'>;A       00000000000pppp````Ю//``@@@@@466>>>~~ ~ ~ ~"v"f"F"F"F"D"@"@@RRRRIRISIOIOHMLML̀Ĺĺd́d́d$ qpppppppt`t @t @t @t t 4 4 4 4 < < <<>>;       0 0 0 00000p````````@111qqqqq{ >k@ @=#uc2/9DU՝ݿ)ܿ޿R$* c:<             !!!!|!|!T!T0T0T0T0DD@@@@@@@      CCC a a a a a    0000000000 # ' ' ' %%%-         P Xxxxx~n.....@.@.@/@/@/@'@@@@"""""###a i< n@a#<h& />0Bؖۿ\ !FC  @B5B5B5F5F5F5F5N7N &N &N &N .O .O .O .O .G ,W0,w0,w0 s q q q q q q      @@@@@0@(0@(0@(0@x0@x0@x0@x0@x0@x0@x0Px0P: : 22 22 22 22222 " " " " " + /   1>1>0>0<0<0<P<P45000000qcccckkkkkkKKKKK '' ' %.!.!.!.!.1.1.1.@@kځ$ٿ$M՚D~!:@@$ _"F?      `````` @b @b @b @b@b ЊЊККx888888(((a(a&`'`'@'@'@'Cbbbbbz    @ @ PPPPPPXXXX             ` `(`(`(`(`(`8`8BBBRRR[[[ CGGGGGGGRGRRrrrrֿ׿k@PKә܀!!J*'?c?E+%; +F!6d!$(< 37  @@@@@hhhhhhhhhhh(((                      iiiiiiihPPP@@@d$$$$$$$$$$$&&&&&  +rsqv,~1?a# >`[  ME ;;;;3331qCaGaGAGAGAG@G@@@@@@@@@@@HH\L^L^L^LVLF F0G0000 0 044446!6 6 wW߃σσ""""""` ` ` ` ` ` ` ~0~0>0:0:0:0:0;:;@?@?@?@?@?@?@@@@@@<DDDDl$l$l$n$~$~$~$~$ ?( 9(.ueXPқٿڿ%ڿ] l@h@$?@"=]!R-cZ M@>> > > > !~@ ~@ ~@ ~@jAbA@A@A@AAXAXAXAXAPAPAPBPρFPρFP΁FPNFPNFPFfPFfPFfPDfp~qqs33########@@@@@`L`La La La La Lc Lc Lc Hc H @0@0`0`!0`! ! ! ! ! ! % % % d l "l l L H ʇ@@@@@@@@@B''''&""   < = = = ===    $H%H%H%h%h!h!h!h!h@)h@9c@@9c@9c@9c@9c@9c@9c@#@#@#@@@@@@@,L;AՅyjM+@&?*<eY?jڿ%O֛$- %(3H~$dޤIߘ 7:  B B B B B BF(F(F(DDLLLLL(((((( P P0@0@1@1@1@1@1@@@RswmM   7 w w  ~~~~~xxxxhHHHHHH           2:::::>  ^ ^ ^ ^ L D D`E`E`E`A`A`A`A`AbABABBxW11v`0WrtX1^EVyQ  OL             & & & . / /`/hhhhhhhhhh   #''''   @@@ B B B0 B0 B0 B0 B0 C2 C2 CCƄƄ„„„„¤CSs1 1 1< 1< 1< 18 10 10 1 1 1 1 1 !              @@ h h h h h 000O0O0O0O0o0"?@@فjk@8??.%Iҙ[%ٿ'U ~ V$@ ;:   EEMMMM M M M ] ]         HIKKOOOă$$<<<<0=0?0?0;0;0;0;0;;9900H0H0H8H8H8H8H8H8H888 ( ( , ,  FFFFFF":2 2s0yOͳށV%AeC@c DD   HHHHHHHHHHH``bbbbbbsWWWWUUUUUU @@@@@@``hxxxxxxx|        0000 KCCCCCCCCCBBBBBB     000e|?@Fق&V՞X*'JG  (((((****////? {zzz           hhh   !!!!!!!!!      64444t|lVLVHVHVH^H^H^HJHHHHHHHHHH@HHH !` ` ` `p`p`p`p`p`p`p`p`p`p`pЂЂЂЂ@???<:$ . * ?<   888888xxxxxzzzzzGGGއއނ܂܂܀̀hiiiiiiIIAA@@@@@@@`````````` !!!!!))FFFFFNIIIIIIIIII       888888888888888xX@@@@EEEEEEEEEEG111111113333333ɿ ߿>@@@?"BD'''B!       '-====F=F=Fj؀A" & 68   PP PPpppppptuuw__     ccCCCCCCCC@@@@@@DDDDD D @ +++**********...dDDDDDDD ڿٿ%?DJxzK,3222    HHhlllll||||vrrrrr@r@s@s@@H(((((8888?PPPPTTUuu$w$$&  ###!!((((( \\\TTTTTTTTPPPPPGPGGGGGGGCCCCCCC"C"S""#######Nߚ?0EfD~|<6-E>8888<<4`4p4ptpTpTPDDDtttvvvvv66&&&&&ae555?????       ..Q`Q`Q`Q`Q`@@@@@@@@@@@@*.Q'Q&  #E@=U9'& .1  0000000qqqqqq                       +////////PP@@@@@@@@@@LLLHHHHX8000000000000001!NLLLLLLL:?@.~#,AA  @@@@@@@DDFFFFF!O!O!O!!! 0 0 0pptt|\\LNNNΰΰΰ BBBBBBBBBBCCCCCCCCAAAAAIXXXXXXXX pPPP$P$P$P,P,P,P,P,P,@,@,@?@;@;;;;SSSSS  > \|dPmܿD)lrq-)@**=<  @@@B$$ $ 4 4 4<0<0<<<444444000PPPPPPp + /         7 7 7 7 7 7 7 7732222222:::::::;-         ppppppQQQQQQQQQQY22222:::@tv ܽoR"?(AC((@@        G"G"G"G#G##ǀ#ǀ#ǁ#ǁ'ЁЃЃЃЃ   888((((( ~~~~||||||8888880@@@@@@ ((<<<<<<= = = =,=<=<<<<>>>666622@@@@@800000     / / / / o  pppppppppppPPPPxxxxxxxp@@@@@@@@@AAl D O Ҿ./.["=@A@  8 8 8 8$8$8$8$y$y$y%y%A%AACCCCCÃǃƃƃƂƂƂƂĂĂĪXXXXX|X|X|X|X|X||\XXPPP@@@@@@@@`` ` ` ` ` ` `hhh+h+(+(/////'P    nfffffffgcCCHCHCHCHADHTLT T T T \XXxxxxxxxxazb@`+"d|?MA?͕ %.ܿ'fڦ([ уh{(&?JI 444444>>>>~~~GGCECU@]@]@]@]@Y@x@xBxNx~x>p>p>0>0>0>0> > > :        ` ` ` ` ` ` ` ` ` %%%%%!!!!qqqqќќӜpҜ0Ҝ0   @@@@@5@5@4@4@4@4444444444<8mmm///////'&&&&;@;@@@@@BpM%(.B & = AA \m6i@]OAFE   :;;; ; / / / / / . .  l  L 8 L x L x L x L x L`x L`p L`p L`pL`pa0cCCCCCCCCpp ph ph l l L L L L  L  L I I       $}}}|\\\\P @!@!@!@!!!!!%gg8g8g8g8(`_`^`^^^^^^^Z€€####/?===}}}}$ 3GҘ(F.B)?r;Ehf|Aj( +N)h66 01111111111!CCCCKKKKK @@@@@@@@@========hhhhhhhhhhhhhhn ξ 4<}@ >;              2222BCCCCCCCC $$$$$$$$$$$$$$<CCCBBBBBB $$$$$$$$b'"=?'Pӛܿ8 ' 37    )))!!!!##"bbbbbbBBBBBBBB@::::::::::::::  0000000000  ~v`#a_OxV2##H%AV    I$&I&$I&$8I& 8Y' 8Y' 0]' 1\ 1\ 1|`1~a!~y~Y~Y>Y.Y&&"""FXFXFXFXFFFFDDD@@`````@`@ `@@@  13333 BB ..8(((((((   ` ` ` ` ` ` aa@a@a@a@a@}@}  @ @ @ @ @ @0@0@0@0@0@0@0@0@x@@@\?~,~,ܸ~%ݹ~Em8IDyR2+ UBB?%%"$$ ;T_eݼxWױ|;ܶaB >@ ... . / / ///-!!!X!X1X1X1PPPSss$$$$$$-=========988<<44$BBB (@(@(@(@(@(P(P,,$$$$$$4$4$444444,........         ]W[~W $ #\_"@! 03IIIKKKK    @@@@@@@A******>>>>>>>><<@@@@@@@BBBBBBBBB#!!!!!!!55444444< Y!U ?^Ҽ"FC     @@@@@BB\\\\\\\\\\XXyyyy{{{{{zzzzzzr2rrrrrrrrrrrrrR R ""##C#C#C#C#S#########XX\\,\,\,\,\(\(\\XPPPPPsssrrrrr@@`````b " " """""6666      assssssss׾S׿Si%<S #E###victoriageneral@$"E!D /C(B3A6@.18?+5 >#'*4=%)2<-;&, 7: $ 0 9&C*HA<FF7MM:O;DJ?DE82E1A=@AJF6>7V@3FNULL 0 Joined 0 |Broken|0|1 0 0 67 1 42 2 72 3 65 4 60 5 70 6 70 7 55 8 77 9 77 A 58 B 79 C 59 D 68 E 74 F 63 G 68 H 69 I 56 J 50 K 69 L 49 M 65 N 61 P 64 Q 65 R 74 S 70 T 54 U 62 V 55 W 86 X 64 Y 51 Z 70 4 linear essential -0.250000 0.750000 linear non-essential 0.000000 1.000000 linear essential 0.000000 1.000000 linear essential 0.000000 1.000000 0 1 significant elliptical 177 0.519973 0.451816 0.317885 0.174391 0.005727 0.002272 0.001521 0.000400 1 1 significant elliptical 160 0.520581 0.241003 0.329272 0.081274 0.005629 0.000892 0.002075 0.000400 2 1 significant elliptical 144 0.509359 0.377447 0.361165 0.153754 0.004105 0.000762 0.001614 0.000400 3 1 significant elliptical 124 0.509167 0.365719 0.338836 0.151745 0.002002 0.000742 0.000776 0.000400 4 1 significant elliptical 108 0.478443 0.355089 0.294198 0.154514 0.005688 0.002081 0.001234 0.000400 5 1 significant elliptical 143 0.539582 0.401923 0.335856 0.162943 0.005499 0.000757 0.001868 0.000400 6 1 significant elliptical 143 0.469078 0.394968 0.309249 0.158299 0.002281 0.000779 0.000993 0.000400 7 1 significant elliptical 146 0.611328 0.305025 0.346613 0.134819 0.004970 0.000782 0.001635 0.000400 8 1 significant elliptical 128 0.513123 0.454007 0.312225 0.165558 0.003196 0.001482 0.001015 0.000400 9 1 significant elliptical 113 0.573666 0.389802 0.308559 0.157875 0.004124 0.000929 0.000985 0.000400 A 1 significant elliptical 115 0.420754 0.374901 0.302514 0.158424 0.002441 0.002289 0.001002 0.000400 B 1 significant elliptical 60 0.508398 0.484863 0.320117 0.180469 0.001238 0.000853 0.000441 0.000400 C 1 significant elliptical 51 0.529029 0.368474 0.361673 0.167279 0.002030 0.000606 0.000722 0.000400 E 1 significant elliptical 67 0.519006 0.418050 0.359025 0.172341 0.005021 0.000983 0.002415 0.000400 F 1 significant elliptical 69 0.595279 0.342499 0.318105 0.158118 0.006302 0.000839 0.002381 0.000400 H 1 significant elliptical 61 0.521452 0.487846 0.328957 0.198578 0.003878 0.002834 0.001528 0.000400 J 1 significant elliptical 36 0.402886 0.306901 0.307943 0.167535 0.004602 0.000844 0.001963 0.000400 M 1 significant elliptical 55 0.516903 0.528487 0.332599 0.214133 0.003499 0.004032 0.001351 0.000400 P 1 significant elliptical 47 0.626912 0.399493 0.317071 0.174701 0.003066 0.000682 0.000928 0.000400 R 1 significant elliptical 74 0.527924 0.484692 0.335674 0.178632 0.004211 0.002285 0.001827 0.000400 S 1 significant elliptical 97 0.507611 0.400290 0.330743 0.164344 0.003479 0.000893 0.001494 0.000400 V 1 significant elliptical 50 0.623984 0.388625 0.321797 0.158203 0.005778 0.001636 0.001294 0.000400 X 1 significant elliptical 47 0.529754 0.411877 0.367271 0.162400 0.003107 0.000987 0.001405 0.000400 Z 1 significant elliptical 53 0.532724 0.384943 0.382591 0.157503 0.002967 0.000727 0.001363 0.000400 Y 1 significant elliptical 81 0.607639 0.324180 0.325328 0.142892 0.008281 0.001986 0.002279 0.000400 G 1 significant elliptical 66 0.469579 0.426420 0.305043 0.172644 0.005201 0.000733 0.001992 0.000400 U 1 significant elliptical 43 0.514989 0.462182 0.322311 0.193405 0.005661 0.002803 0.001512 0.000400 I 1 significant elliptical 30 0.546224 0.293437 0.374609 0.102083 0.004490 0.001027 0.002084 0.000400 L 1 significant elliptical 38 0.431846 0.309663 0.356497 0.151830 0.003961 0.000598 0.001489 0.000400 W 1 significant elliptical 43 0.517260 0.465198 0.319404 0.189135 0.002360 0.004658 0.000466 0.000400 D 1 significant elliptical 64 0.504944 0.450952 0.317749 0.176147 0.004372 0.003073 0.001602 0.000400 N 1 significant elliptical 33 0.544508 0.517613 0.343158 0.191643 0.002770 0.003742 0.001218 0.000400 K 1 significant elliptical 30 0.549870 0.469531 0.371094 0.160156 0.003360 0.002093 0.001713 0.000400 Q 1 significant elliptical 53 0.548054 0.440515 0.314785 0.171285 0.004437 0.000856 0.000682 0.000400 T 1 significant elliptical 47 0.659907 0.317786 0.376164 0.124169 0.002933 0.000505 0.001302 0.000400 F" $%#!     #  $  !%"  openalpr_2.2.4.orig/runtime_data/ocr/tessdata/leu.traineddata000066400000000000000000011533121266464252400244370ustar00rootroot00000000000000Qֻl39 NULL 0 NULL 0 Joined 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Joined [4a 6f 69 6e 65 64 ] |Broken|0|1 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Broken 0 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 0 [30 ] 1 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 1 [31 ] 2 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 2 [32 ] 3 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 3 [33 ] 4 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 4 [34 ] 5 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 5 [35 ] 6 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 6 [36 ] 7 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 7 [37 ] 8 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 8 [38 ] 9 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 9 [39 ] A 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # A [41 ] B 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # B [42 ] C 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # C [43 ] D 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # D [44 ] E 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # E [45 ] F 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # F [46 ] G 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # G [47 ] H 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # H [48 ] J 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # J [4a ] K 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # K [4b ] L 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # L [4c ] M 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # M [4d ] N 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # N [4e ] P 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # P [50 ] Q 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Q [51 ] R 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # R [52 ] S 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # S [53 ] T 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # T [54 ] U 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # U [55 ] V 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # V [56 ] W 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # W [57 ] X 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # X [58 ] Y 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Y [59 ] Z 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Z [5a ] O 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # O [4f ] I 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # I [49 ] ''  @@ @ @@@@@@ @ @@@@@@PPPPPP@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@        @ UD D D D D UD @    @ UD D D D D UD @    D  @D @D @D @D @D @UD@@  DD @UD @D %@D %@D %@D $@QD@@ @@@AD@D @D @D @D @AD@@ @@D@D @D @T @T @AT@P@@@@@AD@AD @AD @AT @T @T@P@@@@@AT@AT $@AT $@AT $@T $@T@P@@A@AT@BT  %@BT  %@BT  %@P  %@P@@  @P5@P $5@P $%@P $%@P $%@P@@ P5P $5P $%P $%P $%P@ % $% $% $% $% ! $! $! $! $!@@@@EE@EE@TEE@TEE@PD@@PD@ @PD@ P @@T@ @]E@]E@]E@UUEE@EUEE@UEQEE@UEEDD@@D@ @D@ @ @@DT@ E@UE]EEE@I]EEF@I]EEF@IUEEF@IUEEF@UEQEEE@UEQD@D @D @D @ H @T@ E@EYEEE@IYEEE@IYEIE@IUEIE@IUEIE@UEQEEE@UEAD@ @@@ H@TAE@EYEEE@IYEEE@IYEIE@IUEIE@IUEIE@UEQEEE@UEAD @ D@TA E@ETEIU@ITEEe@ITEEu@IUEEu@IVEEe@UEREEU@QDB@@@@@@  D @T@E@ETEEU@ITEEe@ITEEu@YUEEu@YVEEe@UUSEEU@ATC@@@@@@D @@ @@EU@E@EUEEU@IVEEe@IVEEu@YVEEu@YVEEe@UUSEUU@TCP@@ @DH@IU@E@IUEEU@IVEEe@VIVEEu@VYVEEu@ViVEEe@eSEUU@dCP` P P  @@@@@TYQ@E@UYQUUU@ViReee@ViRuuu@ViRuuu@ViReee@uQUUU@t@P 0000 @AA@UUS@U@UUSUUU@VeReee@WeRuuu@WeRuuu@eReee@uQUUU@tPP  0000000 @B@B@UUSPe@UUSUUu@jeReiu@keRuyu@keRuyu@&UReie@UQUUU@DPP 0 0 0 @@ 0@ @QSPe@USUUu@&eReiu@'Ruyu@'Ruyu@&Reie@UQUUU@D@P000 @ 0@000@ @QPa@URUUu@&eReeu@'eRuuu@'eRuuu@&UReee@UQUUU@@P      @000004 $@Ue@UUu@&e%eu@'e5uu@'e5uq@&Q%ea@QUQ@P 00 004@ `$@ `$QUeQUe%aUe$aUe$!Q$QA @ `$@@@@@@@@@@@@@@@P````PDTdd@APd@APd@APT@A@@A@@A@@A @D@ U@ e@e@UUUeYG@EUUeYF@dEUUUUE@eEUETD@jEY@@D@nE]@@D@^EM@@DZE@UPE@]UU]f ?^g?Vfg]O:Fff]JefYUIufTHZ @H@E^A @E@EMA@EE 4 @]tSIE@]U}VYUE]jJ^oOfoOvjJ@evYM@ug @ @DA @@D A @ E ; n7, @uSi G@E}VmYGڊ]nJ^oOfoOvjJeuVYUue @D @@D @E"7  { n', @eQi F@EmUmYFڊ]nJ^_OV_OVZJeEVVYUUeEFT @ @ @ @EQbw  {  @UQU F@EYUUYV֊XZjX_}U_}VZiUUSUUUUQTCT @0@0@ @@Q b w  @  @UPTE@YUUUU֊XYeX]uU]uVYe@UUSUUUU@ATCP@$@4@4@ @@@@@@@@Q  @ @ @ՅUPDU@ՅUUUUUVUeWUuWUuVUe@UeSUUUU@dCP`$P$P$$@@@@@@@Q  @ DH@MUPDU@MUUUUUJVUeWUuWWUuVVUe@uSUUUY@tCT` $` $` $` $P@@@@@@@@@@@@@@D H @]UPDU@]UUUUUVeiWWeyWWeyVVei@uRUUU]@tBT` $p04p04 $@@@@@@@@ @ @A0A UYWPEeUYWUUUe^Vfj_Wgz_WgzVVfj@uQUUU]@tPT:  (<?00<4?00<4 (AA @A0@Q0Q UUWPEeUUWUUUunVfzWgzWgzfVfj@uQUUUU@tPd:  (0?0 ,0?0 ,0* ( @0@S$ $0S$ $ WUSPEeUUcUUUunrfzsgzcgz&Rfj@UaUUUU@TpT:00> 0>0*     $ @  $0@#tp|4@#`,@PEm@cUU}.rfz?sgz?cgz&Rfj@UaUUUU@TpT)0 9  9 ) 00!  ( @#pp|43tBp|4#R`(RPibUy*af&v;av'v; 6'v&&&f@QUU@T%%%% 00! ( @3pAp|4@:dAl%@*dQh%@UQUU@UaUU@%faFiF@4faFmF4& -F$)FE 00% ( @6`A`l%@%PPT@%PPT@QPU@Q@E@A@E@A@E   % @%PPTPPP@@@P@@@@P  @ @ @T@@!@1@1 0@0P0  @UaP`G@UaP`F@UePTUE@UeAUE@eiBJF@uyCNG@uyCNG ( P@@@@@@1F@@1NQ2*/VZoGVZjFdVfUYUIeVfEUIjfjBJJ@nu~COG@^u}COGZ ( EP@@@@@@5@!uSE@!}VUZ"* ڷ/WڷoOzVnJe۶Yuۧ]˫ @E~C OG@E}COGh ETTDDDD@A ^6) @uSu G@U}VuYG۷۷πeYu]@EjBNF@EiBNFh EXTDDDD@A ; n7. @uSu G@U}VuYGꚭ۷۷πuYu]@EVAIU@EUAIU ETDP@P@P@P@Q@"@7A { '. @eRu G@UmVuYGꚭfggπڧfuWWYYeGY(@UD$@UD$@ @EDD@P@@PA@PA@PA@QQA @bA@wA {B * UQe G@]UeYWꊬjkkڧjUUSWYUUQUCYT$44@ @D@PAPA@PA@PA@PA@PA@QQ @bwB A  UQU F@]UUYVꊬjkkڦjUUSUYUUAUCYP@S$@S4@S4@@$@DP@PQ@PA@QA@QA@QA@QA@QA @A@D@@UPUU@]UUUUꊮjkkꦚj@UuSUUUYuCQPc)S%S%@%@P@PQ@PA@QA@QA@QA@QA@QA @ @@ D@ԍUPEU@ՍYUUUUꊪjkokjꦚj@SUUU]CQT !$c !$cA !$@b@ !$@UPP@EPQ@EPA@EQA@EQA@EA@A@@@@T H ]UPDU]YUUUUj{o{jꦪj@SUUU]@SQT@ "$.@q024*@q024&@a "$@UP@EQ@EA@EA@UA@UA@A@@ @P @AT0H ]VPEU]VUUUUj{o{jꦪj@QUUU]@QQT: "(>?q03<;@?qp3<7@a`"(@UP@EQ@EA@EA@UAUAA @AP0@R5R)VYWQEYUYWUUUYnj{{jꦪj@eaUUUY@d`Qt: "8>?0 3<;@?0 3,7@*` "(@Q@A@A@A@Qa!  !@Q5SeRbduSmWEmUUmnj{{jڦj@UaUUUU@TpUt:082? <2?2*PQAAAQa11Q!"$a@aQbduou|won_EmUU}nj{{jڦj@UaUUUU@TpUt)04!9 4!9!)!11Q%!!"(b@'qas|w?vr/ْo٢U.qjj?q{{?0;{* *j@Q UUU@0UT%0% %% 11Q%" "*@7rqs>dqo.aQ[aU[@)FqZjj@@?q!03<;@?qp3<7a`"(UPTQE AA@!BA@!BAU#AQAA @AP4@VTUT5֕T)֕UUYUUY߀jڦ@UaUUUY@pQt:0 "8>?p 3<;@?t`3,7@*d`"(@ePT@UPD@U@@@U@@@U@@a@!P  !@UPQUT5WezeuWmU]U]}߀iڦ@UUaUUUU@TpUt:086? <6@?6@*T@UT@UD@U@@E@@U@a@1PU1U!&%e@WaQjeuov#}wk"o[U_UU_m}}߀iڦ@UUaUUUU@TpUt%045 45%!1U1U)"!*")f@/ra{#}ww3o"__UU_muj|u|4?߀h$*@TQ UUU@0Ud%0$% $%%!1E1)#!*"*@?sq3>gr7n&^[UU[@iJuZj@|GuO|4?h$ *TUEU!!D)!)# *'*@>cqo7@-Wq'o@-Ve&@]UUV@UUeUV@iDuPJ @|Du@O |$ h TEAD-5 '*@5Sq['o@%VaFZI@UaFZE@UUQEUE@UUQEUUE@eDA@@@pDA@@p`PD@$E5 I@5RaFZI$AEEAEEDAEEDAEAD@@@@ @@ @@ E$E$AEEA@AAAAA@@@@@@E@D@E@D@UP@E@UQQQE@eQa%E@eQa%E@eQa%E@ Qa%@QQQ@PAQ@@@@@ @ P@  @000@@uAqpD@PeAa`D@TUUUPUE@UUUUUUE@UefeeE@uuuE@uuuE@$e%@UUu@TEupPP@@0@0P@0! P010@`ueqpU@`eua`UdvTUefUUij)u~K)u~K)$ꪪ* @YUu@TEupPP@@@@0WP@0WQI0**]0??uWejjV߶Yuߧ]zꮪ~}oO~}oOj(* @UYUu@\Euq Q QQ@Q@@&%@1gRe@1gVeY2*]3?wWfjVYu]ڮm_Om_O( @UYUu@\Euq @U@@U@@U@Q@U@Q@@@@A@DI@Z6) @Y5vSuV@YuvVuUVY]߀Yu]ڮހ]^NZ]^NZmZ @UYUe@XEeDa@ E@U@EAUFE@EAUFE@EQAUEE@EQAED@FA@D@A DH+SH nb7.]uuSu W@]uyVuYWi}߀ꦪ]eYـƙ@UZEIU@UYEIU@YE @UEYEUV@DXEUB@DAQB@EAUE@EAUFE@EUFE@$EbUEE@!GcE@"G@'SAD {S nb7C. ]uuSu W@]uyVuYWi}߀ꦪY]eYYYD@UVEDDe@UFETDe@ET%@UEUTDU@ETEUEA@DAQFA@EAUFE@EAYFIFYFI$FbYEIqGcrGwSA {R n'B. ]eRu W@]mVuYWi}}߀iꦪeeSV]UUeeCFYTTgCBD@dWCAD@uWCAD@u@D%@EEAD@U@ETAUEA@FQFM@GUFM@,YFM(YFM$rYEMaEsbFwRA U @* UPu W@^UuYW隮߀}߀iꦪ@UuSU]UYEuCYUEgE(EWD8AW@@8G@$GQ@@UFTQQEEJUFM|OUFM|YFM8]FM4r]EMaIrE QA@U@) @UPe W@^UeYW隮߀}߀iꦪ@YSU]U] CYU-S-DWCP)VEP%VQPQUQPEEDFL|DFL|DFL8JDFL4EsCEE\asC @Q @T@@UPTV@]UUUV銭߀߀@YSeUU]S%QU !%c !%UgV`a%UfUa%YeQUUYQEExڥDFL|uDFL|uDFL8ZgFD@4SAET@aCA@@T@UT%@T!@UPTU@]UUUU隭߀߀@URuUV]@R%QV@* "%.@*q024*@jqQpr4*ZqUb$YeUQTUYUQEExeEFMtuEFMtuEBMZgBE@SEAU@ACADD$@UD$@cTAP@P5XEP@P%]UPEUU]]UUUU隭߀߀@UauUUY@a%Ut:! &8>?q!07<;@?uQp7<7y@&(iPTUPEE$`BM@$qGBM@$qGBM@jcGBE@%SUQUA%CQXR$D @STAP@P4@WTeT5X֥T%]֥UUU]UUU隭߀߀@UaUUUY@qUt:1 *8:?`! ;<;@?tQa;,7*xQa*()iaRTU)aRDE$aB@I@$QA@I@$QA@I@bRA@E@1RQPU1RUY %@WPQUT5VezeuiUY՝UY隭߀}߀i@UUaUUUU@TpUt:P086?P |6@?TQ\6@*TQX@)UQTU@)UQQDE@$VQAAI@$VQAEI@$VQAEI@bRAEE@1RUUU1RUUUU!&ee@VaQjeuov{knU^U^߀}߀i@UUaUUUU@TpUd%0$5 d@5T@%T@TU@DE@$AH@$EH@$H@"D@1UU1UUU)"!*ij@ora{{wo_U_UU_iyj|y߀|5?߀h%*@TQ!UUU@ UT% % T@% T@% T@DT@DD@$AH@$EH@$H"D!UE!UU)#!*j@?sqgro_ݗU_UݦU_iJuZj@|GuO|4?h$ *TUEUDD H$H$H DUE-UY)# *j@>cqo-Wr߀mfڪꛀ]VU[UfU[@iHuPJ @|Du@O |$ h TEEUE-YU5 j@5Sq[@)UaZ@YUeZV@YUUUUV@UUUUUV@eDEP@@pDE@@p`PD@UE@,YU@5 Z@5QaVZ@$EEE@EEE@DEUEE@DEUEA@DD@@D @ @@EE@ YEE@$EE@$AEEHDD@DHLL@@@@@A@@A@@@UQ@EA@UQQQUE@eQaeE@eQaeE@eQaeE@aQaeE@QQQQUE@PAQ@ @0P@0 @000@@uAqp@@PeAa`@@TVQU@UVUU@Ufe@uuI@uuJ@eeJ@UUUeUF@TUe@ @0P@0 !@000!@uAqpeeAb`eߖUuߗUyꪪ))π)ꪪ@UUUuUG@TUu@@@0SP0SQE0*%"U0:5"@uzufejefUuUz~~πj@UUYUuUG@U\UuHLLHAQA@UE@EUA@qSQU@qSUUU1j%&U1z5&uzufejefUuUπ@UUYUuUG@UE\UuX@@\AP@\AT[E@XEUZE@EQUUUE@EQUUD@EE@D@EE@D@VRFDE@f&B)@ugReU@ugVeUU@Uujef@Uuzuf@uzufejef]e]ـڀ^N^NZ @UUYUuW@E\UuD@EP@EU[E@EZE@EbUUE@FbUH@FR @H@VRE DHgSBDMj7C.YuveV@YuveUV@Uuef@Uuuf@uzufejefח]eח]ז@UYUIU@UYUIU@EYU V@UEYUeW@E\UeGDEؕ,ԕ[hdrYasbSgSDLH @.]eu W@]euY[@Uej@Uuj@嵕zuj奖jefeWV]UUeeWV]UTeWVMEeUVUMEeVFUUDe@EU&@UFYUDV@JXUUUBIIؕ,ԕhʵdv]MqKw  AD  @. ]uUu W@]uUuYW@Ueef@Uuf@Uzuf@Ujef@UuSU]UUUuCEYUTgGAHEdWGAHEuWGAT@uT%EET@UTEUEEKǕ,Ǚxηtw]EMKw  A Q @* UPu W@ݥZUuYW@奛jef@Uzuf@eUzuf@iejef@YuSU]U]YuCYUU#,US9EWCP9EP%ݏUCUݎUFEՊOǕ|Ǚxηts]EMIsE QA@U@D) @UPu W@ZUuYW@啛jef@Uzuf@eUzvf@mjff@SeYV] C%YV-S-DWWPQ)[UQ%[ՑZUܚՑZE镕ו|׉x޷tsMU\aYs U @UE @U)@D$(@UPd)W@^UeYW@啟jef@Uzuf@Uzvf@zjf@]RuUZ]R%UZ%%%c%%%UgUea)fUa)fՑYUՑYE啕|uӕ|uI8Zg 4USV UTaUSV U@U%@UAU5@ET&@UPTV@^UUUV@剞jef@Ezuf@Uzvf@zkf@UQuU[Y@Q%QZ@*&"%.@*q625*@juUvr5*jyUb%m}QUUmQUE|꥕VtuVtvZdjg@$iSUQU@eiCQZiF$@UBF4@gUAPP5@XEPP!@]UPUU@M^UUUU@Ijef@Ezuf@Uzvf@zgf@UQuUWY@a%Uv@:1"&9>@?137<;@esw<7~yUf(}}vUTUivUUEtvZtu^tv^Tjf@~RUQYE~BQXVjD(@WUAP@8@WUUT5YUT%]UUUU]UUUUYjef@Uzuf@Uzuf@Ujef@UUQUUUU@`Ut:0"*86?0#;|7@?ac;l7>xU*=vU)vE4v4U4UjU@}QUUU}QUYiQ%@WUQUT5@Veveuֶeeֵ֝UUU՝UUUYjeF@UzuF@UzuF@UjeF@TUaUUUU@pUt:08&?0|&@?aR\*>jUR=juVY)ZuVI4Zu4WU4WUbU@1QUUU@uQUUUU@eQ&ee@VeQfeuov뽻nU]U]YjeJ@UzuJ@UzuJ@UjeJ@TUaUUUU@T`Ud% $5 d@5AT%UATU!FTYU!FDI4V!FA@4WEE@4WE@b@1UU@uUUU)f*iov{뽻oU_U_Y]jeJ@U]zuJ@U:uJ@Q*eJ@TQUUU@UT%@E %AE T@%EE T@%EE T@EEDU@EEDE@4FEA@4GE@4W b aUE@-eUUY)*joU_U_ImZeJ@EmJuJ% 5J %JTUEU@A@AP@AP@AP@A@E@A@E@0AA@4E@4 UE@-UUU]9*j>on߿nڪڪ^ݖU_UݚU_HIPE @DI@E TEE0 4 4  UE@-UUY]9j=߿[߀)mVZYiVZVYUUUUVUUUUUV@HEP@DE@PD@044UE@-UUYU@5iZ@5mVZ@$EEEE@TEEEE@TEEUEE@PEAUEA@@@@@@  @EE@$EYEE@$EEE@$EEEEHDD@DHLL@@@@@@@@@@DUQQ@EQ@DUQQQUU@DeQaeU@DeQaeU@DeQaeU@DeQaeU@DUQQQUU@PAQ@ @ P P 0P @0P @@ P @@TURUQU@UURUUU@fee@uuy@uuz@eej@UUUUUUV@TUU@ @0P@0P @0P 0@uAQP`peARP``ߒUՀUߓUՀ檪@UUYUeUW@XUe@@@@QA@DQA C@0CQF0B%Q%!V0Q5Q%1@uQuQeueQfUeeUuUꪪ@UUiUuU[@UlUu HP LP@\P_G@XAQZF@AQQUUE@AQQUD@AA@D@AA@D@UQE@D@UA@qSQU@qSUU@F1Re%!@V1Qu51@uQuuueQfueUuY@eUiUuU[@eUlUu hQP lQP@\QU_G@XEUZF@EQUUUE@EQUUD@EQE@D@UQE@DVQE@EfA@qSQUU@uSUUUU@F5Reee@V5Quuu@uQuuueQfueӗ]eӗ]窮Ꚁ^^Z@UUYUuEW@UE\UuXEP@\EP@\EU_GXFF$rY%s&c &b @HgWEDMfA UsYU@UsUUY@Vrem@VvQu}@uQuuyeQfueeV]UUUeV]UUiVEU@Y]UEU@Y]UEU@I]UEV@UEYUuEW@UIlUuGXͼ\l_ˀhεd϶]qϷrϷgۧDLWETM@.]vjV@]vfUZ@Vven@VvUu}@uUuuy@eVeuf@uRU]UV@UuFU]UV@eJUMEe@UNUMEe@VNUYFe@IY&@UYUeFW@lUeVCͼ,ˀxεt϶]ۗTLQhM@.(]ez)W@mevY[@feen@fVUu}@fUUuvy@eVeve@YuRU]VUYuVYVU#GX%USGY5UWGf 6Ew 'ϙUKWάUJFȎ⚊lʀxηt{]{dLU8 e@*< Uu-W@VuY[@Wej@UWuy@vUVuvy@neVeze@]uSUYZYmuWYZu#-uS=eWWXf :U\w']OWYJF暊|ʀx޷t{]UM[ V  QQ$ U9D)<Uu-W@VuY[@Wej@UWuy@vUVuvy@neVue@uRuU_]-uR%U^4#-4S-dWWXb*Ub&ܟ_VܞZF՚|̀xާtkWMUYYoW UF UE %@iU)@D),@UPu-W@ZUuYW@[eef@U[ueu@UVufu@Uuoe@]QuU_]@-Q%U^@5%%%@5c%%%egUia*kUa*{ّZUݺZE՚ҕ|ÌxjM@ticYNUU@cY @#I @"I%fW]6Wh&@WUPdW@UZUeYW@U[eef@U[ueu@UVufv@Uuof@]Que_Z@a%a^@*!&"%.@*!625*jevr5*iUb%}ՑUUՑUEtߺڧtkπdjn@$~cY^RY@es][@V3 K+@V" N;@gVQU6@ZUT&@ZUPTV@I^UUUV@I_Uef@E_Ueu@UVefv@Uugg@UQufW[@`%fV@:0"&):@?037};@esw};~yUf}UYiUItjtߺotoπTn@cY^VYs][3G/@WbV;@gUQU5@YUU%@]UUUU@]^UUUU@I_Uee@E_Ueu@EVeev@Ueeg@UQeVUW@`%VT:0"*(6?0#;l7ec;l;~{U*}U]iUIt귛jtצoto΀Xn@QU^UYQU[kQ-@WUQU9@Vefeuifee]eUUU]UUUUI_UeE@E_UeE@EVUeE@IUUeE@U]UUUUU@dUT*4V&?0V\&?a\*~U}vU_ivUKtjtotWo΀\jj@QUUZY@QUUY]@kQfem@VeQfeyov{}溪ijם׹UUY՝ߕUUYI_UUE@E_UUE@EVUUE@EUUUE@TUUUUUU@dUT%dE5`UT9aT)U)uUZ)uWUJ4u[j4eYoU΀4WeIo΀bj@eUU@eUUU])fjiov{}U^U^I^UUE@E^UUE@EUE@AUE@TAUUU@@UT%PPE %QPE T%UQF T%UUF TUUFETUUeFUTE4VeEjQ@4W%@U@4W%@ RjUUE@mUUUU]ijoU_U_I]UUE@E]EUEEETUE@U@@A@AA@@EA@@EA@@EAE@E@EAU@E@4FAjA@4GE@4W jUE@mUUU]yj~޿ګڪݗUUUݞUUUHM@@DM@TEE@@@D@D@0HA 4LE 4L HUE@mUUUY]j޿@mښV@iV@UUUU@YUUU@DI@@DI@PD@DD0H4L4LHUE@iUUUYUU@iZV@mޚV@EUEEE@EUEEE@UEUEUEE@QEYEUEEHH@  EE@TEEYEE@EEEE@EUEEEPHPD@D@@DPHPHPH@@@@@@@@@@QUQP@UP@QUQQQUT@QeQaeU@QeQaeU@QeQaeU@QeQaeU@QUQQQUU@AQ@@@ @ @ @ @@UUQVAUT@UUQVUUT@bfei@buuy@buuy@beei@UUQUUUU@UU 0 0  @@ @ @@ @ @ @0@ @0 @ URWEeTUURWUeT@euUUU^@%tUU " 0#@@0#@@ "@A@@A@@@UQE@DUA D0DB0 @ C 0@1@eq@PqebD`aSEUWUՀ@yUeU_@yUe`*Up/@XUuoG@XUejF@UQUQUE@UQUUD@EQE@D@EQE@D@UQE@D@UA@aEqDB0 @ W 0@11@eQq@queQbDqe՞ӗEUחY몪@yUuU_@yUu`*Up/@XUuoG@XUeF$UbUU%VbU%FbE%VbE@HVVQEY@DVVA@a3Y@q3TF02P W Pu1@eQqPuueAbDueeVEUeV]Uj^^Z@eUiUuE[@eUlUu hU`* hUp/@XUuoG@XVfFhUy۳]z˳M̀jMDLguE_DMg6AD @W3YD@V3TTF2P-WjTu=@eQqXuy@eAa\uf@uSU]eV@uWU]eV@i[UeU@Y]UUU@Y]eUV@Y]uEV@eUYuuEW@eYleuGhݼ*hݹ/X٥oˀXεh϶]}]~Nk۶ODLweU_Mk&A [3[T@j3WTv2WU-wjUu=@geQqYv9@Z!AaXv'@Y0RYV@Y5VYV@%ZYE%@U^YE%@U^%jF&@Y4z&@]YuvFW@]levVGXͼXhˀxεx϶]]NۦOLUUMA. ^2kh@n2gXv2gU.wj!Vu=@gU!qV9@^!!aV'@]0U@m5U@e)'VF)@eY'VF9eZg)f :f,w 'imKWliJGͼ⚆hˀxޥtߪ] ڪLUUUIeD* ]1vl@m2vXv3f.wf#V5=wV"pV:9.f!`f?&]uPemu@e@u*'D-@u_7T=e_wX:f\'Ϲ]_WμYZFͼ⚆hˀxާtޫgYIZIZ IY IVUfD*%ul@&uX'f.V'V5=wU&pf:9.e%pf?%epe-e e@4j&-@4_7-d_wX*f\'m_WiZFպhĀ@[fMeU@i^ u@e qUiWUy}gD*|gPulePuXe'`f.U'pV=U&pf:U%pf&]Uqe-UU!e@5Zf%@5_v%e_vY*f/ݯ_UݮZEպ_h[YŀjM@~kiNfY@վky v@U+y v@U+iejW6E_Y:W5Dj\&WPu\UPuXU'Pf*U'PV9U&`f:%pv'Yqv@ie!f@fV%*@fVu*fVy*jizYUպYE궋dπXn@XgY^WY@Yw][GV7 KW/V7 _U?gWrW:Wuf&UPuUVPuUgPf*UgPV9U&`f:%`w'Uaw_@Ud!g^@VVfi6@WWw}7@k[w};fUYUIjdπXꗦn@cY^WY s][3G/WrW?@gWQUv@ZUUf@UUeV@ZTe@ kPd&@EkPU5E&e6%w'QwR@dgV&Vji6'W{l7k{l;~jU_UKjdπ\ꕦn@bY^[Yr][^rn@WbYz@Vefeu@ifee@]UUUU@]UUUU@IKPTQ@EKPTQEDDQDP@dUT&شV&7W'{+~}U_iUKd껟jddπ\ꕆj@QUY_Y@QUY]]@kQfem@VeQfeyov{}j檪ij]שUUU]UUUIOPTP@EOPTPDIDPMDP@LUUT%XeF5eWy}}U_izUKtzjt:]UtV*M π\Vj@UU_U@UUUU])fjiov{}םUUU՝UUUIOPDP@EOPDPDADTADP@@ET%PaF %QrV %r ef eVEJefVUJtfUj@t%PU@tV%@ \j@\U_E@]UUU]jUUUݞUUUIN@D@EM@DDDPDP@DP@@QE@@AQEP@EQEP@EUEP@EUEEPE@EEEUPE@4FDEjQ@tFU@tF Xj@XUE@iUUU]zꪪj޿߀ުYUUUYUUUM@D@M@DDDPD@D@@A@@A@@A@@A@@AD@@AD@@0AHA t@LE t@L TH@TUE@eUUY]j޿@nٚV@jV@YUUUUU@Y]UUUUMDMDDDPD@DDHLLH@UE@eUUUYYU@jZV@nݚV@EUEEE@EUEEE@EUEUEE@E]EUEE  @  @EE@UEUEYEE@EUEEE@EUEEE@H@D@D@@D@H@H@H@QUQPPP@QUQQPT@QeQa`T@QeQa`T@QeQa`T@QeQa`T@QUQQAPT@AP@@@@ @UURTdT@UURUdT@Redi@Ruty@Ruty@bedi@UuaUETY@5 EP 0 0000 @@QADQA @!@!@ !@UUSWEtU@UUSWEtUJJZꦪZ@vUUT]@vUUQ f! UU1@PUU1D@@QU!D@QQQ@D@QQQD@AAD@AAD@UQE@DUQE@PQ@PQ@B@ GP!P!P !@USWEtU@UUWWEtU窊ꦪ@vUUT_@vUUUf*UU:@TUU:G@TUU*F@UQUQE@UQUD@EQE@D@EQE@D@aED@bE@`RT`RTDPB P@P W`P@u!@UQ@uaeR@ubeWWEuVeWWEuV몊@yUeT_@uUeUe*UU:@TUUzG@TUUF$bQ%c%c%bHzOIĀzղKI@6cIcDPTR"@P`WjPu!@UQQPuaeARTucuSVUuWuWV]uWjjY^~߭Y^~߬ZZj[@yeud_@yeuUNi*Ui: @TUezK@TVUFhQy߳UzϳÈjIO_̀{ֶK^@ws[@VsWPX@V2SPlWjUum@UQQUi@%AQTg@uRUUW@uVU]W@iZUeU@Y]UeU@Y]euU@YmuuV@iuue[@heuUKx*y:TezTNeh]]NO^ߔkJ^@gs[@fs[TY@v2[UmwjYem@gUQY)@^aQY'@]pQUU@]uUUY@eZUYU%@U^UYe%@U^ij5&@Yxj5&@mYyve[@mlivUKdټdT濻ThߝΔϔ]UM^E^I@[{[dU@jwWTY@v6[Umwj1[emgU1P[)^e!Pj']uPemue@e'VW)@e'Vg9@egf::@]f|w>'@i}nW@hiZG͸d濻٭٭Ɣ٩M@U^UI@e^Ej @zghnvgXv6kU-w5[e-g5[)n%Pz']ePumu@u'ȖW-@u7ܗW=ew:fl'ym_W|iZFͼd꿷i@ijU}E@iUmE@eeI@UI@VjV@f)j@9vlm6vXv6j.w6[%-g6k*)n%z/']Uu-Uu@4fȒ-@4w-dw*f\'ߺm_W޿yZFݿ庅T뿙e@ukiNvU@uoJvV@eovV@UkVW6g5*}5lmuXv&j.w6[-g6k *j%`z'YaviY%f@uj&@uv&ev]*f]/m_VyZFԞչ_߳T۳ت@ؾkiNwY{ w; w;j_6_i;~[5jm&}gDunmeuZv&j.w6[-g6k*f%P{'UUw@UmUg @UٽV% @UVu UZykiկڹYEծYEԮ测T뿺ꗦ@gi^kYwm[{V7Kw/V7_e?k2W:5f&TuDu&@j.w6P[-g6Pk*@V%P{ '@UUwZ@UmUgj@V齖fi@Ww}@kw} zf UIUIT뿺\ꗦ@cYZ[Ys\[3G/W2W?g_QY:ZUY&VUUuUVTuVgh.gW{Y-cW:PiE*@)Q{I'@UwY@eUgijy6k{|7k{l;jU_UKT꿶\ꖦ@bYV_Ya]Va].Wa]>@feQfu5@iVfe%@YWUeU@YWUeU@JG@h@gGK@Y cG @I @VAK@UEGPeEWd.*?+{+~U_UK꿟Tꏺ\֕@QUU_Y@QUU^@Qff)@VeQfu9ov{|jjij@]YUUY@]UTU@JK@X@GK@X@GH@@H@@DPUEUd%e5糟y}U_kUK?\UTU*L \@\U_U@]UUU]ijiov{|ꮪj]יUTU]TTJK@X@GK@X@GH@FH@D@@EED%eJ %rZ ir if iVEKiJZUKTJU@T U@TE X@XU_E@YUUU]jꮪj]יUTU]DTJN@H@FM@H@H@H@AD@@ED@@QE@@QEP@TQEP@TUUEP@TUUEEPE@TUEEUPD@dUDEQdEUdE @U_E@UUU]ꪪ޿䛀ꞩZ@]YUTU@]UDTNHMH@H@H@AD@AD@@AA@@AA@@AA@@AA@@AAD@@@AD@$@Ad@d@ @UZE@UUUYڮ޿@֝nٔV@jV@]UUUTU@]]UDT @ @@@@@@@@DDD@D@@ԔUUE@ՕUUUUU@֙nV@֝nٔV@I]E@E@IUE@E@UIUEE@E@QIUAD@@@@@@@TDEDE@UEUEEDE@I]EDE@I]EDE@D@@D@@D@@@@@@@D@@D@@D@@D@ @QUQP`P@QUQQ`P@QeQa`P@QeQa`P@QeQa`P@QeQa`P@QeQQ@PPeQ@P@ @@@@@DDD @UURTtU@UURUtU@Rde@St@St@Rd@USETUQ~SUDPE@z @ @u0@0@u0@0@ @ @Q@DQ@DPPPPPTPPP$@USTEtU@USUEtUIIII@gETYgUDQY&@!u@1Pu@1@a@!QP@QPA@DA@DUQE@DUQE@DQQ@PQQ@PR@PVPP`$@eWTEtV@eWUEtV窊ꚨ@wUT]wUUU]&Q%uQ5TuQQ5VDeQQ%VURUQUVRUDFRE@DFRE@D)aD)a%SQ@QQUTT@RQPPTVQPUQQUڡRe@ٵWVUuW@ٵWVUuW몚퀪Ꞙ@z]T]vUYU]&U%uQ5@TuUQ5V@TeUQ%V(Q9۳U9˳DT̀)D~Iz߶MvoVM@ViVU T@RiPPTViPUU@UQ@Q@VUUW@ZUUWꪪjY^z߭Y^z߬YjjY@yemd]ueiU]%e%vQ5@TvUQuZ@TeUQVhQ}U~D̀nHM{߶MwgN@VgUT@&PPT@VjPUU@UQU@Q!W@UUeW@]UeW@֩]eUU@Y]eeU@Y]euU@imuuV@yuyf^@ueyW^@%u'^@V%Q6^@TVfQuZ@TVVQVhߕ߅I܌]؈{_@fwT@&vQX@gjQUY@UPY@eP)W@uQTe[uUTee[diV)U_tYf)U_xi9%Umxi=&@iyun_@diu[_@g^@v^@TuZ@TZVhսխŘ͜U]eMe^e]I@{[U@wTY@vUY@gjuUY@gU5PY@^e%P:W@mePu[meu@u'fW-@u'Vg9@e'f::@]&|v>&@m}vnW@mdiv[[@dWZ@dVZ@TeZ@e}V@in}U@imEihIYM@U^uI@e^eYI@{WdU@wTY@6U]@5U]@5Y@%:W@}%v[}%v@u'W-@u7g=@ew+:@Yfl/'@imwoW@hirZZ@VZ@VZ@TeZ@eV@u[iսV@u^սE@e^ɹI@UZI@Uk@e+j@;ehU@5%XY@5*U]@w5]@g5Y@Z%:W@y%u[9eu@4fؒ-@4w-dw*f\'zm_WyZZԉzuYzuZTzuZejyV@ukeRzV@ٵoVz@ov@Օk7_7j@}ulV@m5XZ@v%*^@wy]@gy/ Y@Vi:V@UYuZeYedj&dv&dv+fm/@Zm_V@_yYVԞvuU^ruVTZryVffyV@ugiJ{ZwK{7Kw7_j7_i;~7jm&@}'unW@m%5[[@v%*^@wy]@gy/ Z@ViP; W@U]UwW@UmUg@T޽f%@T޵vu@Tvu@Wfe@WeEUV@VuUUWVzuWVzuVTz{Vf_V@fiVoZwlWof7Kg/f7_e?{1W:~1f&@}!TuW@i!T5[@v%@*^@wyP]@g}Q/EZ@mQ;]W@]Uw]W@mUgm@Ui@TU}@TU}@fUi@ԮUuV@ԮUuVԮﺥqVzuVdz{Vf_U@bYV_Yr\Vf2G/w2W?{QY9UY%UUuUUT5YVe@*]wWy@]cW}@/EZ@mA+]V@nUg]V@nUgm Yy hY| hYl Y UZ@UZԮ瘟VzuVdz{Vl֕f_U@,aYU_YaYU&aY)waY9@feQvu5@eVfe%@UVUuV]@UVU5V]VV)\WWZ]SWN@EY@]AMU@jVFwV|x EOkUKޯG^uFTN{F\F_E@\QUU_U@]QUU]@fQfe%@feQvu5@ow{};@ji*@WUeU]@WUeU]@JF@)L@PGJ@MPG@ I@TJA E@t PF&@mV}u.V@}UTiuZ@tWT]@dWe^@TWfe[@egW@e{Y׼V@e~׬GenըKU[O@U^uI@e^YI@z[U@u[UU@uUU@iUU@)U@(:W@y)v[y5wu7W-u7g=e7+:]&|/'@M}v/W@MUirZ@TU]@de^@dfe[@gW@{eV@u~@enٸJ@UZI@U^V@eZV@UiV@UiZ@&UY@jUY@mIY@m:IV@uivYZuyw[tzأW-twW-dw)Xf\%@HVmqU@HWiqY@TWe&]@dve&^@dvei[@weW@[eQV@ܵ[U@Z@ԕ^V7_7k@7gnW@6&jZ@&*VY@jVY@mYY@m*YU@emfYYe}gYd}%du%du+ei+@ZiAV@[yA[@[u&[@za&Z@dva)[@ve-W@fee/Vw|e7Zw7_7_Y;7k]'@}7g^W@m5&[Z@e*W^@iV]@m.UZ@ڭ*]V@ޝe]Z@e] @e @Tvu @Tvv@Vf&@ԖzecV@ԗuc[@ԗu%[@ze%Z@tue+[@ueW@fUV/Zv\Wo6Kg/6_e?@2W:@2f&@}2vf@m16[j@e*Wm@iAVm@mAUZ@ڭ]V@ݝW]V@흕Wm@yUi@TuշU}@TeշU}@eUi@ԮuWeV@ԞuWeW@ԞueW@zeeV@tye+W@|ueW@laYVZ]a\V@!G.@1W>{aY9zaY%yQUu]iQU5[]vbeA*G]wiAF]@cmAEY@n]Y@ZZZ Y lY| hYl Yi @警VuZ@kVu[@]kaW@EZeeV@tZe*U@xUeU@hQUQYYQYUfQY)w^aY9@giQfu5@Qfe%@QUuV]@QU5W]VU)G]WYF]S]AEY@]]Y@Z|x EN@[zUJ@ԝ_UF@^UeF@DEZE*E@HUEE@XQEQ^U@YQUU]@fQfe%@gmQfu5@ow{|;@ji*@VUuV]@VUu]VV)\UY]Q]@ EY@]A EU@ V  |볯 x EtM@ZzU4I@Ԝ^U!E@A@DEY@IA@T@IA@UEUYU@UUUY@jy*@ow{|;?ꪪ*@WUeV]@WUe]Z)\UW TUW @ ET@UVA EU@5* V8& 8f (ڲZ d d֦ݕETM@IJUH@\LEDD @DD@E@@E@@EUUE@UUUUꪫ*?/*@՝WUUU]@՝WUU]Z\EWTEW T@EV T@TUVT QVP QVP TQPTQPQEPA@ABU@@XDA@D@@D@@@EUUE@UUUU*/޿@]WUUU@]WUUX@T@T @DP @QD@QD@AP@@AP@@DEP@@DEP@@@EPD@@@@D@@@@D@D@ԝEEU@՝UUUUޮ޿֝nݔj@]WUUU@]WUTH@D@@@@@@@@@D@DH@]EEP@]UUUT֝n֝nݔ@I]E@@IUE@@EUED@@EUAD@@@@@@@@D@D@@DED@@EUED@@I]E@@I]E@@D@D@D@@@@@@@@D@D@D@D   @QP@QUQAPQ@QUQA`Q@QUQA`Q@QUQA`Q@QUQA`Q@QUQAPQ@UQAPA !@!@!@UP@UUQUTU@UUREde@URtv@URtv@URdf@URTU@PURUPA@e@u@u@ @@@@@@@P@P$D@P!PP!ȠP)!@٠TTU9@UUUyUJYi޳Y޳IڲI@USETU@PiSWDPUPiR$PuQ$PuP$@ P P@@@@@EPE@D@EPE@D@EPEAD@EPUAT@ETPBTDTPPEPEP.@FTU-W@VUUiW꺦Jiώʊ@UWITU@Q~WHPUPzR$PuQ$PuP$@ P TPP@APE@D@EPE@DaDDDDEE@VEE@ETQBTDTQPEPEP"@ETeW@YUe]W꺪Jiʎ@gMU@gHQ"RhuQhPuP(@ P%@T@@PQD@AQIDDE@QJDD=D9H5I@WIu@WVQbTYQIPIP2@ITuW@]Uu]WꪭZY߬߭@w]fY@wUXSY@"Qi@QiT{RTa)@%STp%@Ap@REdRETEE}yۍuM@UWMuԀ1WVRbtYVYX2ϫ@Yuk@]Uuk@]eUY@Y]eeY@Y]euU@iZeuU@VU]f]@VUXW]Ul_UmT[RTQaiDUcTBqe@pU@LdJ]XJ]ɌۍMWMeրWVR vYvYzX:޻@yYv{i]v/^jU-U^tZe)U^tY5%iZdY5%myUdY&=yUdY5T-%1T.@rUek@sUg@YӼV@YeY׬GXeY՘K^ٜof썀螮fɀ䮪VMŀ廦VMeŀ{VRJY^]^]z@uiv{u9w/u:gV-u+We=e'W98]&hV9$]eXe*iXekT-%1T.@5rUek@9sUg@isUV@YurUGefe՘KUkf٬@U^uI@e]M@ԪYM@վUMyUJY^]^]z@uiv[zuyw[.uzW-uwg=e7+8]&l/$]V\e/]YXeU[T-e5T.uvTe+rT'@rT@qrU@ageٸK@U[uKU_^ZeoZVzkV}V@~gVyF־WY֮Z]]]]i@umfWYu}gWt~أV-twV-dw)\f\%LU\AMUAU-e5.u6i+6T'@6db@u6f@%'Z@U[^Zٚ;_+;['ݵ{W~W@ݵwzFֹW֭Z^ٞ@֭VUY߽WU}%Tu%Tu+Xem/HvmqIv(qU6$/e6$.u5Y+}5T'mdq}elu@*k.@Z;_/7_Y;7[m'7Wng@6kvfjn@^U]E@ծU]@e@Tvu@dvz@Vf*@zes@{ds{$/z$.uu+}5T'mTq]\u^@[V/@6_U?@2W:@2g'@}2gg@n2&[zf*jBnB@UmI@Um @Ue @XUu@deշUu@Uee@vegV@weg@wU/6T.uW5T+yU5'iUTAYXE@G.@1W>{aY:~aY&}aUvj@naU6[z@fqe*fj@fծBY^]]lYudYevYe@ԮyWe@՞wyWe@՞wUe+U*uEU)uDU%eUUUAUYUUU@f^UY)@w^aY9kQfu5zQfe%yQUuVm@iQU5Wm@Vae*Vj@VB ^  |t  ЮVq @ў{zV1 @^{U! F @EEYDY @UDY@UEAY@UUUY@Ufe%@gmQfu5@o{|;@ji*@UUuV]@UU5]@Ue* Ui UAI@ Y@ ^  |볯 t EP @ќZBE @\ZAD@EDYAE@AEݕEEUݕUUUjy*o{|;?ꪪ*@VUuV]@VU5]U* UU  UUI@ I@UUYA Y@e Vh h hZ dZ VZ]UDP@ IAD@ HD@ADI@E@@E՝E՝U몹*?/ꪪ*@WUeV]@WU%]U* UU UUE@ I@UUUA I@YUV@YU@IU@IQVP@TEQUP@TEQUP@MQUDP@ AAD DD@ADH@D@@D՝E՝U*/޿@ՙWUUV@ՙWUX AT AT @AT @UD@UD@E@@E@@@E@@@E@ID@DDADHH@]YEEP@]UUT֝^Z@YUUUU@YUUHADA@@@@@@@@D@DH@YEEP@YUUUT֝^֝^@IYEI@@IUEI@@EUEE@@EUAA@@@@@@@@DED@@EUEE@@IYEI@@IYEI@@D@D@D@@@@@@@D@D@D@D     00 @QP@QUQATQ@QUQAda@QUQAdq@QUQAdq@QEQAda@QEQATQ@EQAPQ `1`1`!@U`PT@UeQUTU@UeREde@UUREtv@UURtv@UURdf@UUQTU@UQPQBA  `1` 1 `"@UpTTQ @UuUUUYVJZ޷Z޷JڦJ@UREuU@UUREqQAUB AUA0AU@0A@ A@@@EPE@D@EPE@D@EPEAD@EPUAT@EaBDDaP`"@XeW@Ue]W꺦ϟʊUWEeUjWEa@AiB d@AUAtAU@4A@ EDDP@AE@D@EPE@DaETIEdIEeH@VEeT@EabTEaQ`2@YuW@Yu]W꺪ʎugMUՀugMQ@U"Be@U]AQuUY@Q5A@@%@ED@@@DPAD@AIADE@QIEDE,rET(Ed$Eu@WEu@GVabTYad]`|]2@Yu w@]u]g@ꪭ@ޭߝ@ޭߝ@@eWMV@eWIR@URRe@U^QQRuUZ@a5A @p%@E!D@p@DP!AdA"IATErNED|wƜxۊtITWIu׀pGvbbtyWt}x\2@yYu{@i]uo@]U]@Y]eeY@Y]euU@iZeuU@eWU]fU@eWEYRU@eRAe@e^QAuUDa5E%#D%@E%3HW@D`2SXa&YK\w^L|wxێt׍uWu׀qvbuyu}_e\z@uiv{umvonjVmVnuZeiU^uYuhhZeYudiyVUYfT)9VUXWY&>UTWi&1QUVzrTUa;%sTW'@ 5sX@M0rX!fYҘK\Uw^\oM뗍Wuǀfbae_퀕Uj@uevWzuuwW.uvgV-ugWe=e'Wy8]e&hVy$]5TUjyTU[TVW)&1PWV:6rTWe;>rTg'@mrT@]prUX!fe֘ Ukf׬OY^uEY^fJŀZVEƀWVEyƀgbJj_m_퀕]j@umvWyu}wW-u~V-uwe=e7*:]f&h.']&XE.]iXEVoDF-f1@W>v1DWU;~1Dw'@m1Tw@]q2Uw@Ta'e@U[uFU^NxZe^J|VfZF}V@vWFyFzgIj]m]ŀi@֭fWY߽gW~أV-dwU-dw*,UbX'-QTemQeVf-f5W>v5WU;~5v'@m%Tv@mu&Tv@e'j@U[^tZZ;Ox+j;K}'ukG}W@ugFyFzWIպ[c]պ_c]ŀ߲Y@՚VgY@՟Vg@m"@Te%@de+xUei/yeUvievV%v.f5g>v5WY:~5v&mTu}eTu@*k.@Z;_t/7_X;7[-'7W-g@7ivygy{Szc׀j@Z޵UuI@jޥUu@fa@Tfvu*@dfv{*@tVf/*@uvUw@evTw V6w.f6g>v5WY:z4v]&iTu\yYTu\@kT.@_6_T?sW:3W'}3UWg@n3UZvyg׹{c׀ ՚ENEB\Su;@dgշSy?@Vb)>@rUw@rTwvw/f6g>vWuW :vUtf &eUUTeeYUXe@^UW.@_qW>kbYU6~rY&}rUgf@nqU&Wv@Wqe*[{@ZC E^EB\St?TgSd?Vb%>ўUg@wQg@wg/e>@fUU@UU9@XDDDU$@YUEEU@YUUUU@^UYU%@W^aYU5kQftu~afee}aUuVi@jaU5Wy@Wqe*[w@ZC ^ \t/Tg`/V.@ўUW@ўwPW@ҞwWU@VUYAUT@XADT@]EETU@]UUTU@ifde@WiQftuo{}{{jij@uUUuVm@fUU5}@Wae* ku@jAMi Ye ^h h XۢdTQ`VU@]DUU@D@T@TT T@FTYADT@ADT՝مEU՝ՕUUjo{{檪@UUuV]@UU5]@e& jujuEAM@ieUAY@U ZT T TZ `TQYPVY@]UE@@DDT D@FT]AD@@AD@՝EV՝U^j@UUuV]@UU5]e& jujuE@M@)eUAY@UUTU@UUT@EUP@EQUP@TEQUP@EU@]TD@@DDTD@FTMAD@@XAD@]EU]U]j߀@٥UUV]@֕UU]d ,Bt4B4@@M4@$@AI!@U@AD@U@AD@E@A@@E@A@@@E@A@@@E]DDDTD@BTL@D@@X@D@YEe]m֭^ZU@eUUVU@UUUU B00 @@ @ @ @@@@@@@@@@TUUEEe@UUUu֭֝@YYE DE@YUE DE@UUEDU@UUAU A00 @@@@@PDUEDA@UEUEDE@YYE DE@YYE DE@@@@@@@@     00 A P@QeQQUQ@QeQQea@QUQaeq@QUQaeq@QEQaea@QEQQUQ@EQQQAA p0p0` EpTT@UuUUUU@UuVUee@UUVuuu@UUVuu@UEVee@UUUuU@UQqQR PQP  p1p1 #EqTe @Uue]Wj޻j޻jjeWUue[SUu@RR$`@RQTpBT0BD AD@@@@@AQED@AQED@AQED@AQUT@Aa@@q@Pp@2EXu W@Uu][ڪ뮪yw]u}s]u@^bR%f@ZUQQUuFAU5F@E%E@EDA@E@E@APE@E@EQIAE@EQIA$E@EQEA%D@EQUA%T@E"TEٱdQͰx2Xu W@u][ڪۮy߷]e}۳]e@^bRef@ZYQQUu@FYAUu@FT@Ue@ETAQUUEPQE@IQDE@EQIQDEb΅TIʅdIEuHVEuTFb@Tٱ@dͰx⌠2Xu [@u][ޭީ]U]U@ZbRUf@Z]QQUu@VYAUu@FUAe@EUAQUEPAQBIDERNDF~ƜMyۆuETWEu`FbdddҜ2 @eYu z@e]uYn@]U]@Y]eeI@YUeeI@iVfE@U}WV]U@U}SVYU@V}RR e@V]QQUu@VYQUv@VUaf@UUqUVE@qQZXX ^E]LOMyۊy׉|Wu׀|ʢhd[Tޜj@TivUzUyvUnzjU]@UjZeI@UVYuE@eVe]E@UuWU]U@UySUYU@V}VQ e@W]UQVv@WYUUv@^ee+f@]uugWMpuc[!XҘ _U]\OM|Wuǀ|ʦhd[Tj@pevWyquwW-qvgVUNgVe UUYuY$iHu@Y5BUHfW@9yRIWV@6~V We@7eVv@WeUv@^e;g@]uwW]puTw[`d֘ TelOVUmuUUgJWFWuGʦhh[Xj@tکfWyuߵgW-qvUNge UUj ]dhj@]uATTjW@myQ[Z@f~UWi@geVy@weZUz@~ezg@meTvW@mu%Tv@fe%d@fUYuhU^MX U^I|ZU}FWUyFhhY逨Y@VgiVg-~ؖTfdV,eRX@-uQTe^W@myQ%[Z@f~U*Wi@ge*Vy@weUy@~e:f@meTu[@}e%Tu@e%i @UY]T ZSNX jcJ=ecF=V@ecFyVfIwIwYـnY@^ޱUvE@ԞޥUv@^"TU}%dU}(uaUwZZ@eaT7[Z@Ve;Wn@WY*V~@WVYXUy@YYT*Ue@_YTuUU@_UTu@Ed@_QUkZRYTnjrYmurUWg@jqqUUv@pe?w@~C U^Ub\st?dosd?^b%>rUwVY@rP7WX@vP;Wl@WZP)V|@WU]QUy@XQ Ue@]UEUU@]UUU@^YT@W^QYT[jQfuu^fafee]uaUeVe@ZqaU%Vu@Wpe*w@~C } e^ll\t.Tod.".@@U%TY@]AP%UX@]P)U\@WU PU\@WU]Q UY@YQ UU]ٕUUU]ՕUUUjeeWjQfuun{}{jjij@iUUuVe@VUU5u@W`d* t@~A M}A YUZXXX`T[Q`Q@@UTY@]APUT@]PUT@U PUT@WU]Q UU@Q UUٝUVٝUZꪹjn{{檪@iUUuVY@VUU5Y@W`A& pA@~pAM=dAYYTVYTZPZZPTVQYPY@UT@\@T@\@T@T @T@WT]A UY@A UYU[U_j@eUUuV]@UUU5] & 0@~p@AM@=dPAY@UPUTU@UPUT@EPUP@EPUP@DEPUP@@@@\@P@\@ P@T @ T@WT]A EY@A EYUkUoz?߀*@eUU%VU@UUU%U & O000@ 0 $@ @@@@@@@Cd]AE@BAEUeU}:?׭^ZU@eUUUU@UUUU C00 @@  @A@@@@A@@@@UYUe@UYUy֝^׭^@YYEEU@YUEEU@UUEEU@VUAU 00 @@@@@@@@@@@@@PDUEEe@UEUEEu@YYEEu@YYEEeBBAA@ A B B    A P@AePQU@AePQe!@AUQaeq@AUQaeq@AEQaea@AEQQeQ@EQaQA `Ap0  0 0   EpTT@UuTUUU@uTUee@UUuuu@UUuuu@EVuee@UUVUuU@URUqQ@eQ `@eQPp P0 @ @@@@  p p  EqTU @UuU]Yjj޹jjewYuesYu@"R$b@eQTq T1 D!AD@@@@DD@D@TT`pp2EqXu W@Uuu][ڪ뮪]u]u@bR%j@eQQUy$APU9 @P%E@TDTD@D@@EQIA@EQIA@EQEA@EQUAT@E@E@QP2EaXu W@Ueu][ڪۮ]u]u@fRej@eUQey@eEPey@eDPe@EUDTQUEATQ@HQD@EPIQD@EaTH@EadI@EaAeI@EaUAeU@Fb@G@TSTR2 UQXu V@UUuYZڪۮ]]@fRj@eUQey@eUQez@eUQf@UUUUQVEAETQDHDDPMDE@^\MٲhMٲuNVuWbTRTb@UXuu@UU\uUi@[\UY@[]eI@[UeIfg]嚀c]՚@fRj@iUQUy@iUQUz@eUQ)g@UUUUeWEAETaJXX ND]LO@_ÜM)W,Wu,涢Ƣ(o󖷮_Vf@ThuUiUxuUmxi]@Fiu]dI@VUu]uE@eVf]E@YuWV]U@yySVYU@zyVbe@{iUaUu@WiUaUv@VeeQ9g@UUuUuWUAuTq[X _D\O@WQe\ UQg( iW5F|WuG|Ʀhk󖷞T[VTf@PɨeQiAɴdQ-Atd@UEeyTdI@UUyXuF@UeiLF@UuSU]V@uySUYV@vyVae@wUaVu@WUaUv@^eQ:g@eUvWaeTv@ad @WQe\@VUtXUUd(iU5F|UuG|Ʀh󖷞TVTV@PVSXPVSPtTF`me TQme\`)e@]uRUTfW@}yRYfV@yVmfe@YmVu@YmUv@iU9g@iUu[@eeTu@ed @fUtXUQX UQ8RU9FRUyGŪ|󅻞|U|U@T^VVY^VV@Jd@J`U @UQX@eaXY@uqUdZW@}yqY%ZV@}em)Ve@mYm-Vu@iYmUv@ie9Yg@yUuY_@uTuI@eD@UQTZRX ZR<RUyQUv^Y@iQU6[Y@VUm:Wi@WYm-Vy@We]mUy@ie)Ue@_UeUU_UeU_dT_QTTYZQYTYZqYYeqUUe@ZaqUUa@`a?qr@~rS}gUUt^TUt@b0Ts$?d_s?t^b>uUQUuXY@eUQQ5YY@VeUa9Ui@WeYa-Uy@We]aUy@iaUe_ٕUUU_ՕUU_TU^QYTUfQfuuUfQfeeUUQUUUe@VQQUUu@`a?qb@~rC}fUZXHXd.d_T.^".YPU5TY@YQU5UU@iUe)Ue@WeYeUu@We]eUy@eUiٝUYٝUY榥eYffun{}{jjijiUUUuVU@VUTU5U@W``& p`@>pAY=dAYYVYZ@[d@T[QT@Q!@PU%TY@]QU%UU@mUe)Ue@WeYeUu@We]eUy@eiU[U[kn{@ieUUuVU@VUTU5UW`@" p@ @>pAAI@=dQAY@UQUTU@UQUT@FQYP@FQYP@TFQYP@Y@UT@]PEEU@mTEEe@eYEEu@[e]EUy@EmU_U_j@eUUuVU@UTU5U " 0 @>0@@I@=$@AY@@UTU@@UT@@UP@@UP@@@UP@@@]DP@mD`@e Dt@[e]EVy@ZEmYUo]Uoz?ހ*@eUU5VU@UTU5U " O0 00 0  @, $ 4@Kd]A F9@N^AF-]{oYꞻ:?۽]ZYZU@YeUUUU@VUTUU 00 @  B0@Bd]A@1@B]A@%UZUeUVVu֝ZZۭ]Z@YUEEU@YUEEU@UUEEU@VAU 00 @@@@@@@@@@@A@@@PDQEEe@UEUEEu@YUEEu@YUEEeBBAA@ A B B A@APEAPE!@AQ E1@AQ U1@AQ U!@AQU@Q@ @00     EpDTEtTEUtTEe@UuEu@UuUu@EUuee@UUUUeU@UQUaU@eQa e@uQqPu00P1 @!@@@@    EqDTeEuTeEYؘf@ڕf@ڙffUuZUuZ@fa(f@uUq\u00\1  !A@@@@@T X`X`X1 TEaHu TEeuIYئ@@YuYu@f&*n@uu'^}0t#^> d"&EDTDT@@ @PA @PA@PDA@PTAT@X@YY1 VEQHu VEUuIZت@@ۮ]]߀f&۶u'˶t#d"@EUDTaWEATa QD @PQD@ @Q D@ @Q E@AQEAE@AQUAU@BY@FY@VYV!UEUHuUEUuUYت@ߟ@ۮ]]߀&ֶ'ֶ'&@UUUUeWEAETa DD @P DE@_AD@Q(A@QU%E@QU%U@R"]@V]@VQ]@VaU@UETUuUUEXTuUUKXUU@KYeE@[UeE瞥޷]ڳ]ۀ*//:@UUYUuWEAUTqKHOLE@_AQ(V%FVeVb^@Vb^@VQb]@ZabU@TITUeUUEIDTeUUFDdiU@FUuYdE@ZUuYuE@Vf]@iWW]@}SWY~*{/k/j:@UUYUuWUAUTq[@H_@L@WEdUQd(iU5F(UuG(֦b_@(Vb^@VQb]@^QbU@PMTUQTAMTQAET@UEUyT`E@UUUyTqE@UeVjDE@YuSVUU@yuSVUUz{kj:@UiUuU[EeTuE@Ed @gEd@UtX UUAdiQUF|QUUG|梣f_hf^TQb]QbU@P]QQQT@\TPQ@D`hTDamdTTmtXd")@YtcUTW@yucYV:@yUuU_@uTuE@dH@UtX UAT EA$iQU%F|QUeG|榣f_|v^|@v]<@fU@]աPUU\դPU@DT@DP\U@PQ\X@bi@rUTiW@}rYUiVZ__j@yUuU^uTuUeDUQTZP ZQ0hQUD0V@|UDuW<"f_d_>t^":uUPUuXU@eUQUuYUjfjojoonojٝU^ٝUZjXjfyn{}jj)j@eUQUeU@VQP%Q`P!P*`PP@*`QAUT@)dQAUU@YQUTU@YQUT@JQP@KQT@P[QT@P!@PUuTU@UQUuUUfjoooojU_U_n~*@ieQUuU@VQ@5QV @!Pj @P@*`@AET@)dPAUU@UPUTU@UPUT@FPYP@FPYT@PFPYT@Y!@T%T@YPD%EUT*o/o/n*]U_]U_mj*@ieQUuU@VQ@5QV !Pj P* P)$PP@@@]DP*  /]/_~^*]{Uo]Uz?ڀ*@ieQUUU@VQ@Q     @, G4 4@KtYA _9@NZA-]knYz:뮡?@yQZU@uQZU@YeQUUU@VQ@Q    @  C 0@GdAP1@tYAP%@UuYEUe@UeUUVu@YUZu@iQZe@eEEU@eEEU@UeEEQ@VQ    @@@@@C@C@@DA@@DEEe@UEEEe@UEEe@eEEeBBAA@ABB@@E@E!@ E1@ U1@ U!@UQ 00 DTDTEUDTEe@TEuEu@TEuUu@TEUuUe@TUUUUU@PUQUQU@PeQae@PuQqu001 !@@@@@@ !DTueTuEY eȘf@Yٹf@Yf@YYyf@UUUUeU@UUQUUeU@eUa(e@uUq\u00\2  "A@@@@@T@T@T@T@ TDDTuTEUTuEYIZئ@]@]뀩꺦UuUu@&jn@u7_}03_> "'EDT!DT!@@ A A@DA@TATDTDUDUD UDDTuUEUTuEYJ[ت@_@_۪YuYu߀&732@EQHTqWEATq QD QDM@@  @@A@DAA@TAUAD@UEDPUEDPUD`UDDTuUEEDTuUYKHت@ߏ@ۮ]]߀*;;:@UUYUuWEAETqIDDM@ D@_@@@A@AQUA@AQUA@AU@@EUP@EUP T`D TUeEIDTeUUKDdUE@KUeeE@[UeE瞥]]߀*??:@UUYUuWEAUTqK@HO@L@_@@PA@QQUA@UQUAU@UU@Q@UU@Q@UUPQTPQD TUUUEMDTUUTEDdYT@EUuYdE@YUYtEYڧY􋀽ڣY*??:@UiUuWAeTq@H_@L@WEd UP@dUPUPUUPQ@UPQ@UPPQPPQ@ PQTQA TQA T@UEQqT`@UUQqTp@URbD@YSSUF@mSSUV*??:@yUuU^uTuEdHgEdL@EtUT@diPUhQUU(aQ (qQ q@Q a@Q@@]QQ@Q@\P@Q@`DDadDTatTd"%@UtcUDF@uucYV *@yUuU^@uTuE@d@EtI@@D@@i@U@xQQU8aU (qU (q@U (@U@]QPU\PU@@@D@d@PQd@bd@YsUTtG@ysYEuVJ__j@yUuU^uTuUdIPI@DA hAUD @xQDe8!U (qU (qU (U@YQUXQU@@@D@P@TQP@bT)@]rUUiW@}rYUiVZ__j@yUeU^uTeQdMPIL HAUD@UPQDU!U)qU)qU )aU@ZQQUZQQU@ET&$TT&,T'-UU]W@}YUYVjj@yUUUZ5TUQ$X M\ YQD@@D@AUD@UPQUaU)qU)qSU)bU@VRQUVRQU@bIs?$YѤs?d_>t^":ueUuXU@ueUuYUzjjU_U[kXjҦy@l{~hjjeQQUQQPQPP@PPA@TPA@UPU@U@U@F@K@`[@`@!aPUuTU@eUQUuUUjfj~j]ٻU_]U_ll@}~yjiQQUQQ@Q@T@@X@@X@P@X@@X!@PT5T@UPT5EUd*??~*]ٻU_]U_m}󿿿@nojjiaQUQQ@Q@ $@@YD%P* ?U?_~V*]gU_YU_z@Y/_Y*iaQQQ@Q@, G44@KtA _9@tVA)MyViYiVy*몡/_@uQUZUuQU UiaQQQ@Q@   G4@KdP5@ tUAP%@IuUEUe@YeUUVu@UUUZu@eQUZeADQDQQQQPP@APD`QQDaQDaADaBBA@ABBEEEEEEAD@TEA@TEAD@DtEED@DtUED@DtUEDPDTUE@P@QU@ @ @ @0 0 @@@@@@DDEATUEA Af@YEYf@YEYf@YEYyf@UUUUUUE@UUQUQUU@eUaU@eUq\U 0\  A @ @@@@@@@@ DDDuEATuEAIBئ@]]@]]@řy@UUeI@UUUeY@e&*Y@e7_Y 3_ 2EHT1DT1@@ A A@DA@DA@@@@@@@@ DDDuEUTuEIJWت@_@_۝ˀڪYuYufwsڀr@EQHTqWEATq QD QDN@  @@DA@DA@@@@@@@@ D DDeE DDeUIHȪ@GY@KY˪YuYu߀j߀z@UUYUuWEAETqJDDN@ DO @@DA@DA@@@@@@ @@D DDUE DDUUEDDUE@GUEUE@KUUEǚUU߀j߀z@UYUuWAETqHO L@O@ @ @@@@@DA@@@DA@@@@@@D@@@@D@@P @@PD DDUE DU@DY@EUEYdDYUYtEYN@UO@U_j߀z@UiUuUZAeTqE@H O@ L@WDD @@@D@@@EE@@@EE@@@A@@D@A@@D@A@ @A@@ @ADA DADAED`AYDpD@SUUE@SUUUj߀j@yUuU]uTuEDH gDDL@VED@@@D@PEE@QEE@QA@@QA@@QA@@QA@@@LQA@@L@@DD`DTpTd@UdSUDE@UeSYU ߀*@yUuU^@uTuE@D@fEDQI@@D@@@AE@QAE@QA@QA@QA@QA@HQAPHPDTd"$@UtcUTtF@eucYEuV߀@yUeU^uPeUdaIPPI@@@@@AD@@AD@A@QA@QA@QA@DQAQDQ@Q@@@@D@@@TQ@@bD$@YsEUtW@ysYEuV߀@iUUUZePUQd `MPPIL@H@AD@@AD@A@QA@QA@QA@EQUQEQTQ@TET&TT&T('UUhW@}YUiV殦߀@՞YUUUV՞PUQXPM\PI@D@@D@AD@@A@@@A@A@EUQEQTQ@bIs?$YФs? >0" :15Uu\T@5uUuYU:殦~~z榦YٷUo]Qk\gXjfu@dw{}`&jfPPPQ@Q@@@@T@@ @`@`@!a%PUuXT@eeQUuYUjf~~~榦]ٷU]Qlh{@t~pfjaQPPQ@Q@T@X@X@@X@@X!@QPT5T@UUPT5EUjd&~7~7>&]ٷU{]Ql갺x񿰾@npofj`aQPPQ@Q@ 4@@UD5P& ~7~7_Հ>V&]fUVYQZꦪpꀸpo@䖡Y _VY QQPQ@Q@ $$$$ F$@Jd_@ dUAZ@IeUEUU@YeUUVU@Y [@䖡Y _@UQUJUUQU UQQQQ@Q@P@@T@@@EUEEU@UUQUEU@UQUJU@UQUJUQQ@AAAPAQQQ@@@@@@@EUe@AXHe@AXHe@AXHe@ATDU@APTP@APP@APPAAA@@EAUA@QE]M G@UE]MG@UEYIF@UUUEUE@UUQUUU@YU%U@YU%]UI!]E1EHT1@T1@@AAAA @eAeB@SE]MLG@WI]\GJXJZUTIZUUYZ噦[ݧKܣF@EQHTqWAATqQDQD@ @@AA @ UA U@EYI\F@IY\F˪XNUTNUU^暪ޯޯ@UUYUuUWAAETqEDD@ DC @@AA @ UA UFUTJUTT@WUTO@WUU_暪ޯޯ@UYUeUVAETaEHC LC @@AA @ DEA DUDYEDYTIDYTōDY@UUUE@QUUU撪үү@UYUuUUAETqEHC LCD @@@@E@E@@D@D@ @@ DA DADADADQD @Q]RUUdE@UYRUUeU斪֯֯@UyUuU]EuTuE C QD@@@@@E@@E@@@@D@@D@@H@@@H@@H@@DDT @UTSUDtE@UUSUuU嚦 ޻޻)@yUeU]uTeE ADQE@@@@E@@E@@@D@D@D@D@D@DTd @UdSUDtE@UeSYEuU媦޻ݹ޻ݹ@iQUUZePUUd aEPPE@@@@D@@D@@@D@D@D@DADPA@@@D@@PQ@`b$@YtcEEtV@YucYEuV媦垫ݹ垫ݹ@YQUUVUPUQT`EPPELH@AD@@AD@@A@DA@DA@DA@DUADPA@@D@T@(@UEhW@mYUiVii幚i幚@^UUUUV^PUQJHPEL@ED@D@AD@@@@@@@@@@@@A@PA@$B@$S0@$$S0@, $R 0@-0U\T@-qQUYU)i繺i繺撦@]QUuU@]UPUq@ID0@@ED@T@T@TTAPA(B 8S0 8S0 ,(R 0@!0U\T@5uQUYU9梦y罺i罺i梦@UYUum@UYEUu)@@Ԕ4@@T@$@e1@$@$ @P@( <0<0(" 0@1Pe\T@5uQUeYU9fy罺i罺i榦UUUuUUQu;@$4'@$e5@tQy\1$@( P@@(@, @, @(! @1PuTP@uQUuUU)fn)n睺)n睺)fWU{WQ; k'xy\5@tQ}\1$@( P@@@@@@@!@QP5P@UUPT5EUi`l&i|'i}')e&VUvVQ6j&x}\5@dQmp\%$@h`$P@@ Q4@@UU@D5Te@L% eL% eM%Y%E%Y@UEUUu@UQUQU5@dQipX%@dQmp\%@TQY T@X  $$$  @PY@TQAY@TQAU@TQUU@TQY T@TQY TTDT@@@@@TDTDTD@P````PP@@@P@@@EPM@@EPM@@EPI@DEPEP@DEPUQ@DETY@DEXYLXLX1HT1P1@@@@@@@P@@PEQM@PEQM@PEQI@TEQEQTE@UEQUQUE@VEUYUE@WEYYUE@ALXUF@ALXUF@AHTqUGAPqEP@P@@@@P@PDQ @HQ @HQ @@HQEUTE@IQUUUE@։UYUE@׉YYUE@׉]YUE@]YUF@UEYUeUFAAPaE@@@@ @ @@ PDPHPČP@ԌPEUTE@ՉQUUUE@։UYUE@׉YYUE@׉YYUE@YYUE@UEYUUUEEAPQE@@@@ @ @@ PPPP P@PLQEUTE@UIQUUUE@։QUE@׉QUE@׉QUE@YQUE@UEYQUUEEAPQEB@@@@@@@@@@@@@ @@PLQUUTE@UIQUUUE@ՉUUE@ՉUUE@ՉUUE@UUUE@EUQUUEAPQEBA@@@@@@@@ @PTRUdE@UURUeE@ՙVU eE@ՙVUUE@ՙVUUE@fUUE@IeQUUII%PQA$AAD@@ @TTRUDtE@UURUEuE@ՙVUeE@ՙVUuE@ՙVUuE@VUeE@ՍUQUUIōPQQAD@E@@@PP`P$@TdPEDtU@UeQUEuU@iVUeE@YVUuE@YVUuE@IVUeE@MUQEUEMPAQI@EE@@@@P P@0PQ@TT@qQQQUU@aVQeE@UqVQuE@UuVQuE@iVQeE@]UQQUE]PQQI@@E@  0@ 0 @ 0@0PAXT@qQQQYU@aVQee@UqQuu@UuQuu@UiQee@UYQQUe@EYEPQ!@ @ 00@00@ 0@0PQXT@%uQQQYU@%uRaee@euqyu@Uuqyu@Ueaiu@UUQQuu@UUAPu5@0@$`0   0000 0@1QTT@%uQUQUU@%uReee@%uuuu@uuuu@eeeu@UUQuu@UAPu5$`0$$`40p 1     @aTP@UQUaUU@eRiUe@uRyUu@uRyUu@eReUu@UQUQeu@UAPe5 ` %0p 5@pq01  @1P@UPT1EU@ePh"Ee@uQxEu@uQyUu@eQeUu@UQUUu@UQQU5@`a %@pq05dQ!0P!$@  0@@E@D1D@E@H!D@EAHT@EQIUU@EQEUe@UQEUe@UQQU%dQ! P!dQ!0P!@ @@ E@E@E@A@ @@ @@@@@@@@@@PDDDDDD@@@@@@@@D@D@D@D@QAE@DQAEDDAEHDHDHDHDQ@PPP@@@@@D@D@D@PDPEU@QEPEU@ETE@ETE@ETE@QETE@QETEUA@P@@@@@THPEU@UIPEU@ETE@ETE@ETE@UETE@UETEUA@P@@@@@@@ @@PLPAU@QIPAUT@ETAT@ETAT@ETAD@QETAD@QETAUDAA@PD@@@@@@@@@PDQAU@PEQAUT@EUAT@EUAT@EUAD@PETAD@PETAUD@A@@D@@@@@@PDA@PEAP@EAP@EAP@EA@@PEA@@PEAT@@A@P@@P@P@@@@@ @PTAD @PUAD`@UAD`@UAD`@UAD`@EAD`@EADPA@@P@@@D@ED@UEDP@UED`@@UED`@@UED`@IED`@MADPM@PH@D @ Q@@aAPT@aAPd@DaAPd@DeAP`@DiAP`@DYAPPDY@P@@ 0@ 0@ 0@PA@QQAQP@aQAQ`%@TaQAQ`%@TeQAQ`%@TeAQ`%@DUAQPDU@P@@ 0000 0 AeQQQTeQaQd%eQqQd%eQqQd%eQaQd5UQQQT5UAPT5   0000 0 QeQUQTeQeQd%eQuQd%eQuQd%eQeQd5UQUQT5UAPT5  0     QQQQDaQ%QD!aQ5QD%aQ5QT%aQ%QT5QQQT5QAT5 00 PD!P$D!!P4D!!P4T!!P$T!PT!@T! 0 @@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@DADAAAAATP@@@@@DADAAAAAD@@@@@@@@@@DADAAAAAD@@@@@@@@@@D@D@@@@@D@@@@@@@@@@@@D@@@@@@@@D@D@D@D@D@D@D@@@DDD@D@D@D@D@@@@@@@@@@@@@@@@@@@@@@@@@@ @ @ @ @@@@ @ @ @ @0@00@@ @ @ @ @ @      EEEE@@@@@@@@@@BBAA@@@FFEEAA@EEEEEA@UUQPUUQP$$$ !15%$44  $$,%55%$44 00%55%$$$ 012&55%1!!  !   GGEEFFF OKEIMMHCCGIOKEIMMH@AACCGI YYQP@@AAUYZZQP@@UYZZQP@@PTVVA@@@PT@@@@PP@@@@@@@@@@@$$$$    $$$$$&4'4'$" 04$"$3$7$&(*8787$" 44,*,;,;%*-*=797%" 1 2$$,*,?,?%*%%%!112-*-?-?%*1!"!          G G E EGGG  K K I IJJJ  O K MICCG I  _ [ ]X@AASSW Y ^ Z ]XAQQQV Y - n o UTQQUY-noQPQQ U U%joQPQUUU$hhhTD@@QUffUU    QUffUUUAUUU$44$     $$44$$&4'4'$"40  04%"9397)&)*8787$"$  ! "44-*=?=?)*-.=797%"!1$244-*=?=?)*-%%!15(2($-*=?=?)*55,2)%-]]IIGGG5^^JJG GG^^NJFFF ^^]Y ^_]YGGGI^_]XDEE[[[Y ^_]XAUUYZY-~UT@PPUUYY-~UT@PPUYYU%~QPUY UU%~~~vuQP eijjUU$<<,QUfjUUUQUUU%==-%$$$40  $%==- %*4'4'$"40  04%"=#='-&).9797%"40 ! "44)*=/=/-*-.=797%"$  !5$244)*>/>/.*-%%!55(284)*~/~/n*Z[[[55,295)*^/^/N*JGGG5,)%)*^/_/O*JG GG *^/_/*ZZ  *^/_/*]Y    *^/_/*]X GGGI .^/_/*]XDEE[[ [Y .^/_/*YX@PPQUUY [Y-.~//+%U"T#'EUUUUZY-.~//+%U"T##EUUUYYU%*~/~/~+zuQP%ei UU%&~'~'n#feQP%uynnUU$&<'<'," uyjnUUUUUUU@@AAAAU$$  PTemmmU)*,','("40 PTe"n3n7n&V).<7<7("44$ `de"n3n7n*V).=7=7)"44$% !tte*n;n;n*Z-.=797%"40 %5$2tte*n;o;o*Z[_-_%%%!$  55$2tte*n;o;o*Z[[[55,2xte*^;_;*GW  5,84%*^;_;*VV   ,($%*^;_;*]Y   *^;_;*]Y  *^;_;*]XEEE EEEEGGGE*^;_;*]X  EEEEEEE[[[U.^;^;*UT  EUUUUUY[U%.j;j;z*zu%U2T3'U'.ZU%.i;i;y*yu%Q2P3'Y"'7/YU%.i;i;i*ie%Q2P2"Z"77.UU%*)7)7)"!!!! *zy"7n:n*UU$&(7(7(" uyj&n:U%U%U eee&U%@@V@TV*./'/'*"%  PT!15)V*.?7?7*"54$PT!15)V..>7>7*"54$ `de%^5_5_)ZZn.~797%"54$5$1dde%Z5_5_)Z[_)_%%%!44$    55$2tte&Z5_5)[[%$      55)2xte&Z5[5)WW    5%,"84%&Z5[5%ZZ    %,84%&Z5[5%]Y III E EEH))%%&Z5Z5&]X III E EEI%Z5Z5']X III EEEEVVVU%U5U5'YY J UUWWWE)U5U5u%uu%U"T"& Z UU[U-U5U5u%uu%U6T7/ Z +.[U-U5U5e%ee%Q6P7' Z&+?.YU-55%%66& Z&??.YU-55!!!!! /~}&?n>n.UU-55!$}}n&n>U%U)U eee&U%AAAAAAAEEE@@VUU@T VU...*e  @T!!%)VY.??*y8(PT!!%)VYn.~?~?j*y8)PT%%%)Z[o*o7i7e&u4% 51UU%%%)_)_%%%%44%   95%1dd%55)[[%$$%  95)2x|&55%FV      5%-"8<-&^5V5%]Y ]MIJ NNJ%-88)&Y5U5&]Y   ^NJJ OOJ(88)&Y5U5']Y NNJF EEH%%%%&U5U5']Y NUUVVVU%U5U5u'uuUU ^ UUWWWE%U5U5u&uu&U&U&. ^ eU[U)55 %  %67/ ^ '*[U-55%%67+ ^&'?.ZU-%%%%66& ^&??.YU-%%! ^&?n>n.UU-%%!$mmn&n>U)U)UYYY&U)AAAAAAAEEE@@EEU@@FE---)Y@DFI.??*i,,@DFJN*^?^?j*y<-QUFKO*_7Y7e&y<-  /*QU_)_%%%&48)  /?:UU!15%WW%48) ZFFJ III?>):im"15%FV%%%) ^NJJ MMI>%-&8<-"]1U1"MY ^OKJ NNJ%]my}m"]1U1#]Y   ^ OJ]my}m"]1U1#]Y  ^[Z%%))"X1T1t#xxYY  ^VV  "X1T1d#ddUU ^ eU___EE%T55$&$$&&'/ ^ eU%54$%67/ ^ eY?'/&(44 !67+ ^ m&}'>?..!&&& ^m&}?9?). ^_m&}?)>%.VVfjii&i>))%)AAAAAAAEEE@@EEE@DFE)--)Y@DFI*??*Y@DEJN*^?^?j*i,- AF Y[_*_7Y7e&y<- /.AFY%%%%&8<-      /?:YZ%15&gW%8<- ^ G G J MMI? Y]%11#eU%59) Z N J^o"1U1#AQ%%)) Z O JVf"1U1u#x|]Y   Z _Z]iu}m"]1U1u#x|]Y   Z [^]IEMM"]1U1e#ddUU  Z j^]"\1T1"    Z i]^n~ue"P15%  Z iY_ooee%P55%    Z iY'&%54$):;/ Z m&Y'?.!&&* Z Zm&Y?? .  Y Yimmm&Y?>.UUUYYY&U>))%)AAAAAAA@AAA@@@@DDDEEIHHHDD@@@DEEEEEE)--)IIIIEE@@@DEEIIIJ*??*Y]]YEE@@A GG[YY Y[_*_?Y?U*Y]]^GGB A E GG[Y]][_&_7Y7e&ilm^GGB A  I GG[Ymm++%%%%&8|m^GGJ I   J GG[]fV%8|m^ GG J M   [ZZZ]aQ)=}m^  O J^_VeuqaQUiy}m^OJZ[VED@AQ**))VKJ]UVk^]U   Z i]]]U Z i]ZQQ   V iY_oueQ     U ]mY_'o&oeeP    U Uey}m&Y'?.        U Ueimm*Y??. U UUYYY*Y? > . *>))))AAAAAAA@AAA@@@@DDEEEEFFFEE@@@DDXXYYY)--)EFFFEE@@@DDX\]]Z*??*IIIIEE@@  A GG[]]][_*_?Y?U*Y]]YEE@ @  E GG[]mmko&_7Y7U&Y]]ZGGB A E GG[]m};+%%%%&)mm^GGF E J GG[]m}9%)<|m^GGFF NO ]myuaQUi||m^_ Z^_^mxtaQUi}}iZ[ZJKV%$ !VJJYY^WZ^FGKKYY^J^]V]i]]F]]i]YUE    U]iY^UE   U]m}}mY_'o&ueUE  QQeimm&Y'_;o*oeeU  PPT X\]*Y;?.  *?>.*>))))AAAAAAA@AEEDUUEG@@]Z)--)EG@@][*??*EG@@   -mko*_?]?Y*YZ@@ -}{o&_7Y7U&YZFE   -=:&%%%%]F F  III G G [ ] - 99)Yi"!%%Z[ NO ] - 95)]i"!%%Z[ NO ^ - 84)]I"m!!"WZ^_ Z % $$)]IM]i"m!!"VZ^ Z  Vjzyy"m!!"Y]^ J  Wggee&m%m%&I]] J      %]%]%&i]Y J       %T%\%l&llmYZ F     $T-\-l.llmY['&ZF   -((---&Y'_;o*{ueUE   *Y;_?o.keeU  *Y?>. *>))))AAAAAAA@EEEDEEEG@@]Z)--)EG@@-mk*??.EGD@-}{o*_?]?I.EGEE-y{o&_7Y7U*UWEE/   )9:&&%%%VZZ*/I?IIF F Z Y ) 95%UY%55%^_:?N>NNN J Z Z ) 84(\I"11"no:>N%O^ % $$(\I"}1}1=">?&%^_^  ]i"}1}1}"vvVZ^[ ^  Vf"}1}1}"}uY]^ N  Wgwuu"|1|1m"]UI]\ N      _ogee&\1\1L"LDH\Y N    '%%%T5X5X%X\]YU N    )55==-Y['&N  $44==-*Y'_;*[J     44,*Y;_?o.{zjZJ $$,.Y?^>^.ZUYYI .Y>))))@@@@@@@EEFVUF@@!%k*//.UWQ@)9{*??.UW%%!!UD)9{o*?}?i.U[%%%%UE%5:*&:797%*[%%%%ZZ?  %55%&%U%U%%%%%^_677    % 55%UU%55%no23M6MIIIUU % $$(\I%55&~22N%NNNN^]   \i&z5y59&>>"!NOONNN^  ]i&y5y5y&uuUY^[ N   Yi&y5y59&95]\Y N Wg&y5y5)&($\\ N [kwuu&U5Y5 %\Y N    _ow&u&u&T5T5% YUN  '%%5%555)Uj'& N    %5599)**j+;+ N%55)))?.;?+N!55)))?.?o>o*kjn^M $$().m>))))E@@!%+/*//-/)*ZU@%5;/*??=?).Z&62"UU%5;/*??=?).[%55%YY%5:*&:7y7e.U[%55%^^7%55%&%U%U)U[%55%no237   ! $$TU%55%~236   \Y%55&~22%       \Y%k5i59&::"!EIMMII I  ]i&k5i59&55LIMMII I  Yi&j5i5)&$$\Y M Vf&j5i5)%\\Y N [k&Y5Y5%\Y  N _ow'u'u&U5U5)UUI Y  N _ow&u&y&Y5Y5)UU/+ N ZZf%e5i5Y5Y5)UU./;+ N%5555*UU.;7'N!5555*UU.;o6o&{~. 44$UUj.n:Z%Z%ZUUUU)Y)!%++EBCCBA@!5;/"/'-')*YnoofUP!5;/"?7=7).Yn&o7o3f"UQ!5:.">7=7).Zn*o;o;j*^^!59)"9797%.Zn*o;o;j*no7!%%%!%U%U-U[o*o;o;z*~237TUU[o*o;m;y*~236\Y*_;];y*zz22%     \Y*_;];9*55"!    \Y*_;];)*%%LIEEEE E  Yi*_;];*LIEEEE E Ue*_:]:*\YUU U U E [k*^:]:*\Y Y E _o*n:m:e*UUY Y  I _owuz*o:o:j.UU/+ J   Zjv&u&z*o:o:j.UU./;+ JUUe%e5j:o:o:j+UU.;7'I!5*6/6/6*+UU.;o6'{u 4*4/%/*UUj*n:Z%Z%ZUUUU%Y%!%++!5;//)%.?;&!5;/"?'9'%*.&?';#&"% !5:.">797%..*?/;/&*&"#!%%%"5757%..*?/;/:*>?"#3!%%-.*?/?/:*>?237TUUZn*~/}/9*:;236 LIEJN*^/]/5*6622\YU[_*_/]/)*%%  \Y*_/]/*      YY*_/]/*     Ue*_/]/ * LIEEEE E   Zj*_/]/ * Y Y UUUUE_o*/}/e.UUY Y E_o*//j.UU  E[k*//j/UU EQQu%u%~*//j/UU{vPQe!e!n"''*'UU o{u.??*UUjn_okeUUUY 00    *;;&!5:.">'9'%&*;;&% !%))"9797%***;/;/6*50#%"5757%..*?/;/6*63"#3!%%-.*?/;/:*:;233)*9/9/5*66231@EEIIYY%%%11@EEJJZYPUUZ^^YPU_Y QU_Y  Rf_Y  U U U@@@@SgUU U U UUQQASgUU  UUUUEPeUU uuUUEPQUU{uPQuv~*UU o{u%%.??*UUjnWWWUUUUU    %%%%   "0'0' &%%%% "0707 &%*%;%;5*50# "0707$*%*%;%;5*50"#3!%%-%*%?%?5*50233$*$?$?%*%%23111@EEIIIIPUUZZZIQUZIQe_Y QQQ@PeUQ Q QUQPUUU UQQP@PQUU eaPQUU eaPQuw{ki%UUjfUQ)))%UUjjUUUQUUUU  44$%55%  44$%55%%%   0 )99)000)99)00               @EEEE E E  PUUUU U E  PU U E  @@@@PU T P @@AAPU U U QQPQUUAAPQUUAAUUBBAAUUBBAAAAEEAA!!!!!!!!%%%%$$$$@EEEEEE@EEEEEE@EEE@@PU@@PQD@FD@@@@@@@@@@@@@@@@@@@AAAAAAA@@AAAAA@@AAAAA@@.>I;F       @ @@@'@''!'!#'!'a'a'a'a&bB bB bBbBbBbBdbBdVddddd 1 7 7 7 7 &&& & $ $ $hhhh``            """&"&""&!""&!""?'""?'""8'#0'#0'#0'#0'#0'@10!@90!@=0!@=0!@=0!@`=0!@`=0!`0!`0!`0!`0!`!`M!$] ] ] ] \ \7\7\7PPP8888880066!!! ! @! @! @! @! @! @! @#8#8"8"8*8.888NNN~ | x x x x x x Pp"Pp"P0@"P0 @*P0 @*P0 @*P0 @*P0 @*P0@;PZEZ(Z(Z(^8888cׁLӚۿ_o ? 1 - )#?(F, ( 0&؀b]V'''ٿ ?Z @a6)%} <wB@ 5.<*.        PPPP -------``@Aacccc.c/cococococoboqqqqqqqcC@@@KKKKKKkk{0 0 0 0 X0 0 0 0 0 0 0 0 0 0 EEEEEEEEEEEEEEEe#!K!K!KKKKK  ?  0= "  -߀@@>3)))  B}.83# EFCE     @@@ @ @ @D#D#D#D######3qqqqQQQ ͈ȉȉ ŠŠ+Š+Š+++ + " """""PPPPPPT(| 8| 8| 8| 8| 8|8|8|<|<|,$$ $<<<F8F889x9xyx}x}xuxwxwxwXwHw@g@g@f@f@D@@DDDDDD@!@!@!@!@ !@ !@ !@ !+!+X)X)X)X9X9X=X=P=P=r=r=r5r5r 5r 5v"f"&"&"&"&"&"&Z"&Z"&Z#&Z3Z3Z3Z3Z3ZZZJNNNL, ' H~"׺ ~$*: #  @ J o : } % 40gZ|i֧ۀqx,%Fs/"#"?N-DKE=    @@@BBBBB0 J0 J0 J0 N0 N0@v N0@v np@6 np@&op@&-p@'%p@'%p@'!p@/!p@/_!`/_!`!`!` !` !` !  # # ԐԐ            @@@@@@H@@@GHGHGHCHrh 2h 82h 82l 82l 2| "} "}***? *`  ` C `c`c`c`f`` @ P P PPXXXXX@XXXXXX= = =    BXPPPppbpbrbrBrBcB ` `@ `@ `@ b@r@r@r@r@R@R@RRRZZ<< < < < < < < < < $4444<< = =  F'ڿO֛f @ G  ?$5  m&F ~ s $>~$: fW݀iVۀ# yN3!=h'DC ۿ~!'vb ' ق{B(;A>6     !!!!!!!CCK6K6K6K6K7IGIGIHXi`` @ @Z zzzxtptppppp`p`p`p`   @@@@@@ G G G G G G G W(8W)8U 8q 8q $4q $41 $41$4&4<O.6<O/&<O<O<G<G<G>F>D>D2D22222222000qqqqAqAqAqAqAQAQAA@A@@@@@@@@ DH FH FX FX FX FX FXGXG         pCpCpKpK`K` K`"K`"K`"K`8"K`8"X`("X`("X`("X`("X`("X`("X`(&`'8`'0`'0`'0a0a0a0e0uE0 E E M M M M M I ~ U[) MӚ X e%  ->0/Z < a}kQ>i'rqϻ2  px  py+m1f*MMEF   `````ddt t t t t****+++knknon @ol @l@d`daaaaaAAA[___r____OOO " " " " "&&667676747777?FX~FzFxFxFXFXFXFXFXCXHAXpHAXpHAXp@AXs@AX3AA3AA3AA3AA3AA3AAAAAAA@A@(ǂ(ǂ ƣ ƣ ƣ ƣ ƣ0@s0`s0`@q0`@q0`@q`@q`q`q`q`qpqpqpPpPpPtPtP<(((((((((,lBlBlBlBC  80000    ! ! ! ! ! ! % ((dž(dž(dž(džۿ MӚ& ? ,  i M A{ـ eB #?  %  11  l) ">n*>"#"e&lUz+L@D;  @@@@@@@@`ppxx8ă8ć8ć8ć8ԇWWSSS.S/.S/....*,*,*,j#,h#(h#8h 8h 8h 8xh 0xh x@ x@ Px@ Px@ Px@ Px PH PHPHPH@@@````8`,L,L , , , ,  ,,000 4 4   A A A A"AAA A C C C C        ((h@h@h@*h@*h@*h@*h@+ho`o`g`g`g`g`@g`@g@@wP@w@w@w@UUUUTXXXXXXH0H0h h"h*h*h*8h*++++++///[, *@տH՘") - -@ \e؁o !E Zm ^ ! d /q)? ^[ b+i'?!!|7E՗yQ ށb489:       ()))R)R)))))3333###R'RǧSGSGSGSGSGSGwG}EmAmAmAmAmAmAmA mA e@ d@ d@ d@ d@ d@ `@ @@@@ppppppppttB4B4"B"B"B"B"B2B2B2B2B3B3J3J3J3J3^3^3^3~3~3~31 q} q} q} q} q= q% y% y% y% y!}}mmmLL XHHHHHH#H" " " " """"""  P P$P$PPP@@@@@@@DDP, P, P, P, P,$P,$P,$P,$@,4@(4(4(4(4(4(4(t)TTT\\XXXi'w | U,<g ~ WZ-@eo}+@cl.*!  ~*c$;EFLS @@@@@@@@@@@@A@I@ I@!K@!V1K@%R3K%R3[%Rs[%Rs[%Rs[5rsZ5rs4zs68S69Sl9Sl9S(9BǤ8)BǤ8)Bݤ 0)ݤ 0)ݤ0)݄0)݄0)0??(?(?(? ? ? ?                 0 8 88xxxxNxEFx ā!Bx Bx bbbbbbbc ! *! *! Ҁ:! R:!S:!S:`_2`0`0`0d00<p0<0> >(                      @ @@@@@@@#@#@3@3@3@;:@;:@{@zzzzzzz    @ @< @= @ @] @]@`U@`U@Q@Q@@QP@AP@AP@A6P@A6PA6 PA6 PA6$PA6$P@6$P@$P%p5p5p5p5p?0?  UӞ׿Z$-P,)*A  F  @ Y #? `rq vibU ؀d.G *k%F d%Ah&@Z tb~]P$%(m; zLׇ}7s hEK$) ,NCB;        # 3@ 3 3 3 3 3 2 2p      0a50a40Xa4pXa4X!4X!4X!4x!L4x!4x<h<(>(>(>((    P?oggaaPPP222# # # # # #   N N .N .N ^vvp6` 6` 2` 0@ 0@ 0 0 00  D2D2d2d2d2d6d6d6``````` ` `      # s { `{A`zA`zA`zA`zA`zA`~A`~A~~||| | \                    `( ( ( ( ( ( * "b(?H+ D 2 ,))_"  $n d EiWԞՁ~ x |f) # .[ߠ o|+]! d`K,}67]!+bAl|9  4@6   PPPPPPPPPpPpPpPpPpPp p ` ` % %%%      @@``=`}p}p}r}r}r}r}~}~}~i~a~A>A>A>       @@@   @@@@@@@ ``````554444             1V& va0(joeML~pyxX1QM,,L!xU  2SQHH    !##'77 7 ? ? ? ? ? ? 0+ 0+ 0+ 0 0 0 pإH$pإH$pXH$pX%HdpX1HtpX1HtpXq@tpXqtpXYVXYWPYWPYWPXWQZWEZSEZSZӀZۀZۏZ000000888@8888Đ8Đ8Đ8Ԑ9Ԑ       DEEG G G G+G#C#K#K#K`#K`"K`р"Kc`"Kc`"jc "(c "(c "(c  c c ay ah ah aH a@ a@ a@ a@ a@   sa si @si @ri,@ri,@ri,Ari-Ari-r|%R%%%''###3   &"6"6"6"6"6"6"6">">#>c>c<c@<c@<C@<1C@<777 7 7 7 7 7 ? ? > >~~~~NH!aHqaHqa Hqa Hqa qa  sa  sa"?UI(jzL-3m~&9 h ~RӜ ؃  ( , Z ھ ؿ  }- ~, A  K ce ( ؀$ ؾ@f%@$$ր`~& qh >( ">?8= %%%%%% % % 5 ‡5 ‡5 …`aa‘a‘aafafafa$a,Z,Z,Z,8Z 8J 8J (H (H (Ȃ hȊ hȞhȞhhh```  B@B@B@B@B@B8B8b8b8f8Hf8f8f`>`>`>`@$ۻ>> Կ~* ܀܀$!)  8yP޲%<=;           @ @ @ @ @ 3A $3A-$3A-$3i$3i$i$i$i$i$a$a5```dDDDDDDDDDDDDDLLLLLLLLLL 1 1 1 1 1 1 1 1 1 1 ! ! ! !!!!]^^1^!^1A1A1@1@1@1@0@0@0O@0N@0N@0@0@0@0@0000000000@@@@QPP P P P P      D D D D D D t pppppp(p(p(p(p(0(0(0(0(10 .000000000?! 4L~%޺F3& >@ W" k@W݀ͅ*KKDH @#@#@#@#@#@'@g@f`f`f`d dKdIdIlHHH7@7%@3-3X-3X=3Y=3Y=:YzYzyqqqqqa0!0!0!0!00  ,,ooo o"o"o"o"o"4o"4o"4ob4ob<838088:::a:!:!:!2!2!!!!! ! !. !. !n!n#n3l?ll@l@l@l@l@l@D@      ( (((((((P(!P!R!R!R!R!R0r0v8v8~8n8nL>L>L&H&H'H'H'h%h%h%h%`%`%````X8x8x8Px8Px<Px<Px>X>X?x?8x?8x?8x?8x':x:|:~:~:n:.:."G@GGGGEEEEEEA@A@@@@@@@@@@@G(G(G(G(E(E(E(E(*     " """""€"€b€b€bҀrژpXpXpXpxpxpx8Px8Px8Px8Px8Px8P88P888898A)8A! @! @! @!      CCGGGG0G(G( @ 0ހ;lA ݀݀Kי&@@jOm0? Ӿ~Wp* j(??;? @@@@@@@ @)@)@)`-`-`-`---/''GEFEFEƠEĠEDDttt t P   !Ѕ!Ѝ!Ѝ!Ѝ!Ѝ!Ѝ!Ѝ!Ѝ!Џ!Ў#P#P#P#P"P"P"P"P P   0 0 0%%%%%%HT'HT/xT/xT/xT/xT/xD/xD/xD/xD/xx p p p p p 00ppppppppx@@@@@DD@@@@@@@@0888889-!-!-!-!-!-1-1///tppppppp $ T  ) X [ ڀ ځD  @ &&!b׽%l}b@U @]!32.2      bbQbQbQbQbQQY[[[ӅKӅKӅKӅKJJjj**** * * * * * * . . . . . . . . . . $ $ $ $ $ $$@@@ / / ? ? ? ? ? ? ""####<#,,(((((        \XXXXXXX@@@@@@@@@@@@@@@@X xX xH xHxHxHxHxH8xh8 0 0 0 0 0 0 0 0 0 0 0 # #CCCCCCCCCCCCCCClX xX xX xX xX xX xX xX x  Ⱦ @ / qv,?ʂm ҿ+=JA  ! ZXXXdP dP dPdPlPLT%MT%MT%MT%MD!MD! ! ! ! !      ###X8 < < < < < < < < < < $ $ $ $ $ $ $0$ 4 4 4 4 4 44 F F F F F        AAA8A8A8A91111000,0,0,0,0$0$0$0#"??ׁG2_p*ho~**o~@}Ti[-CCBC     @@@@@ @ @         ȓ ȓ   (((,,l'd'd'd&D&D&D&D&D$D$D$@   ` ` ````!!!%%%%55` =` =`=`=`aaaaasssssss1s0s0 s r r rB ~PB ~PB ~PB ~PB ~PB ~PB ~PB~P~P~P P @ @ @ @ @ @ @ @ @ @ @ @ @M@K@CCCCCB BBBBBBBBŒ     00202p02p02p02p02p02p02p02p42prvpVpppp p @C @ * *  ܿ "?  F   V  )ZZ"G ',H [ )>E$??<>     @@@@@@@@@C`C`C`C`C`Cbb ܱ ܱ1q1 q1 q1 a1 a1`1`!` ` ` ` @@@@@ @@@@@@@`2`2`2`2`2`2`2`2`rpr0r000008<<1<1<9<9<9,9¬y¬y¨ y ¨y BBJJJɅJJŇJLJJLJOdžOdžMMMA A A A >>>9x| | | t t ttvffffffAQQ Q Q Q Q  8 8 8 8000@p@p@`@`@`@@@@@$@&`&`&`&`&d&$&&&666 6 6 > > @ $#P   x[+ - Yޟ   π W $ @@nр[Ѡ ܽs`E' ]!=@ $AjEH  CCCCCKKKK ss s s sSSCQCQCQSQw??======<<000>0>0> > ??????555511111111111  @@@@@̃̃̂̂̂̂̂  K K K K K K o  ~~~~~~  $$$$$$44\\]]]]]__BJNNN@X}e؁dW," ۿ@*P.*)%0Rۜ+IFIG 0000008@@@@@@@@@ @ @ @P@P@P@@ACMCLCDCDCDCDpp3p3P3P 3X#3X#sX's////        Dddddd001 1 1 9 99ä9¥9¥9¥-…-…//|Ġ<Ġ<Ġ<Ġ<Ġ`< `< `4 `4 4 4 4000008 8 8 8 8 x | | | tP tPtPtPtPtqtyyyy{{{kk@kHkH+H+H+HH           MMMm00000111~9  ؁ [ k ~'  H - '&'  = @ "? c&aԣؼ.l~pz+""Y քZ[ '>@V=(L1,NLIF    BBBBBBC G G G G ) G ) ") ") ") 2)M2)]2)_2!_!!~~~>A&A"A"A"A"A"A"E"E"׀헀I=L=L= L= L= L9 8 8 8  @@@@@@@`         @@wg'#+;;;;:>/>oև>k>k6i6i4i4i4i4aaaaaaaA@ F@F@F@F@F@F@@F@@B@@B@DC@LCA cA cA cA  cC  C C@C@@ @ @ @ @ @ @ ` ` ` ` ` d       ::>>00000f@ O-  ; o A  kx ( Qӛ  D 5 %  !' , &j ad+%g &'!=yP2~(f׀ۼyS" ~*9w5542   01111%1%1%1%1%1%!%a%Fa!Fa!Da!Da!Da!D!D#D#D#D#D"D"D"D"D"Dâtuuuuuuyy y y y y y y Xi Xi A          ((((((((((   B$B4B4B4B4BBBBBBBBB0j0j0j0j0jjjooB=====?=?=?=[[[[A[A[A[APAP@@@@@@k@k@k@@@@@QAQcP#P#P#P3P3P3P3P3P3P3P3@3@3`37> PPPPp{kkkkkkk@ 1  qx7, z ?  @ 7sn-=@̂pR@ 8=C=   @@@@@@@BBB!!!!!!!!  ,((((()\)\)^ ^^^^^^V 0 0 0000? 00     ``````H`H`H`H@HBHBHBHBHBHBHFhp000000000 0 0 0 0 020Kՙe؆Q"?'ܿ'?ڀ@* *!2?5'    000A1A1A1A1A1A1A1AAAa * *  TDDDDDD@@@@@@@@@JJ    % - - - - - - -* -* -* -*-:-:-:-}}}}}}}}]\TPPPPPPPPPPXXXX7722 2 " " " " " " " " "   @@@H$H$H$H$H$H$H$H$$   pppppPPPPQM ~ udyN3 & M " |%w\0%G.1<ڼ3n$F*@A?6      `````@`@@@@ @ @ @ @ @(D(D8D8D8D8D:L  B BF F  F  F  F  F  F    ^E^E^E~EA 5A 1A 1 1 1 1 1 ! !!!!00@@@@(@(Z@:Z@>P@>P@>PB6PNC6PNc6POc P+ P+ P+ P+ P; P; P; P; P; P9IPIPIPIPIPAЩAйA9A9A99999910000 PppPpP@%pX@p\@`\@`\B`B`B`B`B`B`B B B B F G G  -== 98H8H8H8H8H8H8H8H8Z8Z0Z0Z0ZZZX\\\LLN׀ U} k~!޺|?صD&;*  * ( tk.S " d ޿"\w~*'9}2-7mzI޴va>>>>>??? ? 7 ' ' ' ' !  . ....&&&&&&A&A&A6A66777333@@ggggeeee`U8`8`8`8`8 ( ( h h h h"`"`"`"`&&         U o~ )ֽ C 30# T   @ # 2ܾ m);@\}!>\eۂ""A<D     0 00000000V0V0WW G G G G,GPopopoplplphp(p(`(`(a(! ! ! !MР!OЀ!OЀ!OOOOOK`````ppp p p q qC qC qC qC qC qC         0 0 0 0 000    ,.@n@n@n@n@n@n@@<455555Xxxxxxxxxxxddddd====--,BBBBB<<<<<@c'$ )(',k(BpzgfցֿM՚`g2!4H,i'AO-,)ۿ#:&   $$$$$$??AAAAAAAAAYYYYYYXXX X /////.....>:22222222gdddddd NNNHHHHH`H`H`H`H`H`H`H@h-%%%%%%%%%%%%''7      444H`H`H`H`H`H`H`Hj0@@u@(>'$germany netherlandsserbiabelgium@%!DQ ?R1S*P)T(U-V~<W}$I|CX{9z6Yy2Mx#Zw&v+K;Hu>[t/\r,Oq .]p 4o 7^n ALl 5@Gk=_j3Ji%hB`g"Ff:ae8d0bcENm's'I<FKAML:SN@S?DL=KA<?3JC?HIN5C?A@7GD:NULL 0 Joined 0 |Broken|0|1 0 0 73 1 60 2 70 3 75 4 65 5 77 6 76 7 58 8 83 9 78 A 64 B 83 C 63 D 68 E 76 F 61 G 75 H 65 J 60 K 63 L 51 M 74 N 67 P 63 Q 72 R 73 S 78 T 53 U 67 V 63 W 65 X 64 Y 55 Z 71 O 68 I 58 4 linear essential -0.250000 0.750000 linear non-essential 0.000000 1.000000 linear essential 0.000000 1.000000 linear essential 0.000000 1.000000 0 1 significant elliptical 292 0.327523 0.315210 0.201306 0.148973 0.002159 0.004267 0.000747 0.004737 1 1 significant elliptical 242 0.301653 0.174151 0.189356 0.092556 0.008652 0.002706 0.003092 0.005204 2 1 significant elliptical 226 0.306295 0.262175 0.208777 0.143857 0.002604 0.002968 0.001481 0.007347 3 1 significant elliptical 228 0.312106 0.272511 0.203468 0.146724 0.003866 0.003052 0.001315 0.006648 4 1 significant elliptical 190 0.281867 0.235894 0.175288 0.135567 0.002238 0.004141 0.000799 0.004428 5 1 significant elliptical 221 0.320896 0.310881 0.196461 0.168941 0.002511 0.003280 0.001069 0.007449 6 1 significant elliptical 234 0.295990 0.279891 0.185079 0.148521 0.001577 0.003276 0.000827 0.005816 7 1 significant elliptical 210 0.343899 0.221280 0.184543 0.147154 0.005485 0.002908 0.001805 0.009515 8 1 significant elliptical 160 0.304639 0.295286 0.187402 0.136084 0.001866 0.004359 0.000800 0.004210 9 1 significant elliptical 214 0.319071 0.281465 0.187975 0.149953 0.004479 0.002508 0.001082 0.004790 A 1 significant elliptical 197 0.273953 0.235271 0.193686 0.101206 0.002237 0.002695 0.000775 0.000400 B 1 significant elliptical 141 0.318484 0.328696 0.199994 0.151291 0.002417 0.004580 0.000998 0.003572 C 1 significant elliptical 188 0.315056 0.264777 0.199780 0.171626 0.002497 0.003234 0.001148 0.009757 D 1 significant elliptical 207 0.328521 0.352995 0.206673 0.192029 0.002078 0.006442 0.000853 0.007234 E 1 significant elliptical 95 0.329194 0.281435 0.226768 0.121875 0.002151 0.002835 0.001141 0.000697 F 1 significant elliptical 79 0.367830 0.247636 0.194867 0.146410 0.003904 0.002727 0.001551 0.003044 G 1 significant elliptical 117 0.324586 0.329985 0.205930 0.152811 0.002007 0.004338 0.001303 0.003362 H 1 significant elliptical 102 0.311045 0.343953 0.210670 0.190640 0.002727 0.005879 0.001292 0.005141 J 1 significant elliptical 105 0.255804 0.243590 0.197768 0.189137 0.008571 0.002084 0.002976 0.008625 K 1 significant elliptical 180 0.351628 0.316074 0.226128 0.158637 0.001403 0.002208 0.001092 0.004969 L 1 significant elliptical 149 0.313051 0.239547 0.194448 0.199796 0.001432 0.001041 0.001969 0.007421 M 1 significant elliptical 51 0.348346 0.364476 0.212239 0.153493 0.002087 0.005151 0.000714 0.000449 N 1 significant elliptical 97 0.332796 0.352328 0.212065 0.173848 0.001280 0.004850 0.000442 0.004104 P 1 significant elliptical 86 0.383267 0.287754 0.199945 0.160338 0.003705 0.002912 0.000984 0.003235 Q 1 significant elliptical 88 0.318848 0.309242 0.200684 0.132457 0.002297 0.003463 0.001101 0.000520 R 1 significant elliptical 85 0.352022 0.357762 0.227619 0.167279 0.001190 0.002350 0.000830 0.004477 S 1 significant elliptical 149 0.335492 0.313090 0.217203 0.160838 0.001607 0.002332 0.000990 0.005788 T 1 significant elliptical 127 0.363650 0.203980 0.195989 0.128291 0.005459 0.002382 0.002404 0.006103 U 1 significant elliptical 107 0.348313 0.315048 0.221342 0.136609 0.001615 0.001923 0.000905 0.000653 V 1 significant elliptical 139 0.363731 0.295754 0.195959 0.177608 0.004240 0.001624 0.000954 0.005556 W 1 significant elliptical 56 0.347098 0.364397 0.213449 0.178572 0.002738 0.002286 0.001096 0.003536 X 1 significant elliptical 144 0.312337 0.289803 0.209608 0.169407 0.002251 0.003873 0.001684 0.008449 Y 1 significant elliptical 41 0.432641 0.257488 0.228087 0.122523 0.000442 0.000563 0.000400 0.000400 Z 1 significant elliptical 96 0.325439 0.296834 0.221191 0.185547 0.002185 0.002781 0.002285 0.008440 O 1 significant elliptical 66 0.351563 0.325396 0.223189 0.155836 0.000400 0.000667 0.000400 0.001940 I 1 significant elliptical 138 0.358101 0.181377 0.240008 0.066067 0.000481 0.000762 0.000400 0.000408 $#"!      ! & $# "   %! %    "$$#"! %&     openalpr_2.2.4.orig/runtime_data/ocr/tessdata/lgb.traineddata000066400000000000000000011345051266464252400244210ustar00rootroot00000000000000!7ݶ37 NULL 0 NULL 0 Joined 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Joined [4a 6f 69 6e 65 64 ] |Broken|0|1 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Broken 0 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 0 [30 ] 1 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 1 [31 ] 2 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 2 [32 ] 3 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 3 [33 ] 4 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 4 [34 ] 5 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 5 [35 ] 6 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 6 [36 ] 7 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 7 [37 ] 8 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 8 [38 ] 9 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 9 [39 ] A 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # A [41 ] B 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # B [42 ] C 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # C [43 ] D 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # D [44 ] E 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # E [45 ] F 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # F [46 ] G 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # G [47 ] H 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # H [48 ] J 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # J [4a ] K 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # K [4b ] L 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # L [4c ] M 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # M [4d ] N 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # N [4e ] O 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # O [4f ] P 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # P [50 ] R 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # R [52 ] S 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # S [53 ] T 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # T [54 ] U 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # U [55 ] V 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # V [56 ] W 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # W [57 ] X 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # X [58 ] Y 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Y [59 ] Z 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Z [5a ] %% @@@@@@@@@@@@@@@@@@@@@@@@@@@@            D D  @ @ @ @@@@@@@@@@@@@@@@@@@@@@     $ $ $                      @E@E@E@E@E@E@E$$$AE$AEAEA@ A@ A@ A@ AEE@EEE@$EEE@$EEE@$EEE@$EEE@EEE@ @AEE@EEE@$EEE@$EEE@$EEE@$EEE@EEE@@  H @HAEE@EUEE@EUEE@EUEE@EUEE@hEUEE@EAEE@@@  D @D@EA@DTEA@DTEA@DTEA@DTEA@@DTEA@DPEA@P@   D @D@A@DEA@DEA@DEA@DEA@@DEA@DEA@@@@@AA@EEA@EEA@EEA@EEE@@EEE@EEE@@@@AA@EEE@EEE@EEE@EEE@@EEE@UEU@@@@@EA@@EDE@@EDE@@EDE@@EDE@TEDE@UDUT@    @@@@UP@@UDU@@eEu@@uEu@@uEu@eEU@UEUTA 00000 @@@DDP@TUDU@TeEu@duEu@duEu@$eEU@UEUPA 00000  !@DP@UDQ@eEq@$uEq@$uEq@$eEQ@TEQP 00000  0 !@$DP@$UDQ@$eEq@$uEq@$uEq@$eEQ@TEQP 00$ @$DT@$TDU@$dEu@$tEu@$tEq@$dEQ@TEQP 0@d@d@TD@DD@DD@DD@@@ @d@@@@@@@@@@P@EPD@EPD@EPD@ED@ED@ED@ED@0@2@2P@ERRPD@ERQPD@EEQ PD@EE@D@EI@D@EI@D@EI@DE1E$2J <2Z @@US~TTφuϗu݀(*<o;<o;π(***@EUE@EUE!EU1%EU2&*""'>3ۚ@VW~3ۚ"TuTu݀h*j|_?|_?πh**j@TEUE@EUE@VaUQEU@euVQEUvڀ֩UQuU5݀*&΀nZ.&nZ.&**&@TEE@EE@D@D@E@F @F@F$V@QUUUU@UyVUUUzڀ֩UUuU5%@EZEI@EYEE@EE@TEE@EE@@@D@D@E@ F 0G 4G (\@LQUUUE@]UyVUUUUzWWڀW֪UEŕUeUUEń TE D@EEDD@EEDDTD@EE@EA@EA@EA@EA@EE@ @ 4 4 h\@MQUQUE@MT|UQUQFGCրC檢@AESUQUU@AES@T@EB@D@GB@D@GB@DC@AD@AEA@@EA@@EA@@@EA@PEA@PEE@`@ t t h\@MAQUE@ME\UQUQFGCրC檢@AESUQU]@AS@@ @ @G@G@ED@EEA@DEA@DEA@DEA@EA@EE@ @  $ $ XX@IAQUE@IE]UQUQFGC׀C@AESUQU]AS@@ @ @G@G@ED@EA@@DA@@DA@@DA@@A@@AD@@TD@AQUA@E]UQUUCCۀ@USUQU]S@TTTT@T@@@P@@@@@@@@D@D@D@D@D@D@D@DDD@@EAUA@EYUQUUCCۀ@URUQU])UR@(!((!(h!(h!hT@T@D@@P@D@@@@D@@@DADD@DADD@DADD@@AD@@@@UAUT@EYUQUUCCۀ@UQUQUY8v\=9vV@HlifR@DVVQ@@DVUQ@DDVUQ@TDVUQ@\EUU@\EUE@\EPD@TEZPD@UYeIEUMd eMT PEqHT @Qd1D@ Q`!@Q U@- W`E@9WPT6[`"Jt&( t7< h7<# X:<3X9=sW@HhicR@DȨVVQ@@DVUQ@DDVUQ@TDVUQ@E]eU@E]eŕE@EYeőE@EUeEZ9V (e TCEpTCQd0@P` @P @ P`@P@TTEJ*E ;E d7,# T:,3=-sW@licR@ȨVUQ@@DRUQ@DDVYQ@DVZQ@miV@Emyj9f9< (&e T%CEpT$CQd0@(P` @P @ P`@ PPT TUI.EjE df"E Tz"MU}a]V@ElYaR@AfQ@@ArЕQ@@EvU@@EfZU@@oiB^@P|T;<d'<, ("6GE6GQd`)Pd`P$ PdDPQYATU]@e]@@eYUP@TeUeP@TeYeP@U]eAPE@E]eAU@EfU@@EvU@@EvўU@@Ef^U@@kir^@P{|3T;<3d',xEx"Fh5FUX5FQTP)QTPQ@P Q@TPUAQ@eYUQU@e]UP@e]e@uYe@Tui@Tfz@VzQPE@FjQV@EfV@@EvV@@EvU@Ef^U@fir^@vl26,2d&(ETVQATVQ!ATUQ5AUTP5AATP)ATPA@TP A@TPUAA@eYUQE@uP l l ([Q`IKQJEfF@EvF@EvE@Ef^E@VaYa^Z@VaX!FVa!FVaA@UeQeQ%@UeUe!Q%@UUUU5QUUP4AT(@T@@TP @@T@UA@@hUQUQE@|UP |h| |(_QpMKQKErVG@EaVG@EVE@EV^E@UQUQ]U@UQUU@eQeU%@eQeQ%jP:`*@UUUdQU$@(@@@@ @A@@UUAE@UP "(s<߳ <(_QpM Q1KrV%G@aQGEE@TUQUQ]U@UUQUPUjP*jP:P?P*@UUUTQU@@@@@ @A@@UUAE@UP "` (b` (` (` @(^QPI QH@VD@QD@@@TUUUQ]U@UUUU@Uj@*P?/*@UUU\Q@@@EA@EP@EP@EQP@EQP@EQP@EQPE@@@"@@"@"@"@@TUUUQ]U@UUUU@U*/߾Ȱ@UUULP@@@D@D@D@D@D@D@D@TUUU@X@UUUU@T߾İ@E]UP@EYUP@EUUHP@@@TEUU@P@UEUU@P@EYUP@E]UP@EE@EE@AEED@@EE@@AEE@@EE@EE@D@D@D@@@@@@@D@ATP@ATP@ATP@ATP@ATP@ATP@AT@@@@UUQET@eQEd@uQEt@uQEt@eQEd@UeQET@AuPD0@ @@@@@@@T@@P@UUQETUbT$bT bD bD @UeQEDT@AuPD0 @ @ @ @PPADDADDDADTD@P@QQ  TD@Q @UUUETU$Ϙ0ψ0 @UeQEDT@AuTU@0 D@ D@ D@ D@PTTTADADD$D $ٱD0dձFE1Ԁd`!@PPQ@Q  D@Q @UUETU%qq쀪a@UeUUDQT@EuUU0 E  E!QE!QERQUVQU JSD @JD H,D08D0t߳E1܀db!@UQAU@U@! @! HPa @UYUdYUꚪea~aja@UeUUTQT@EuUU0 E  E@P!QEQ@P!QEQRQU,WQUD@ԟ>D@*A@T&AT*2ATUuAEuAEE AE AM AN A uEtqxbdS8+ <;@,;UG@*UB,&k<"k<&kdfB i@eYUUE@u]UT@u]UT@fYUP@TfUUP@TfYU1@@TUYu@EHuAEHE ADE AM AN A% EE%d'h$'8&XE<7X#G@,7U'WW@&U&WB,&*k<"k<&kdfB ijUEV f @uf @Tuf @TeQe!@TUQeaPUPeaQQTUE$ATQE$AM!AM!A%AMU%TE&XE$&XE(bE(b"G@(b6WU@"6WA,"*k<"k<&kdfB ihwUF| |hX Hy@HUuPP@EePQ@ATUU@ATQUAAIAAIUUUAIUUEVUE$VQE@eQeQ%@eQe%Q%@UQU5QU@P5A,"*+<"+<&k$fA i(wUG< <( y@DV5PPE!PATATA A @UUUUAY@UUUU@eUeU%@eQeQ%j:j%*@UUUU5QUA@5@@@* @@ @T@@PQA (sUeG<ճt <(߳ bi@@U@P@@@@@@@TUUUDU@UUUUUj*j:j(@UUUU8QA9@* @ @T@@PQA (rUeE(ղt (ڲp (` ` bY`@@U@P@@@@@@@TUUUDU@UUUUUj*o,@UUU,Q)* @ @T@A @EQEU@EQET@EQEP@EQEP@EQEP@EQEP@@EP@@@@@@TUYUU@UUUUU*o߾[Z@UUUUPU@@@@@@@@@@@@@@@@@@@@@@@@@TUYUT@UUUUT߾[@E]UPE@EYUPE@UEUUPE@@@@TEUUP@UEUUP@EYUP@E]UPE@EE@EE@AEE@@EE@AEE@EE@EE@@@@@@@@ADQ@ADQ@ADQ@ATQ@ATQ@ATQ@ATA@@@UEQEU@EQEe@EQEu@UQEu@UQEe@UUQEU@AUE@@@@@@@@@@ @ TD@a @UEUEeUfV*fW.bG.bF*@UeQEEU@eE @@@@@EQED@EQED@TEQED@TEQED@PEQAE@PE@# @2TH@q @UEYEeU*ϛ?ϋ?*@UeQEEU@eU 00A A@D@PE@EQEDb@ b@ TbA!Tb!@PEQAE@PE@@# @@2TL@q @UE]EeU*??*@eeUEMU@u U@p`@`p0A A@@D@HP@@IQ@D,D (D0dϳE1̀db!@PEQAE@PE@D@# @D2TD]q @UE]eYUjj~j~jjj@eeUUMUU@uE QU@uAd@eAu1A!A@@D@ HPD@MDE,΄ 8˄0tϳE1̀db!@`bA&E@p77Ep7@#;`*2.TE]q @UE]eZI@UEYVI@UE]eWI@UU]eWE@UU]eUE@UeUUMUU@Uu QU@U1Qd@Q!QuA1A!A@TDP HP@M͔E, 8˄04߷E1̀$f! fB&07707C#;ǀ`*2*@TnrKdogK dkW dJntVhU]tEXU]dHeUTHB 5B0  1T1T!@D HT @I EhdxttEudje`f&p7707'7ƀ`*&&@P.fJ`?gG t;W t&xV h"xF\"(F\dDCI4DCE0D  T1@T2E@d"E@@E@TPH@TUP@XEUTE@VYE@V]E@V]UEE@VYUEdfep7u07'5ƀ &&&@&&J 7#G 47 47| (3|"(dDC $DC D  T1T2Ad"ADUET@TEUe@TEUE@jB @nB@nUFF@jUFDeUe@euU5ŀE&@*I ?G $?F $;\A (7\A &"BTdC dCT!T1T0d uETq@Uib @TUR (+S (?@(?UG@*UB``UŀUBh@*UTE.TD.XD*X@&X@&2@XTt@It@ET!AT1AT0Ad AuETqDhb$S8# <3@,3UG@"UB,"<3<7ǀdfBi@eZUUE@u^UT@u^UP@eYUP%@TUUUP%@TUQU0@%@TUt@EtET!AT1ATI0AdI AuIET1Eh"$8"\E<3XK@,3U[W@"U[B,"k<3<7ÀdfBihgUF|V|f@hef9@TUf9@DUQe05@DEtPEtET AT0ADE0ADE AEEUTEXE$\E$bE$r"G@$r&WV@"&WB,"&k<3<7€dfBihwUG||h>T:@bi 6@AEdPAdATATADADUIEEUEEVEE$VQE@QeUe@Qe!Ue@QU5UU@5A,"&+<3?<3€$bAi(sUG<峖<(/߳+bi&@AETPATATATU DU D@@UUYUEUU@UUUUU@eUeUe@eQeUejzj&@UUUU5U@5@& @@@_@PQAY(sUG(ճ((߳bi@AETPATATAT@U@AD@U@AD@@UUYUDUU@UUUUUUjjjzj&@UUUU5UA5@& @@T@@PQAbUeFbdb`b`b`bY`@AEDPADADAD@A D@A D@UUYUDQ@UUUUUj*o*@UUUU9U9& @@@@@QA@EQEU@EQET@EQEP@EQEP@EQEP@EQEP@AEPAAAA A @UUYUQ@UUUUU *o߮[.@UUUU-U)$@@@@@@@@@@@@@@@@@@@@ @ @TUYUQ@UUUUU ߮[@E]UUE@EYUUE@UEUUUE@@@@TEUUQ@UEUUU@EYU U@E]UUE@EE E@EE E@AEE E @@@@@EE@AEE@EE @EE E@@@@@@@@ADA@ADA@ADA@ADA@ADA@ADA@ADA@@@UEUEE@EUEE@EUEE@UUEE@UUEE@UUUEE@AUE@@@@@ @0TD@q@UEUEeEff*fg.fg.ˀff*@UUUEUE@UE@@@EQED@EQED@DEQED@DEQED@@EQAE@@E@# @2TL@q @UE]EeE*ϻ?ϻ?π*uVGY5 4ՀՐ $@Pq1E!E@PE@EQEE@EQE@D@EQE@D@TEQEAD@TEQED@PEQAE@PE@# @2TL@q @UE]EeE*??π*uf]%ـ& $ـզ" $@eAuQ1E@!E@A@A@PE@@EQE@E@ @ TA!Tb!@PEQAE@PE@@# @2 TLq @UE]eYEjj~jjꚪj@eUU]e@uUE e@uQA %@eQAu@QQq@V@a@V@QQ@Q @PIP@EMPE,⎀ (ˀ0dϳA1̀db!@`bA&E@p77Ep7@#7`*2&TE]q @UE]eUI@UEYUI@UEYeUI@UYeUE@UYeUE@eUU]UU@uUE UU@uQA d@eQAu@QQr@V@b@V@UR@Q @PI@EMEl$xˀ44ϷA5̀$f% fB&07C707C#7ŀ *"&@jbI{gG T{W @TJfuVH@UUuED@TeHD@!UUMUT@Uq UT@Vq `@aq@Qr@W@)c@W@eSE@EaI@TI@\EMEhdx׀t4Eù$fe fBf07Cw07C7ŀ *&@*VI;WG D;W DJ&xVHUxEL(H@\ IFP@Yp VP@Vp d@`u@Pv@S@)g@S@uWE@UqQI@TU@@XEUTEDVY DVY @VYUEI@VYUI$fd07u075ŀ &&@&I7G D7 DJ&| HU|L(@\ GS@YpWS@VpVf@`Uu@UYUv@REig@EuWETqQUdEUDZQA DZQA@UQUEE@PQUAPQePuUqŀEb@"RI"B"BJ"\AU\@  @@ %DR@`%UR@`Uf@`Uu@UYUt@REid@EuTETpQ Eh` JQQ D" D"@"UF@"UBPPUĀEUBi@EYUUDEYTEEYXAEYXAUUX@@0@@\5DU@YP5UQ@Pa@Pq@Up@J)`@J5TJT0U h O " 3@3UG@"UB,"<3<7Cǀ$fBi@EYUFEYTEYTEYT@%UUT@%@T0@%@T5DU@UP5Q@Pa@P q@Q p@B )`@QF5TFT0U h O "\3X@3UW@"UB,"<3<7À$fBiwUG@i9@Y9@d05@T5TU@U5Q@a@q@p@`@UTTUXD_\Db@s@@sQW@"UB,"<3<7À$fBi(wUG<<@(n=@^9@h04@Q5TT@V5T@D@D@ D@ D@U]UTDUEDUQ@@aeUf@ae!Uf@aU%UV@!%UB,"&j<3~<3~À$bAi(sUG<ճ<@(_-@_)@ $@Q%TT@VA%T@AD@D@EA UE@EA UE@UUYUUU@UUUUU@eUeUe@eQeUej&zj&@UUUU5U5& @@^@PQAYbUG@_@_@i@AUTT@FAT@AD@ D@EA E@EA E@iUYUUU@iUUUUUj&jj&z7j&@UUUU5U5& @@_@@QAY@EQUUF@EQT@JQP@JQP@JQP@Y@@AUPD@AA@@A@@@@EA A-B A}kaU}ge~&7?*@UUUU9U9& @@@@@QA@EQEU@EQET@EQEP@EQEP@EQEP@E@A@DA@@@@A@-B @}UkqU}Ugu*?߮?.@UUUU=U9$@AA @@UUYUQU@UUUUU*߮?@E]UU@EYUU@UEUUU@@@TEUUQE@UEUUU@EYUU@E]UU@EE@EE@AEE@@EE@AEE@EE@EE@@@@@@@@ADA@ADA@ADA@ADA@ADA@ADA@ADA@@@UEUEE@EUEE@EUEE@EUEE@EUEE@UEUEE@AEE@@Q@UEUEQEfb*fg.fg.ˀff*eWGU50ŀА @PA@@"2L@q@UE]EaE*Ϸ?Ϸ?π*uwU574݀՚"(@UEUFF@EQE@D@EQE@D@EQEAD@EQED@EQAE@E"2L@q@UE]EaE*??π*߷Y5߷4݀՚(@UUEUQTG@G@Q@Q@TEP@EQEPE@@D@@D@AD@aD@EQAE@E"2LPq@UE]UaUEjj~jjj]uـuـժ)@eUE@QU@W@U @W@UQV@TQ @TIP@EMPE⎀ ˀ0γA1b!@bA&E@&&E&@"&*"&EYa@UEYaUE@UEYUE@UEYeUE@UYeUE@UYee@eUU]e@eUEe@eUE)@eUE@QUE@WAUE*@WAUEeW@Ta @TI@EMEL Hˀ @AL@JbHbB&&B&&B&&&@EUQIATPD TTP @TEUuTH@UUuDD@UUeHD@eUUMUT@UuUE UT@VuUA T@WeUAU@WUUAV@WEUA*W@WEUAuWEDTEqITILMH TDH TD@ UAUD@IQUU@bBf&Bf&B"&"@&QI7PB D7P TJ&yTIXUyDEX)HB@X!UMUW@Yua UW@Vua V@SeaU@SUaV@SEQ*W@SEAuWEEUqIDTHTDDDDD@UATE@EQUTAbBd&Be&B!&"@&H6BD6DJ&l HUlL(@\!US@YueS@Vuef@SemUe@SUmUf@RE]*g@EEuWETqDdD@ZQA @ZQA@UQUAE@PQUAPQBePBeUB!EB!@J!UDJ!TAJ!XAJ!\AU\@ @@\!UDS@YuUUS@uUUf@e]Ue@U]Uj@J])k@JEu[JTp Eh` JQQE @_ @Z@UUF@PUBPB遀PB遀UB鄀EQBi@EQUUDEQTEEQXAEQXAUQX@ @X @@\ U%DV@YtU%UR@tUUf@d]Ue@U]Ui@J])i@OE%YOT  h OE @" @3@3UG@"UB"郀2郀2郀bBi@EQUE@T@T@T@%@T@%@T0%@X%5U@Y`5Q@`a@` a@U a@J )a@O%UOT  h OD T"\@3X@3UW@"UB"郀2郀6郀fBi@EUUG Ĥ Ĥ@9@9@05@T5U@UPA5Q@PAa@Pa@Qa@Ba@UWUUWTTXT[\@@UQUPV@T@W@UQUQW@@UUB"郀2郀2郀bBisUGų@N`=@^P905@U5U@VTQA5U@TUAE@TYAE@T]A UE@D]A UE@UU]QUU@UUUQ@UUUP@UQUPU@eQeUe@eQeUe@UQUUU@@A")2)2ib@irTGղ@_`-@_P)0$@Q5T@VUQE5U@UUEE@UYEE@U]EUE@E]EUE@iU]UUU@)UUUUU@)eUeUe@eQeUej%zj%@UUQU%U!"  @@Y@PQ@Y@UaTF@U@^@_`@_P@@AQT@FEQEU@EUEE@EYEE@E]EUEmFE}jV}fV~%jj%z7j&@UUQU5U5&  @@Y@@Q@Y@EQTUE@EQTT@FQXP@FQXP@FQXP@X@A@UT@B@EU@DEE@HEE@E]ETEmFE}{W}w~&7?*@UUQU5U5&  @@@@D@D@D@D@D@DADDB@D@D@D@EAE-B$E}{uW}wu*?ߢ?*n@UUQU5U1 @@@@@A-B @h{qUiwu*fߢ?@EQUY@EQUYE@UEQUU@@TEUUQE@UEUUU@EUUU@EQUY@EEE@EEE@AEE@@@EEE@AEEE@EEE@EEE@@@@@@@@DA@DA@DA@DA@DA@DA@DA@@@EUEE@EUEE@EUEE@EUEE@EUEE@EUEE@EE@@D@@D@@Q@EUEQE*fb**fc.*fc.ˀ*fb*ewQ570" @EAE@@!!@@a@EUEaEj*Ϸ?Ϸ?πz*uU54݀(@EUEUDKG@DQE@@DQE@@DQEA@DQE@DQA@!!L@a@E]EaEj*??π*U54݀ٚ(@ٕeFUdKdG@TQ@TQ@TEP@DQEP@QM@@QI@@QEA@QE@QA@HPQ@EYUQUEjj~jjjYuu݀隦*fFـfڀkf" @W@UaW@Ta TI`@QMPL HAEQEQEEEEUTQ@EUUQUE@UEYeUE@UEYeUE@UYeUEe]uـuـꚦ**j//ـj//ڀkj**@WAYEuWE@TqIDL L H@A@@EQ@@EQ@@E@@EA@@E@@EUQ@ATP@TTP@TEUuTE@UUuDE@UUeHE@eUUMdU@eeUe eUjf *j/ـj/ڀkj**@WEYAu[EDTqIDL HX@HX@@UAQD@EQUQ@@EQQ@@EQ@@EA@@E@@EA@@@@D@@TDy@@UQy@@UQi@@eQUEW@UuQaWff sjoـsnoڀc.**@EAu[EUqDXX@DDDD@U@TD@EQUT@@EQT@@EU@@EAD@EAD@EUDDTADDhDDlHTlL(@\!US@YueSf& s*oՀs.oހb.**@JEu_JTq Dd D @ QA @ QA@QUAE@QUA@QTA@UA@EAD@EAD@EUDETAEXAE\AU\@ @\!US@YueS&v*~oՀ.noހ.^)*@OEe^OT` Eh` QQD @OQ @JQ@EQUE@@QUA@@QA@@A@@@PAT@@PUTDPTDPX@PX@PX@ @X@@\ UDW@YteUS&fj:noր>noހ.^j@OEU]OT Eh OQ@ @ @@UUF@PUB@PB@PB@PB@@QATA@EQUETTT@%T@% T @&@\%U%DV@YpQE%URfFj:kҀ>kҀ.j@OUUOT h @ @T[]@X@UUV@@UB@@B@@B@@B@@QATA@EQUE  @=@905@T%5TU@U`QA5UQfBj:kҀ>kҀ.j@U_UU@U_UQ@TZY@@T[]@@UQUP@T@@UQUPU@@UTA@@B@@B@@B@@QATA@EQUF@=@905@U%5TU@VdQA5UUfjjkʀnkʀ^ j@eU]QUU@%UUUQ@$UUUP@UQUP@eQeUe@eQeUe@UQUUU@@A@@B@@B@@UB@@Q@UA@EQTFENO@-_@)0$@Q5TT@VUQE5UUfjjknkn}nVU=fVU=ffe@eQeUe:%満*%j@UQU%UU!@!@@@@UA@@Q@UA@EQTUE@ET@JP@KP@[P@ @AQ%T@FUQE%Uf*˚j+ښjkj}{W=w>%:%?7*&@UQU5U1!@@@UA@@P@UA@EPTUE@EPTT@FPXP@FPXP@VPXP@X@A@UT@B@EUD(H(ZYhi}{W=w>&?7??**@UQU5U1!@@DDDDDDADDBDD  @JX)}{W=w>*??+ߢ?**nZ@UQUU@@@@A%B AhwqW)wu**f+ߢ?@EQUY@EQUYE@EQUU@@A@@AA@EUQE@EUU@EUU@EQUY@EEE@EEE@EE@@EEE@EEE@EEE@EEE@DD@DUEE@EUEE@EUEE@TEUEE@PEEE@PEAD@PEAD@@@@QDQDb @HUIcG@IUIcGb*JTQ%IP IP D@PEQAD@@@@@QDQH @\LMG@\MMGh*dU5d4X&(@\EUM7ELD 3H"D@@@@@@@@@QDQH @\LMG@\MMGx*tU5t4̀Y&(@]ŹM7EO 3K"G@Xa@TaD`@P@ @@@@@@@DQDQEHF@\HYFF@YYVFfUuù&*ջO7ѿ3΀k " @W@YqG@Tq D`@ PL H@EQEQEEEEUQDDQEDH$E@TDYeEE@UYeUEeYuù**׻;?ӿ;?΀**@VAYEuGE@TqIDL L H@@@EQ@EQ@E@E@E@EQ@@D@@DD$@@TDUu@@UUu@@䚥@ @䚥EI@eI+;;΀**@VEYEuKEDTqIDL HXHX@U@@EQU@EQ@E@EA@EA@EQ@@P@@D`@Dp@@p@@`@@!AQEF@UeQaFf {΀.**@IAuNIUq DXXD@D@@U@P@EQUP@EQP@EP@EA@EA@EQ@P@D`D`H`H @X!QC@YueC& ;?̀.**@OEeMOTa Dd@@ @@@@ @@@@@Q@P@@QP@@P@P@P@P@P@D@D@H@L@\!QC@YueCf&r;r?̀b.*@OEUMOTP D`P @P@ @@ @ @@@Q@@Q@@T@T@T@@T@@T@D@P@@P@@P@ @P@\ AQS@YtQeSffr;r?πb.@OEUMOT D` @@ @@ @EQP@@QT@@T@@T@@T@@QT@EQTTT%T@% T@&@\%UDW@YpQEUSffFr;r?ˀb.@QOUIAOT@ ` @ @P@@P@UQP@@QT@@@@@@@@QAT@EQU  @=@90@6@Y%ѕ5TV@YpQE5URvF; ; Ӏ* @UZUUEZTQ@P@@P@@UQQPU@P@T@UQQP@@QT@@@@@@@@QAT@EQU@=@905@Q%5TU@VtQQ5UUݻ ߀ @iUjRUU@)UfRQU@(UfRPU@UQQPU@eQeUe%@dEe@UQTU@@@@@@@@T@@Q@T@EQTTD@@,@(0$@Q%5TT@VeQU5UU;;߷;߀}wV=wV)[k>)Xj~)) %%))AAAAAAAEEEEEUFEU@EFFEe(,@EUUUY]]iu8,@EVVVYXXht8,PUVV$48,PUUT$48, UEIT$$(, ###!aaAP#33!a``PP@PSCCCCBQ!aaaqp`PSRQEEEQaaaaa``PSVUIIEAAQP[jYLHD@@PP[h\\XTPPPP[h\[k[[gWXXhiffVPPPQQQQAAAAAAAAAAAAAAAU @EEUUUUUUYYYIE@@@EEYYYYUQQQeeim]IE@@@EEYYYYUQPPdtxl\HD@@@EEYYii%%$4xl\HD@@PTUY]my5%$48,Y\lT$48, QTT\\LTT````QQQTT\\LDSBAEEQQSfUIIQQRiY\hQQZi]\heeeUQZh\XXUUUUUYYiyuhXUUeeedTTTTTTTTAAAAAAAAAAAAA@@@@@@@QQEFFFEE@@@DDX\\YUUUUUEE@@@DDX\lieaQQUUUUUEE@@@DDX\lhddTTeeiiYIE@@@DDX\lx4$%5yiYIE@@AEEY\lx8($48,,<<,Tdtxl]YQTT\\LDD@@PPQQ D@@QQQQEGRVEFRRQEEYmVVQiY]iUUUi]\hUUUUeuuh\XhiiiUUUUeeehXTTUUUUUUUUUUTTAAAAAAAEEEUUECC@@UUECC@@,ieEGG@@,yui]]YUVV@@,xthXXYUVV@@,84$%%eePP,<<,TeUU,<<,DAAQ$,,,DAaa""""QCCCCCF D@@P`aa"22"IKGFT`pqaaa!YoWV  !!aEEYn[Z QAAEEII]m[Z UUeeeh]\hZZ TTdddh\XhZZ ThyzjZZ TddddTTTTTTTTTAAAAAAAEEEUUE eeECC@@,yuECC@@,yui]MIEGGD@,xthXXYUWWTP(84$TUTP(84 @EFU(,,,DAEU%$  DAuu%40 UGGGGWVTauu%51!Y[WVTaqqaqq!11!Y_WVT`pp`pp Yn_^ P````P@@DDHI\l_^ P@@DDHL\h_^ Xh_^ Th_^ ThxxhTTTddddTT $$((()SC@@(84(<<)SC@@(84(<<)WGTP$44((()WGTP$$$$TUTP$$$ PQd` @AGU%40 Tavu%40 UFFFFFFTauu%40 UGGGFFFTauu%40 Y[[[WWRPauueuu%X\WVPauueUEYm_^ P`dddTDEAAE_^ UU_^ TT_^ TTXXTThhx8(TThhTTTTTTT((()RB@@ 44(<<)RRPP$44(<<)W[TP$$$((hiUW[TPTUUW[d` PQKYitp PQ[Y)40 PQoi)40 Paki)40 UEEEEEAPaji)$ TDEEEEAPaji%X\]ZVVVPaUUY]^^ PauueUEUUUUZZ PaeeeUUUUZZ UV[Z UVXXUVji] UVjjUUUUUUU((()QAAAA@@(<<)QaaaaPP $$(<<)VjnmiTP((hiUVjnmid` TUUWkomitp PQUWKO]itp @AO])40 PQ_])40 PQ_])$ Pa_]Pa_Y@@AAAAAPa^MUUUUUUUPaZIUUUUUUPauyiii%UUUUPaeeiii%UVUUUV] UVjiUUVjjUUUUUUU    P@@@@@@ 00!QaqqaPP 00!)==)$     !)==)40 )==)40 PQUVJN])40 @AEJNN])40 @AO])$  PQ_]PQ_]Pa_IPa_MUUUDDDDPa}%UUUUUUUUUPa~}%UVUUUUUPauvn~}%UVUPaeem}}%UVU,<<$UViiUUUiiUUUUUUU     00  00  00 $44$40    !%55%40 %55%40 %40 @AEIIIY%$  @AEJJJY@AJYPQ_IPQ_IPa_MUUUPa}%UUUUUPa}%UVUPa}%UVUPaefn~}%UVU,<<$UViiUUUiiUUUUUUU    $$$$$$$$40 $$$$40 %$ @AEEEEU@AEEEEE@AEFFFEPQZIPQ[IAAAPa{y%UAAAAPa{y%UBAPa{y%UBQ,<<$URQ 00 QRaaQQQaaQQQQQQQ@AEEEEE@AEEEEE@AEEEEEPQUEPQVUPQWU@AAAAAA@AAAAAA@AUU@@@@@@@C   ?   < < < < < < <,ddddddDDEpppxxxx|pppp`   bbbBBBB@@@@@@`ddddddduuu55555           ``b#?V.#?@aD?Xџ۾ـV}eۿ.ۿ'& )9yyyyyyyqppppppp@<<4446&&&&&&&&&&&"""""bbccCCAAAAA HHHh``ppppppppppp001&>$EـX|;A  !!!!!!qqqq@@@@DDDddllllllhhhhhx{{{{{{{{{@@@@@@XX\\\      >bccccc####'7777@7@@@@@```````````222222  ,$!=QD<io(?B/$<<       `@@@@@@6 @6 @6 @6 @6 @v @@RRRRRBHhhhxxxx0x0x0p P P D D D D     ( ,  BAAAA A A A A A A <<4WWWWAAAAAAA,ݾݿP՛ܿAS1C$#pv?h?F >3@@@@@@`````````` ((( ( ( , . .>>><40000CC&""[b[b[b[b[b[xZxZXXXXX$ ||tt@4@4@4D4DDDDDDDDDDVVV633333333;;;))hhhhhxxxՀj?b kyR'2}4/ޏ׿^!D. C  """bbccccswwwwwwvT       O`     @@PPXXXXXX||<<<<<888@@@@@@@@` bb b b b b b bbbkkkk++  @@@@@@@`````````d"<ۀg$;yP&2 '?Cٿ\Ҡ ܾKG  @@AAAA[[{{{{jn"""""  @@B@B@@@@````aa!́̓͡͡͡ * * * " " "b(b8b0`0`0`0d0e0E0000000  !$$..@.@.@.@.@>@>@?@??;;;;P\\\\\\\\N@@@@@@'%&WО_ڀiHxE>#? $>T.@c@3   ppppppppppPP\\\\\\\\\\\\\] pP8(((((((((((GGGGjnӀj(1?lB    6 BBBox0?0?0?0?0=0=====,,,,,@@@@@AAAAAAA cCCKKJJ  &&&&&>88888000@p@p@p@p@p@prb`B@B@B@B@B@B@B@C@C/-------<<<<<<<< <Xџـ%$/&d}b|8C?Ag>'U/Ab"? #> I  `````xxCCCC  ``````=@=@=@=_=_%%%%%$PPPPPJpp`` ))))))p 0 0 0 0    4j4j4j4j4j4j$h$h@$p@,@,@,@,@,@,`.` ` ` ` ` ` ` a @@@@@@Uѝۿڿۿ.}c_ـ?g=?&EQ/b@@g!= 2BBBBlFdFdFfFfFfTFTFTBtpp00888888888((       Ddd/`/`/`/`7`6`64000000PPPPPPP@@@@@@@@@FDDDEEEEBBRWWWWWW__}}}=====-(((8(8(8(8 8 :::::::zzzzjFFFF܁\ O~\jyO3w[1<QF]0 D   ppppppBpBpBpBpBpJpJp@Jp@Jp@JP@JX@@@KCCCCCCCsrrrrr000000      ,,,,,, . .. . . >     QQxQxQxQxAxAxAxp@hp@hp@(P(P(P P P P PP@@@@@ @ @@%@%@%@'wwwwwwrrrrrrrRRRP..G.G,G,G,G,G,G(G(F8FFF Yҟ$$e?|rq}hڀE}/.ݻ&C >=@wZ1 A :  $$$$$,,,,,,,,<<4 4 4 4 4 4 4 4 4 4 4   ```xgggccccbb"=== = ( ( ( ("h"h"h"hb`b`b`b`b`b@b@bC  AP)@bCtD*6#& [Ϡ+% @  PPppppp@@@@@ nn.....""""""""""""2221yxxxxpPPP  EEgfffvvvrrrrrrrr@2@2@@@@@@@@ddddddd$        &?>LL >|77ـۿ#Xҟ ܽ-Yt G  ____          PPPPPP `bbbbbbbcc#!!%%%  ,,,,<< 9 9 9 0 0 0 0/0/ PFFFFFF=9 9 9 9 9 9 9!? =!?!?~#ں(' %?~ۀ<~8 \\\\|||||~bbbb#####!!@@@@@@@@@@@@@@hhhhhhhhh(((((,8,888888800 \P@@@@@@@@@@@;@;@;@@n n n n n n f d d d"d"d"d"D"D""#HA}zKܳο"??C C ((((8888888 8 8 : : + # ########``````` && & " " " " " " ""  b````` ``          ````````       h```````a@!=~!%:V-׽Vў`s- J'0ڿ?; A@@ДЀЀ B b(b(b(b(b# # # # #            # ##:::::>t>TVV@@@@@@@@xxxhhhhh>>>>>>> f`````qppppxxx!ݼ޼C' &@@ =!?gM = -    ```ddfffn{{;  (((  AAAAAAAAAAAAAAAGGGFFFF D D D D D D@@@@ @ @ @ @ @ @       ((((((((99/D"Yޟ?;g% 4>: ((((88888xXXX8X8H8H0H @ @ D E E E !!!#%'7@T@T@\@\@\@\@\@@@@@@@@@@@@@@@@@@e%%%%--           ڻR %X '%F X@ > * ````````GGOOOOOOONz:::::000000000000000```````````````dƼ??-T/8 @@@@@HIIIIY Y YlYlylslwl7l7l6`6`6?===C=K<K,KKKkoooooo'aaaaaaa!!!!!!!!8hhhhh``@@@@@@@@@@ ">"'~(9O(\ >]EU~| < 9  ]]\\\  """" " " " " " ++ D D@@@@@@@@ @ @ @ @ @ @ 38888<<<,,,,,,,,lLDDDD@@ @ ~bI ܽ' ">qRL A$B!!$!$!,a,a,a,a,a,a,a*:::::22222"""" ``D`D`D```````       xxxxxxx|<<,,,,,, ,:::::z{sccccQ-?Ygـ_#?'/\Ҡܿ$B;  ###### # # cbbbbbbb@                `pPQYYYYY[8DDDIIIiiy;:::::::>66666 B~#:# >??΀0_ݢӿ|? }dH= CCGGGGGGGGEDD    ! !!17,?$?$?$?$? ? ??>>@@@@@@@¡¡¡¡¡pPPPTTT8899999= %      >;+++      @A @A @Q @Q @Q @Q@Q @Q @Q @Q @U (((((A!<'E <eP؀( پ0Y B uedE       pa         }yqqqqQQQQPPPDDCDCDCDCFCFCaemmll,,           0000000 `b 111qqqQQSSSSWW_NNNNNܼ%ٿMҚfۀxgwBP. Bah!;F 5`DDDllllllhhh((((((@  н @?B|1; 9          P P P P P P  ;???>>>>>>$$$$$$$$$$$$$$$$$PVVVVVVVFF           (((c ="?@ܾY-?! ؀Uӝ :     Q     0 0000000       3;::::*****(hhhhh          111110@@@@@@-G:IrRthuf" %Q|:6(5 @ @ @,@,`,`,`,`<`<`<`8`0`0 00p0P00011@BC      0pppxxxxx33}}}}lLHHHHHH^^^^^^^^ LLLLLLL  0233xxxXXZZZ#EA`"&H#߽~)߹|8/G AzJ~8Ie~>$$$$$$[[IIIIIDIDADDDNNNnnn???;;;;;9BBB     HHH111111!!!!.@.@.@.@.@.@.@>@<<<<<<<@@@@@@@A7&Wڽ'_ "% YA@_ BUۀ\ * GGGFFFF        @@@@@@@@=====<<<<<((((((((888888:CAAAAAAA@@@@@@@@@,,,,(([ <&e% & c! ?    ````aa   ###ccccki   00000   պw]Rh$;Y Q %?#"uk@#!      %C&A<3CG3BI2D:@G8CA-:*89B;=E59:5>*?NULL 0 Joined 0 |Broken|0|1 0 0 67 1 38 2 65 3 60 4 51 5 67 6 71 7 51 8 66 9 73 A 50 B 68 C 58 D 64 E 71 F 56 G 67 H 65 J 45 K 58 L 42 M 56 N 57 O 66 P 59 R 61 S 69 T 53 U 57 V 58 W 53 X 62 Y 42 Z 63 4 linear essential -0.250000 0.750000 linear non-essential 0.000000 1.000000 linear essential 0.000000 1.000000 linear essential 0.000000 1.000000 0 1 significant elliptical 100 0.459922 0.410555 0.287578 0.177773 0.000615 0.000400 0.000400 0.000400 2 1 significant elliptical 100 0.459687 0.360391 0.317148 0.165000 0.001016 0.000970 0.000400 0.000400 3 1 significant elliptical 100 0.460938 0.359480 0.307187 0.165000 0.000887 0.000611 0.000400 0.000400 4 1 significant elliptical 91 0.449305 0.297991 0.266140 0.149038 0.001133 0.000511 0.000400 0.000400 5 1 significant elliptical 100 0.461914 0.386445 0.300898 0.165586 0.001156 0.001069 0.000400 0.000400 6 1 significant elliptical 100 0.457188 0.412020 0.293789 0.173672 0.000821 0.000603 0.000400 0.000400 7 1 significant elliptical 87 0.552173 0.283526 0.308055 0.141478 0.000815 0.000400 0.000400 0.000400 8 1 significant elliptical 89 0.464624 0.421585 0.291784 0.175342 0.000650 0.000639 0.000400 0.000400 9 1 significant elliptical 70 0.465904 0.405335 0.290737 0.170033 0.001499 0.000753 0.000515 0.000400 A 1 significant elliptical 100 0.396250 0.312652 0.285742 0.143359 0.000854 0.000673 0.000420 0.000400 B 1 significant elliptical 90 0.467144 0.424340 0.298438 0.171528 0.000669 0.000779 0.000400 0.000400 C 1 significant elliptical 96 0.462972 0.341573 0.314290 0.170776 0.001472 0.000684 0.000611 0.000400 D 1 significant elliptical 100 0.464219 0.419559 0.295430 0.175469 0.000760 0.000713 0.000400 0.000400 E 1 significant elliptical 87 0.471803 0.400005 0.316541 0.170393 0.001120 0.000792 0.000400 0.000400 F 1 significant elliptical 100 0.542461 0.328906 0.286328 0.163672 0.001764 0.000683 0.000418 0.000400 G 1 significant elliptical 100 0.456094 0.397195 0.295625 0.173281 0.000806 0.000695 0.000400 0.000400 H 1 significant elliptical 97 0.466334 0.421351 0.291720 0.188185 0.001086 0.000830 0.000400 0.000400 J 1 significant elliptical 94 0.383851 0.276920 0.297665 0.156292 0.000965 0.000513 0.000447 0.000400 K 1 significant elliptical 100 0.468555 0.393129 0.303164 0.169219 0.001253 0.001000 0.000440 0.000400 L 1 significant elliptical 98 0.386161 0.283203 0.314892 0.158801 0.000849 0.000400 0.000415 0.000400 M 1 significant elliptical 89 0.472656 0.416380 0.301176 0.189563 0.000977 0.001119 0.000400 0.000400 N 1 significant elliptical 100 0.477305 0.443148 0.293945 0.188711 0.000686 0.000900 0.000400 0.000400 O 1 significant elliptical 92 0.463103 0.414275 0.288935 0.181726 0.000599 0.000502 0.000400 0.000400 P 1 significant elliptical 100 0.547539 0.352590 0.279102 0.174023 0.001281 0.000573 0.000400 0.000400 R 1 significant elliptical 83 0.474068 0.423903 0.297251 0.173663 0.000736 0.000639 0.000400 0.000400 S 1 significant elliptical 70 0.470647 0.384900 0.303962 0.167132 0.000722 0.000713 0.000400 0.000400 T 1 significant elliptical 76 0.561369 0.275159 0.311369 0.131425 0.000947 0.000400 0.000400 0.000400 U 1 significant elliptical 91 0.465574 0.415041 0.292024 0.183336 0.000689 0.000504 0.000400 0.000400 V 1 significant elliptical 100 0.554141 0.326563 0.286680 0.144609 0.000921 0.000400 0.000400 0.000400 W 1 significant elliptical 83 0.472750 0.372360 0.305346 0.171404 0.000607 0.001859 0.000400 0.000400 X 1 significant elliptical 100 0.468633 0.358562 0.317266 0.161211 0.001284 0.000921 0.000522 0.000400 Y 1 significant elliptical 100 0.546562 0.288734 0.290898 0.138555 0.001532 0.000517 0.000400 0.000400 Z 1 significant elliptical 87 0.471354 0.358333 0.329786 0.163120 0.000642 0.000495 0.000400 0.000400 1 1 significant elliptical 50 0.477422 0.213805 0.300781 0.070234 0.000654 0.000400 0.000400 0.000400 "$#"!      openalpr_2.2.4.orig/runtime_data/ocr/tessdata/lkr.traineddata000066400000000000000000012337071266464252400244510ustar00rootroot00000000000000E4# New Segmentation search params enable_new_segsearch 1 segsearch_max_char_wh_ratio 1.3 # Adopted from chi_sim.config. edges_use_new_outline_complexity T edges_children_fix T edges_max_children_per_outline 10 edges_max_children_layers 5 edges_children_count_limit 1000 tosp_force_wordbreak_on_punct F textord_force_make_prop_words T textord_noise_sizelimit 0.2 textord_noise_normratio 6 textord_max_noise_size 7 # Force use of a single x-height mode when the text is written horizontally. # This information could come from the unicharset (script_has_xheight), but # it is better in the config file so as to be available when training. textord_single_height_mode T # Use character height as x-height, and estimate it from character pitch and # kerning width. textord_use_cjk_fp_model T textord_space_size_is_variable T segment_nonalphabetic_script 1 assume_fixed_pitch_char_segment T 49 NULL 0 NULL 0 Joined 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Joined [4a 6f 69 6e 65 64 ] |Broken|0|1 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Broken 가 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 가 [ac00 ] 거 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 거 [ac70 ] 고 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 고 [ace0 ] 구 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 구 [ad6c ] 나 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 나 [b098 ] 너 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 너 [b108 ] 노 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 노 [b178 ] 누 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 누 [b204 ] 다 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 다 [b2e4 ] 더 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 더 [b354 ] 도 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 도 [b3c4 ] 두 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 두 [b450 ] 라 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 라 [b77c ] 러 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 러 [b7ec ] 로 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 로 [b85c ] 루 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 루 [b8e8 ] 마 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 마 [b9c8 ] 머 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 머 [ba38 ] 모 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 모 [baa8 ] 무 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 무 [bb34 ] 바 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 바 [bc14 ] 버 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 버 [bc84 ] 보 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 보 [bcf4 ] 부 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 부 [bd80 ] 서 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 서 [c11c ] 소 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 소 [c18c ] 수 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 수 [c218 ] 어 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 어 [c5b4 ] 오 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 오 [c624 ] 우 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 우 [c6b0 ] 저 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 저 [c800 ] 조 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 조 [c870 ] 주 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 주 [c8fc ] 하 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 하 [d558 ] 허 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 허 [d5c8 ] 호 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 호 [d638 ] 0 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 0 [30 ] 1 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 1 [31 ] 2 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 2 [32 ] 3 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 3 [33 ] 4 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 4 [34 ] 5 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 5 [35 ] 6 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 6 [36 ] 7 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 7 [37 ] 8 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 8 [38 ] 9 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 9 [39 ] 11@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAA@A@A@A@@@@@@DDA@DDA@@DDA@DDA@DDDA@DDDA@DDA@@@@PAUEAAUEA@AUEAAUETAAUETAAUEAAEEA@@@@@P@QQUA@QQUA@QQUQQQUTQQQUTQQQUQAAEQ@@@@@P@QQQP@QQQP@QQQPQQQPQQQPQQQAAA@@PPQQQPQQQPQQQPQQQPQQQPQQ@AA@PPAQQPAQQPAQQPAQQPAQQPAQ@AA@@@EUA@EUA@EUA@EUA@EUA@EU@EUQUUQUUQUUQUUQUUQUUQUUAUUUUUUUUUUUUUUUUUUUTUUUTUUUATUUTUUTUUTUUTUTTUTTUATUUTUUTUUTUUTUTTUTTQ@@UUPUUPUUPUUPUUP@UUP@PA@@UUUUUUUUUUUUUUU@UUU@@A@@UUQUUQUUQUUQUUQ@UUQ@@A@@UUQUUQUUQUUQUUQ@UUQ@@A@@@@@@@@@@@@@@@@@@@@@@@@@@AA@BBDBDA@DA@@@@@@@@@@@DDEEADDEA@ DEEBDEEEBDEEEBDEEEEA@DEEEA@@@@@@@PBDEUEAFDEUA@IDEURLDEUURLDEUURHDEUEUQ@DDEEEQ@@@@@@ @ PEUUQUAEUeQA@IUeQRLUeQURLUUQURHUUQUUQ@DUEAEQ@@@@@@@@@@@@ @ @ @@P@UUQQQ@eUaQ@aUaQ`UaQQ`UQQQPUQQQQ@@UAAA@@@@@@@@@@@@ @@@@P@UQQUQ@eQQU@aQQU`QQUQ(`QQUQ$PQQUU$@QAEE @@PPQUEQ`QE`QE`QEU`QEUPQUEU@QUEE@@PQUEUU`QEU`QEU`QEU`QEUPQUEU@QUEU@@@AUUUU@AUU@AUU@AUU@AUU@AUUU@AUUU@@B@UUUUUV@UUUV@UUUY@UUUY@UUUX@AUUUUX@AUUUU@@@@@@@A@UTUUU@UTUU@UTUY@UTUY@UTUX@AUTUX@AUTU@@@@@@@@A@PUTUU`UTU`UTY`UTY`UTX@QUTUX@AUTQ@@@@@@@@e@UUUUUejUeekUeUkUeQjUUeP@UUUUUP@EUPA@@@@TT@@T@UUUUUTjUUeTkUUeTkUUejUUe@UUUUU@DU@A@@DPP@@P@UUUUUPjUeePoUeePoUeejUee@UUUUU@DU@A@@@PP@@P@UUUUUPjUeUPoUeUPoUeUjUeU@UUUUU@DU@A@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ @ @ @@@@@A@     HTQ@Q@  P@@@@ @ @DDEEAHʁ@ J JEUUUUU@EUUUU@ 00 P@@@ @ @ PGDEUUAF@ILzLZXDVUUUU@UDVUUU@" 100000 @@@@ @À@PEFUUQUEF@ILzLڢZXUVQUUe@UUVQUe@%" @%100@%000@% ` @P@@@@AA@@8(DTEUUQQUUe-@q?pr?`ҢV7TUQQUU5@UUQQU%@% $@504@504 @%  d @P@@@@E$E@8@%4J@%$DJ@TE@UQQUUU@e,@q<pb<`VtPQQUUUtPQQUUUd $dX0 $d\0 $dL  dHE$$I@%4J@10M@! @I@PE@QQUEQU@a,@q<pj<`ZtPQUEYtPQUEY d X X H HE I@10M0 @ @PQUEU`.p=pj=`j(PUUE]PUUE] 0  @ @@AUUU@&@9@j=@j,@AUU]@AUU] @ @ @@G@UUUUUW@j@j@j@j@QUUUUUX@QUUUU@@ @ @ @ @@ @@N@UUUUUZ&j:j:j:j@UUUUUUX@UUUUU@%@@%@@%@@%@@@@@ @@ @@ @@@@}@PUUUUUy*j:j:j*j@UUUUUUX@UUUUQ+ ; ; ) @@@@@@ @@ @P]@Y@PY@@PUTQ@UUUUUUꪪjzz񀪦jj@UUUUUUP@UUUUA+ ??) @@A@@PU@@PY@@P]@D: @U* @@PUTQ@UUUUUUꪪj{U{jj@UUUUUU@UUUEA+ ??) @@A@@U@* @: @D?00T* @T@PUTPT@UUUUUUTꪪjT{T{jj@UUUUUU@UUUEA  @@A@@U@* 0?00D?00P* @P@PUPPP@UUUUQUPjP{P{jj@UUUUQU@UUEA@@@@@A@@U@* 0?00@* 0* @@PU@P@@PT h$0x$0x$@ hD@TDA@@U@* 0* 0@U@ @U@@U@@$$$A@@U@@U@ @U@ UUUAUUU@@@@@@@@DEA@@DEEEA@@DEEEA@@DEEEA@@DEEEA@@DEEEA@@DEEA@D@@@@   @EEQ@EUEEQ@ III III HII HII@DUEA@D@@@AAAA@   HUUQ@UUUQ@ ߞߞ @TUUA@D@@AA@A@   L DADDUEAH@ Ϗ O ʎJVVUUUU@VVUUU@ږߟ@TUUE@D@@AA@  @ €\ TAGDUUUA@ڮZXVWUUUe@UWWUUe@@TUUE@D@@AA@ @ ,À (\TEGEUUUE@ڮZXVWUUUu@UWWUUe@%#fdZ@%2vt^@%1vtN@% fdJ@TUTE@DA@@@@@@TQ(Q@ ,@8488($(hTEVUUUUU-@?s?ҢV7TUWUUUu@UUWUUUe@% fXddY@50uXtt]@50tXttM@% dX`dI@TTPPE@@A@@A@@A@@A@A@ATUU$$$$U@8488@%(4444J%($$$dJTEUUUUUU,@<s<VtTQQUUUtTQQUUUd $dX00 $d\00 $dL  dHP@A@@AA@@AA@@AA@AA@AATUUU$$d%$$I@%(4444J@54p!00M@%$`! `I@PPE@UUQUQU@-@==^tTQQEYtTQQEYd ,X  8X  4H $H@A@AA@AA@AA@AA@AAPQQU$$`! I@54p!00M@10 @!  ` @P@UQUUU@.@==ndTUUE]TUUE],84N$O @A@A@A@A@A@APQQ ` @10  P PDAUUU&9=nlDIUU]DIUU],84N$O @A@A@A@A@A@APUQ P  @ @KDUUUU[@UYUU]UX@U]UU]@  @ @ @ @ @A@A@A@A@A@AAUA@/Y+@@@UUUU&??>@UUUUUUX@UUUUU@%@@%@ @%@ @%@ @@ D@@@@@QUP).D@@A=Y@@A)@@@A@@PUUUU*??>@UUUUUUX@UUUUQ+*;*;*)*@@D@@@@@@@AU@@A)@@A=D@UAU}UY@UAUiU@UAUUUU@UUUUUUꪪn@UUUUUUX@UUUUA+*????)*@@D@A@A@A@AUAAE@UAUUU@UAUiU@UAU}UDzYjj@UAUUUU@UUUUUUꪪUn@UUUUUU@UUUUA+*????)*@@D@A@A@A@AU@AE@UAUUUjzD?Ujj@UUUUUU@UUUUUUꪪjUjj@UUUUUU@UUUUA*..*@@@@@@U@AD@UUUUU:?D?TjjT@UUUUUUd@UUUUQUdꪢjdTjj@UUUUQU@UUUUA@@@@@@@@@@@@@@U@AD@UUUUUꦪ:?D꿪?PjjP@UUUUUUP@@TTP hP<0|P<0|8 hD@TD@@@@@@U@AD@UUUUUꪪ:꿪?@@~UUU)@UjUUU@UUUUU@T<<8U@AD@UUUUU@jUUU)@~UUU)=TUU)TUUTUU @ADTUU)TUU=TUU@@@@@@EEQ@EEEEQ@EEEEQ@EEEEQ@EEEEA@DEEEA@DEEEA@D@@@@@@@@   @DEEEQ@EEEUEEQ@JEIIIJEIIIJEIIIJDIII@EDEUEEA@D@@@@@@ADADADAH@ L  L  HHFVUa@EFVUUa@ڒߞ@EDUUUA@D@@AA@A@  L DADDUEEAHJ@ ϏO ϏO ʎJXVWUUUu@UWWUUe@ږߟ@VTUUUE@D@@AA@  @ € \ TAGDUUUUAz@ڮZXVWUUUu@UWWUUe@@VTUUUE@D@@@@@@@@@TQQ@ @ ,À (\TEWEUUUE@ڮZXVWUUUu@UWWUUe@%fZ@%v^@%vN@%fJ@TUUUE@@DA@@AAA@@AAAA@AAA@AAA@AAADUUUQT)Q@ ,@(488($(hTEWUUUUU)@:s:ҢVvTVWUUUu@UVWUUUe@%ffieeY@5vuiuu]@5uuiuuM@%deiaI@TUUQE@A@@AA@AAAA@AAAAA@AAA@@AAA PPAAA PTUUUed%$$e@(488@%(4444J&($$$dJTEVUUUUU(@8s8VtTQQUYYtTQQUYYd (hY00 <h]00 <dM (dIP@A@AA@AAAAA@AAAAA@AAAA@AAAA PQAAA PUUUUe$$d%$$i@%(4444Z@54t!00M@%$d! `I@TPE@UUUUQU@)@ϳ9ϷO9NtTUQU]TUUU] $$(h, $<@UYUUUUX@U]UUUT@&,PE@&8PI@&4PI@%$PI@P @AD@A@E@A@U@A@U@A@U@Q@U@QUUQP)P.D@@A>Y@@A*@@@A@@QUUUU*??>@UYUUUUX@U]UUQ+*;*;*)*E@@E@ADU@A@Ee@A@U`@A@U`@A@U`QA@U@QAUUQ@@Q)P@@Q>D@UAU}UY@UAUiU@UAUUUU@UUUUUU@ꪪ@@@n@UUUUUUX@UUUUA+*????)*d@Pd@ADd@A@Ed@A@U`@A@U`@A@U`UAEAU@UAUUU@UAUiU@UAU}UDzVYjjV@UAUUUU@UUUUUU@ꪪ@U@@n@UUUUUU@UUUUA+*????)*P@PP@DP@@EP@@EP@@EP@@E@UDAEU@UQUUUUjVzVDUjj@UUUUUU@UUUUUU@ꪪ@U@@j@UUUUUU@UUUUA*..*@P@@@@@@U@AU@UUUUUUꖪDUjj@UUUUUU@UUUUQU@ꪢ@U@@j@UUUUQU@UUUUA@@@@@@@@@@@@@@@@*U@Aj@UUUUUiꦪD꿪PjjP@UUUUUU`@@TT` `<0P<08 D@TD@@@@@@U@ADQ@UUUUUUꪪ꿪@@~UUUP@UjUUUP@UUUUUUP@TPP<P<8U@ADQ@UUUUUU@jUUU@~UUU@D=TUUUD)TUUUDTUUU  D@ADQDTUUUD)TUUUD=TUUU@@@@@@@DEEA@EEEEEA@EEEEEA@EEEEEA@EEEEEA@EEEEEA@EEEEEA@D@@@@@@@  DEEa@EEEUEa@JEII @JEIII@JEIII@JEIII@EEEUEEA@D@@@@@@@@@@@@@@@   DFWq@EFWUUa@^^@GEUUUA@D@@@@@@@    H DADDUEEAHFJJ@JJJJJJJJJTVWUUu@UWWUUe@__WUUUUED@@@@@@@@DQDQ@  @ €\ TAGDUUUUAVZZ@ՌZZZԍZZZZZZTVWUUUu@UWWUUe@WUUUUUD@@@@@@@@@ATVQTQ@ @ ,À (\TEWEUUUUEVZj@ُZZj؏ZZjZZZTVWUUUu@UWWUUe@fZv^v^fZ@VUUUUU@@DA@BB @BB B B @AAA DUUVaT)a@ ,@(8z8׀((ihTTUWUUUUUURRb@ٯRRbدRRb%RRVuTVVUQYu@UVVUQYe@effiaiY@uvuiqy]@uuuiquM@eeeiaI@UUUUQE@A@@A@BB!@BB B B PPABA0PTUUVud)g$e@(8{8@)(44u5Z*($$eeZUUUWUUUUUVR@ݣZRܣZVb$ZZVdTQQUYTQUUY`!%(,p1-<hq1,<dMa ,(IQAA@ABB%BB00 PQFA0PUUUUu$$d%d$i@)(44u5Z@54t!40M&$d!$`ITPEVUUUUUbFR@ݣbJR@ܣbJVN@RJZNT@TUQU]@TUUU] $$(h- $<Y@@*@@@@QUUUUU@Zej@ѿjej@ѿjej@jej@UYUUUUT@UYUUQT+jU;jU;jU)je@p@U5DAJUV*jp:~`UU5@~@UUeUai@@Q)P@@>D@UAU}UY@UAUiU@UAUUUU@UUUUUU@Zjj@տZjjڕ@տZjjڕ@Zjj@UUUUUUT@UUUC+??п)jd@pt@U5DAtJUdVp*fp:v`UUuAv@UUeUee@UAUiU@UAU}UTzfYjjV@UAUUUU@UUUUUU@Zj@տZjU@տZj@ZZj@UUUUUU@UUUUA+*????)*`@P`@DA`JE`VP[Pk@UDAj@UUUUUijfzfTijj@UUUUUU@UUUUUU@Zj@կZjU@կZj@ZZj@UUUUUU@UUUUA@*@.@.@*P@QP@AP@EPVP[P{@U@Az@UUUUUyꖪ dujj@UUUUUU@UUUUQU@Zb@կZbU@կZb@ZZb@UUUUQU@UUUUA@A@A@A@AP@AP@AP@EPVP[P@U@A~@UUUUUyꪪ t꿪ujj@UUUUUU@@TT,U,(D@TD@@@@@@@D&T&X&|U@AD}@UUUUUyꪪ 꿪t@~UUU`@UjUUU`@UUUUUUa@Taa,Q,($$$U@ADQ@UUUUUU@jUUU @~UUU`D=TUUUD)TUUUDTUUU  D@ADQDTUUUD)TUUUD=TUUU@@@@@@@TEUE@UEUUUU@EEEUEQ@EEEEEQ@EEEEEQ@EEEEEQ@EEEEEQ@D@TEUE@UEUUUU@JEII @JEIII@JEIII@JEIII@EEEUEEQ@D@@@@@E@@E@@E@@DD@D@TdtTFUu@UGUUUe@^^ZGEUUUUQD@@@@@EPEP@EP@ED ] @ED Y @EDUA@DTUUE@DTUUE@DUUUUUEUUUUeEVUUUuTVWUUUu@UWWUUUe@Z__WUUUUUD@@@@@DUQDDYQ@ED ] @M€N€JDUAVDUUUEVTUUUE@UTUUUUUTUUUUUeTUfUUUuTVgUUUu@UWgUUUe@jkkWUUUUUD@@@@@@ @A @DUUVaDVa@I@ӀDUqWUUUuZUUUUd@]UUUUUT\UUUUehhUfUUhdVfUUi@eVfUUi@fZvv^vv^fZWUUUUU@DEBB @CC00 @CA0DUUVqVWa@_@,9׀,jTUVeGUUUVeJUUUUUe@]UUUUUU\UUUUUixUUUUitUUUUi@uUUUUi@eeeieY@uuuiuy]@uuuiuyM@feeieI@VUUUUE@B@AE@BB @CC00ƒ PPCA0PTUUVudg$e@,{9@)4v5Z*$eeZTUUUUGUUUUUUJQUUUUU@]QUUUUU\QUUUUUxUUUUUtUUUUٕtUUUUݔ`%e)X@p5>Xm@q5>XmM@a$*XI@QT@AAEAE@BB!@CC00ƒ PQCA0PUUUVu$dg$i@)4w5Z@5ttau5]&ddaeeYTTQUUUGUUUUEUKQQEEEUOQQEEEUOQQEEEUIUQEIEU@UUQUY@TUUUY $d)h- $?|}!$?|}J!$+hJTAAEUEBB%CG0Gp‹F`TQKAUpTUUYUUu$ddeedi@5ttauu]@9p`! M*``!` PPQQFQUUUUKQUEUUOQUEUUOQUEUUIUUEYUU@UYUU]@TYUUd).+~+}J+hiKTT @AEUEEBBV%SW0kpҏj`TQOQiPTQQYQiU(``!dI@9p`!`M@- I@) P @VQ@EAUUUKAUUUOAUUUOAUUUUIEUUYUU@EYUUY@D]UU,d)+8.:4.5N$*h%OT @AEUE J%W0+kp*j`TQ%LUiTQeYQi( PT@- P@E@@@V@F@EUUUUVKYUUUVOiUUUUOiUUUUIiUUUU@UYUUUV@U]UU@lU)'@xU=&@tU=%N@de)U%O@Pe@AeUEEJVW/kp*~`TQ%Le}TQeYaiP@ /U+WVVWW@AUUUWBYUUUVBiUUUUBiUUUUAiUUUT@UYUUUVT@U]UUT&lQi*U&R}*Y&R})^%riU)n@Pr@E5UAJUV/kp:`DU5@~DUeUaiP)P .P@@>Y@@*^@@VW_@QUUUV[RYUUUVRYUUUURYUVUUQYUVUT@UYUVUUT@UYUVT+Xj(U;ؒjP@UAU}jY@UAUij@UAUUV@PUUUVPYUUUPYUUUUPYVUQPYVU`@UUVUU`@UUUW +Tk(%?<?ӿ}<)Գji(e@Pr@U5TAJUV/kp:`UUuA~P@UUeUeiP@UAUiUiP@UAU}UiT~jjj@UAUUUU@PUUUUUPUUUUPUUUUUPUWUPUWU!@UUWUU1@UUUW0+@+( ??(?ӿ=()j)(`@@b@U%DAJE+V+ [P*@UDA~P@UUUUeyPj~jj@UUUUUU@PUUUUUPUUUUPUUUUUPUUUUPUUUU@UUUUUU@UUUW@@+@@+@@)@@j)`@@Q`@UA`@E`;VP;[P:PU@A~P@UUUUUyPꖪ꺠jj@UUUUUU@PUUUQUPUUUQPUUUQUPUUUQPUUUQ@UUUUQU@UUUUU@@AU@@QU@@QU@@QUP@@QP@UAP@EP;VP;[P:@U@A~P@UUUUUyPꪪ꺠꿪jj@UUUUUU@@TT@@U@@PDPQTDPU@U@U@U@U@@D;T;X:|U@AD}P@UUUUUyPꪪ꿪@U~UUUe@UjUUUe@UUUUUUe@T%%<<< U@ADaP@UUUUUeP@UjUUU`@U~UUUdD=TUUUPD)TUUUPDTUUUQD@ADQPDTUUUPD)TUUUPD=TUUUP@@@@@@@TUUE@TUUUUU@DEEUEQ@DEEEEQ@DEEEEQ@DEEEEQ@DEEEEQ@D@   TUUE@TUUUUU@HEYI @HEYI @IEII @IEII @EEEUEQ@D@@@EE@EE@EE@TDD  d $ d$ $ d4TUU%%Uu@TUUUUUe@Z__󀊊ZFEUUUUQD@@@@@EE@EE@EE@@EEI]A@EEIYA@EEEUA@TTUE@TP@!T d U! d( U%! d8TVe!%eu@TWeUUUe@Z__ZWUUUUUUD@@@@@ @ @EEUaDEEYEa@EEI]IQ@Iޞ@Iޚ@IEUUq@UTTYu@UTQ$@aT!i`U% h(`U%% (dVu!%i@dWuUUi@{{WUUUUUD@@@@@@ @A0@EUUVqDVWa@Iޞ_@ӀުZEUUqVUTXuZUHE@mUHEYleDEyxe%DE\teuUU]@teuUUY@fZvv^vv^fZWUUUUU@EDEBB @CC00ƒ TACA0TEUUVqVWa@_@9׀+[UUUuGUTXuJUHE@]UHEY\eDEyxe%DE^teuUU@teuUUՙ@deeie@tuuiuy@uuuiuy]feeieYWUUUUUG@EAEBB @CC0C0ƒB TQCAQ0TUUUVUufgde@{y@骶5&[UUUeGUTTeJUDE @]UDEI\UDEIxUDE^tUTUٝtUTU $d)X@04>Xm@14>XmM@"$*XI@T@FEEAE@BB!@CC0C0ƒB TQSAU0TUUUVUuffdi@骶vu@uuiu5]eeieeYWUUUUUUGUTUUUUKUDEIOUDEIOUDEIIUDER@UUTUڕ@TUTU $d)h@ $?|~@!$?|}I@!$+hI@T@EEEUE@BB%@CC0CpB`TQ_AU`TUUYUUeeeiedi@uuiuu]@yui=)-]jei))iWUUUTUGUUUUUKUDAUOUDAUOUDAUIUDAUR@UYTQ@T]Td)++>+=J+h)JT EEEUEBB%CW0kp®`TQ_APTUUYUUhei))hY@yui=)m]@, >l=I( *hi VTUEUUUUUKUUAAEOUUAAEOUUAEEIUUAEER@EYUUU@D]T,d)'<.6<.5N(*h%OT EEUUEJ%W0#kp2`TQ1\uTQeYe( h)hi@, >l}@;>E@+k@VW@EUUUVKUEAAEOUEADOUEBDIUEBEER@UYUVUVQ@U]UUP@lUi'U@|U}&Y@|U}%^@hUiU%_@TUDEUUEEJװ/p:`TQ5\uTQeYeh)hi@:|} /6E+gVW@EUUUVKUEAAEKUEADKUCDIUCAEP@UYSQVP@UYUUP&hUi*U&V}*Y&V})^%viU)o@Tv@EuUAJğְ/p:`DQ5\~DQeYeih)h% .x5@@>Y@@*Z@@VW[@AUUUR[CU@ABBVCU@ABUCUCBAUCBB @UUSQU`@UUUW`+Tk);Ԫk=;ԫi=)Իji)@Tv@U5TAJְ/p:`UU5P~@UUeUei@@e)d@@>d@UAU}jY@UAUij^@UAUUV_@@UUUV[@U@ABBV@U@ABBU@UCBB@UCBB @UUSQUp@UUUWp+Tk(??(>=(Ѐ)j)(@@vAU5DAJ?֐?,P>(PUdA~P@UUeUeyPjij}jj@UUUUUU@@UUU@EDB@EDB[@EDB@E@EB&@UE@QUUu@UEUp@@*@@*@@)@@j)@@e@U%A@?֐?P>пPU@A~P@UUUUUyPꖪjjj@UUUUUT@@UQU@E@B@E@BZ@E@B@E@AB@UE@QQAE@UEUU@@@UU@@@UU@@@UUP@@UUP@@UP@UA@??>U@A~P@UUUUUyPꪪ긠꿪jj@UUUUUUu@@TQT5@@:@@@@@@AD@QQ@EDDU@DU@DU@DU@DU@D@A@??>U@A~P@UUUUUyPꪪ꿪@U~UUUe@UjUUUe@UUUUUUU@T   <<<$U@ADeP@UUUUUeP@UjUUU`@U~UUUdD=TUUUPD)TUUUPDTUUUQD@ADQPDTUUUPD)TUUUPD=TUUUP      $ $ 4 4 $0  00 00 0TUU!%E@TUUUUU@dEEUE%Q@tEEEE5Q@tEEEE5Q@dEEEE%Q@TEEEEQ@D@0  p 0 0p 0 0p TUU!%UE@TUUUUUU@hEZIi@xEZIy@yEJI9@iEJI)@UEEUEQ@D@@@EEE@EEE@EED@TDTDP0  q 0 00p 0 00p TUe1%eETUeUUUUj@o@/󀩊*UEUUUQD@  @ EE@EEE@EEE@DEEY QDEEY aDEEUaTTTeQ$0 0!q0 000p0 000deu1%]deuUUY@_@_ZVUUUUUUD@@ @ @@ DDEEUADEEYFADEEYJ A@Eޞ@Eޚ@AEUUq@TT\u@Q@1 1!}0 140|0 140\teu1%]teuUUY@{@{jWUUUUUUD@@@@@@@@ DA0DEUUVqDVWa@Eޞ_@@ު@QEUUq@TX\u@ @5 ̏_4!ď41%\tuuUU]teuUUYfZ@vv^@vv^fZWUUUUU@DE BA@ BB ƒB‡B TAGEQ0TEUUVUqVWTa@_]@=׀/RUUUuDX\u @5̏_4)ď49$ntuuUUteuUUՙdeeie@tuuiuy@uuuiuy]feeieYWUUUUUG@EAEBA@ɀBBɀƒBƒB TQCEQ0TUUUVUufkde@}@᪺5'SUUUuDXXu @ȏO(ďO48$n44$TU՝4$dU $d)X@04>Xm@14>XmL"$*XXTGEEAEBA@ɂBB@ɂ‚BP@‚B`@TQREU`TUUUVUefjdi@᪺~u@uuiu5^eeie%jWUUUUeDXXUe ȏOďO%b@%TU@$T $d)@ $?~@!$?=I@"$+h)I@T@FEEUE@BA@ɂBB@ɂBP@P@TQRAPTUUUUUeeiedZ@uuiuu^@xuh=i-_heh)i)UUUUUDYUU " C3 C3Fb@TQZ@T[d)k@+~@+=F@+h)F@T@EEEUE@BA@ɂBF@ɂJP@P@TQ]AݐTUUYUٕheh)hi@xuh=im@,$>l=K($*h) VTUUU"33b@TQ@T,d)'<.6<.5J(*h%K@TK@EEUUEV@IU@ɂVP@ɂ"jP@2P@UU1]uUUeYe($h)hi@,$>l};|>F+hjVTV@UUURABA"A@3@3BAb@WQVa@\UU`@lUi'e@|U}&Y@|U}%^@hUiU%_@TU@EEUUEE@I@ɂ]֐@ɂ*mP@:yP@UU5XuUUeYeh)hi:|} ,/6E(+'VW@UURABA%A@5@1BA @SQVp@XUUp&hUi*e&U}*Y&U})^%eiU)_@Te@AEeUEA@I@֐@*P@:P@EU5XEUeYe(h)he ,.xu@|?E@h+@UVW@AUURABB&AC5C1BB @SQUp@TUWp+Tk);Ԫk=;ԫi=)Իji)@Tv@AU5UA@I@֐@*P@:P@UU5Uj@UUeUei@he)d@|>d@xU}jY@hUikZ@TUUW[@@UUR[@Bf@CuÃC1B @SQUp@PUp+P+(???(>=(Հ)j)(@@v@AU5EA@I@>֐@>P@>P@UUA~P@UUUUUyT*jj;j~?*j@TUUUU@@QQBC_CBB&@@RQUu@@Up@@*@@*@@)@@j)@@f@@U%A@A@>֐@>`@>`@UAA~a@UUUUUye*j??*j@TUUUU_@@QQBCC@B@@QQAU@@UUP@@UUP@@UUP@@UU@@@UU@@@U@@UAA.A.A .A0UAAEiq@UUUUUie*j?+j*@UDUUUUZ@@QQ @@@@@@AQ@EDE@DE@DE@DE@DE@D@A@.@.@.@U@ADYQ@UUUUUYUj+@ULUUo@UHUUj@UDUUAU@@,,,U@ATQ@UTUUUU@UHUUj@ULUUoD UUA[DUUAZDUUAU(((D@APQDUUQUDUUQZD UUQ[    00 0 0000 0000 $ $$$ 00444 00444 $$$000 0 000000 000000 TUU1%ETUUUUUdeeee%Ytuuuu5]tuuuu5]deeee%YTUUUUUTP000 p 00000p 00000p TUU1%UETUUUUUUhejii@xuzyy@xuzy9@heji)@TUUUUU@TP@@EEEPEEEPEEEPTDUTQ000!q00000p00000 TUe1%ETUeUUU@߯@߯@@UEUUUU@TPQ0@0@0EE@EED@EEE@DEEYI QDEEYI Q@EEUEQTT\UUQ000!q00040p00040 Teu1%ETeuUUU@ߟ@ڶUUUUUUDQQ00@0TTEEUADEEYDADEEYI AHD@EUUaTX\eQ00<!u00840t00440\tuu1%]teuUUY@˻@VUEUEQDAQ0Q0@Q0QQ TQ0XEUUTqHVWTaH_]@@ު@QEUUq@TX\u@@50<_408404LtuueEteuUEՙf@v@vRfRGUEUEQ@DEE @AAAE @@AAAE @BAE@FAE TAFEU0XEUYVUqVWTa@_]@=@/@QUUUu@DX\u@ @5_4,48$ntueUUteeUUTeeiU@TuuiUu@EuuiUuQFeUiUQGUUUUQW@EAEZ@AAAP@]@AAEEP]@BEEPX@FE`TQFE`XUUYVefkde@}@鮺5'UUUUuDX\u @_,o$8j$4TU$$UU$e)X@4>Xm@4>X-@$*X)PUGEEAEJAEAAP@MAEAEAP@MAAEAP@HAEEP@TQUEPXUUYUUfidY@鮺}u@tyui}5^dieij&jUUUUVuDX\Vu  O OJf@TUV@UW%f)k@%?~@%?}E%+hiUUGEEUEJAAAA@MAAAAA@MAAAAAQ@HAAEAQ@TQAUEՑTUUYUՕdiemid@tyum}u@x}h=h-Ohih)h) UUUUTDYUU" C3 C3Bb@TQR@TWd)k@+~@+}E@+hiU@T@FEEUE@JAAEA@MAAEAA@MAAIAAQ@HAAIAQ@TQAYEՑTUUYUhih)hh@x}h=hm,,>l=K((*h) VT@UEU "33 b@TQQ@TQ(d)'E@l};|=+h)VT@UUUBBbB33ƒBf@WQf@XUUfhUi&f|U}&Z|U}%NhUiU%O@TUK@EEUUEEV@IAUEEU@MAUEUP@MAeEeP@IAuEuP@UUuUuUUeUeh)hi:|} ,/E(+EVE@UUUBBAaB11ƒB$@SQx@TUUVx$hUiV)i$xU}V)Y%xU}U)^%hUiU)_@TU[@AEUUEAV@AAUEEU@BAUEUP@BAeEeP@BAeEeP@FUeUeEUeUe(h)ie ,.xu@|?VE@h+WE@UVWE@AUURUABBeA5qBd@SQx@TUWx(Tik)8*k=8+i=(+hi)@T&@AEeUA@AEUEE@BIUEUՐ@BIUEeP@BIUEeP@VUUUeUP@UUUUeUQ@he)eUQ@|>dUU@yU}jU@iUikV@UUUWV@AUURAB&@51 @SQp@PUp(Pi+(<>?<**@UUU_@AUUK@N@_&RQu@Up(@i+(<>?(??**@TUU_@@@@A&QQe@U`@i)*)*)*h)@@A@AUUA@AUAA@BiAQ@CiAQ @CiAQ0@VUABUiq@UUUUie**???*j@PUUUU_@@Q @PQU@TP@TP@TP@T@@T@@@@UUA@UAA@YAA@YAA$@YAA4UUABEUu@UUUUUe*ꪩ?**j@@UUUUU@@QPE@@@@@@A@@@%@5@ADUu@UUUUe*ꖪ*@@UUo@@UUUj@@UUUAU@@@ADTU@TUUUU@@UUj@@UUoUUA[UUAZUUAU@A@QUUAUUUAZUUA[    00 000000000$ $$$ 00444 00444 $$$000 0 000000 000000 TUU1%ETUUUUUUdeeeeeYtuuuuu]tuuuuu]deeeeeYTUUUUUUTPPPppp 0 ppp000 ppp000 TUU1%ETUUUUUUhjiixzyyxzyyhjiiTUUUUUUTPPEAPEAPEADP@T@TT@PP@ppp!p@ppp10p@ppp10 @TUU1%E@TUUUUU@߯@߯@@TUUUUU@TPQ4@4@R4RRQUAT@EAD@EAD@DEEUEPDEEUEP@EEUEAP@TTXUQT@PQQ@00p!q@00p40p@00p40 @TUu1%E@TUuUUU@ߟ@@ڶ@UUUUUU@TQQ4@PR4@PRV4PRVTRVTUQUTUUUT@DEEUDD@DEEUEA@HD@EUUQP@TXXQT@QQ@ 0<!a@ 08$ p@ 04$ L@Tuu!%M@TeuUUY@ˋ@˳ʲUEEUEUTQQ4TRt@TVVtTVVTTVVTTUUUDXUUUT@LVTT@LUQަXEUUS`@UX\Gd@Z@$1<_@ 18o@ 14H@dueeUI@deeUUYZeVV@JUVR@JEVRJEVRFEEUEUPDEE5PDEBFu@PDEFFuPDEFFUPDEFUTQEEEUXUUYTQVTTQUQ@1@#@XUUUu@UX]u@ @$-_@ -o@ 9j@duUUU@deUUUTUUiU@TEUiUU@EEUiUUQFEUiUUQGEUUUUUW@DAEE5ZDDEBu@]DDEFFu]DDEFJUXDDEFUTUEEEUXUUYTUfhdU@yq@论5@')@TUUUu@DDX\u@D @T[@P,[@Q(Zf@Q%TUU@Q%UUTe)XX@>X\@>X\@*XXPUUUGEDAEE!JEDABaMEDAFEaMEDAJIQHEDEJQ@TUEUIQXUUYYUfidY@论}u@tyui=8N@diei)( @UUUUTE@EDXTUU@EL @U\K@QXK@QTJf@QUUUV@RUWf)k@?~@?}@+hiPUUGEDUE!JEDAA!MEDAAA!MEDAAAQHEDEAQ@TUEUEՁTUUYԅdiei)h@tyui=xx}i=(,hii)((UUUUT@QDYUU@QNb@Q OC3@Q KC3@QFBb@QUUQRQ@BUQg)E@+E@+}E+hiUUUGEDUEJA@AMA@AAMA@AAQHADAQ@TQ@TEՁTUTUhii)(hx}i=(l,-<,()((TT@QUDU@Q BBb@QC3@QC3@Q BBf@QQTQVU@@UU+g)E@?/I@>/uJ@)+heZ@UU@GEDUEJAEEMAFEEMAFEEQHAFEQ@TU@VEՑTUTU()i()i,-<,}8|(hTT@AUTU@ABBAb@AC3@A3@A€BA&@UTQV@TVUkW)W= ~W=eiU)Te_@UUT[@FEUUDEVJAEEUUNAFEUPOAFEUPKAFEP@VUDVEUUTUi(ii8|}/-E +)EUE@AUUU@ABBA`@AC@0@A@p@A‚BAe@URQUz@TVUU{$jU)T)k$zU=T)Z%zU=T)N%iU)T)O@UUTK@AEUUDAFAAAEUEBABUUPCABUUPCABUUP@FUTRUUEUTUU )i)ie.-xu@&=E@k&)E@VVUԗE@AAUUU@AABe@A@C5@ACq@ABe@URQUz@TUUW(Ui)k(8*(k<8*(i<(*(i(@UT[@AEUUDAVAEAEUUBEBUUPCEBUU`CEBUU`@VUTRUU`@UUTUUUa@i%)eUQ@~&=dUU@zU}jU@jUikU@VUUWU@AAUR@AAB%@@@C5@@Cq@@Bd@TQQU@TPU(Pi+(<>?<>(<,")*((VTT@UEUDU@UKB@Ba@TOC@B!@TOCAB!@UIBABe@UUQWUVV@FVUV+g+F?/F>/zE)+jjU@UUU@DUEe e Ab ASS@@VETU!))((h"-><,|?:|++hVWT@UEUVU@UCBAUEa@TCC@U!@TCC@Ua@UAB@UUe@UQQTUUZ@VVU[kV)KV>J~V>UNiU*UU@UUU@DUUEE E F F` F`@@RFFTUU))ii>9|}/?.x+++hVWUՕ@UEUVU@UCBAUE`@UCCAUU @UCAUU`@UAAUUe@UQQUUZ@UVUU_%jU)UO%zU=UJ%zU=UiN%iU)UiO@UUUK@DUUEAABBaBq@@RRFqTUUe)))ieU.>-xuU@'~E@k'jA@VVVQ@UAUVQ@UCAEA`@UCAEQ @UCAEQ`@UAAEQe@UQAQUU@TUUUU(Ui)(h8*((|8*((|(*((h@UTK@DUUDAFAEBPCeCu@@RBFu@TTUUe@i%ieTU@~&}eTU@zU}mE@jUiiE@VUUUU@UAUUQU@UAAEAe@UAAEA%@UAAEAa@UAAEAe@UQAQUU@TPUU(Pi)((<><<<<=(>,")*+(VWT@UEUFUAQAAA@UUQWUU@FVUU+g+)?/;F>/;zE)++jU@UUU@DUEuuArA#A#@@PEU"TTU%!)))(hZ"->=,|[?:?+++VW'@UEUVeAJa@!AaAVU@UQQWUUZ@VVUU[kV+KV>J~V>UIiU*UI@UUUI@DTUEEEBRC#C3@@RBF2TUU%)))hV>9=|W/?.{+++kVWg@UEU^eM`M E`EU@UQQRUYZ@UVUU_%jU*O%zU=J%zU=iN%iU)iN@UUUJ@DUUEaq@@RBqTUe)))idU.>-ytU@'~f@k'jfԁ@VVVVՑ@UAU^UM`M E`EU@UQAQUi@UUUUi)Ui((h9*8(|9*8(|)*((h@UTH@DUUD%5@@RB6@TTUU&@i%ieTV@~&}eTV@zV}UE@jViU@@VVUUP@UAUYUPI`I E`EU@UQAQUU@TPU(P)(((<><<<<<(?E>?*+k@UU@UTUE@Eq@EGr@E K@D J@T UMY@TUUUXFdie)(hItyu).|"))**hUV&\@UEVFu\@@E@UUQWUTJ@FVUUK+g+)K?/;J>/;z)++j@UU@DUE@u@@r@@@@@@PETTTT!))))hJ"-==-|K?:?+++VW'@UEUVuJp0pjU@UQQWTiZ@VVUUOk+O>J~>UIi*UI@UUH@EDTUEE@EE@FR@F#@F3@V@RB2@UTU%)))hV>9=|W/?.;++++VW'@UEU^up0pU@UQQRUm@UUUm%i*i%y=iJ%y=iM%i)iM@UUH@ADTUE@A@B@B%@B5@B@RB6@ATU&))))dV.>-9tV@'~'@k'k'Ԁ@VVWgՐ@UAU^up0pU@UQAQU}@TUT|(U()(h8(9(|8(9(|((((h@UTH@ADTUD@A@A@A%@A5@Q@RB6@QTTU'@i%i%TW@~&}%TV@V{V~VE@VkVjV@@VVVVfP@UAU^ePp0pU@UQAQU}@TPT<(P((((<<<<<<<<<<(((((@PTH@A@UUD@A@A@Ae@Ae@UTABFj@UTUUUk@UiUiUh[@VzV}UlZz~iij@UUUUUyT@AAU]U}Tp0pU@UAAQUU@T@T(((((<<<<(<<<<((((((@TD@A@UED@A @A@Ao@Ak@UUABFVj@UUUUUojjz~ŀj@UUUUUyT@AAUYQ}00pT@UAAQQU@T@T((((,((,((((@TD@A@UED@A @A@A_@A[@UUAAFF@UUTUUހɀj@UQUTUiU@AAUUQi ƒ ƒ`PAAQQAP@T@@@@@T@@A@UD@A @A@A_@A[@UQAAFF@UQTUU݀ـڀπV@UATTUiU@@AQi  `PQAPT@@@@@@@@ @@@U@AEDY@U@TUUY֚ڀ@@TTO@@TTUJ@U@TTTUE@@@P  @@D@E@U@TTTAE@@TTTJ@@TTOETTAKETTAJETTAE@P@D@AETTAEETTAJETTAK@@@@@@@@@A@AP@AA@AP@AA@AP@AA@AP@AA@AP@AA@AP@AA@@@@@ET@AQAEEU@AaAEEe@AqEEu@AqEEu@AaEEe@AQAUEU@QQ 0 0  @@@AA@A@CETQ@ֆVEUQeFƥaƷaƷaǖEQ@VFUEUUPQQ  0  0   A@ CAA0CA0 @CEUQ@ׇVUUQ۩˚Y@VFUUUU@QQQQQ@aaa!aa@aqa!aa@aqa!aa@aaaaa@QQUUU@QQUUUAAEDA@@AA@ GAA0CA0ƒ @CEUQ@חVUUQ۪ۙY@VVUUUU@QQQQQ@aaa!aa@aqa!aa@aqa!aa@aaaaa@QUUUUE@QUUUUAAADEI DEIDEEUDA@@@PAA@ GAA0CA0ƒ @CEUQ@חVUUU○○瀚i@VVUUU@QQQUQQ@aaaa@eqaa@eqaa@eaaa@UUU]UUE@UUUUUUADEIDEI DHHEUUTA@ @RRQAC GAC0CA0 @CEEQ@VUUU@VYY@wy@wy@vy@UUUUUU@QQQUQQ@QaeeY@UqeeY@Uqmi@Uam@UU]M]@UUUUYBDZXD\/*LEUUTA@ ARRQAC G0C0B @EFEVEEQ@UVUUUUUYUTFuyu%4tNu9u%4tNu9u%4t^UUUUUU@UQQUUUT@UeEEY@UuEEY@U}MI@UmM@U]MMM@UUUYYBZ.X/\;88&((LUUUTE@MERVQAC 00 @EEUUQQQ@UUUUUUU)Eu5e)%%Mu5e)%%u5e)%%TUUU@UQUQUU@UEEEY@UEEEZ@UMMI@UMM@U]MMM@UUUYYFj.(h?8xĪ:44J&$$JLUUUTE@MERVQAC 00!@EAQWQQ@VUUUU%+Y2%%=^I2%%=^1%%(Z@TU@UUQUU@EEEa@EEEb@E MI@E MI@EMYIY@EUUYYYVe*$dJĪ:4tJDyu)48MHie)$(IXUUUXE@YEVVUYT`00%@UQQWQQ@VVUU'+)?=?E?=?-(+k@TU@UUUEE@EEEa@EEEb@EM E@EM E@E] UIU@EUUUYUV@ie)$hIDyu)4xM@A|i=)lL@Ahi))hHQUUU\D@UEVVU]T`00%@UQQWQU@VVUU'+)@S?)F@R?)@Q-(kk@QTU@UUEUEE@EEEEq@EEEEr@EMIER@EMIER@E]IUEUR@EUUUUU@Ahi))hH@A|i=)lLA-=>-|))*)hUV%\@UEVFu\@@E@UQQWUhJ@FVUUK+'+)K@C??-{J@B>?-{z@A)-(kj@@TU@EEEUEE@EEAAU@EEAAR@EEAA@EEAA@EUAQEUEUUUUUA)))ihIA-==m|IB>:?*++VW'@UEUVuJp0p~U@UQQVT}@EVUU**E>=JE>=UIE)(UI@DUI@UEUUEEU@UAAAU@UAABQ@UAAC%@UAAC5@UUAUEF5@UUUUUU%A))ihUB>9}|U/?.;++++VW'@UEU^up0pU@UQQQU}@EUU}))i9=iJ9=iM)(iM@QH@UEUUEAT@UAAAT@UAABP@UAAB%@UAAB5@UUAUFF6@UUUUUU&)))idV.>-ytV@'>'@k'+'@VVW'@UAU^up0pU@UQAQU}@DUT<() ((9 ?-{{@A)-(kk@@UTU@UUUUUU@ZVUUUU@ZVUUUQZVUUU@ZVUUUUUUUUUUUUUUUA%))Q`IA%==QpI@B>:t@**hVV"]@UAUFe]@J`@Ĭ @Ŝ @~@UQQUT}@EVUU}*)U@A>=VUJ@A>=VU@A)(VU@@UTU@UUUUUUVUUUVUUUVUUUVUUUUUUUVUUUUV))`FB>9ApFB/?++VW#@UAUJu@p@˯0@ǟp@U@UQQAU}@EUU}))U9=UJ9=QE)(QEUTQE@UUUUUUUVQUUQUUQUUQUU@UQUUV@UUUUW))`WB.>ApV@Q'@k'@VVW#]@UAUJu]@p@˯0@ǟp@U@UQAAE}@DUD<($($I($E($ETT@D@UUUUUUTVQUTUTUUUU@UQUUZ@UUQU[@i%[@Q~&AZ@U{VUV%U@UkVUV%P@UVVUfiP@UAU]ui@0@ϯ0@ǟp@U@UQAAEy@DPD8(<E<E(TT@@UQUUUUPRUUPUPUUUU@UUUUUU@UUUUUU@UiUUU%@UzVUU%Zzjjviijf@UUUUUyP@AAU]U}@0@Ϗ0@Ǐp@T@AAAAEe@@@D$(<D<D(D@@UAUUUUBUUꂕUꂕUUUU@UUUUUU@UUUUUUހjjjfހzjjv倩ij@UUUUUyP@AAUUQ}@0@Ã0@Ãp@P@AAAAAUP@@@@@(@(@(@(@@UAUUUUBUVꂕVꂕVUVU@UUUUUU@UUUUUU݀jij@UQUUUyP@AAUUQ}0Ã0ÃpPAAAAAP@@@@@@@@E@UQT@U@@U@U@UQUUUT@UQUUUUـjڃij@UAUUUyP@@AQ}000AA@@@TEEUAEDT@UAUUUUՀjڃ@BUd@AUUYP@UAUUUUP@@@QEEAATU@UAUUUUU@BUUd@BUdDUUUUPDUUUUPDUUUUP@QAATQDUUUUQDUUUUPDUUUUP@@@@@@@@@A@AAA@AAA@AAA@AAA@AAA@AAAA@@ED@AAAEEE@AAAEE@AAAEE@AAAEE@AAAEEE@AAAEEE@AQ@@@@@@@EDQ@EEEEEEQFEEDžqdžqdžqƆEQ@FFEEEU@QQA@B AB AB BB @GCEEQ@EGGVUUQJEJۙ۱۱˚Y@FFUUUU@AQQQQ$$$$@EQUUE@EQUUEAADDA@@AA@@ABB @AAAABB @AAABBB @CBBBB @GGBEEQ@GWVUUQIZ۪ۙY@FVUUUU@AQUUYTƖdd׆dFd@EUUEUEU@EUUUEQAA TDA@PAA@@ABB @AAAABB @AAAABB @ABBABB @EFFAEEQ@UVVUUUUZYZZnޟnڛi@VVUUUU@QUUUYU֖eeזeVe@IUUUUEU@IUUUUEQ UTA@QQA@AABB @EAAABB @AAAABB @AAAABB @EEEAEEQ@UUUUUUU@YYYUYY@mmyuyy@mmyuyy@iiyuyy@UUUUUUU@QUUUYU֖eeזeVe@MUUUUUU@IUUUUUQ ,)UTA@AQQA@AABB @AABB @AABB @AABB @EUEEQ@UUUUUTYTFty5$4tNty5$4tNty5$4tNTUTTTE@UUUUUU֖eeזeVe@]UUUUUU@YUUUUUQ( ,(488(%((UTE@EAQUQA@EAAABB@EAAABB@EAAABB@EAAABB@EEUTQQQ@UTTUU(E@t44($$Mt44($$M44($$MTTTEUUUUU֒eeזeVe@]UUUUUU@YUUUUUU($(((488(4444J($%$$JUTE@EARVQA@EABBBB@EABBBB@EABBBB@EAAABB@EAQTQQ@UTTU$(@p4$J%%WSADADUQ %RJ%UEAUEAUEAQDA@@@@UAUAUEAUAAUAAUAAQ@A@@@@UAUAUAA@@APAPA`A`A`A`AP @0@0@ @@P@@@PUUPUUUeUuUuUeUU A0A0A AATABDQQ $$$UUPP Q0Q0Q QUTQ_HQTQTahu|u|ihYUTUD@D@D@T@T@T@ `U !Q!Q!U"UUVU_JUUYUm}yi]UTU FEFEFEVEUE dEqqTUu2XUX!USaURaUUbUUVU_JUUYU]UTU FPEKPE`Q`Q`qTqudWu:lU\U[UUZUUUVUVZJUUUIUIUE]UTE@APPQްQٰqd5W:RU\QUZUTJUTEUTUUTUIUeUIUeUIeIeETE]\D@PPOQ^5GBU\AUU]ETEUETEUEUUQEUUEReQERuQEbuPEePFPPFPhD@PP_KQ^OKFUXYUYXEUXEUEUUQEUUESeQUSuQUc5P%PVPWPdF@PP_HQ^MJTETeTmUUlUEUEEUAEUeSqc15!5VuW U[ ^dKMI@L@L@HgDEfE!A TATDdUlUUUEUAEUeSec%5%5vuvUjz`oyy@T@P@@e@@EuP@E1P@Q T@PD`UmUUQEUAEUFVVVV  uJuJ%5Ro55D@P@dtG0S QQTQae]eMmeMUQEUAEYY[][]YuU_uV^%S]5SoH5D5D@P@dtC0S`UVTUVTv]vMarMQQTEAQDEEQAYcQ]sP3*`a/`UoaVnS]HDDQ@Q@@DCSDUWeWe]MaqMQQTEQUDEUUAYiQ]yP8*Pd:PT:QV.SQAVEQVEaVEaJokVRQQMQQDEQUDEUUAYiAYi(%d5@T9QV)SDDQUEQJ)aJ/kW՗SՅQQMQQDEPDETATAT T$@T$QVP$QR@Q@Q@Q@QUEQ)b?~jWVSEQQAIQQ@EP@TTTTTP@UP@UP@Q@@@@@@@@UEQ)b>eeUUQEAADA@D@UEQ%R%UEUUQ@D@DUEAUAUAUAEUAEUAEQ@EUAAUAAUAA A A A AAAAPAAPA`A`A`A`AP A0A0A A@A@QUQUeUuUuUeUU A0A0A A@PA@@QQ`d$$UUPQ Q0A0E EAPEC@UUUUehu|u|ehUUTU@A@A@APAPAPAQU!Q1A1E!EUOIUUeuuiYUTUEEHEHXXUquTUq:TQT!Q_1AJ1E!UUZIUUꨞ]UTU FEKE`Q`Q`qTqudWq:hQX!Q_!J!UE!UUUIeUIeUIeIeIUIU]UTAPPQްQٰqduW:RU\QU[QJQUEQUUUEeUEuUEuEeETE]\DDPPQeW&RU\QUVQFQEQUUQeQQtQb4Pf$TFfTFjTihDĉDPPUVRU\QUUQEQEQUUSeQSuQc1`!`F`GPdE@PPjJUjN)N%JTEhUxUEyTEeEUAUAUeSuc51%1FqW Q ў`JY@\P\PHfDEvE5E%TETDdtEuhEfEVEUBEUVSVc15VuWU[nanmiDT@T@De@DFuPDF1PDR TDTDdUhUUVUBEYQ[Q[R27V{VZZzboyyDT @P @ d tG0S QTaQ lQ QRUBEIAQ_QQ_P+aeee_eR^SSDU @Q @ d tG0S UUTUeTe]eM`aMQPEU@EIAXOQ]OP "`E/tEouVnS]HDDU@Q@PTCSTUVeUVeU]TMaPMPPEQ@AXC]C" A6pEzuZn[]HDDU@U@AVEVEVEJoukeםaՍa@M@PEE@UTUUU! U5`UyeZm[]HDDDDU@U@PUEQJ)aJ/qkVRQPMQP@E@@DTU T$PT(QV(W@@AUEQ)b?~jUVQEPAMP@E@PQQQP@QPARPAR@A@A@A@AUEQ)b>eeUUQE@AH@@D@@@@@@@@@@@@@@@UEQ%R%UUUUUEUEQDEEA@@UEAUAUUAEUAEUAEQ@EUAAUAAUAAAAAPAAPA`A`A`A`AP A A  UUQUUeuueUU@Q A0A0 @@UUU$($( ( (UU@Q@A@A@A@A@AQU Q0A0 @P@@UUehu|u|ehUWTUAU@E@EPEPEQEUU Q0A F0F UBPUB@UUiꨞuueUVTUEU@E$$(qu\Uq:HQD Q0A F1F!UBUUBIߕUYUYnnjYVTU@@@FEKEtQtQxq\quWq:QH Q 0! 0u UTUDeUEeUEeEeEUEUE]U\EEL@DDPtQtQٸquW:QL Q !  u VVeUtUEtEdEUE]X\EHDDPPU܋eW*RQL`Q ` `4`4(]%Q]4QDj0Pf TfTjTmlDDPPUԋUVRU\PUP$ P4 P4$Q%QQ5QDb5`%ddT`EDPPeJUeJeJeJTUlPUlPUmPd fP4 P5%%R%Db%1%1qQ`eY@XPY PI ePEYuPE5PE%TEbTDehUxUyT f WCRTb15uU՞aiĝYDT @T @D e@Zu@f1@b%TTDdtud f WCDB@QQPQQTRrYUw^Uw^YVZ^nannjDU @Q @ euW1S!UTe`WCD @Q_PQ_Tkiuiu[iZZZٻVȻĺDU @Q @ eeW%S%UTe`P@ P_PQ_Tk&du'tuku^n^ޝVDU@U@QUCSUVeEfeEYDI`@H@PD@ P^PQ^P"6z^n_]HDDU@U@QUUUUUUJouJkeE]DMa@L@PD@TUPUUP!5yZm[]HADADE@E@PUEQJ)aJ/qkUםQՍ@@L@PD@TQPUQPf g$ghVhWXHADADE@E@PUEQ)b?kUםQō@L@@D@P@@Q@@UUUVV@@AUEQ)b>jjUE]QDM @@@@@@@@@@@A@A@A@A@A@AUEQ)R)UUUUUEUMQDEMA @@@@@@@@@UEAUAUUAPAUAPAUAPAQ@@A@@@UAAUAAUAAAAAAAAAAAAPAAPA`A`A`A`AAPAA@@@@UTUQUUUVeӕuӕueQUU@PP`A A@ @ @@UTUUUUUj++*UWe   AP@@@@@@@@@@UdQ AB C CBUUUUUUjjU[Ue   EU@EUdQ0A B0C @CP@B@ߕUUUUi}}mU[YU  EU@E448au\a:HQDdQ0A F0G PCPPB@UUUUUUij^lj^,j^,jV[]U P P PEUHEtUtUxa\quq:QddQ%0a V0G PTPDeEuUEYuETeEUUUUV\U F PFPUPVUZEtUtUٸquYzYQ_XeQ_1!J1u %U4UEY4EU$EUEY]\\ELEDEEDDUPEYEtUt܉eU*TQdQ0! V0uG uSiRY%QY4QEI0PU TUTUXQXPEHEDEEDDUPEUEedJYedJeJeIUUTQdQ  V 4W 4S$R%QU%QEV%`%ddTT`EDEDIEDDUPEUPEeTEYuTEuEeET\Q\PQ] V0W0C BQQQDR15uU`UUIUDDUPEUPEePYuP0P%TbTalQlPQmPfWCB!QaQTbqqqQ`UUYU@UU@Q UD e@Zu@g1@c%TRTUhTxPTyTfWC BAQAQTFrYUw^Uw^YVZY^ei]iYeDU e@Q UD eeW%S%TdtTuDefDWDC FA_QA_UkiuiuWiiiVji]jemjijeDU e@Q UD UUWWUXeTTDeDTDE@DID@A^QA^Ujeuuuku]j]ٯUȯzĮvDU f@U ZD Z ZFVEUiEeeEUDE`PE@DI@AYQAYQƩ.׵>ߵz^n^֝ƈFFDEF@EFDFTUFYUEmUEmJouJkeEYDI@@H@PD@@UQAUQƥ-=yNmO֝ƈFFDE@E@PUEQJ)aJ-qkUE]QDM@L@PD@PPQQPQV W$[(Z,[EEEUEU*f?kUE]QDM P@P@AQ@AVVVVUEEEUEU*f?kjUE]QDM @@@A@A@A@A@EAEAEUEU*Z+UUUUUEMQDM @@@@@@@@@UEEUIUUAQMUAQIUAQEQ@AAA@@UAEUAIUAM@@@@@@@AAAAAAAAACCCAAQAAQAaAaAaAaAAQA@@AAAAAAAA@@@@UTUUUUUVווUUeEAtA0A AAAA@PPPAA@@@@UTUUUUUj++*UWeECtA0A AAA@A@@A@@@@@@TTQABCUU]UUUjjU[UeEOtE0E  EEPE@@EUE(84dU! ER C UUUUUi}}mU[YeEO dE E  EEUE@EEUalaxUtgU!2EIR1C @@UUUUUij^lj^,j^,jZ[]UUO TUUUUUUPEUtUtUxa\qqQfQraJVqG`UEUUEYUEXUEUUUU[]UK TKPYPUUUEtYtUոqEYJYQOeQOu!JVuuduPi@Y%UEY4UEY4EU$EUU\UJ UJQYPUUUEdYd܉UUJUQOeQO5!JV5uG$uSTeREU%QEU4QEE0PU TUTU\Q\PELEDEEDDUPEUEeTIYeTIdIeIUU TQdQ0 04 4T$E%aU$aV ` ddXXPUHUDEEDDUPEQEaTEYqTEpEeE$T4\Q5\aQ%]   0 0$)a iqKj04tTT`UDUDIUDDU@@Q@a@Zq@1@%Tb$Tc \S!\aR%] A   )Q}Q[~pttT^`UMU UDU@A UD e@Zu@g5@c%TZ_,_,ZmUPP!qTvY^d_T[VdUUUU@XV@Q]VD]fZfW%W%TdtuUPPUPY@!^Qq^UujiyyyTyiTiU]eU]U]UDUYV@UYZDYZZZWWUZeUuQuPePPUPY@YQEYUEieU}um}hummhfmV^eW^S^RDU V@U [D O NF VUEUiUEeeUEUUDEDPDE@DED@ܑxlܟȏKĎJDE J@E KD OUUNYUImUEmJnyJjeUEUUDEPD@DDD@Ԝxl֜LjG FDE @EDEUUEYJ*iJ.ykUEYUDIP@@@QQ,, ( , FFEETUEU*?kUEYUDIP@@ADAFI  ETUEU*?kkUE]QDM @@@A@A@A@A@EAEAEATUEU*+UUUUUEEQDA@@ADUEEUUUAQMUAQIUAEQ@A@@ADUAEUAIUAM@D@D@D@@@@@DDDDAAAAAAAA@QA@Q@a@a@a@aA@aA@` AAAA@@@@PTUUPUUVאבUQeEAt0 AA@@@AAP@TUUTUUj++*UZeEJt 0 A@A@@@VAAERBUU]UUUjjUZUeENt0 EDEDPP(84E!EERQBPUU]UUUi}}mU[UeEOt0 UPEDEDAlAxEtEaEEReFBdUUUUUihZlhZ,iZ,iZ[YeUO t0 UUPEYDYDEXA\AAEUeJReeERdeEPiE@YUEYUUEYPEXPEUUUU[]eO d & *VU.bE-dYdEAEZJZUOjUOz%JVueNdeNPeJAUUUEUUUEUEUE%U5U;\T/ TP$P(VU,bE,qTIYqTI`IUIeUjUUoeUou%Zue^de^TeZEUUQUUQUPUTU%TU5\Q:\UQ* UQ$P(VU-bE-qTEYqTE`EUE$T8TU=dUm4$Z4$^$$^T$ZEUaYUaZ`[dW%dV%\Q%\P% U$T8=-DDYDDDD$0[1Ze%Zq%^a$^U$ZE)UamUqnpttXXF`HED$D8@=-%@AZ5@A5@%Tj$To0\_1\Ze]Zp^`^Z)_a}_q_~pttTTV`DEDDP&PEZ&PEg%Pg%TZ$T_ \_%\Ze]EZ`Z`ZZ)_}__~+_%^%d_$T[VdEDX@DYDYZW@WTZ((UiUPUPUPi@)jQyjUyjiyzyTzhTjTZeI DT@EUDefgWYT d t EtEPUPUPu@%uQeuUeeee}uY}duYmdeIV]E[M_M^UEUAEUDUY ^^UEUiUEeeUEuQUEuQEePETPE@PED@ܑtd V^KNON NUEM AEM DOUNYImEmJnuJjeUEvUUEuEaDPDE@DED@՜Ԭܼtd _OONNE  OUUNUJ*eJ.ukUEUUEPE@DED@TG\FlGlOdNdMV]E     EQTUEUUjkUEUUE@E@DE@EFFJJIFME     EQTUEUUjUEUQD@@@A@A@A@A@EAEBIAQTUEUUʪU֍UUEEQD@@AQDUEUEUUՍUAMUAIUAEQ@@@AQDUAQEUAIUAM@@@@@@@@@@@AAAAAAAA@A@AA@AA@AA@AQ@AQA@AaA@`  AAAAAAAQTUPUEאבUQUeEAd  AA@A@A@AADADPA@AAQUTUEUUUeEEt0 A@A@@A@AAAFQEAADAAQUUUEUYUeEIt 0 ADADPP(($E!IFQUIATIIYYUUEiVZUeENtN0Y MDIDPPAlAxEtEaIFQUIATIIYYUUEi`iJ`iNei^eiZV[UeE_t_pY` M DI DE`XAU\AlAxEtUaYFQUYFQUYFQUFAUUAUUUEUPUEPUEeUUuYUV{YeUo t0' ;WQ>bA-qEYqEE`XAU\UUZQeZQeJUJEUEUUUEUTEE%U5UV;dU/ d $ 8WU......///kkkKKKCDADADADADADADAD@o@k@k@kkk{{xxxxXXXXXQQQ#Q#Q!Q!!444444 4 , * * * *        pPPPPPPPPTT44444;z,?Y ݀a|s@#>'% ?!I  fggg;;;+ ++++++ # #:!:!:!x!x!x!x!x!x!ppppppppAAAEAEAGAGAGAGWW^^2868&0&0& & & & & & & " pppppppp 00000pppppppttt8t8t0DpDpDpDpDpp~ B%"  AJKM@!!B vvvvvvvffffff''''  \||xxxxxxxxxxppp`          0pp p p r z,Z(Z(Z(Z(Z((hhh``@@AAAQQQQQpP P P P P P          |tttttttttttttttp;")<CZ*>Ѐ  )&,%&= @@```````9`9`!`! ! ! ! ! ! ! aaccccccggGFF'''7511yy###""""*((((,,H,H,H,H,H,H,H,HdHtHtHtHtHtHtttTTPPPPPP X X P P P P P XXXXXXXZZZZZZZZ[['''''''''/////o o  X X X X X X X}\ч@AAP *$B 0000000404040404040tddddddd     O O O O O N N1LLLD` 00000000X0X0HHH@@@@@PBPBPBPBPBPBPGP=====80 ***((((((((((((xxP@PBPBPBPBPBPB[ш~"(w:! ;>Aj-!C D;#:##3333oooooooMML AE E E E E E E 8800:0:0: :::::*** B B B B BCCCCCCCCCCAp p p p p p p p              p p p p ~$κֿY 0A?m ABH         # # #######'..>>>>>2<r<r<p<p<p<p<p88800000pp `````````  `ppppPPPPPPP      00000000  <<BBBBBBBBBBBBBBBC00000002+DC  AvSDֿ,E SSSSSSSW W T T T T T 666(6(6(4(4(4 4 44000000 ........,(O(O8O8O0MMMMMMIIIIIIIIIIIIy000000044444444?           %10000000000000004444444@@@$$Z Ӄ| $" O ppppxxxxAxAxAxAxAxAxQxQxX             ] ] ] ] T@DDDDDDTTVVVBVbWbbb?b?b;b;";";";"; pppppppxxxxxxx       0 pp````````````h%?! * ###\@A@y =}-8χ}\ R  9999@@@@{yy9911!@@@@@HIIIIIIIMMLL"" 0ppppPPPX ?;;;;;;;;;;;;99@9@@1@1@5@5@5@5@5@5@5@5@5@5@5@4@<>. B}57BCC%{DʹY$ &  Y     NNNNFFFF0000BFFFFFF݀1      F O O OMMMM}y999991110          pppppppppppppppP~D_(ٿ  P9+& ~&$9C$E U,llllllll|| xxxxxxpppPPSSCCC{{VVV!D#D#D!#D!#D!#D!#T!#t!3t:t:t:t:t:|z<z8~8^8^8N8N8N8N8N8LH@@@@@``````df&'Ggwwwwwwwwwwwwww@{@```````""پ%#~ Ѕ~U] BDC!E=~,,8~[N % SCCCCCCCcccccccca!!!)L-,,,,,<<pppp     4444444444$Q$`QD`QD`QD`QL`CL`C`C`c`c`cBbbbb"&...  77777@7@7@'@@@ @ B B RRRRp------(sss3777%>@*\ ###$.C AB(?~aO~}TӀyP2di      #######km}}}}5=5=5=5=5<5<1<11!!   11111181811111 ) ) ) ) +D+D D D @ A A A A      p p p p q } ooooooooooo00000   \ \"\"\"\"\"\"\""  (?DCC uc/tj.޼ݽ&h}&؀v`ʰg   @@^^^~~~~~~ns{}}}}}}}#=#==<<< &&&&&& '##PPPPPP`P`P`P@@@@@@|??>>>>>>>>>>>>><݅ *(%%''?@ )>} XN Q    EEMMMMIIIIII@@@@@ < < < < < < , , , , , , , , ,,,,,,./// /       @ ,,,,,,,,,,,,,$'7+% }1۷~݀??t@T  шшXXaXaXAXAXMZMZMS S S S WWww'&&&&&&$    '77755uu}|||||\\`\`\a\aX!X!X!X1133333????>>>>>> tttttttTTTTT @```ppppppppp ~|@@M Ap2 >&'L$ P  mmm m m m`m`m`-`-`-p-p/p/p/p/pp      DTTTTTTTTtdlhhhhh(zjjjjjoo///-%%%%@@@@@@@`!               @@@@@@@ AB >@&=b$ $~$Ѻ%Z   73 81 80 x0 x0 x0 x0 x0 x0 xp hp hp hp hp hx hx hhhhhh./////////'''@ @ @ @ @           `aaaaa::**))))!!!!! ! ! ! hhhhhhh 3 2@@@@@@@@@@@@@@@yxxxxhh'& &&  '> &>@~%&:qu, *~̀7H]  A@AAAAAAAAA   V \\\\\\ x x-(-(-(-(-(/(7(7(7(66666666@!@!@!@!@!@)))))hhhhhxxxxxxZZZZZZZN     ??????====911111            H@@@@@@@@@@@@@@@<@|111z!!S!!D"8 >@&%ހ; >@@t@M  $4tttttt%$$$dddDDDLLLNNNJJJJJ 88(((V(V  h@@@@@@@PHRxRpRpRpRpRpRppppp2p2p2p2p6p&0-----------  @@ЅA} >nI$A& A@<,%9&Z wzw8w8w8w8w8w87'''''///////+    { ssssc######@` `````((hhhxxXRRRRVVVVV<<<,,T0T0\0\0\0\0\0\0   B@@@@@@@@@  !####PPPPPPPPPPp0000P0P0P0ڿ&^!n*)}2ӷr\{z)@D BCkB^  `pppppxxxAxAxAxCxCxCxC|C~Ggffffffffdddddttttt|||~x~XzXzXzXzXzHzHzHzHzhhhhhh  N@L@L@L@L@L@H@H@H@H@`B`B`B`B`B`BbC"C"C"C"c"a&a&!&!&!/!           m}}}}}@}@}p4rrrvv         ||llllKoooooomm%FiQ}CB~##:Z ׿''$z(g&P @@#@cccaaaaܠ        hhhhhxx|||||\\\\\9))+//.nnnnnnn```````@@@@@@@@@@pppPPP"!"=>.( =v_ 0rT|p]=LYYYY]]]]]#kcccccccccA@@@@@@AAAAAIII9I9Y1000=056564&4&4f4f6f6F&F&F&F&F&FFF@@@PPPPPPP          `` l;D ?Z!  ={<Jϼ!3M  Eeeeedd|d||t444444$$$$$$$$@@@@@@@@@@@@@@&444444"4"4"4"4"4"$"$" ";;;;;9...999933@@@@@@@@@@@@@@D@@@@@@@0@0@0@0@0@0@<,,,,,.. <H$/ $+FxS! ~((94Hl Կw]ްP """?;;999999t9999        @  AAACCCCcXcXcXcXcXsxrxvxvxvxvxtxT8T8T8T88(((((      ^^       xxxhhimmEEEEEEE݅W@]@[ B;,c-$($ud ] {L @@@@@@@@@@@AAAAAAACCCCCCCCRRVVVVhh@@@@@@`hhhhhhxxxp00YYYYY{{gg&&&&&&&&&  pppp``@@@@@@@F@FFFFFF     800000 o~e ӾjH'*&<S+'?@j;\ 888888888242434444444444<<<=<=<=,<,<,<,<-|-|-|+t+########""0000000        !8!8!81 9 9 9 9 9     KKKKKKDKDKDKDIDADADADAD@|||||lll  ++++++ ? 7-7-6-6-6-6-4-4/4'4''''''4@4@4@4@4@4@|@|]c -,I|:ܶ'HO/@,G}T(<~fR @@@@@@AAAAAAAAQQQ0Q0QpPpRpRpRpppppppPXLOO$77777773333)(((((((55%%%--m m m l l l l l h@@@@@@@@@@@@@` `    ''gggWWWWWWWWW__@_@X@X@@@@@@"!!(~2CuRޅ|k\;%CAug/n/;T       e"Z"Z"ZbRbRbRbRbPbPrPr@r@Z@Z@Z@Z@@@@@@@@@@ ((((((((8Njhhhhhh`ppp000000āDDDDDDDDD@@@@@@88((((()!00000044@HHxH8H8H8H88D{@%?'V*=ѻ=!(k Q   DDDDDDEMMMMMMO__{{{{{{{{z:22" 000000 0 8 x x x h hllhLhLhLhLh hhhh@PЀЀррр%%%%%eeeeeeeddxxx>>6666666677''''gAAAA % qv )?yQ/2&~ &:,GX z !N         c ' . . . . ..Q.q.q,q,qqqqqqqsrr" İİİ԰ppPPPPPPPPP c#####"""hhhhHHHH@bnup{   w[ܱ[}T?@dD@g'&%+"M 0ррAAAAAAA'A'A'I' I' / ) ) ) ) ) ( 8 xxXXXXXXXPP@@@@@@@@@@X@X@Xxxxx|||llllmmmmmmiAC   ::*+ + +B /@ /@ '@ '@ '@ '@ '@ @ @       ```@@@@ @ @ ``@@@@ @ @       #~%9*'?e|[CaXCzm#_|8 #A !!!!!!!!### ##0#0#0#0#0"0"0"0, ,((((((( pppP@@@@@@ 01qqqyyyy   < < >>~zzzz{ h h h h hhhh````` 111@@HHJJJJJJ A~%ҹ:{C#9$YzJ|m);f$&@@i$CFfffbb""""*:HHHHH@@====            0 oOOG000     ,,,,,,       ```````AAAAAAA000Q,))+$iO d AA?RӜׂ}%0 33333222ffffffff@@@@@@@@@`++**(((((((((((8pPPPPPPPPPPPPPPP@!,$'8? ~CՁ!& E   @@@@@@@ ; ;?////////  EEEE8888880000 00@@@@@@@@A        ,,,,,,,<00000M&%#)%?OI ~nzM.3'D :;KKKKCBBBBBBBBBxxPPPP@@7p7 7 7 6 &!&&& @@@  s scccccceEPppppppp`2`2 2 2 r r r s Ah{L՚ٿ %SV&B7HK|(C  "&&>>>>>#####D@@@@@@#'777777w 0ppppr       lllllhhhhhA`AAAAAaa````pppZN6בd * b#Ck<d$2)C !!!!!!!!##ccccggFFFVVVVV V ^LZLZlXlXlXlXlXlX|||~??;;;;@@@@@@@@**        ``!`!`!`! ! ! ! ) ) ) ) ) y x xzzZZZ ">%^jUM}SҜ) ' " A=*F KJJJHHHLLLL#L&D&&&&ԦԦXX X X X X XXXX˜˜#3;====H=IIaaaaaaaae%%%%%%%!!!!!!!!##33333s LLHHHhhjbbbbb@cT h@|ۂg!&2gӿPӛB+ 7  ########ccbbffFVVVVV^ 1100000000p @k(}~em,W         `?`?h>h0h0H H HHXXXYY U @@@@@@d@l@|@|||||||||80000@`````ppppppppqqq014t||||||LLLHLHLHLH HH` ` ````> > o c c c cccccccaAAAAA            DDDDDDDT@tttttttp x 8 9 9 9 9 9 ; +[ [ [ [  AW`&?d|ׁ~g@hAQU*,CP-*ELҙֿ&[-E       @@@@@DDdddd$$44<<<         0000001999999xxxXXXXXXFDDDDD[[___OOOMMddddddd$$ &ffffvvwahPӿ,$??%U+b*?a#.numbersletters@/# "!     -,+*)('&%$1I>IB=BCHEORYUSigQTPZ]MZ^PLMPL\RTQNMAC0EDCCF7WENULL 0 Joined 0 |Broken|0|1 0 가 73 거 62 고 73 구 66 나 61 너 66 노 67 누 72 다 69 더 79 도 82 두 89 라 85 러 83 로 105 루 103 마 81 머 84 모 80 무 90 바 93 버 77 보 90 부 94 서 80 소 76 수 77 어 80 오 76 우 92 저 82 조 84 주 81 하 78 허 77 호 65 0 67 1 48 2 69 3 68 4 67 5 67 6 70 7 55 8 87 9 69 4 linear essential -0.250000 0.750000 linear non-essential 0.000000 1.000000 linear essential 0.000000 1.000000 linear essential 0.000000 1.000000 거 1 significant elliptical 50 0.521641 0.460078 0.302031 0.221719 0.001821 0.001655 0.000563 0.000714 구 1 significant elliptical 68 0.542682 0.378694 0.284180 0.191062 0.002607 0.002019 0.000578 0.000827 나 1 significant elliptical 51 0.472427 0.421270 0.276118 0.227635 0.002112 0.001326 0.000522 0.000669 너 1 significant elliptical 54 0.471209 0.460684 0.277271 0.242766 0.001654 0.001715 0.000512 0.000794 노 1 significant elliptical 52 0.387019 0.411163 0.286208 0.215069 0.001720 0.001493 0.000680 0.000400 누 1 significant elliptical 52 0.484225 0.395921 0.239183 0.198843 0.002152 0.001758 0.000402 0.000730 더 1 significant elliptical 59 0.507150 0.506005 0.287937 0.232786 0.001680 0.001655 0.000429 0.000623 도 1 significant elliptical 56 0.476772 0.494008 0.322266 0.204729 0.001320 0.002694 0.000646 0.000514 두 1 significant elliptical 64 0.539002 0.493866 0.264893 0.198975 0.002641 0.002262 0.000416 0.000400 러 1 significant elliptical 50 0.505313 0.512234 0.279766 0.224766 0.001890 0.002467 0.000400 0.000614 로 1 significant elliptical 35 0.507031 0.620379 0.320647 0.212723 0.000632 0.002194 0.000400 0.000400 마 1 significant elliptical 36 0.505534 0.589041 0.279839 0.216471 0.001293 0.002201 0.000400 0.000410 머 1 significant elliptical 47 0.501662 0.572989 0.283660 0.235206 0.002525 0.003396 0.000746 0.000874 무 1 significant elliptical 40 0.549414 0.504512 0.268945 0.201660 0.001642 0.001380 0.000400 0.000400 버 1 significant elliptical 58 0.492794 0.543117 0.277882 0.220030 0.001369 0.002258 0.000400 0.000577 보 1 significant elliptical 41 0.473609 0.552487 0.307546 0.223418 0.001114 0.002216 0.000558 0.000400 부 1 significant elliptical 55 0.534091 0.507060 0.262003 0.203835 0.002803 0.002469 0.000416 0.000639 소 1 significant elliptical 67 0.376982 0.409894 0.274953 0.179804 0.000664 0.001423 0.000463 0.000461 수 1 significant elliptical 40 0.495898 0.409023 0.249707 0.199707 0.001646 0.001056 0.000573 0.000400 어 1 significant elliptical 49 0.493463 0.546070 0.278540 0.227041 0.001885 0.002825 0.000603 0.000596 오 1 significant elliptical 69 0.447577 0.451596 0.307914 0.195709 0.001733 0.003215 0.000895 0.000899 우 1 significant elliptical 59 0.523173 0.465142 0.258673 0.184124 0.002266 0.003457 0.000413 0.000983 조 1 significant elliptical 68 0.447495 0.478533 0.307158 0.191349 0.001608 0.002790 0.000713 0.000625 주 1 significant elliptical 34 0.543773 0.485903 0.274012 0.206457 0.001328 0.001406 0.000446 0.000400 하 1 significant elliptical 46 0.480978 0.556505 0.277259 0.210428 0.001829 0.002727 0.000596 0.000427 호 1 significant elliptical 53 0.427108 0.469170 0.294885 0.206515 0.000887 0.002245 0.000400 0.000720 고 1 significant elliptical 48 0.462891 0.473844 0.324382 0.201660 0.001484 0.002036 0.000917 0.000564 라 1 significant elliptical 45 0.516146 0.498073 0.283246 0.212066 0.002490 0.003809 0.000437 0.000507 루 1 significant elliptical 48 0.539388 0.566113 0.259440 0.193604 0.002093 0.007150 0.000400 0.000592 바 1 significant elliptical 29 0.521552 0.601347 0.285021 0.214036 0.001421 0.002090 0.000400 0.000400 서 1 significant elliptical 50 0.461875 0.463750 0.279453 0.198828 0.001164 0.002087 0.000413 0.000794 모 1 significant elliptical 52 0.473182 0.511448 0.314002 0.204026 0.001274 0.003009 0.000545 0.000498 저 1 significant elliptical 53 0.486660 0.492011 0.287294 0.199587 0.001673 0.003590 0.000678 0.000956 허 1 significant elliptical 56 0.481027 0.525990 0.281669 0.220912 0.001951 0.002725 0.000611 0.000738 다 1 significant elliptical 38 0.505962 0.474404 0.294716 0.224507 0.001352 0.001350 0.000400 0.000400 가 1 significant elliptical 37 0.503379 0.465192 0.288640 0.185177 0.001618 0.001564 0.000413 0.000400 0 1 significant elliptical 98 0.309750 0.263349 0.182358 0.099689 0.000696 0.000734 0.000400 0.000400 1 1 significant elliptical 100 0.299766 0.144699 0.179883 0.056211 0.002312 0.000400 0.000400 0.000400 2 1 significant elliptical 100 0.330625 0.239230 0.215586 0.091172 0.001194 0.000400 0.000449 0.000400 3 1 significant elliptical 100 0.303203 0.223285 0.192813 0.087187 0.000473 0.000400 0.000400 0.000400 4 1 significant elliptical 100 0.313672 0.234481 0.176172 0.091719 0.001187 0.000400 0.000400 0.000400 5 1 significant elliptical 100 0.294336 0.263984 0.203359 0.099609 0.002994 0.000400 0.000400 0.000400 6 1 significant elliptical 99 0.288352 0.235298 0.190459 0.093237 0.000584 0.000400 0.000400 0.000400 7 1 significant elliptical 100 0.397734 0.210469 0.205703 0.089727 0.000611 0.000400 0.000400 0.000400 8 1 significant elliptical 99 0.327730 0.292034 0.197167 0.103377 0.000732 0.000400 0.000400 0.000400 9 1 significant elliptical 100 0.369688 0.231910 0.187422 0.093125 0.000848 0.000400 0.000400 0.000400 . %!&$#"     0/.-,+*)('openalpr_2.2.4.orig/runtime_data/ocr/tessdata/lsg.traineddata000066400000000000000000011345071266464252400244440ustar00rootroot00000000000000#9߶37 NULL 0 NULL 0 Joined 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Joined [4a 6f 69 6e 65 64 ] |Broken|0|1 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Broken 0 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 0 [30 ] 1 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 1 [31 ] 2 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 2 [32 ] 3 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 3 [33 ] 4 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 4 [34 ] 5 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 5 [35 ] 6 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 6 [36 ] 7 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 7 [37 ] 8 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 8 [38 ] 9 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 9 [39 ] A 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # A [41 ] B 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # B [42 ] C 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # C [43 ] D 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # D [44 ] E 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # E [45 ] F 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # F [46 ] G 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # G [47 ] H 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # H [48 ] J 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # J [4a ] K 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # K [4b ] L 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # L [4c ] M 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # M [4d ] N 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # N [4e ] P 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # P [50 ] Q 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Q [51 ] R 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # R [52 ] S 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # S [53 ] T 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # T [54 ] U 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # U [55 ] V 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # V [56 ] W 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # W [57 ] X 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # X [58 ] Y 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Y [59 ] Z 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Z [5a ] %%@PPPPPP@PPPPPP@PPPPPP@PPPPPP@@@@@@@@@@@@@@D@TDDDDDTDD@TDDDD D TD   DDTDDDD D TD    DDTDDDD D TD  DDDDDDD D DD  DDDDDDDD@D @@@@D@D@D@DDD@@@@D@D@D@D@DDD@@@@D@T@d$@d$@d$d$TPDTd$d$d$d$TDTd$d$d$d$TDUe e e e U!!!!@E@E@TE@PE@PD @PD @PD @@$$@$EEE@$EEE@dEEE@`EE@@`D@@@PD @@@PD @@@@@D@@TE]EEE@E]EEE@E]EEE@EUEEE@EUEEE@tEUEEE@pUE@D  @@D@TA@TEYEEE@EYEEE@EYEEE@EUEEE@EUEEE@TEUEEE@PUE@D @@DDTA@TEYEEE@EYEEE@EYEEE@EUEEE@EUEEE@TEUEEE@PUD@D @  @TDTA@TDTEEA@DTEEA@DTEEA@DTEEA@DTEEA@TDPEEAP@D@@@   $ $@TETA@TETEEA@ETEEA@ETEEA@EUEEA@EUEEA@DEQEEA@@@@@@@@@@@  T@ @@ETA@ETEEA@ETEEA@ETEEE@EUEEE@EUEEE@@UQEEUP@@PP D@@@ETA@EUEEA@EUEEA@DEUEEE@DEUEEE@EUEEE@UQEEUP@@PP@@@@@@EQA@@EQEEE@@EQEEE@@EQEEE@@EQEEE@EQEEE@UQEEUP@@ $ $  @@@AUQQ@AUQEUU@BeQEUe@CuQEUu@CuQEUu@eQEUe@UQEUUQDP  0000 @ @ @EUQAEQ@EUQEUU@FeQEUe@GuQEUu@GuQEUu@eQEUe@UQEUUDP  0000   00 @TQAEP@UQEUU@eQEUe@uQEUu@uQEUu@eQEUe@UQEUUEP     0000 @TQ@EP@UQEUQ@eQEUa@uQEUq@uQEUq@eQEUa@UQEUQP 00@t@@4@d@@$@T@ET@UEUE@eEUE@uEUE1EA!EAEA @t@@4@T@@@T@@@T@AT@EEAE@EEAE@EEAEAAA@T@@@E@E@E@E@E@E@E, ,@EeZVE@EeZVE@dEUUUE@dEUUPD@hEI@@D@\EM@@D@\EM@@DHD(-<. @  >  * !!@uQED5@kvaJE6n*]]YQ]nbb{b&b@UqUQUQ0PP)099)"" "@'vaJE6@;wqOJ7.*ݓ]ݓQM.b@?gqbo;#0b/&" b*0UQA0P0"" "@7wqOJ7@:WqOO@*b@RY@RQI@$bb@4GqOrc00r#  b"QAP" @6WqOO@%VqJO@%VaJJ@UQEE@EA@AA@DA@BB@DA@BCBBA@! @%VqJOEEJEEJEEEE@@@D@@@D@@@@@@@ EEJ 00  00  00@EppD@E``D@EPUE@EUUE@EeeE@EuuE@EuuE%%    (0<@UEp|G@E`hF@$FPUE@$VUUE@(fj&jF@(Fez'~G@(Eez'~G($*&*   $*"* 4?3?@Uu3Ge"uWtWxk&@|F}o7_G@|E}o7_Gh(*&TU%%)))*"*-;?3? @-Ww7O)&uΗtϗ̀.@F}_?_G@E}_?_G(*TDDDEEE@V@6@UQ5A@UUyWQUEimmπiu]t̀-@FmJ-FE@EmJ-FE( )T@EE@EE@EE@EE@F@@F@4 (6@YQuEE@YU}WUUEimmπie]d̀@FUEEE@EUEEE@T@DEE@DDE@EDE@EDE@$ED E@$ED E$A@E$4 t (6@]EuEE@]E}WUUEimmπiUYɀTȀ@EEE@D@EEE@D@T@@DEE@@DDE@@DDE@@PDDE@@`DD E@@`DD E@`A@ E`p xB hB&@]eEE@]mVUUEiȬmmπi@UERUUUIPBTPDPPP@@@D@DDE@@DDE@@DDE@@@DDE@@@DD E@@@DD E@PA@ Edx lA A@UEE@]UUUEʬ]]ǀY@USUUUIP@P@B@C@C@B@@ED@DDE@@DDE@@DDE@@DDE@@DD E@@DD E@A@ E$( \A A@UE@]UUUEʬUUǀQʦ@QSUQU]PPPRS@W@V@@UT@@DDE@@@DDE@@@DDE@@@DDE@@@DE@@@DE@A@EAA H@@@TA@]UUUAʮUUǀʦ@SUQU]PPPRREVEV@EUU@@E@EA@@E@EA@@E@EA@@D@EE@@@EE@@@EE@@E@@D@@@ŅUA@Յ]UUUEUUπʦ@SUQU]PPPa(a(V!(V!$@UUU@@EDEA@@EDEA@@EDEA@@EDEE@EDDE@EDDE@DD@UUU@UYUQUUAA߀ڦ@bUQe]"TP``" (p 400  @UU@EDDE@EDDE@EDDE@EDDE@EDDE@UDDU@D@ETAPP$X(Y֑YYڕQYNJ߀@UqUQeY1TPp* 10(?0 4?00*  @UU@EDDE@EDDE@EDDE@EDDE@ED@E@UTPUPP @TAPP$@ZuQeTui]Q]~z߀&@UqUQeU0UPp* 00 >  >  * @UU@ED@E@ED@E@ED@E@ED@E@ED@E@UUTUUTQ!%a@uQeTu@kvaz!uvn!j]]YQ]n~zӀ&@UqUQeQ0P`)0 9P @9P@P@)P@@@Q@@T@A@@D@A@@D@A@@D@A@@DADQUQ"" *!%b@'vaz!uv@{wq3zn"]]YQMn@~Wuz4?&$* UQA P DD@@D@@D@@@D@@@D@A@D@A@@A@@QQ"" *"*@7wq3z@:Wq3׀."ݗ]͗QM$@4GuOs043 $ "QAP@@@@@@QQ" *"*@6Wq3@)VqZ3o@)b"@RY@ BA@B@DA@CA@@@@@@@QQ! "*@%VqZ3o@EQUZ@EQUZ@EQUUE@EA@@@@DA@@@DA@@@@AA@EQUZAAAAAAA@@AAAAA     00 Qa %a05a05a %QA000 00@A0pT@A `T@EPUU@UUUUE@eUeeE@uUuuE@uUuuE@eee@UUUE000 (0<@UAp|WAGTWU(gf(fgK(fgK(&f @UUUUET000$*"*$>3>@Ue~3~W"uޗUtߗÙxf|wO|wOh*f @TUUUUEDDDDDD@UA@D!!!%*"*&>3>@Wf~3~["uUtÙ߿O߿O* @TUUUUE  @EQFE@EQFE@EQEE@EQEE@EA@D@EA@D@VRA@D&@Q%E@UeWUUUۀۀeUdÙnInI* @TUEUEE @EUQFE@$EUQFE@$QQEE@$QQEE@$RA@D@FRA@D@tWA@L$6@QuEU@UuWUUUۀۀUYTYɀ@FUEEE@EUEEE@EEE@TEUEUE@EYUEXUYUTUUEhVUIxRUIxPId@H@tGA@LtS (R6@UuEU@UyWUUUۀۀUEՕUTEՕTE@EEE@D@EEE@D@E@@TUEE@D@DTUEATUAUUETUUElVUI@|FSUEI|BPE dt xB hB@]UEU@]YWUUUYUۀUۀ@UERUUUY@PEBTTQD@@FB@DAD@@GB@D@D@@GB@D@D@D@@UED@TUEATUATUAPTUATUA@DPUEAAPEx lA lA@]UEU@]]UUUUieӀeӀ%@UESUQUUP@QDDDG@UD@TEEATEA@TEA@UEAUE A@DQE EAA@ EAhA \A A@UU@]UUUUʬe׀e׀!ʦ@SUQU]PPURSDW@QT@PUU@PU@@EPU@@DQUE@QUE@@QUUEAPEAAL@@@TQ@]UUUQʮe׀e׀!ʦ@SUQU]PPURREVV@UUU@ETQVAETQA@ETQA@DUQE@UQE@RQUEPE@D@@EUA@E]UUUEa׀a׀!@RUQe]PPda$(a$8Ve8Ve$@UUQU@@EQUYA@RUA@RUA@RUERUI@SUTIPD@@TTA@@@TE@@@UUEEU@E]UQUEEǀEǀ@QUQeYTPp`0(p 4[04Z $@UUAU@@EQUYE@@RUI@@RUI@@RUI@RUI@UQUT]PD@TA@@@UTAPP$TPP$UQUUEUQUEIǀIǀ@UqUQeUQ1TPp*`10$?p 4?04* $@UAU@ EQUTE@RUI@RUI@RUI@RUE@UQUPU@PP@ @TAPP$@ZuQeTuiٙ֕YىޕQIYƀYƀ@UqUQeU0UPp* 00$>d $@>d@T!*D!UEI@EI@EI@EE@UUUU@UUUU@eeUe@uQeTukb!z!j]QMYƀY€@UaUQeQ P`) 9TP @9TQAP@)RA@@RAU@RAE@RAI@QAEI@AEIDQUUU@UUUU*"!j+b!zssnb]Q]YQMY@YFe~Y$>$*UQAPDP@DQAP@DQA@@DQA@A@DQA@A@DQAAA@DQAAA@AAA@UQUU@UUQUU*"b?sss߀n]ݗQ]Y͗QMT@TFeOrP$2$ "QAPD@D@@@D@@@D@@@@D@@@@D@A@@D@A@@@A@@UQUU@UUQUU*"s߀)rڀ-fWQ] GA F@DE@BA@@@@@@@UQUU@UUQUU)")r@EQUZ@EUUZ@EUUQUE@EE@@@@DE@@@DE@@@@UQUE@EUQUE@EUZ@EQUZEEEEEEEQEEEQEEEQEEEEEEEE@@@@@@@     00 Qa %a05a05a %QA000 0000 @DPU@UUUUE@eUeeE@uUuuE@uUuuE@eUeeE@UUUUEE@000 $04@A0tQA GPTWWUU*gf+fg+fg*ff@UUUUUEEA@TA@@000%"%5"5@UQu"uU"EUUÙjfwwπjf@UUUUUUEEA  @EQ_F@EQZF@EQUE@EQUE@EA@D@EA@D@$UQA@D$111%"%5"5@UQu"uU"eUeÙ߿߿π@UUUUUUEEA " "@EUQ"_F@$EUQ"ZF@$QQUE@$QQUE@$QA@D@$EQA@D@8VRA@D(&@a%E@eeVUUU@efe@Uvu@UvueUeÙ톉톉@UUUEUEEEA " "@EUQ"_F@(FV"Z8b8b(b@8FRA@D@|WSA@Lh&@UaeEU@UeeWUUU@efe@Uvu@UvuEUEՕUUEՕUE@EEEE@EEEE@EEEE@YEUEUEE@E]UUA"ͭ"Tͥ&Fl*|ϳ|ϳhβ@|GSA@L@WA @LR@UUUEU@UUUWUUU@Ufe@Uvu@Uvu@Efe@UEUUUU@TEUTQD@EEAD@EEE@D@EEE@D@EE@D@XUEE@D@\UUA"ͭ"߆Tͥ&߆*S@GA @LR  R@YUUEU@YUUUUUU@ Ufe@Uvu@Uvu@Efe@EEQUUUU@PATTQD@dB@DAD@hB@D@D@lGB@DPDD@UED@XUQUAbTͩb߆T̥f߆j@DWU]UMAPU   E E@]EUEU@]EYUUUU@jEfe@wEvu@wEvu@&Efe@UESUQUUPC@UdB xC xGS" @qaD@pEaUARP͠R߂P̥V߂Z@DUEYUAE@UE E EE@]UU@]YUUUU@jfe@wEvv@wEvv@"Eff@USUQV]PPU$4tW&@ePT@dUPW@P@EP߀@DT߅@T@ERUTUEEPUEEL@ @TU@E]UQUQ@Efe@sEvv@sEvv@"ff@RUQV]PPU%R%SeW@՚UPU@ЊTQPWA`@E`߂@Dd߆@d@cQdUJ@#P$E D#$ H@ h@ @EUU@E]UQUU@Efe@sEvu@sEvy@"Efi@UQUQiiQPPh(a$(a$8Ve8e$@UUQU@EUUQ^Ab@ϧb݂@ϧb݆b@sUaTO@3P DT3 X@ @dPA@@D@@UUEQ@E]UQUU@Efe@FEvu@FEvy@Efi@UQUQieQTPx$a4$q$4[14Z!$@]UEQU@LEUUQ]EHbDϧbݎ@ϧbݎb@SUQU]PE@PA@@UTAPP$@TEPP$@UUQUU@E]UQUE@Efe@FEve@FEve@Efe@UQUQeUQTPp*`0$?t 4@t@U1jU"]GQWLWQGHbَDϧbՎ@ϧbՎb@URUQU]@RPU@B(@TAPT$@VuQeTueeTeUUUUEUQUEEfe@TEve@TEve@Efe@UaUQeU UPp* 0 >dP @>dQAT!*T"ݳQW ͳQGbbՎcbՎ#bՎ"b@UQUQUU@UQUUU@eQeUe@uQeTuk!z!jٙ֕YىޕQI@TEee@TEee@Eee@UaUQeQ P`) 9TP @9TQAP)bPcQVcQFbbю@GaAbU@!AbU bPUQUU@UQUUU*!j+!zsbQ]QM@TE]eeTe%e%UQAPDP@DQAP@RA@@RAQA@RAQA@RAb@DQAbE@AbFbPUQVU@UQUQUU*bs߀n]Q]YQMT@TEYEaP!!QAPD@DA@@DA@@DAQ@A@DAQ@A@DAbA@DAbA@AbBbPUQVU@UQUQUU*߀i޲ڀm]ݗQUI͋AD@DDI@A@A@A@A@BBBBPUQWU@UQUQUU)i޲@EQUZ@EUUZ@EUUQUE@E@@@@@@@@@PUQUE@EQUQUE@EQUZ@EQUZEEEEEEEEEEQEEEEQEEEEQEEEEEEEEEE@@@@@@@    Qa %a05a05a %QA        @@PU@UQUUE@eQeiE@uQuyE@uQuyE@eQeiE@UQUUEE@ PPpp0!!!!@@PTU@UQUUUE*bf+bg+bg*bf@UQUUUEEA ( , , (@@@@UQE@D@UD@D@uDEU@uUQ0!!a@E@UUU@UWQUUUŀjfwwπjf@UUUUUUEUA "* 3/@TTa3/C@DTa"*B@DPUE@EQUUE@EQE@D@EQE@D$RE@H$E@H@uEEU@uUU1!!@UAUUeEUUUɀ߿߿π@UUUUUUEUA "* 3/@UUa3oG@FV"jJbIbI$bH$RE@H8S̀(@uEEEU@uQUUUU@1PUUe@PUUe@UAUUeEEVUUVՕU@UUUEUEEEA "* 3/@EUa3oG@,FV"j<ϳ<ϳ(β8S|S̀h@UuQEEEU@UuQUUUU@1PUUe@PUVe@UQUVe@EUVe@UEUUVU@UEUUUE@EEEE@EEEE@EEEE@EEEE@YEUEUEE@E]UUA"33G,"<|h|SW ̀@UeQUEEU@UeQUUUU@!PUUa@PUVa@PUVe@PUVe@EEQUUVU@TEUUUUE@EYEEE@E]EDE@E]EPE@YE`E@XUEupE@T\U1uAD"T3T7ǀ*ЈW @WGH L@VHH@UUUUEEU@UUUUUUU@PTUUa@PTUVa@PTUVa@TUVa@AEQUUVUPEUTUUIdjVJhnVKlnVK*v @uu E@|UqeAbTsTwǀj]̀̀ƈЈ@GGH L@UD PI@UD @H@YUUUEEU@UUUUUUU@&QTUUa@7QTUVa@7QTUWa@"TUWa@QEQUQVUPEDU d*x; x;  *2@uqE@tEqWARTSTWǀV@EYEUeI@EDeI@ED`I@EDPI@iEUU@eAUUUUU@fATUUa@sATUVa@sAUU[a@"VU[a@URUQZUUTQY$&47 t7 &"@uaU@|UaWAPPEPPDTŀ@d@ЅjUeuFЅ.T%u.%!l  |@yEUU@eAUUQUQ@fTUUa@sATUVe@sAUU[e@"AUU_e@UQUQ^eUTQY)%"-%"e"&@՟eQU@ԏhUQ_A`@Ep@Dtǀ@d@sUuuO3T5u3&! X  h  "@EU!U@AYUaUQ@XUUa@sAXUUe@sAUUYe@"AUU]e@UQUQ]u@UUQX9@eED)@eED9@VeEE9@eEF%@UUEQ_U@EYUQ_EbDs@sˀb@sUqUO@3T1ET3"X  dR F"@UU!U@A]UaUQ@\UUa@CA\UUe@CAUUYe@AUU]e@UQUQ]u@UUPX5@eED%@uQED5@_uQEE5օE%օQ]U֕Q]EbDs@sπb@sUqU_3T1E3"R @UVATP(@VETP$@UEUUU@AMUQUA@LUUA@CLUUQ@CDUUQ@DUUa@UQUQeu@UUPp5*"*?b:b:j*]QWLQGHbDs@sπb@RUQV]@RTU@B,@VATT,@VuQeTu@eeTe@UUUU@EUQUE@DUUE@DDMUUE@@DUUA@DUUA@TQUQeU@UUP`*"*>s/~s/j*]QWLQGHfbDws@7sπ&b@UQUQZY@UQUYY@eQeUi@uQeTuk!z!jUUUEUQEDUE@DDMUUE@DTUUA@TUUA@TQUQUU@UUPP)"=s=s)ݷQW ͷQGfbю@GuAsU@5As[$b PUQ[U@UQUYU*!jk!zsbٙ֕QUɉΕQEE@DDMUUEDUAUADUQUE@EUPP" b b b cQFcQBbbт@DQAsU@As[bPUQ_U@UQUQYU*bs߀QUQAA@@DMEQA@AADEQQE@EEPP@ED@@EQD@@EQD@@EQD@@EQDQ@E@EPDQ@DP@bDP@s@sbPUQWU@UQUQUU*߀i޲ڀi@UUUUQUU@AE A@ @ @@A@@@@@@@A@A@BCCBPUQWU@UQUQUU)i޲@UEQUZE@UEUUZE@UEUUQUE@AE @@@@@@@@@@@PUQUE@EQUQUE@EQUZE@UEQUZEEEEEEEEEEEQEEE EQEEEEQEEEEEEEEEE@@@@@@@    Qa $!a04!a04!a $!QA 00  00 @@PT@UQUTU@eQehf@uQuxv@uQuxv@eQehf@UQUTUE@ 0000 PP``  00 @@PTT@UQUUTU*bf+bg+bg*ff@eUUUTUeA  (0<0< (@@@@UQE@D@UQE@D@uQE@T@uPP0 00 @E@PTU@UUQUUUUjfwwjf@UUUUUUUUA "*03>03> "*@T@EQUUE@EQE@D@EQE@D$bF@H$bF@H@uQE@T@uPP0 0p@E@PTV@UUUUUU߿߿߀@UUUUUTUUA "*03>UTq3>FT"*`@aUE$b@H$bF@H8̀(@uQED@T@uDPP0@ @0@UAP@St@EP@Sd@EUPTVU@UUUUUU@UUUUUTUUA "* 03>@UUq3~G@FV"j(8|̀h@UuQED@T@UuDPP0@ P0@@PPWt@@PPWd@EAPTVU@UEUUUUE@EeeE@EedE@EedE@EedE@YEUUUtE@E]UuA"33G,"<|h| ̀@UuQUDPT@UuDPP0@!P1@Pq@Pa@EDQPTVU@TEUUUUE@EYeeE@E]utE@E]upE@EYe`E@XEUUupE@TE\U1uAD"T3T3ǀ,",lЈ @UY E@UYE@UeQUDPU@UeDPP @!P1P1`!ADdPEe d*(f% h.8V4 l.8V1 *(v!@uu1E@|UquATTǀFQFF@EY E@UUe `E@UQe `E@YUQUEPU@UUDPP&@!#P1#P1"`!QD`PE! d*("%x;,$|;,! *(2"@uq"E@tEqfATϓTχǀ@EYUEeE@E]UeE@E]e `E@EYe `E&8&(iUePD&@ #@0#@0"@ T@UA$&47t7&2@uqU@|UqWA@E@Dŀ@hUuuGE-T5u.(&!*8l7< |&("yU1ePpf` cP0c@0"@ T@4UA9%"-%"e"&@WuQU@|UQ[A@@@ǀ@@@@{UquO@?T1uT?("% X;< l7, |&("yE1e@pf` cP0c@0"@ TQQ@\5@UQUAX5@eQUT%@eQUT5@VeQUU5@eUUV%@UUUQ_U@E]UQ_ED@ˀ@@@{UqvO@;T1uT;("%X;,d"t&"uU1e@pV` CP0C@0@ TUQ@\5@UUU@X5@eUed%@uQud5@WuQud5iեd&YՖQ|VIٖQ|FD@ˀ@rUqV_2T1E2""@eVQ)@VU!%@UU!@@Y`H`@HP@@@ TQQ@\5@UQU@X5**;:{:*QWQGD@ˀ@UaUa[Z@UaT!Y@Va!*@VQ)@UuQeTu@eUeTe@UUUTU@EIPP@@@IPP@@D@IPP@@@@ TQQ@P%@UQU@P%**>/~/j*]QWLQGHfDvϳ@6ϳπ&@UQUQ_U@UQUYU@eQeUe@UuQeTuk!z!j@UUUU@EPT@@DPT@@DDMPT@@DDTQQDP@UQUDP)=}i]QWLQGHf@DDuAU@@5A[$PUQ_U@UQUYU*!jk!zsb@UUQUU@EPD@@DPD@@@DMPD@@DDDQQDP@EQUDPb b b  @ͱQPF@ͰQB`DPAATUQ_U@UUUQYU*bs߀@UUUQUU@E D D@ D@DDDQAD@@EQED@@EQE@@EQE@@EQE@@EQE@@EQEQ@E@@Q@@@ TUQ_U@UUUQYU*߀j޾ڀ@UUUQUU@E D D@ DDD@D@D@@@@@AATUQWU@UUUQUU*j޾@UE]UZE@UEYUZE@UEUUQUE@AE D@ D@ DDDDD@@TUQVE@EUUQUE@EYUZE@UE]UZETEEEETEEEETEEQEE@E @ @ EQEEEEQEEEEEETEEEE@@@@@@@    Qa a00 a00 a QA 00  00 @@P@@UQUPT@eQe`d@uQupt@uQupt@eQe`d@uQUPTu@0 0000 PPP@P@ @0@0@ @D@PD@@UUQUDPT*bD+bD+bD*fD@uUUDPTuA0 (0808 (@@@@UQE@D@UQE@D@eQE@T@Ue@PA A0A0A @E@PDA@UUQUDQTjDDDjD@UeUUEQTeA  ")0"90"9 ")@T@EQUUE@EQE@D@EQE@D$bF@H$bF@H@eQE@T@Ue@PA  A0A0A @E@PDF@UUUUDUTߌߌ܀@UeUUEQTeA  ")0"90"9 ")@T@EQUUE$bF@H$bF@H8D̀(D@uQED@T@UuD@PA0@!AP1APqAPa@E@PTFU@UUUUTUUܗܓ@UUUUUaTU%A ")0"9T1"9T!") W @GWTUM(D8D|ψ̀h@UuQED@T@UuD@PA0@!AP1A@PqA@Pa@EAPTFU@UEUUUUE@EefE@EegE@UecE@UebE@YUUUUqE@UYUuA@Tj"i@Uz"y@Uz"yF@F"iTTh|ψ̀@UuUUDPT@UuD@P0@!P1P 1` !@EDQPdJ@TEUUeYE@EYefE@E]uvE@E]urE@EYebE@XEUUeqE@TEXU!uA@DEj"i@TEz"y@TEz"y@F"iJɀXJTɀ暈@UY̐@UY@UuUUDPU@UuAD@P0@D!@T1@T 2@d"ADPtPEu d*(f% h.8V5 l.8V1 l*(v!@XEeu1E@TEdUquA@DEji@TEzY@TEzY@EjI@EUQUE@TEUTUE@EY@EY̐@U]epE@UYe`E@YeUUEPU@UeAD@PU @D!Q@T1Q@T 2Q@d"QDPtPE5 d*("%x;,%|;,! l*(2!@Xuq1E@T|EquA@DJi@DEJY@DEJY@DEJi@DEYUQuE@TE]UUuE@E]edE@E]etEn8V0*(f YUTeUP@dU@d Q@T0Q@T 0Q@d QTPtUu $&"87x7 &2@WuqE@|UqVA@jj@@zy@@zz@@jj@@HUAvE@E]TEun(F%n8V4l;(b%X;(>( *@eUQU1UU@UT5ET@)E@@E@@ E@@ E@@ULUU@UUT@VUT%@VQT%@uQedu@eQe!ee@UQU!VU@E%GA&& 7LwCǀHfHOL\+\/\/ *@UUQU!UU@UT%ET@)E@@EA@D]A UE@D]A UE@UU]UUU@UUUTU@eUede@uQedu*%z*%j@UQU5VU@E5@& @LDUAM@HHUIHOL\\\@UUQUUU@UTETE@EA@D]A EE@D]A E@UT]UU@UUUU*%j*%z7j&@UUQU5VU@AE5@@& @@DDAM@DHIHsJHs Hs Hb Hb b @EEQUUE@ETD@@@DAED.BDUnYUfYj&7;߀*@UUQU5VU@AE5@@& @@@M@@I@DQADE@EQED@EQE@@EQE@@EQE@@EQE@@EEQEEE@E@@@ E@ @U\UU@UUUUU*;߀ޢ/ڀ*@UUQU%VU@E%@&   @@@@@@@@@  T\UU@UUUUUU*ޢ/@EQUZE@EQUZE@UEQUVE@E@@@  T\UU@UEUUUE@EUUZE@EQUZE@EDED@EDED@EEDED@E@DDE@EEDED@EDED@EDED@@@@@@@@DAAEAAEAAEAAEAAEAAEA@DD@D@UEUEEE@EUEIE@EUEIE@EUEIE@EUEIE@UEUEEUEP@@@@@"@DD@qD@UEUEqEEfbfgfgffUEWEUEQED@DQDD@D@@@@EQE@EQE@EQE@TEQA@PEUA@PEUA@ @@ "@DL@qE@UEUEqEFbwwπfuwUEu3܀"D@UEDUDDD@DD@@@@@EQ@FQE@aE@aE@TaA@PEUA@PEUA@ @ @ " @DLPqI@UEUUqIF߷߷πꚪYu ܀b D@UQEDUDDE@DD@@@@@FQ@GM@@(ϲ@@$ϲ@AdϲAA@PEB @PEUAG @ @ @ " @DLPqI@UEUUqYFi斉斉險]Uu U؀f E@UUEEU@UUDE@EUDE@UEUDEDDQ@P@@G P@@\GMDxϲD4@EDtAED`FpwCGp?̀`."@TE]aEE@UEUaUE@UEeE@EeE@UeEUfeeUV]UeeQF UeUF E@UUEE@UUEE@EUE*E@UEUE%EWDDa@H@\G D@\F]Txϲ4@D4AD J0wCG0?̀ .@mQE@dQE@Td$E@EU4D@UU0D@UUe!D@uUULT@UuQU UT@VuUU E@WUUUE@WUUUE@VEUU*E@UEUU5UWTATTuPXBX@\F]TXEXTX@@D@ED 0wC0;̀ *@09UAE04UP t6P @XET0M@XUD0I@XiH!F@YtQUM%W@UuQU eW@VuUQ eV@SUUQUU@SUUQIV@REUQ*MW@QEUU5]WETTuXEXEXTTU4 TU(@TUU@=E@TUAE}E`BpwCp7̀`&@p5GD04V46! E1 U0 h!@YtQU%W@UuQUfW@uUYfV@UU]VU@UY]IV@EYY*I[@QEYU5Y[ETTuX E$ E4 dYQ@4 Tb@(@DbU@=F@D"AD}BDRB@SCPWCPFB@`9QGD 8TV$6XE\U\X@]4UW@]5UWS@uYWR@U]WU@U]IV@I)Y*I[@qIiU5Y[IdTtX IT`$ IQP4 h_Q@0 Xs@ @HsU@1G@D3ADuCD`B@PCPUCPEB@P(AWDhTVfXERX,URX,TRX @mdaU!V@]eaU%WV@a%Y*WR@U]WU@U]EU@JY*EY@q]U%U]DTdT D`$ OQP4 ,_ 3!@H3U1G@D3uCH&L7\wÀXf@TEUAfEETTfET!$ET58UT58UT %@yeQU1U@YeQU5EU@` *EQ@T EQ@U EQ@J EQ@uUUU0TT4 $ 8O$ @,[\P3X!@H3U!W@D3eCH&L7LwCǀHf@HuOu1+M!=4] =4Y )@uUQU1U@UUQU5EU@TUA*EU@TUAEU@T]AEU@D]AEU@UU]UUU@ TUT@$YT@([]T@VQUP%b&@XbV@D"UBH&L7LwCǀHfHO +@Op=@4_p=4Z (@uUQU1U@UUQU5EU@TUE*EE@TUEEE@T]EUE@D]E UE@UU]UUU@UUUP@VUUP%@VQUP%@uQedu@eQe!ee@UQU!UU@E%EA"& 3 sCǀbO +@Op-@$Op- ( (@eEAU=U@UEQU=EU@EUE*EE@EUEEE@E]EUE@E]E E@UU]UU@UUUU@eUee@uQedu*%z*%j@UQU5VU@E5@& @ DQA]@HQYK @K`@K` ( @UEAU-T@UEQU-ET@EUE)ED@EUEEE@E]E UEnF FYZZ*%j*%z7j&@UUQU5VU@E5@& @DQAM@HQI@TF@T@P @EQP @EQP @QE@TDQE@TDEDDEEE@E]E EEnF F]ٿ[]ݷ_j&7;߀*@UUQU5VU@E1@" @DQAM@DQAI@DQADE@EQED@EQE@@EQE@@AQE@@DQ@DDQ@@@@EE ED.F D]Y]w]*;߀ޢ/ڀ*@UUQU5VU@E1@"   @@  @ A@ TX@U]UU*ޢ/@IQUZE@IQUZE@UEQUUE@E@@@  TXUU@UEUUUE@IUUZE@IQUZE@EDED@EDED@AEDED@E@@DE@AEDED@EDED@EDED@@@@@@@@DAAEAAEAAEAAEAAEAAEA@DD@D@UEUEE@EUEI@EUEIF@EUEIF@EUEIF@UEUEEU@EP@Q@Q@DD@QD@UEUEQEfb fcKfcKfbʊegQřa#Ř"H@EALULH@D@@@EQDEQDEQDEQ@EQ@EQ@@!@DD@qE@UEUEqEb wOwOfʊuUu܀fH@EUEUDD@@EQ@EQE@EQE@EQE@EQA@EUA@EUA @ !@DLPqE@UEUUqE ߷O߷OꚦʊUu܀fH@UUEUED@@@EQ@EQM@@I@@E@@AA@EA @EUA @ !@DLPaE@UEUUaUi IIꚪYu ܀f fUff"@UEUD!DDa@D`@@EQ P@\EMDXϲD@EDAEDFIvBFI:BI*E@TEYQQEE@UEUUQU@UEee@EeeE@UeeE嚪e]Uu U؀f fՀfրf*ʗ@UEUE5WDDq@H@@\E D\\H @M@D@MADHvBH6H&D@EUQD@TQ@TTe @EUupE@UUupEUUfaeUVLueeQV uf fՀjրj*@UEYU5EWTTTu@XX\\XXXX@EU@D@EAYDZLvBVL6BL&BD@M5AAD 4APD 4h@ @TEQyD0E@TUQyD0E@TUQiH!F@UuQUM5W@UuQU uWf fՀjրj*@QYU5E[ETTu@ EX XdE TE @EQU@!E@EAeA&fA6BVA6BF@&BG@@5QG@4TVT4h!XEl1\U,0L(!@YtQU5S@YuQUuWf fՀnրn*@amU5U_IdTuDI E dYQ@ TYQ@ @UQU@1E@Q@uARfARBVAVBF@FBG@@!QG@ TV XE\U!@]41U%S@]51UeSv6Հր>*@q}U%U^NtTdDJT` IQP d_Q@ Tb@ @bU@1F@"@uBPfAPvAUv@Ef@@EV@ETTVETXEPXUPXTPX@]tqUW@]qUUW6&Հ.Հ>@q}UU]OtTTD JTP OQP `_ P3 @3U0G@3tC&dB7uC7uC&eB@EeAETTeET!$ET58UT58UT &@}uaU!V@]aU%UV& *˔&Հە.ՀJ. @uUU]pT@ p  pO @$[Q\P3X@3UW@3TC"dB3uCsuCbeB@@uATu 1( =4=8Y )@yeQU1U@YQU5UUfB*˔fŀەnŀn @e_]UUU@$_UUP@$ZUYP@$[Q]P@VQUP%@%@UQU@@UA"eB3uCsBuGbeJ@LtO t 0+=4=4Z()@uUQU9U@UUQU9UUfF*fŀnŀn @UU]VUU@UUVP@VUVP%@VQUP%@uQedu@eQe%e@UQUU@EEA"EB3ECsBUGbUJO +@OP-@$_P-$((@eUAU=U@UUQU=UUfF*fɀnɀn YZZj@uQedy*%uz*%%j@UQU%U@E!@!@@@DQAUE@HQUIJ@KQP@KQP @(@UUAU=T@UUQU=ETfF*fɀjɀj][_*%n*%uz7~j&*@UUQU5U@E1@!@@@DQAUE@DQAUE@DQATE@EQUT@EQUP@EQUP@EQUP@T(QU@T-DQU@T-DD* @Y^ɀj][]_j&7~;o߀**@UUQU5U@E1@!@@@DQAEE@DQAEE@DQADE@EQED@EQE@@EQE@@AQE@Q@Q@ @FUENŀfF][]_*;o߀ޢ/_ڀ*@UUQUU@E@@@@DAB&B \g]g]*ޢ/_@IQUZE@IQUE@UEQUE@E@@@ @ @@@TTUU@UEUUUE@IUUZE@IQUZE@EDD@EDD@AEDD@E@@D@AEDD@EDD@EDD@@@@@@@DEEEEEEDDDEDI@EUEIF@EUEIF@EUEIF@EUEEE@EQEAD@EQA@D@EQA@DDQDDQEDb @FUIcK@FUIcK@bJ@QI@PH@fHI@EUALE @@DQEDQEHb @\GMsO@\GMsOhbʊdQōdŌfH@EUIE"D!D!@ @@EQE@EQE@EQE@EQA@EUA@EUA@!HaEDaEH @G]O@G]OꚦʊUùfH@EUIE"E!D!@ @@EQ@EQM@@EQI@@EQE@@EQAA@EUA@EUA@DQEDQUH @FYI@FYIꚦ։UùMj" @UEYD1DDq@D`@@EQ PLLHH@EE@D@EAAD@EAD@EUAD@EAD@ED@EUQEEDQUD$e@EeeE@UeeEꦙYu ̀ ΀n*ʋ@UE]E5GDDq@H@L L\HX@EU@D@EAD@ED@EUAD@EAD@EAD@EUQADDTADDd @EUupE@UUupE䚦`Luɀe uɀ ߷߿΀n*@UE]U5EKDTu@ X \XXHX@EQU@D@EAQD@EQD@EAQD@EAAD@EAAD@EQAADTAPDh@ TPy@0TPy@0TPi@ @UeQUE5F@UeQUuF ߷߿΀n*@amU5ENIdTu@IX XTD@EQU@D@EAU@@EU@@EAU@@EAE@@EAE@@EQE@TTDh Hl0H,0H(!@Yt!U5C@Yu!UuG& 7?̀>*@q}U%UMNtTe@ J `IQ@ PHQ@ @AQU@!E@A@eA@AeA@AAUA@EAE@@EAE@@EQE@DTUDXD\TP!@]t1U5S@]1UuS6 ŀ>@q}UUMOtTT@ JTP IQP `_Q@ PZQ@ @PQU@1E@P@uA@PeA@PuA@Uu@@Ee@@EU@EPTUEPXEPXUPXTPX @]tqU%W@]qUeW67?ǀ>@u}UUMOtTT@ JTP OQP `P@PUD@PTA@PdA@PuA@QuA@AeA@AUA@TUT$T5(T5(UT&@muqUW@]qUUW6 7? ǀJ. @u-UUMp$TP p pA\X@PUT@@TA@@dA@@tA@@tA@@dA@@tA@Tt 0( =4=8Y$*@}uaU%V@]aU%UVR*䷓忓 ۀ @U[]UUYUUPEYA]@UQTP%@%@PQU@@TA@@dA@@tA@@tA@@dA@@tA@Tt 0(=4=8$)@yeQU5U@YeQU5UUZ* ߀ YVZVV&@UQUP%@uQddu!@$$e@QQU@ADA@@DA@DDA@DQATE@DQATE@DAdETd )- - ((@eUAU9U@UUQU9UUZ* ߀ ][_n@yQedy@(vѸ%u{("%%j@U@@@@DQAE@DQAE@DQAE@EQU@JQ@KQ@[Q@(@UUAU=T@UUQU=ETZ*//݀*][_(%n@(zѹ%u{@V@q}UUUMOtTU@ J P@@@@@@P@@P @Id1U5S@Y1UuSi6uuŀa>@q}UUMOtPT@ JTP PPP@@@@@@P@@@P@P@PPPTPP @]tqU%W@]qUeWY6M7];ǀi:@uyUUMpOtPD@ `JT@PP@P@@P@@@@@@@TT$T5$T5(UT&@muqUW@]qUUWY6 M7 ]; ǀiJ* @e )UUI` $PP` PPPPP@@@@@@@T ( =$=(Y*@muqUV@]qUUVY"M#]#Y"@UVUQeUTP`P P@  P@@@@@@@T (<<$(@UuAU%U@UuQU%UUij&}}i@YUWu[Wp "@!@uQdTt!$`QPAA@@@@@@T (,,$(@UeAU5U@UeQU5UUij&}}i][_n@yQeTy@$vQx!Ut "(!`P@@@@@T @ @ @ @(@UUAU9T@UUQU9ETij*};};i:][_(!n@$zQy!Uy@4wQ|3^ "(" P@@T@T@T@T@T(QU@T=DQU@T=Dah* q;@u;_̀i:][_("@4{Q}3^@4WQl3_ (" P@ Q1@Q5@a* q ;@uGQM;_ŀibJ:jYeZeZ("j@4[Qm3_@$VQX#_ " P@ @!@%@*@@DDQA S@DDQA R@TTQUU@UQUUU@$VQY"Z@$VQY#_@EQTJD @@@@@@@@DDQA@@DDQA@@TDQUE@EQUEE@EQUJE@EQUJE@@@@@@@@@@@@@@PP`EUI`@EUI`@EUI`@EUEPDEQEDEQAHEQAHPP@`@PE]Mp@D@PE]Mp@D@PEYI`D@TEUEPD@TEQED@XEUIHE@EUME  ! 11@ @@@@PP@@PE]M@D@PE]M@D@PH@TPH@TH@XHI@EME L!L1D1@D @@@ @@@@@@PP@@E]IPD@E]IPDꊠHPLdAL抁I͑M̑NnJ@TE]DqFDq@D`@@ P @@@PP@TETE䆐@LTL@TMEUL抅IΕMΕMnI@TE]EuEDq@@ @@@@@@TDUUDD@TEQUEUD檅IMMnI@TE]UeEEDe@@ @@@@@@@@@ @TTQQE%E@TUQQEeE梅I╚M╊MnI@PE]UUEMDU@  @@@@@ @TTUA5A@TUUEuE&EI6UM:UL:UH@PyUUULMtU@ HP@@@@@@@@P @DT!U5R@T!UuVd&JdMdL`:H@`yUUHOtD@JT@P@P@@@@@@@@PTP @HdqU5W@HqUuWH6BL&G\&Gh&F@d%UUE`K$D@`J@P@P@@$55P&@LpqW@LqUWH6BL6G\6GXF&F@TUEP@PP@$(4848P$(@EpQU@EQUUI"bM2w]2wY"f@UUUuUTp   @(( <<<<((@UpAU@UtQUUUijfm{min@UUUUuYTTp  0@$0 $ @(( <, <,($(@UQA%U@UUQU%UUij%fm{min@UUWu[Wp & "0@%1@pQx P4 ( @@@@@$@UQA5T@UUQU5ETij%dm5ym5}i5n][  .@pQy P9@tQ|0P4 ( @@@@@ QQ@1DQU@T5Dah% Da5 I@eѽ5ZIi5JYZ .@xQ}0P9@TQl0P(  Q1@Q5@a% @a 5 A@eJQM5ZE@eJQI5ZE@UYQUUU@XQUP@XQi P@XQm0P@TQX P @@@@@DDQAR@DDQAR@DTQUQ@TQUP@TQY P@TQY P@@@@@@@PP``@`@`@PDDDDPP`E]Ap@E]Ap@EYA`@EUEPDEQEDEUIDEUID   PP@E]A@E]A@EYA@EUEP@EQE@EUID@EUIDLDL!DL1D1@ @@@@@PP\\@TDDPH@TEDAEH@TEUHEI@TEUHEI@TE]HEI@TE]HEI@TE]DqEEq@`@P@@PP@TDUEPD@TEQEAUD@TEIUI@TEIUI@TEIEI@TEYIEI@TEYEaEEa@@@@@@@TDUU@D@TEQUAUD@TEYUH@TEYUH@TEYEH@TEYYEH@TEYUQEDQ@@@@@@@@@@ @PDQQA%D@PEQQAeD@PEQeH@PEQUH@PEQEH@PEUQEH@PEUQQEDQ@@@@ @TDQ5A@TEQuE@TEQeE@TEQuE@TEQuD@PEQeH@PEQUHAA@@@@@@P @TQ5Q@UQuU@UQeE@UQuE@UQuD@EQeD@EQUDE@@D@@@@@@P@PQQ@QQUQ@QAeA@UAuE@UAuE@EAeE@EAUEE@@E@@ 00P @`Q@`UQ@`eA@TuE@UuE@EeE@EUEE@E 0000 @pQ@pQUQ@p!ea@`1uq@a1uq@a!ea@QQUQPP   0000  @pQ@tQUUU@tiee@hyuy@hyuy@hiei@TQUuYPPp  0  0 0  @QQ@UQUUU@eiee@iyuu@iyuu@iiee@UQUuUPPp 0@00  @Q!P@UQU!ET@ei!Ed@ey!Eu@ey!Uu@ei!ee@UUeUQ`! !@11@@Q00  Q1@U@T1Deh!Dey!E@ey!E@ei!E@UUU@DQ@@!!@@Q11@DQ`P @DD !E@EQI!UE@EQI!UE@EQUUU@DQQP@DQaP@DQaP@P@@@@@P@P@P@P@@@@@@@@@@@@@@@@@@@@@@@@@@D@D@H@H@H@@@@@@@@@TDQD@TEQD@TEUD@TEUD@TEYD@TEYD@TEYD`@`@`@P@@@@TDQE@TEQE@TEUETEUETEUETEUETEUEPP@@@@TDQE@TEQE@TEUETEUETEUETEUETEUEPP@@@@@@@@PDQA@PEQA@PEUAPEUAPEUAPEUAPEUA@@@@PDAPEAPEAPEAPEAPEAPEAA@@@@@@QQEQEQEQEQEQA@@@@@@@@PPAQAQAUAUAEAA@@@PPPPPP@PPQPQ!!PQ!!PQ!!PQ!!PQPPTQUTQe%TQe%TQe%TQe%TQUPPQUQUQ%%UQ%%UQ%%UQ%%UQPQUQUQ%$UQ%$UQ%$UQ%$UQP@QU@U@$U@$U@$U@$U@@@@@@@@@@@@@@@@P@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAA@AAAAAA@QQQPQQQP $$ $$EEEEEA@EEEEEA@@@@DEEEEEA@@@@@@@DUUQP@@@@DUVQP@@DUVQP@@DUWQP@@D@@@@@@@@    044 00 044 00 044     $$EEEEEA@ IIIEEA@ IJA@@@@D YZQP@@@QQQT Y[QPQQQQT Y[QPQQT Y[QPQQT Y[QPQQT Y[{wuQPQbbQPQbb    048 00 048 00 049 !   049  $)  ]]IEEA@]^JFEA@ ]^QP ]_QP@@@D ]_QP@@@QQQT ]_QPQQQUT ]_QPUUT ]_QPUUT ]_QPUUT ]_{wuQPUnjUP Ujn(,,( $) )<<(049 )88(049 %$$$049 049 049]^JFEA@ $)]_KGEA@]_QP]_QP ]_QP@@@D ]_QPD@@QQQT ]_QPEEEEUQQUT ]_QPEEEEUUT ]_QPUUUUUUT ]^zvuQPUUT ]^jfeQPUnjUP!!!Ujn@@AAAAUPPQQQAU),,(PTY]]IU)<<(`di]]IU)88($$$pty]^JV%$$$pty]^JVUUTpty]_KWUQPpty]_KGEA@pty]_QP $)Y[QPY[QP Y[QP DDDD Y[QP IIIEEADQQQT YZQP IIIEUQQUT YZQP YYUT YYyuuQP YZUT YYieeQP UUT UnjUTUjn@@U@@V),,(PTYYZJV)<<(48(PTYY[KW)88(44$`diY[KW%$$$44$`diY[KWUUT$$$$ptyY[KWUQPptyY[QPptyY[QP045UWQP (()Y[QP UVQP MMIMMIDTTTTUVQP ]^ZVUQTUUUTUUuuuQP ]^QUUTUUuuuQP ]_UTUUeeeQP ]_UT ]_UT ;7unjUTUjn@@@@@@@DD@@@WUT@@@WUdl,@DWUt|,PTWUixxht|,PTWUedddtx(PTWUUT$48)`dQP$$$%ptA@ptuUVQP <<9URQPEFJNMID<<=]YQP MNJNMID,,-]YQP ]_[_]YTTXXXYYuqqQP ]_YTUUUX YYuqqQP ]_QUUTTTd``PP ]_UT ]_UT ]_UP ]_njUP %ejnAAAAAA@EE@@@GEUP@@GEel,@@GEu|,@@GEIIYiu|,PPGEEEUeu|-PPV$4<-QQUP$48)aaE@$$()999UUE@UVFBAAD<<=]YQPUWKOMID<<=]YQP ]_[_]YTTX||}]YuqqQP ]_YTDHHHL\Xd``PP ]_YT ]_UT ]_QP $ ]_a`0  ]^ap0  ]^zvuap  QQaaaa`AAAAAA@AA@@@AAUT@@CAU\@@CAU\@@FEEAQeel,@@VUUQQeu|,@@U$4<-QQUP$4<-UUUP$4<-%%%YYE@$$,-]WGCAAD99=]YPPYWADT]YuppPPUWYTD\Xd``PPUWYTXXdhl\XUWYX\Xdhl\XUWY\\XTX\\XUVY\UVY\UUuuuUTUUeeeUTQQQQQQPAAAAAA@AA@@@DDDDEEEDDDDD@@@@EEFFFEUUUUUE@@@@EEEEEEUYYYUE@@@@EUUUUUUQQUUXXXTD@@@@EUUUUUUQQeell\TD@@@@EYYii%$4|m]WGCAACG[Yyy5$4|m]WGCAUVVZ]TP$4|m]WGCAZ]TPPdt|m]WZ]MHDD@Pddlm]WEDDYYUPPPPUWUTTYYUVUTX]YUVYX\Yuy}]YUUY\\Xdhl\XYYY\\XTX\\XUUeeeY\UUeeeUTAAAAAA@AA@@@DDDDEEED@@@@EUUUUUEE@@@@EYYYYUUU@@@@EYYYYUUQQUUU@@@@DX\lieUQQUUY@@ACG[]}x4%%iCAACG[]}x4$4|CAACG[]}x4PWU[]}xtTPPdt|m]WD[]mhdTP@@@@EUVYTPVUPPPPUUVE@TVUYYUTXUUYYYX\YYY]Y\\YuyyYYY]mieY\\YeeeUUPPPTTX\TUUUUUUAAAAAA@AA@QA@@@UAC@@,)eAB@@,)eUQAEEF@@<9uUQQUUU@@=84U@@=84PRQ=84PVU@AACG[]-($@YU)))@[]-($@YY)--TWUPPppuY]YTTWUPP``eY]EDXWUX\|xtXX\VUX\lhdX\\VUX\\XTX\XUUTUeeeUUTUUUUUUAAAAAA@EE@UUS@@,)eUS@@<9uUS@@<9uiyyiUS@@=9ueeeeUSPP=84TPP984PQQ)($Pee@AACG[Y@UU9==T]]_[[YPY]===T[YPY]=<<T[Y PPpptX\LLLHDX[Y PP``dX\\XTXX\[Y X\\XTX\\[Y X[Y  TZY TUefjZY PQQQQQQS@@ *//*S@@<95*>>*WD@895)99)WTP995%%%%WTP984TTP%$$Pd`@tp   PVU9<<DMMNJ^]PY]=<<DMMOKOM PY]=<<T_] PY]=88T_] PQuuuYY DDDU_] PQaaeYY EEE_] U_] U_] U_] Ujje``PPUjjRBBAA@@(,,)RBBAA@@884(<<)WK[YUTP444(88)WK[YUTP$$$$$deUWK[Yed`$$$TUUWK[Yed`P[YutpPWUutpPWU540 PWU9<<DMMMIMM PZY)((DMMNJNM PZYPQUVZ^] PUUTTTQ_] PQuuuUUEEA_] PQeeeUUU_] %%%U_] U_] UjjU@UjjQAAAA@@(,,)QAQQQPP(<<)UM]]UTP$$$(88)VN^]ed`$$$%VN^]utpTUUWO_]utpPQUWO_]utp@AEGO_]utpP_]540 P_]%$  P[Y@AEEIII P[Y@AEEIII PZYTTPQQUUYYY PZYUUQZY PQuuyYYUZY PQuuyYYU[Y PQeeiYYUuw; UjjU@Ujj(,,)(<<) %$ (88) 540$$$% 540540PQUVN^]540@AEGO_]540@AEGO_]%$ P_]P_]P_]@AEEEEEP_]@@@QQUEEEEP_]QQQUUUUUP_]UUUPQuv~^]UUUPQuv~^]UUDPQeem]]UjjU@Ujj     00 440 00 440   ! 540 540 %$  %$ @AEFJZY@AEFN^]P_]P_]@@@@P_]@@@QQQ@P_]QQQU@@@@P_]UUAP_]UUAPQuw_]UU@PQuv~^]UjjU@Ujj     00 440 00 440    440$$ @AEEIII@AEEIIIPZY@@@@PZY@@@QQQ@P[YQQQU@P[YUU@P[YUU@P[YUU@PQuw{[YUjjU@Ujj$$ $$ @AEEEEE@AEEEEE@@@@PUU@@@@@@@PVU@@@@@PVU@@@PWU@@@PWU@@@@@@@@@@@@AAAAAA@AEEEEE@EEPUUPUU@@@@@@@@@@@@@@@AAAAAA@AAAAAAF_Q\Q\Q\Q\Q\Q\QXQPQ@Q@Q@q@y@y`y`y`y`y`y`iiii`    @k@k@j@j@b@B@B@@@@@@XXXXXXX!!!!!!!````````hhHHHHHPPPPPPPXAaQ?%#? $=Y,^dڿWԞ.ۿ+  4tttttttttt``````@22222222"""""""""""######ckkmMMMMMMMDDD qp``````````````` > #.=؀ P         @@@@@@@@@@@@@S@S@S@s@s@s@s@s@s@OOOOOOO     ||||&|"|s|s|s|s'?) K   ((((88888y222222E2E2E2222::>>  ywvvvfbbbbbPPPPPP@@dllF lF ,F-F-F! F F F D D 0000 7777777?h/h,h,h,h,h h hhzzzzzzzwsssssrrrfffffnnn,    3 3 7 73 ۽ SӜ%ڿ܀e(?fB@J?%9 >@@@@@DDDddddddd@d@$@$@$$$$$$%%55 ? ? ? ?????? \\%TeDeDeDeDeDeDD@`    #`# # # # ###cqqqqqqqqpP ^^^~~~~b```````%݌ ؾ] Z1~@[ 1?i>NQ  ##!!!!!!!!!@!@!@!H!H!L!L!L!L!L!L!L!    ^^TTTTTFFvw(({hy|y|x|x|P}P}}}uuuuu""À"" aaaaaaaaaaa  p ! ! ! ! ! !  /f d d d d d t t(t(t(p(p(p(p((h|\\\\\\\VVVVVVVV$?@e?N~~"%FhـO %'XО'ٿM hhhhh@AAAI4x44440x00000((***##c#c#c3CsB sB s@ S@ Q@ Q@ A@ A@EHDHDHDH333333333333[{{{{{{{z $$$$$$.>>>>>>>?       ????????@w@@@@@@@O?ڀ%?Z.@%?aRe ^\Yџؿ׿0)+'9  2rrrrrrrbbb``` \\\\\\ookkk+++++#pppppppppp "@e%fԿd M`ff~~~~(((((((( ( ( ( ( ( -      WWWWU~~~~~~zxxxx0    ! $! $!$!$!$! f f` f` fp`dp`dp`dxC`XC`XC`C`C``!!!3333200             ~~nnnnnn ``(`(`(` (` (` (p<(<<<|||||zzzzzrrrCCCCZ^&/XўyP޲ؿٿN"P?P> cc`ڀ@%?Zd$%?&?S. P  aaaaaq111115 7 a7 a7 a7 a7 a7 A A A A A A C O _ _ _ _ _ _     @@@@@@ C CCCCB@@p8p8p8pxxxxpx`x<`x<@x<@x<x< < <,,,,, q s s s s s s { ?         q Q Q Q Q Q Q QDfـ~`@&?T.&?R?QdZٿTѝ/پٿ 9DDDDDDDɡɡϡ!!!n!n1n3n2n2,2,2$2 HHHHHHHPP$$$$%%%g           hiiiii!!!!%%Xxxxxxhhhhhhth.om Q1f?uf Zv`0kQug P  33??@@@@QQQ[wssaaP@Y@Y@Y@Y@Y@Y@Y[@YQ@Y@@Y@@Y@@@@@@@@@@@@@@@@||444@@@@@@PPX  X  X  X  X  X 0x0x0x>0(>0(>8(>8(> 8)? !5 !5L1L1H 1H 1h 1h 1h xppq1CC@C@B@B@B@B@B@@@@@@@@@@@@ d d @ @ @ @ @@337777777TpDpDpDpDpDpDpDpDp DpC @PB @PB @PB @PB @PB @Pf@vvvvvv~~555555u e d d d d d d d!?%?eK A >ځNM>d? eWҞؿ' پd.`_& ? fffffffFF@@@@ @@@@PPPP|||@|@|p| p< p< p< p< p< p<p<0,0000?? ?77777wqaaaaaa@@@@@@@@{@{;;;;;;?S#/+~!ԺKҙeNa!?@ ">V) F   (hhxxxyyyyyyyyԁԁԁԁԁԁ<<<<<<.////   %%BBBBBBBBBBBBBBBJJJwHwvvv6 Pxxxxxxxxh(h(h(h(h(h(h8h0)00000000pPPPPP ھ-%?edہڿXӞ@W A ?LK    8888888:: ; ; ; ; ; ; ;-?=========5555555uuuuu        / / ) ) ) ))iih``````pP    88888888888888884(44444444444444 Ծۀ ֿ%@"?@ ?  11111111111!!!!!!# # #      F F FFDDDDLL```````````````@?@A! 1ڻGhhhhhxP@Q@Q@Q@Q@q@q@q@q@q@qDqDqD1D!D!D!d)d)t)))))))@@@@@BBBBBJJj6j6z>{>{>;>9.===5 5%%%%%    8 x x r r vIwgggggg%%%%        xxxxxxx x 9       ``b`b`b`b`b`bC      &(/] ؾؿTҝ =^! Vz`@`B %>V-B       C0CpppppppР$$$$$$$4<<  PPPPPP^#!!!!!!~~~~~~~~~~,$$$$$$$$$$$$$$$tppppppppppppppCCCCCCCCCCCCCCCCӈݿ޿?((!?!?!?@!ׂ؁/ <,,,,,$$ggoo~~~~~<<<<,@@&@&@&@o@o@```ddey%yxxx8 xppppppp@PPPPPPPp||||||||<,,,,,,.8888888x>>~~~~~nn$$$$$$$,,,,,,,,(        RRRRRR"S"C"C"C"C"C"C"C"G"e e % % % % % -K ^@@%X T%"0.  NLLMMM PPPPPPPRC,,,,,,,,,,,,,,,<PPPPPPPP@? 0ɾG  01qqqqqqqqssS[KKBKBK  d$$000000xxxxxxxxxxHHHHH?=====}}ld d d d D D D D D DDDDDDDDDDD@OJJJJJJ00"0"0"0*0*0*0*0. <<<||||||\\\TTTTP@@@*sl.,#?ف>XIK}ۿtiZ ` F     . . & & & & & & " " " 0000000KKCCC          ;:88888000ppppppp  "?@UW~cܿ))C  OO___\{\{\s\s\ppp0000033333     @@@@PQQQQQQQQQQ11113 3 3 # # # " "                  ODDDDDDDddd`````p 0000000OGGGGGG@@j@\́/-ҿ ]"S@dVߞC       8::: a```````   004P4P6P6P6P?P?p?p?2?""GGGEEAA@@@@@@         MIIIIIIHX X X X X X X @ @ @     MAa~@d@W-$?O͛$U$e&,$H         qqqqqQAAA@@@@@::::20000ppp`@@@ V          ```````@@@@@@@@@@ 55555555uxhhhhhh ț~xS~ T׿.{_%P@d!?@!?O  @@@@xxxxx8888889 9 9 9 9 999      ????  ,,,, EEEAAAAAA@@@@PP_><<<<$$$ ! ``````` @@7@7@7@7@w@w@`!׿ 'VО-+.%aX/@^~#:/Gfv 3   XXYYY     0000000pppppppp 2  ?A\ ~ >    ?&&$$$$$$$dddddd   ,,,,,<<<}}yq q a a a a A A A A A A A A A A A A A A A QQQQQQQYYYYYYYY     !?(ۀ` A?(W֞ݿ6 @PPPPPPPPPTTTTT%%%!!!!)))(***** <<<<<<<<<<<<<<<<<<<<>~~~~~~~~~fBBBBBB@BBBBbbbb 0000000000  FFFFFBBBB$Zw]\OxX1#G     ```     (**X*Y* * * j jQQQP$$$$$$$$ $ $ $ $ $ $ &`m`m`mpmpmpmpdpdpdpdpdpdPd8888888B8B(B(B(B(B(B B G   DDDEEEE0000000PM~wZ&>MJ ۼzM 3T C% >1111111111333322 JJJHHHHH L L 0 0 0  ``````PPPPPPxxx8((((()------- aۀZc $Zb#&%c  3   !!!!!!!!!!!###33DLLLLLHHHHHHHHHHHHHCCCCAAAAAUUUUUUU}}}}}}}}}<(((((((,,,,,,,..@@@@@PPP8888888(\ >`"`Ѐ ! H    """"bbbbbbb bbc AAAAIKKKKKJN N N         @@@@@@@  """"""+++++++++?=M ־O ־݀L#(>Q)="main@#!      %F+PK>QM9MP9P?FK?GB/B.GFCCHO3>6G>3HNULL 0 Joined 0 |Broken|0|1 0 0 70 1 43 2 80 3 75 4 62 5 81 6 77 7 57 8 77 9 80 A 57 B 80 C 63 D 70 E 75 F 63 G 71 H 66 J 47 K 66 L 46 M 71 N 70 P 67 Q 67 R 72 S 79 T 51 U 62 V 54 W 71 X 62 Y 51 Z 72 4 linear essential -0.250000 0.750000 linear non-essential 0.000000 1.000000 linear essential 0.000000 1.000000 linear essential 0.000000 1.000000 0 1 significant elliptical 85 0.347197 0.356172 0.227344 0.177206 0.000431 0.000400 0.000400 0.000400 1 1 significant elliptical 100 0.361641 0.173160 0.226133 0.067539 0.000741 0.000400 0.000400 0.000400 2 1 significant elliptical 100 0.336172 0.360273 0.238242 0.164961 0.000402 0.000400 0.000400 0.000400 3 1 significant elliptical 100 0.353047 0.356254 0.236445 0.158867 0.000599 0.000567 0.000400 0.000400 4 1 significant elliptical 100 0.321758 0.281133 0.194375 0.146484 0.000578 0.000400 0.000400 0.000400 5 1 significant elliptical 100 0.358945 0.385930 0.228867 0.169883 0.000457 0.000400 0.000400 0.000400 6 1 significant elliptical 100 0.343437 0.382672 0.221172 0.167031 0.000400 0.000404 0.000400 0.000400 7 1 significant elliptical 100 0.430273 0.250973 0.232383 0.143711 0.000737 0.000400 0.000400 0.000400 8 1 significant elliptical 100 0.344063 0.393648 0.217774 0.168516 0.000416 0.000400 0.000400 0.000400 9 1 significant elliptical 97 0.348341 0.382140 0.220119 0.166962 0.000465 0.000434 0.000400 0.000400 B 1 significant elliptical 61 0.348681 0.406833 0.226499 0.169634 0.000523 0.000748 0.000400 0.000400 D 1 significant elliptical 41 0.352706 0.370036 0.235518 0.176258 0.000400 0.000442 0.000400 0.000400 E 1 significant elliptical 45 0.354601 0.379158 0.243229 0.170833 0.000415 0.000400 0.000400 0.000400 F 1 significant elliptical 27 0.422598 0.302966 0.212963 0.166667 0.000474 0.000400 0.000400 0.000400 G 1 significant elliptical 100 0.340547 0.362441 0.225469 0.170937 0.000440 0.000459 0.000400 0.000400 H 1 significant elliptical 27 0.349103 0.346369 0.216290 0.192708 0.000845 0.000738 0.000400 0.000400 J 1 significant elliptical 100 0.285391 0.244609 0.228359 0.159297 0.000516 0.000400 0.000400 0.000400 K 1 significant elliptical 100 0.354883 0.339293 0.232227 0.162500 0.000406 0.000400 0.000400 0.000400 L 1 significant elliptical 38 0.258018 0.236914 0.230777 0.160979 0.000400 0.000400 0.000400 0.000400 N 1 significant elliptical 14 0.359096 0.385742 0.219029 0.186663 0.000572 0.000485 0.000400 0.000400 Q 1 significant elliptical 29 0.342672 0.352842 0.221040 0.173357 0.000588 0.000587 0.000400 0.000400 S 1 significant elliptical 100 0.345273 0.358277 0.225742 0.161914 0.000426 0.000400 0.000400 0.000400 T 1 significant elliptical 21 0.433036 0.237258 0.229353 0.136161 0.000696 0.000400 0.000400 0.000400 V 1 significant elliptical 11 0.380327 0.271165 0.203125 0.145242 0.001399 0.000591 0.000400 0.000400 Y 1 significant elliptical 16 0.430908 0.242749 0.227539 0.129150 0.001035 0.000400 0.000400 0.000400 Z 1 significant elliptical 38 0.345703 0.331127 0.249692 0.160567 0.000400 0.000400 0.000400 0.000400 X 1 significant elliptical 19 0.354646 0.321155 0.242804 0.153577 0.000525 0.000563 0.000400 0.000400 C 1 significant elliptical 40 0.342285 0.307422 0.238574 0.167285 0.000531 0.000497 0.000400 0.000400 U 1 significant elliptical 24 0.335287 0.337891 0.225749 0.190755 0.000457 0.000400 0.000400 0.000400 A 1 significant elliptical 26 0.293119 0.283488 0.209736 0.139123 0.000400 0.000400 0.000400 0.000400 R 1 significant elliptical 36 0.367405 0.369868 0.225586 0.168511 0.000400 0.000400 0.000400 0.000400 M 1 significant elliptical 26 0.326923 0.383879 0.210186 0.181190 0.000775 0.000946 0.000400 0.000400 W 1 significant elliptical 22 0.363281 0.376935 0.215199 0.169212 0.000400 0.000798 0.000400 0.000400 P 1 significant elliptical 20 0.419336 0.312520 0.204883 0.168750 0.000813 0.000501 0.000400 0.000400 "! "$#     openalpr_2.2.4.orig/runtime_data/ocr/tessdata/lus.traineddata000066400000000000000000011607121266464252400244570ustar00rootroot00000000000000Qz39 NULL 0 NULL 0 Joined 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Joined [4a 6f 69 6e 65 64 ] |Broken|0|1 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Broken 0 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 0 [30 ] 1 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 1 [31 ] 2 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 2 [32 ] 3 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 3 [33 ] 4 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 4 [34 ] 5 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 5 [35 ] 6 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 6 [36 ] 7 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 7 [37 ] 8 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 8 [38 ] 9 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # 9 [39 ] A 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # A [41 ] B 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # B [42 ] C 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # C [43 ] D 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # D [44 ] E 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # E [45 ] F 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # F [46 ] G 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # G [47 ] H 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # H [48 ] I 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # I [49 ] J 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # J [4a ] K 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # K [4b ] L 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # L [4c ] M 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # M [4d ] N 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # N [4e ] O 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # O [4f ] P 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # P [50 ] Q 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Q [51 ] R 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # R [52 ] S 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # S [53 ] T 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # T [54 ] U 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # U [55 ] V 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # V [56 ] W 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # W [57 ] X 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # X [58 ] Y 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Y [59 ] Z 0 0,255,0,255,0,0,0,0,0,0 NULL 0 0 0 # # Z [5a ] v1 1 O 1 0 1 #1 0 1 D 0 #1 H 1 W 0 ''$$$$$$$$  @QQQ Q Q0Q0  @0@0@QQQ Q Q0Q0 0@0@0@@@@ @ @0@0 0@ @ @@@@@@@ @@@@@@@@@@@@@@@@@@@@@$@$@$@$@$ A@A@A4@A4@A4@A$@@$@$00   APAPA4PA4PA4@A4@@$@400  D0D E@E@E0@E0@E0@UE0@D @000  0D0D0@E@@E@@E@0@E@0@E@0@UE@0@D @  0@0@0@E@E@E@0E@0E@0DE@0D  0@0@ @@@D@D@D@0D@0D@0@D@ D  0@ @D@D@D@D@D@@D@D @@@@@@@@@@@@@@@@@@@@D@@D@D@D@D@@@@@@@@@@@@@@$@4@0@ @@@@@@ @0@0@ @@@@@@ @0@0@ @@@@@@ @ P P PPP@@@@@@@QQQ@U)Q@E)P@UE)P@UE%@@RE$@@RE @@RE @R A""PVQTP&VUTP'VUuTP'VUyTT&EEyTTfEAyTTfE@%@TbE@4DcE@0DSE0DR A!"wlkl@UX@VEUTT@VEUTT@VEuTT@VE}TT@EE}TT@gEA}TTWD@%@P@4@@0@0@ A!$7(w|{l@EX@FXEUTT@JYEUTT@JYEuTT@JYE}TT@IUE}TT@gEQA}TTWDQ@%@P400 A!$78vtvt@EAT@FXAUTT@JXAQTT@JXAqTT@JTAqTT@ITAqTT@eEPAqTTUDP@!@P A!$64e4e4@EAT@ETAUT@ITAQT@ITAqT@IUAqT@IUAqT@DEQAqTDDQ@!@@$%4U00@DAP@DTAQT@HTAQT@HTAqT@HUAqT@HUAqT@@DQAaT@DQ@!@@@@@@ 0D  @DAP@EUAQT@IVAAT@IVAAT@IVAATT@IVAATT@@EQAATTEQ@@P@@@@@  @DH@MAP@MUAQT@IVAAT@IVAAT@IVAATT@IVAATT@@EQAATTEQ@@P@@@@@@D@D@H@@IAP@@IQAATT@@IRAATT@@IRAATT@@IRAATT@@IRAATT@UQAATTUQ@@PP@P@@D@@@@UA@@@UQAATT@@ERAATT@@ERAATT@@RAATT@@RAATT@QAETTQ@@P`@`@ @ @UA@@eQAATT@ eRAATT@0uRAATT@0uRAATT@ eRAATT@UQAETQQ@@ 00@ $$@@&TA@@'UQAAT@'eRAAT@6uREAT@5uREATU@%eREATU@UQEETQQE@  0 0 @  $0 @@*TH@+UAMT@+eAMT@&uEIT@%uEETU@%eEATU@UEETUQEPQ  0 0   00 @@HT@UAITT@*eAITT@&uEITT@%uEETT@%eEATT@UEETTQEPP    0 @ @HPIPIPIPEPAPA  @@@@@@@@@A@A@A@A@A@A@A@A@A @@@A@@@A@@ 0&(10>(q =(q--Y@=UU T@:EU T@fEQTU@fE@@E@jE @(@D@~E @,@D@~E @,@Dj(U&\Q+"Q&%(qf=hq*=hq?-YeX@?YUmT@:UU}TgUU}TwU@%@zI@8@@EL@<@D@EL@<@Dj(U@@!@&@wlk@Vee@VmZXemZXimYeXZUmTV}T@gV}Tw%@ @8E @<DE <D(U@@!$7(w|{l@UTXU@V\UUUU]UUUY]UeUـZUmUـV}T@gMV}TwLV%@ @4@4@4@$U@@!$78zx~x@ETUXU@F\UUUU]QUY]aUـZaUـVaT@eEVUaTUUDV@%@Q$$$U @4@!468itt@ETUTU@EXUUTUȉXQT̍XaT̍YaTUaT@DDQUQTDDQ@@@ @4@4%4Y0@0@@DUPU@ىXUQTUĈXQTČXaTYaTYUaTY@@DQUQTU@DQ@@Q@@@@@@@@@@ @0@0@E0@T @ @@ԌDUPU@ԍUUQTUĊVATĎWQTWQTYVQTY@@ERUQTUER@@Q@@@@@@@@ @D @DDH@MDUP@NUUQTVATĎWATWATYVATY@DSUATS@@@@@@D@DH@MDUP@NUUATUVATWAT@WAT@VAT@QUATQ@@PP@D@@@@YDU@@@iUUATU@VAT@WAT@WATAVAT@QUETQ@@)`-`- @) PT@@tUDUD@|eUUETՀ|VATـ|WATtWATeVAT@QUETQE@*`>p>0@9 U$Y$@@~UCUH@eSUMTՀ{RMTـ{SITwSET&RAT@UQUETQQEP+  ?0 ?0 @9  $54)$@>UCH@?eSUMTՀ;RUMTـ;RUIT7RUET&RUAT@UQUETQEP'  '0 '0 @%  44@@6@ @*@@.BUHT/RUMT+RUMT7RUIU7bVEU&bZAe@UaZEefQ!Jab    @5p@ @@@7d@ @D@*d@@D@UAHT@UAMT@VAMT@VAIU E`0A%p0E%0 ! " @7d@ @D@T@@D@T@@D@U@@D@U@@D@E@@D@E@@D@@@T@@D@A@A@AT@P!T@P!T@P!T@P!TPTPT !0'30"0730# 5#0#%Q@UQPU@UP@E@UQ@E@UATDU@e)BXHd@u8CXHt@u8CXHt%(X`TPTD"%3';823;?vS}d|{ݟIMـogf@Uq_EvwQ!Nrb Q Q @@@@@E@EQ@*"b&(?ws|w|zj讀~ZI՝ZM@viBi@_w5G}W#4=rF4;s0E7s 3bQQ@E@EQ@*"b&(?gs|*vk|jfj@iUUEY@iUEEY@iFE@AD@UFEQTP$ A0 300 30 3 E@EQ@b&(*Vrk|@U%VFhe@UU%VFhe@UUUETU@UEEETU@QD@@D@QD@@DP@E@EQ@!F(e@U!VFheETETETAT@@EEEET@Q@@A@@U@@UP@U!P@U!P@U!PT!T!!!!@UQPW@E@@F@UUP@E@UUUTU@eeUTV@euUTW@euUTWeeUVUUUUUD!%H!6H(16H(1&D(15D@uSu@TW@eCd@%WUPɀ%VUUـ*j@*{{@*{y{*)fUUUUUQTUQb%VQ2'::b2'::# 2%*:# 15{G=3@ uwwC}w@egheߗQuߖUz着@@~j**UUUUUU@TATPAT@AT@AT@VXUU["%VU[w';;rZw7?sZ5/sj5@jv@*gꆨfߗUvUzں@@j**UUUUU UdTdTdDdDTDw|k"%@_wf;;v@wvw{ZꊪfߗUvUٺ@_@[m]k)*UUUU@DUT@DT@DT@DX@EhP@F!h@!%h%$$78wS|{b@_we;u@wy_uzZꊪUUUUe@^eT@VUEe[TeTYUUT@DAUX@HAT@HAT@HA!X@TIA!XT@dFA!X@aA5Xu4wC8{Sx{b@^gEYU@gY]WUڪjۯZ@UYUUY@UIքUEY@IƄTEH@ETED@EFDTIDT DYETT@EAUX@HAT@HAT@HA!T@THA!TT@tEA!T@qA5Tu4wC8z4~$@EUTU@]UUVUڨjۜZ٦j@USUUU@USAU@PEC@ED@TEC@ED@TEC@EDE@EP@EAT@HAT@DHAT@DHA!T@THA1TT@EA1T@A5T4B4Y4@]$@@EUTU@ٕ]UUUUʘ*ǜ?Rإj@QSUEUaS@D`FdGdGC@EP@EAT@HAT@DHAT@DHA!T@THA1T@EA1TT@A%TD $@E 4@X @ @@DUTU@]UUUU暮*?Rڦj@QSUEU!S@@$$CdCC@EP@EAT@DAT@@DAT@@DAT@PDATT@DATT@ATD @U @h@@@DUT@]UEU֛*ן?Rʦj@SUEUS@@GTGG @E @@E AP@D AP@@DAP@@AP@@APD@APD@APD@T@d@@@DUD@]UEUU֛Rڦ@RUEUR@@W]W@]]@TAPT@DAPT@@A@T@@AA@T@@AA@T@AA@T@AA@D@@T@@UTA@@@\E@@@]EUDU@^]UEUU֛V@QUEUQ@@*.ana@}a}Q@dEA@T@TEA@T@PEA@T@@EA@T@EA@T@EA@T@UA@P@@TA@@UTT@E@\T@E@]UDUU@nUEUUګV@QUEUQ@@+?qq}a}Q@dEA@@TEADT@PEADT@@EADT@EADT@UQDU@AUD@A@TAT@@VETץEוEEV@Uq_EeQaNa+` ?q ?q 9a9Q@$EA@@EADT@EADT@EADT@EADT@UQETU@QUETU@%ReET@eeET~׽kEEՙV@Uq_EvwQ!Nrb+` Q?a QQ?a @9a@%Q@@$EA@@EADT@EATT@EATT@EATT@UQEUU@QUEUU@*&Rie>vӽ{IIـV@Uq_EwwQ!NsbQ+PQQ*P@@)P@@P@P@A@P@ADP@AT@A@APEQ@QUEUQ@**bji?{ݟEEՙ@GiBjd@G5Gu4?2F$;" E7" 3"QPQP@P@P@@A@@A@@A@A@AEQ@QUEUQ@**bjj?okvjjꪀvZEvZE@qGI@BD@QGESUP$!A$ #! #! #!EQ@UQUEUQ@*bjj+r@UeVFke@UeVFje@UYUUEUU@UIUUEUU@ADE@@D@ADEQT@@EQ@UQUEUQ@aVFje@aVFke@EAEU@EAEU@EAEU@EAAU@D@@@D@@E@EAEU@EAEU@EAEU@@@@@T@@TP@T!P@T!P@T!P@T!P@TP@QPQT@E@@@UUPEUUUUVeUV@uWuUW@uWuUTW@eVeUTV@UUUUTUUD%D!%D!%D!%D15D@uRu@UW@eBd@%VP5UU:몪+{+{*f@UUUUUUU@QTUU@a%XUU!5X%R15I%1!&F)#1!vF)#@ubvBig@echeߗQuߖUzz@UU]UUUU U A( A(A(@AQhU@AQhU@AhU@AdU@ATD@fVXUU@Wb%VUv'::bv':zcv%*zcWu%~ic@Wuf~ig@VegheߗUuUں@UU]UUUU U Q( Q( Q(@E QhU@E axU@!F %xU@!F$dT@%F(TD@wV|U@kb%V@[wg;;e@Zwg{e@Zv~f@Wukg@Wukg@VejeߖUeU՝ٺ@@k@n@]U]UU]U@EUX R RQbQTbUdaauqt؀w8w|E{"F@_wfU@^wf__U@Zvnf@Wukg@Wukg@VejfUUUYUօUUYՅeVX@YeWT@UYEe[T@UEe^T@]UYEU]T@EXEUX XER YERYYEbYT^ErY@tJ^Eq@qEuutw 8S<{b,@_uEYhU@^uU]YWU@Jujf@Gukg@Gukg@Vejf@UUUUUU@UEDUEU@EDTED@EDTED@EFDTIDDT DEDTT@XEUXȈXERHXERYXXEbYT]ErY@F]EqT@EuTtC8R4~b$@]uEUTU@uYUUVU@djf@tkg@kg@V䥪jf@USUUUU@UW@UU@TEG@D@XEK@D@XEK@D @IPP@TEQXȈTERTERXEbT]ErY@F]EqT@EeTdB44@m$@@]EUTU@YUUUU@dj&@tk'@kg@R䥪jf@QSUUUaW@TdBG@hFG@hG@E@P@čTEATȈTERTERUEbTUErX@EYEqTT@E%TD $@U 4@h!P|!P@DUUU@]UUUU@ej&@uk'@kg@R奪jf@SUUY!S@X44Gd$ 4@čM4P@č\EdĈXETEUE"@UE"X@DYE!TT@AE!TTA!PU!PhPP@DUT@]UUU@jj&@Vk'@kg@Rťjf@RUUU%R@@4A4GAdGA$KA$PI]A$Q@I]AAdDZAA@DWA @DGA @DVAY@DZAP@DAPDTh@@@DUD@]UUUU@Zjf@Vkg@kg@Rjf@QUUU%Q@@))WiWV@UYQ@EYETUEZEPFWE@FWE@FVE@@RED@@ADDTdTA@E@@]EUD@^]UUUU@Zjf@Vkg@Vkg@VUjf@QUUUQ@@*.ene}e}UU@hUEAPTVEBTVEB@VEB@FVEB@QEAD@A@D@A@UTT@E@\T@E@]UDUU@^UUUU@Zjf@Vkg@Vkg@FUjg@UQUUUQQ@@+a?qq}b}RW@hWEA@TVEBTVEB@REB@GREB@QUAD@AUDD@AD@ATD@@UeETmڥET]ڕEUU^UUU@[jf@Wkg@Vkg@FUjg@UQUUUQQ@@+` ?q @q @}}S@hSEATREBTREB@REB@GREB@QUETU@QUET@ReET@eETր~׹鉥EꞟU@[jf@Wkg@Vkg@FU{g@Uq_UwgQ!Nsb+ U?aQ?a@99@$EAEBEBEBGEB@UUEYU@UQUEU@*fie>vӹ{EUՙ@kjf@gkg@fkg@EU{f@UaZUwfQ!Jsb@U@*QQU@*Q@@)Q@@Q@@AA@@ABDTABTABABPQEQ@UQUEYU*jj?{ݟEEՙ@GiBjd@G)Fke$+!A$ +! E' #@EU@PEQU@P@@T@P@@T@P@@T@@@A@T@@@BDT@BT@B(@B(E)Q@UQUEiU*jj?k޷kޫW_E_Eՙ@DI@BD@DESUAEQ@UQUEUUjjjk޳VeFkYViFYUYEYYEY@DI@@D@DEQT@EQ@UQUEUUVaVFjYVaVFkY@QEAEU@QEAEU@QEAEU@AEAAU@AD@@AD@@E@EAEU@EAEU@EAEU@AT@@ATP@AT!P@AT!P@AT!P@AT!P@ATP@@P@@@UUUUP@EUUUUUTUUeVeUTVUuWuUTWUuWuUTWUeWeUTV@UUWUUTUU@QQ@QQ !0!@pAaQ@dA`@UՕPEUٕUYj{{f@UUWUUUUU@Ad@Ad@Ad@Ad@AT@UUQTUU@UQeTUU!%TUR!%E!%EC1%E@CuReAUV@BeReTUߖQUU@UUZUUUUU (! ma@ysyshcEATbBT#ǃ@#ǃ@O@UUm@UUi@*ieր>׹E枟Uՙ@Z]eiVV@V]emWV@VXemW@EUTeiW@UPUeWQE%S@TPE(U@*QQ*a@ibebdbAA@TaABDeT!T!@ , @B<`QUQ=Q@UUUEiUjjE暟Eՙ@J]AAVT@FEAWUQAauQ5@TPE$U@UQEQU@UQA@T@UUQA@T@UUQA@T@TEQAA@T@TEQABDTTAT@,@<UQ=Q@UUUEiUjjުݝE]EY@DM@B@DES@ 0@0@DE D@DEQT@DA@T@DA@T@DA@T@DA@T@DADT@D UQEQ@UUUEYUjjkVmF[YViFZY]EUY]EUY@DL@@D @@UQEQ@UUUEUUViFZYVmF[Y@QEYAEU@QEYAEU@QEYAEU@QEAAUAA@@UAE@EUAEU@QEYAEU@QEYAEUA@A@A@AAA@@@@@@@QTQ@@QTQP@QTaP@QTaP@QTaP@QTaP@QTQP@@@@@@UUQUP@E@UUQUUTUUVeUTVUWuUTWUWuUTWUWeUTV@UUWUUTUU@PU@E@PQ@E@ @0@0@$@UUQUPAE@UYQUUUYnj@UUWUUUUURR@U`V@UpV@EpV@E`U@EPD@UUQQQE@UUSQQEQA B0@Bp@PU@Bd@PTUPEUUY@UU[UeUUU ("  =sy =s@dUi%̈*T@K[UU@KeT@JdD@[8DUD@UeD@UuUTAUmqETEjpET(gpET|c0ET|&UR ETh:UQQETT=qEU,t'Ax7Bl7(:vI->緎~}|cEArB2σ2î@N¾@UUUU}@UUUUi@jfie@~vye@]UUUY@]UUUU@HL@d@D @t@4U$%4AT@9UQ4@U=VrI$-i޳y޳x޳cEA¦@qABFe1V10.p@H>`UTUQ}U@UUUUiUj@]UUUY@]UUUULP PQ !4AT@%QQ4AU%UaE$%aeaerdrbEA@EQABFUEV*@@>@UPUQ}U@UUUUiU޿랪@]]UUUY@]]UUUU  @ 0AT@A0AT@EQE AT@EQEAT@EQEAT@TEQEAT@TEQEAT@EQE@T@DPA@T@*@>UPUQ}U@UUUUUiU޿֙mf]if]@YUUUUY@I]UUUU  @@  @ @@ D@@D@@D@@@D@@@D@@@D@@D@@@PUQDUU@UUUUUUUmfY֙mfY@E]EUU@EYEUU@EUEUU@EEQU  @@@@@@@@@@UADU@QEUEUU@EYEUU@E]EUU@@@@@@@@@@@@@@@@@@QTQ@@QTQ@@QTa@@QTa@@QTa@@QTa@@QTQ@@DQ@@@@@@@@@UUQU@E@UUQUT@UURfTVUSvTWUSvTWURfTV@UeSUT@U@uSQT@0     @PEU@@@PEE@@PU@@PT@PPP@UQUQU@UUQUTQUXjXoXoXj@UeSUTAU@uSQTA0  0 0@ aC@q@C@Eu@B@Ee@A@EU@@dbYTbIU"D@TDX@T$$@UQUeY@UUQUTeYꨂ@UeWUdQU@uWQUU0 "!0c%0c@fVa$cuQd$cv$cf$cZxγxsO3 E@VDX@QTAA$A8@UUUA}Y@UUUTm]ڸ@Ue[UdUU@u[QUU0 "!0c%0c@gVadtcutϳQxϳP݀xϳՈ|޷0xw ;!E@ZFDX@UA\ @A@A$X@A8T@UUUA}T@UYUTmU@ZZ@ZW@Z[@j^@]u_Ud]U@u[UUY@h%*T"@i%>Tc@i%>Tc@kfjTtgutϷQx`݀|0Ȁ޶0̀g0̀'!@ZFX@UAT\@EE@E$hI@E8d@UYUE}@EZUUm@E^UeZ@E^UuW@U^ekf@e]enf@]u]emf@\uYUUY%@DhjT&@Di~Tg@Ti~Tg@kjTdgUdU`܀0@Z0@i[I0B@eE!A@eEEY@eA%T\V @uR@u$hRP@8tUUYT=EZT-E_dUE_tUU^t9ge^t=w]e]4=w@\eYUU]%@Dhj^&@Di~[g@Ti~Wg@TkjV@T[ZUU@dKU@J`@Z0@UU0C@e_U0B@e[U!AE@UeWEEY@eUA%T\ @u@u$hU@8tQP=a=d/Rh/Rl/=\.x=sLi8=s@LiU]%@Dj^&@DY~[g@TY~Wg@TZjV@TVYUU@TFUUU@EU`R@UU0ST_daWd_d!WtT!UYpp% uu$hU8tQ)<q:<t;Cx;Cl;,\:|,sy<-s@hUi%@ZZ%@YN[d@YNWd@@YJVe@UZUUV@E_UUUWE_dQWU_daW/<;C;Ch7(\6lCh6(9`@UTUQ}U@UUUUiUjƮ@]UUTEY@]YUUEUaq@1TD!@$T 1@T@$a1@Հ$aU $aXaXqXqBEaUA@TAAU+@:@UTUQyU@UUUUTiU޿ǾƮ@]UUTEY@]]UTEU ` p@0T@ @@ @T@AQ @T@EQE @T@EQE@T@TEQEAT@TEQEAT@TEQEAT@EQE@D@TA@D'@&@UTUQeU@UUUUTeU޿׾֝mtYidY@]UUTAU@M]UAU  @@@@DA@DA@DA@@DA@@DA@DA@A@@UTUQTUU@UUUUTUUmdY֝mtY@I]EdBT@IYEdBT@IUETAT@I]EAT  @@@QDUATA@QEUETAT@E]EdBT@I]EdBT@@@@@@@@@@@@@@@@@@@@@@@@@@AT@@AUQQ@@AUQQ@@AUQQ@@AUQQ@@AURQ@@AURQ@@ERQ@@@@@@@@@@UUADT@UUQUTPUReTVRuTWRuTGReTF@UeSUT@E@eSQT@   00@ @@PEQU@D@PEQE@D@PEE@D@PEE@T@PAAT  @UAD!X@UUQUTaYXZX_XOXJ@UeSUTAE@uSQTA0 0000 @aPq@CEPu@B@EQe@A@EQU@DTbDTbDT"ED@TJFET@PEATAA$A4@UEDA5X@UUUTe]΀ꨆ@UeWUdUU@uWQUU0 Q00A00A" @aA3PuATsEPb@EQQ@RDhʷhw:Ō@JFX@EATT @A@A$d@A4d@UEDA5h@UUUTemꪦ̀ڸ@UiWUtYU@yWQeY4!Q11R10b@cRaDcuUTdzAX˳PՀX˳|xϻŌ@J_X@E]AT\@AAATl@AAAdh@AAAxd@UYUA}h@UYUTmm@ZZ@Z[@Z[@j^@Yu[Ut]U@u[UeY@4%Q@55R@55b@gVeTdgedϷAxPـ|00!Ō@Zk\@UYAT\@UAETl@UAEdh@EAxt@E]Uy@E^Um@E_Ue^@E_Uu_@fU^Uuke@ne^Uune@]u^Ueme@uZUeY!@4UeU@5UuR@5UuR@fVeTրefUـdEـPـ @Z0C@Z0C@Z!E@UZ[EY@eUZA%T\@%UAuTl@!Uudh@aExt@UE]}@E^=E_dUE_tUYU^t9g]e^t=w]u]$=w@]uYUU!@EdUeU@EuQuR@UuQuR@UfUeV@UVUUU@TFEU@JPR@J S@U^UqC@U_U1B@U[U!EE@UUWEY@UV%T\@UuTl@Uudh@Ext@Q-}@p.=t/Rx/RlU/\e.xsMeY8s@MeYU!@EdUeU@EeUuUR@UeUuER@UeUeEVV@UUUUEUV@TE]UEUV@E]UQR@U]UaSU_aWe_!WeU!IU@UeIY@e%X\@euXl@Uudh@Ext@Q-|@p><t;Cx;Cl;\*|sMi<s@MdU!@ETUE@EeTEEP@AeTEE'P@A%TEE&Q@EYUE%R@T]UE%WE^QWU_aWRgUYg@]u\U@u%lU@%uhT@udd@Ext@Q-x@>8;C;Cl6\6l ru,r@dU@dTeE@TTuE@AUuE/Q@AUe>Q@AZY=R@@+]%WTE+T/Rx;C|;Cs}+UCXw@]pV\@pe\@ tX@tTd@Edd@Q-ѕd@=Ֆ$=C=Ch5X5\QUu Q@hU @XeD @XuD@APIuD/R@A@Ue>Q@VY=@7]$T;T;Cx7C4|7C4r}&UCxv@mV\@e\@uX@5TT@EA%TU@UZAT@_]U_]T[]TAT[UXQT[YXQYYQ@YXU@UXeE@UXuE@AEYuE/R@AEUe>Q@VY=@6](T6T6C82l4<2X8q}"Uxe@mU\@e\@]uX@]A5TU@E]A%PU@UZ]APU@Z]UPU@j]ePU@j]eAPU@TeUeDU@TmUeDU@]UUEU@MYUQ@E]eER@E]uER@EYuE+R@AEUe>Q@QY=@1Y(1l2lChaQ|aQ}a\U@iU\U@e\@]uX@]A5TU@H]A%PU@eXnAP@xYUP@xfiP@hvyAP@hvy@@hni@V@^UAV@NYUAV@F]iEFV@E]yWR@EXy+R@AETi>Q@QU=@QU(QaF@ijReT@yiVeX@yiUU\U@iUU\U@ee\@e]uX@e]A5TU@eLA%PUex||A||֪іCOQUC@FUiEFV@EUyWR@ETy+R@AETi>Q@UUU}@YUUh@UjVeUT@UjReUT@~vyE@niI@]UU]U@]UU]U@Ta\@dTTqX@dXTA1TT@dLA"PU$282<"=t;Rx+Rl/ \.x S uY4 S tUU `QXqQXQu@X&Q%DTX2QEUTTqQTE]TDaU@E]UPR@E]U`S@U_AEW@U_EW@U[UEV@TUWEUf"/r/r.⸸@P-x@p>8t;Cx;Cl; \*| SMuY< CM4U E$UXE4UXA0DX&P T2PEUT1QT]T!U@E^QV@U_AQWUBegeUYg@XeYUj%"*,r,rJ-¢@P-ёl@>Ւ,>C>Cl6C\&|C RMuUՒ,4;S8;S,6 %| sMq4 rM0 !F0 F0\Rp%`R`1pUUT1TET!@EZQ@E_EQUBUWTUUUW@TUUVZ%R4>C8>C,6B%lA rMqT rM0T !F X F \RU%`RE1pQE%T1PE%T!E*U/A+C$+$sl+Uhw@X`X`PP]EQY@P-Q\U@P=R=B=\BX5\B\%\A aMqT aY0T !V  VR%@R5AQ%T5P5T$J:Ԛ+Cx7C4|7C4s|'UCxw@\URX&BR)CS>C~nBj@TZ]APV@Z]UPU@Z]UDU@Z]UEDU@XUUUDU@leUUUmeU Qid fd VTR%@R5AQT5P T$TOT&Cx7\C4|7XC4r|&UCxv@lUR\h&R)|S>|C~hBj@dUAP@UUP@ie@@ineD@V@XiUeDU@XiUeDU@YUTEQ@YUTVTTVTR%@R5AQT5PT$T_\T&\C8qU4a|C~hBjdx|lL@X~yA@XjiA@YUAQ@Y@UATDQTAR@%@R@%@UUIUU%QYEUE$Q_UUFQaUFa@)iReT@=eReX@=uQUXU@)U\U(&R)<S><C~(Bj$8<,M\Xní@TAQ@ @TADP\@@V@\UD@V@XUE@UUYUUUU@YUUUTU@iVeUT@iReUT@nvzd@nfjh@]eUU\U@YQ\Uh R(|pS<|pC}(Bi$8<,M\oXnթ@ДBU@ @TAD@TT@@VDATUT@VDBTUU@eUmRUUU@eUfVUTV@jfjd@nvzdjꨪ@YYUU\U@YQ\UDR,D@S8@P@tA@LA`UIX[YX^YXZ@TCU@ @TATT@@DAQ@DBQumSuvUz모ꨪ@]UU\U@Q\UR,D@S8@P@t@@HA`UiUTIUiTMUTY@ TCU@ @TTT@@D A@DAŀumSuvU몬޾ۻ@]UU\U@Q\UP,@@P8@P@t@@@DQA`U@DQAPU@EQE@U@EQE@U@EQE@U@DEQEAU@DIQEBU@ PCT@ @ H@UXmSUU@UvUTUڿ۪޿ۻ֝mviefi@]UUUXU@ QUXU@@@@@@@@@@@@@ H@UXmRTU@UeUTU֙mfi֝mvi@MYEeTT@IUEeTT@QIUEUTT@A QTT@@@@QDUAP@QEUEUTT@IYEeTT@MYEeTT@AD@AD@A@A@@D@AD@AD@ADA@AEAP@AEAP@AEAP@AEAP@AEAP@AEAP@@EA@EAX@UEUEUT\@EVEUT\@EVEUTX@EVEUT\@EVEUT\@UEUEUTX@PEUA@@@UD@@@UD@@@UD@@@@U@@@@@U@@@@@@$@4Ax@EUEUUljl˻|ۻ|꟫l_WGUUX^VCA@%@%@ @ DD@@D@AD@@D@QD@@EUE@D@EUE@D@EUEDD@TEUEDT@PEYAHT@@C4@ C,484 5x@E]Eul˪wUfՄ@eBTD@eATTF F EXUX@UEQEDT@UEQEDTYYU@TEFT@PEYAXT@@C4@ C,t8t 5x@E]Eyh˪weg%Ռ@eFTH@eETT@F  T@  T@ U\ E\@EQY ET@YE E~Lieň@TJXPj@?C@?C􀀂*¸@Eu@E]}e@J@J@Z债뚺gug%@VeF)TH@VeEmTT@V mT@ iT@YU\LE\@EQYLF@~ELFLŌ@TJ\`n`?C`?C`*¸@UEu@UE]}@VE]e^@fU]Uu^@YYUuZU@i[Vu^Y@iWVu]Y@yWR%YI@VuR)TH@WeAmTT@W`mT$@`QiT$@PQYUd@QLE`@EQILB@ELCLō@TJ]n?S?S쀠*’@U)Ցe@UzՔ-Ud@Uod]@UUZdI@UeWetMV@UuGUu]V@UuWU%YU@Vu)UT@WemUT@W`mUd@V`QUd@UPQUdU@QTEa@kETYB@ELCJVHCJCJE@TJE]j?S?S쀠*’@P%ё]@P6ՒD7SX'SXU" \ey! g@]4%f@]t%Y%@t)Y@`mY@`Ud@`UUt@PUUUt@UTEaEUUBEUHCE]EV]ZYUZ@TEUUZf;S;S쀠.R@P-Q\@P>R7S'S U e| s@]0v@]pY5@p)Y$@`mY@eed@eut@UUuETeEUEYEUAUSUUUUW@TUUUVZ%B8CS8CS܀J-BR@P-Q\@`=R$=C(-S,Uel s@]0v@]p]%@p)]%@`mY@Ued@Eut@%Uu5Te5E*Aؚ#B$#$sX#Udg@XPUTPBP CPCOEBZ@PJ)\V@PKm QKmQZm\BQUU\Ba,u\q@}qu@}q]e@`]@`Y@UeD@EeD@q%Uep5Td5Ԛ"A3B434sl#Utw@XUT&B*{C?{CjBk@TFUATV@PF]UQF\TQV\TEQUTTa,dXa@}`Ye@yaY]e@` ]@P Y@U) eD@E) eD@qN)UepO5TdO5ԟ"A2]B42\C4r\"UCxv@\URXh&R*l{S>l{ChBk@TP@DDXYեhiե@iePUU@ieQUUU@dU@TUT@UUD@EUD@Q^)UUP_%UT_%]A!]A@XqUF4q\qUF4q\aUFxu@XUV\Uh&R*l{S>l{ChBkTH ʷL@X^@@XZ@UUPUE@UUQUUE@TUD@TUD@PUD@@UE@UU]UUUU@TYUUDTQ@T_UUEQ@TaUEa@YiRed@]eRed@]uQUhU@U\U(&R*,{S>,{C(Bk L@\_A@X^@U]UE@U]QUUE@UUD@UmUD@EA-U@EB)U@eU]VUU@eYUVUT@eiVfUT@UiReUT@n@nj٨@]UUUXU@Q\U( R(,pS<,pC}(Bi$((,M@\OA@XN@MUE@MQUUE@EAUD@EEiUD@EE)U@EF)UՀynWUـyfUznjꨪ@YUUU\U@IQ\UDR,D@S8@P@tA@DA`UjI@XKՙYE@XNYhV@MUiE@MQUiVE@EAeVD@IEeUD@IE!U@IF!UՀyoWU}wUԮ~ꨪ@UYUU\U@E Q\U@R,@@S8@P@tA@DQA`U@DQPU@EQPU@EQ@U@EE@U@TEUEU@TMUdU TeA TeA@e@ @e@@MA!U@M!UŀuoUuwUԞ߾ꨪ@]UU\U@ Q\UP,@@P8@P@t@DPA`T@DPAPT@EPE@T@EPE@T@EPE@T@DEPEET@DM@ET @ %%@@L!E@L!AŀumSUuvUԞھ߾֝miefi@U]UUUTU@E QUTU@@@@@@@@@@@  @L  @L UmRUTYUeUTY֙mi֝mi@MYEeTU@MUEeTU@QMUEUTT@A QQTT@@QDAUT@QEUEUTU@IYEeTU@MYEeTU@A@A@A@A@@A@A@A@@@@@@@A@AEAP@AEAP@AEAP@AEAP@AEAP@AEAP@@EA@@@EAX@UEUEUT\@EVEUT\@EVEUTX@EVEUT\EVEUT\EVEUTXEVA@D@EA@@@EA@D@@@@@@EAX@UEUEUU\jlʻ|ʻϻ˚wU՘f@UB@@@UADTAAEEEE@EUE@@@EUE@@@EUED@@EUEDP@EYAHP@B @ B($84 5x@E]Eul˪wUfՌ@UBXH@UARXTFFI\I\@EQEIDT@EEQEIDT@ EHAD@ EAD@EED@EEET@EXAXT@@B$@ B(d8d 5h@E]Euh˪wef%Ռ@UVFXH@UUE^XT@PEENXT@PEEJX\@PEEJY\@EDNI\@ETIMET@IEPIETNLI„Eń@DJT@i@)Bؔ@)B@)¸@EAUu@UE]U}倻꛺gug%@UVF*XH@UUE~XT@UUE~Xd@UUEj\l@UUEZ]lEUDNMl@ETIMF@NEMFLÈH@TJXPiP)BؔP)BЀP)‚@UEՁe@UEYm@VE]Ue^@fE]Uu^@ZYUuVUZ[VuVYjWWuU]jWW%UM@eVF*XH@eUE~XT@UUEXd@UUEXx@UUEUxEUDEhEIBOEЍLCH\L  EL@TIE\i)Bؘ)B؀)B@UEAU@UEITUHd@UUYdY@UUYtEV@UeWftEZ@UeWVuUZ@UuWV%UY@VuF*XX@WeE~XT@UEXd@UEXt@UUUUtEUUE`[DX_DXLETDEM@TEE]i)Bؘ)B؀)B@P%AU@5@4@@X%P@XU g@Xey0'@Yu5v@YuU%Uu@ua*Xd@eq~XT@UqXd@UaXt@UUUUEUUEETETDEDRRQUUV@TEUUUVe%Bؘ%B؀%B@P%A\@5@4@%P Tg d}7@]pw@]u]Tv@ui*Xe@e}~XU@U}Xd@UiXt@iUUeUU入EDERAERSEERUUW@TEVUUVJeB$C؈$C؈J)B@P)\@) Q%R%@RU@c,2l@3@}2Uv@}2]UXv@uijXe@e}~XU@U)}Xd@E)iXt@yUUuUUuFaA_BYcDUUTg@DPUTPBPC PVCKEVBK@@JUQXF@JUUAJUXQZU\AQUQ\Aa,q\Aq@}qUu@}q]U\e@ui\%@e}X@U-}Xd@E=iXd@=UT5UT5_!AԟB44sTUtw@TPUT&*&;fC;fBk@TEUATV@EUURTTRUUXERUQXEa,qXa@}q]e@}q]Te@pX@`X@U-XT@E=XT@u=UTp5UT5Aԟ!A_]@4i\4sTeUtw@T`UT&*f;fC{Bk@EՁP@Iŕ YԤDYԤ,䨄@}`YT@yaYTT@d X@XT@-XT@=XT@e=UT`%UT%Y@_!]@@XZQYDDVXiQYDRXeQUDXV@`EXU&B*fB;fC{Bk@ՁP@HX]hY@iUPUT@iUQUTT@TAXT@EXT@EX@FX@U^]VUT@T^UVDT@T^UVDPU@T^QYDPU@YeReUT@]eReUT@]UQUUXU@UUXU&B*fB;fB{Bk@@LT_@TZ@UYPD@UYQTT@VUXT@Y~XT@]F>X@]F*X@uUnWUT@uUfWUT@ueffUT@UebeUT@n@njٔ@]UUUXU@Q\U B)`B:`BzBjL@T_E@T^@U]@)D@U]QiT@VeiT@i~iT@mF>Y@mF*XՀyoWUywUznjꨪ@UUUUXUQ\UB,@B(P@dA@DQA`UсPjѕP@I@@DKE@DNQYU@M@)D@MQiT@FUiT@JYviT@N]J6Y@N]&XՀyoU}wUԿ~ꨪ@UYUUXUE Q\U@B,@@B(@P@dA@DQA`U@DQAPU@EQUPU@EQU@U@EQUE@U@DEQUUEU@DMQUhU @Ty@ Ty@@f@ @v@@NA2Y@N"XՀyoU}wUԾ߾ꨪ@U]UUXU Q\U@,@(@@$@@ @@@D@D@DE@DU DdA DeA De@f@v@@LA2T@L"PՀijUivUԾھ߾֝mviefi@U]UUUTU QUTU@@$4@L0T@L PUYQUYUUUUY֙mfi֝mvi@MYEeTU@MUEeTU@QEUEUTTQQTT@L@@L@@QHAUT@QEUEUTU@IYEeTU@MYEeTU@@@@@@@@@@@@@@A@ADAP@ADAP@ADAP@ADAP@ADAP@ADAP@@DA@@DA@@DA@EAT@UEUEUTT@EUEUTX@EUEUTX@EUEUT\@EUEUT\@EUEUTX@EUA@D@EA@@@EADTAAAAAAEAXUEUEUU\jl@ʻ|@ʻ鼀gU؊f@EA@D@EAADTEEEEEEEE@PDD@@@PD@@@PDD@@PDD@P@D@@@EAXUEUEUUX@@˪wUfՈ@ENhH@EIS|\G<,F(,JlNl@DDMDTE@PDIDT EPIHA@ EPIA@EPIE@ETEEETAD@@A@A@$E%TUEYDeT@@˪ϷUΦՌޅۅ_ˀVOVK@@UEK]|@EDOMlED METIEPIIETOLLILEHED@DEEET@@EUDT@@AATT@@AATT@@AdT@EAUeT@UEYUeU@@۪߷eަ%Ս*ff@EUE]|EUDMlOLMOLLIEED@TEET@PEUDT@EATT@EAT@EAT@UEUAU@UEUUUUVETeV@fEUeV@ZeVugug%f*jjj문@EYE]xEUDMhOLM@\HIМEЙED@TEET@PEUDT@PEATT@PEAT@PEAT@UEUAU@EEUTUDdT@UUUuTU@YUudEV@YWftEZ@YWWuU^@YWW%U^f*jjj꨸@EYUUtEUUE`@X@XHEhDIXEYEE@TEUEU@PEUDU@PEADT@PEAD@PEAD@PEAE@D@T@@TUyPE@TUy EW@TeWz0Ek@YeSV5Q{@YeSf%Uzf*jjj꨸@EiUUEeUEEdEhDEDAAEQUUE@TEUUUE@PEUTE@PETD@PETD@PETD@PETD@DARXP}WXP}gX`='@Ypw@YuiTv&**..꨸@}UUuUEŵEDEQ@QRDQUUV@TUUUV@PUTV@PTG@PBTG@PEBTF@PEQTE@DUA@hRP}RP}c,3}3@m3w@m3mTv&*:>>ꨩ@}UUuUUuFa@N@ S@UPW@@UT@@T@@T@@BT@@DBT@@DQT@DUUADTXQTU\@RTQ\@b,r\@r@}rUv@}rmUTvv&):>]>Y@=UT5UT5@N!@_@YCDUUPW@DPUP@DPP&@DPT'@DPQT'@DEQBTg@DEQATV@EAUQ@TQUQXDQUQXDa,qXDq@}q]Uu@}q]UTe&):>]>Y@u=UTp5UD5@_!@@_]Y]CDUUTW@DPUT@PT&@PT'@PQT'@DQBPg@DAPV@IU dYѤDYѤD,@}qUe@}qUTefz~F]~FY@e}VUTU@deVDDQ@eZ@@_]@@TZQYDDVDYQYDRDUQUDTV@@ETU@@AT&@@QAT'@@QBT'@DBPf@DсPV@I ]H],@}`UU@yaUTUfBzF~FnF@eZ^WUT@dZVWDD@dZVVD@U@TZQYD@U@TeReUTTeReUTTUQTUTU@AUTU@@AT&@@QAT'@@QATg@DAPf@DсP@I_L_(Z@iU U@iUtU֪z~?n*ـyoUxgWUxgfUXbfU@l㹙hiTUQTXU@EXU@@AX%@DPAX&@DPATf@DQAPf@DсPf@IN_LT_TZ@UY@,E@UYQ|Uf֪z݀۾?݀ڮ*ـyU|wUԿ|Կl㺙@jTUQTXQQA@AP@TADQAPQDPQIPQJ@QKH@QD[DQD^AYQ@E]@,D@E]Q|TfƦjw܀߮7݀ޮ*ـyY|wYԿ~쿀@TUQTUXQUQ@@T@D@APU@D@APU@E@UPU@E@U@U@E@UE@U@@E@UDU@@M@UTA @,@A @,@D, Hw<@O7|@N&hՀiUlwUԿ쿀@߮TUUUUTQUQ@@@@@@D@D@DD@DT DhA DyA Dy@f@w@@LA3T@L"PYjUXfUꚮꬺ߮@]]vhe]UfhaTUUUUTQUQDT d@ d@ ddt@@HA0TT@H PU@TXUUTU@TUUUTU@Y]fhe@]]vhe@MEeTT@IEeTT@PEEUTTQP@H@@H@@PHAUT@PEEUTT@IEeTT@MEeTT@@@@@@@@@@@@@@@@@@@@@@@@@EATEDDETEDDEX@EEUEETX@EEUEETX@EEUEETX@EEUEETX@@EUA@D@@EA@@@@EA@DA   DEATEDDETHH)h@EYi|@Eji|@jl@fEX@fD@EA@D@EAADTEE$F,J,J,IEIDEATEDDETH*@E~@F~ʊ꬀ʷE՘ Մ@EM hH@EIK|\H<,H,<< ,MEI PH PPTTEATEDDETH*@E@J˚Ϸ]ՈޅŻ[{Nj쀆j@@YE]|@EDM,DIPIOLL@ILHA@AEA@@ATA@@@AA@P@@AA@P@@AA@P@@EAUAUT@E@DUEUX*@Ek@JkۚϷYՍ*ϻk{ǫj筸@EYE]xEUDMhTOLMTO@ L@I HA@EEA@@ETA@@@EA@P@@EA@P@@EA@P@@EEUAUT@EDDUEUFTEU@VEUUVU@ZeVߺe߶eަ)Ս*߻kǫn筸@E]E]tEUDMd@TO@ MTD\H@I\EYA@DEEAD@ETA@D@EA@D@EA@D@EA@D@EU@E@EDDUDEDdD@UEUuDE@ZUDEVDEUU횗U&߻gǧn樸@EmUYEeUIDXDXIEhD@IXEYA@DEUAD@ETA@D@EA@D@EA@D@EA@D@EQA@DD(@TUy@UyW@USzk@USWk@USgUjf&{g~樨@}UUeUEdEhDEDAEUA@DEPUAE@EPA@E@EA@D@EA@D@EA@D@EQ@D@DUAhQXT}RXT}cXdS:#@YdSw@UeSfTvf&{g~樨@}UUuUEʴFDEP@PBDPUAF@DPUAF@P@F@@F@A@F@A@F@Q@E@UAhQP}RP}c#93@Y#w@Y#iTv&&;g?݀>@}UUuUEt@F`@F@B@U@F@@Q@@@@@@@@@@@@A@PQPQ",r2@mr6@mrmUT6z&f);g=?g}>fi@=UUT5U@D4@F @G@@U@U@@@Q@@@@@@@@@@@@@@@@A@@@@AA@@DAUA@TQTQT@QTQT@a,qX@q@}q]Uu@}q]UTu& f);g=?g}> fi@e=VUT`%V@D%@G!@[YQUA@U@U@@@QP@@P@@P@@AP@@AAP@@AP@EUA dYdDYdD,dD@}qUUe@}qUUTeff){g=g}nfi@UZmWUTUTZeW@DQ[UZ@Q[Y@Q@TZQUDUDYQUQDQUDU@@TU@@T@@T@@QT@@QAPV@@APV@E ]H],@}U@yՕTUҦ{gտgծf@yUUTU@xUg[DDU@xZgV@U@XZaU@U@eReUTeReUQTUTU@AUTU@@@T@@Q@T@@QAT@@QAPV@@сPV@E _L_,j@meЕ,U@iu|U֦{k߻+ު*yUV@}UUUuU@Eδ@F@EP@@PD@PD@P P!P%#`&#@Y`w@UefTve&;W?Wހ>V@}UUUuQ@Et@F`@F@@P@P@@PQPQ"c3@Yc7@YceT6ej&*;W>?W~>Vj@9UUT`%Q@D$@F @GA@P@P@@PA@PQTQPQTQPb,qPr@mqUv@mqUTvi&*;>;~*j@N)WTUPO%SDQOAGAWaA`@P@P@TAdXdDXdD,dD@}UUu@}UUTuiV*m{W>m~ij@UYWTUTYVWDQ[VVAWeADAUQ@@TA@@TAA@TAh\H\X,X@}U$e@}UdeiV}{W}i@YU۟TVXUW[DRHWVRHQUR@aQeDaPeD@TDQDQ@@TAhL (@]u(U@YuѕhUi暖}w_}iyYT*U5V5Z05%ua)50 UU*>?*U5V5Z05%ua)50 UU*?;*U5U50<5<%85!UU*;&&U%Q% UU&&UUUQUUUUDDD@DDD@@@@@@@@@DDDD@P`TDDD@**Q*Q&X"33"&7U7Y&51 \"33"&7U7Y&%! \"33""3T3X"!! h\!!!!"3T3X"!! dT"2T2X"d`!!T!X!t`TX t ` TX t `  TX u aUYua%  @@@@@@AQQU%U%Y ua)500@PPPPQQQQgfU%U%Y %%ua)500@PPPPUUfg>*U5U5Y05%ua)5B0B A@PPPPUU*>?*U5U5Y05%ea%5A0A A@PPPPUU*?;*U5U50<5<%8%@@@@@UU*;&&UU000UU&&YYUUUUYYDDDDDDDTTTDD@@PPTDDD@QqD@RQ*??D*@**V*Y&*;;D*@&7U7Y&&77D&D&7U7Y&yhX%H%D!D!D"7U7Y&ud"7U7Y&ud"6U6Y& u ` DDDDDD@"&U&Y& u `  DDDDDD@@@EUY u a  DTTTTTP@@EUYuaDTTTTTPP@AAEUYua%  DTTTTTQQAQQUUYfb)500DTTTTUQQQgfUUXfc)5C0B0Tdd eUfg>*U%U%X UQ%5B0B Tdd eU*>?*U5U5X0%%UQ%5E0E EDTeU*?;*U5U50!!EEEDDUU*;&&UUUU&&YYUUUUYYDDDDDDDTTTDDDD   XXXHHHD   RrHDRR*??H+DVZ*;;H+DD@@D**V*Z*&77D'DDHHH&'U'Z*yhX%H%D!D"DDHXX&7U7Z*ud"7U7Z* u d "7U7Z* u `  DXXXXTP"7U7Z*u` DXTP@@""""ua DXTPPP@@EUZeaDXTPPPAAEUZ fb%  TXAQQUUZZZZ VS)5C0R0 QgfUUXXYYVS%5C0R1 g>*UUXXYYVS%5F0Z1 U*>?*UUXXXXUQ!!E I! U*?;*UUXXXXTPDDDDH U*;&&UUDDDDDDDUU&&YYUUUUYYDDDDDDDUUeeeeU 4 4yYjU   Rc jU  R R *??+@DVV*;;+@D VVgwvyiY&Y7U7U'UEH\\UVgwvudTDDD#D#D#H!\\"'U'V*gwvudDD!D!D!X!\\(,,("7U7V*gwv u ` (<<("7U7V*ggvu` @@"77*Wgfe` DXTPPP@@"33"Wgfea DXdPPP@@""""V UQDXPAAV VS%  dh QQQUVVS%5C0R0 QgfUUTTUUVS%5G0Z1 g>*UUTTUUVS!1F0Z1 *>?*UUDDEEFB!!E Y! U*?;*UUDDEEEAEX U*;&&UUDDDDDDDUU&&YYUUUUYYDDDDDDDUUeeeeU 0 4yYjU# 7 6y]@DR Rc w vy](==*PTR Rc w vy](99+PT5-RRcwv yiY%Y5U5'73!\\!41!RRcgv udTDD#33!\\(,,,!01"QRccr q`@@"D2D2X!\\(<<(!0"&Q&R*Scb a ` @@!D!D!X!ll(<<(DT"77*Scb a ` ,,(44$@P"77*SSRQP DXtPPPPP"33"SS  DXPPP 00 R Q dx P$$ BBBRUdx BB%5G0Z0 UAB!1G0Z1 ?*UAB!1F0Z1 *??*UAA  E Y! U*?;*UE@@AAAEX U*;]&Y&UEUUY&]&YYUUUYYUUeeeeU 0 0uUjUE@@@RR r vy]@D  Rb b fi](==)PT$-R b f f iY)99*PT!$ 0 !R R f f eUU%U5U5'73!\\(,,,!0<1>%XTT@R /??%XTTD  !!R/a??%TD 011PQR5+Q;;%XD 001TAB%55'Q''%\D   !TAB!55&QUhhh\\ !5!!  !UUeeeeU500UUjzzjU500 ! UU]967 # D@U*??*myvw c RS$!T@U*;;*iyvw c RS,$-4,!,XLU&77&euv w c R S-5<0>%UE  %&BQQ/e??& ]E 01!PEF6/e??&]E 01!PE"67+e ]E    @E"77&ULDE"7&&UU U"&YYUUUYYQUeeeeU% QUjzzjY)44 ! QU*..*]967 #    D@U*??*myvw c RS$!T@U*;;*myvw g V W-%4!XLLHUU&77&iyvwgV W-5)4,!,hllXTDDDTTeuvwgV V%50 hllXTD@euvwgV U"1hllXTD@euvwgV U```dhl,)e-u-v*ggV UP`ppdx|<*e>e>f*gWV UPXT*e?e?f*g PUA*U;U;V*@UA&U7U7V&QQQ---% UA&U'U'V&QQQ.>>& ]ECUUTUVW>/e  ]EBEEDDDDUU*>?/e  ]EAEEDDDDUU*?;+eLDDDDDDDUU*;&&UUUU&&YYUUUUYYAEEEEUUAEJJJZY(44!AE*..*])66"   D@@@AE*??*]iRS$!D@@@AE*;;*]%y%(VW-% 4!HHHHEE&77&myVV*54!XXXXTDDDXXiyVU&54 euV U&5  euV U"!euV UPPPTXX)e-u-*V U@PPPTXXXXT*e>u>*V U@hT*e?e?*VU@@@UA*U;e;*VUAAA UA&U7U7V&VVVUQQQWfu  UAS&''&TTUUVW>/e  ]ERUUTTTTUU*>?/e  ]EQUUTTTTUU*?;+eHDPTTTTTTUU*;&&UUUU&&YYUUUUYY@DDDDDD@DHHHXT@D""""Y)%%!   @D"33"]1i54QS$ @D"33"]1i54YV*%4!DD"33"]1i54YU&54 DDDDXXY!y%$YU&50 euYU"1 euYU"!euYU@@PTTTeuYU@PPPTTTTTT)e-u-*YU@PPPTTTTTT*e>u>*YU@!addTUA*e?u?*YUAAQaqudT UAS*??*YUQQQWVeedd UAS*;;*YUUVW>.eedd T X X YER&''&YUU*>?.e T T UEQXUU*?;*eTDDDPTTXUU*;&&UU@DDDDDD@@@@@@@UU&&YYUUUUYY""""!! "33"X1X54%%Q&R$ "33"\1X54%%Y&V&%4 "33"\1h54%%Y&U&50 \1h54%%Y&U"10 T!d%$XT"1 ddXT"!dtXTdtXTeuXTPTTTTTT)e-u-)XTQTTTTTT*e>u>*YUQQQUTTTTTUAS*??*YUQQQWVUUTTTTTUAS*??*%%Y UUVW>*UUTTTTTUAR*;;*%%Z VV*>?*eeddTTTUEQ&''&%%Z VV*?;*UUDDDDDQ&%%X UU*;&&UUPPPPPP@UU&&YYUUUUYY""""!! "33"X1X1T0T1P1P"P$ "33"\1X1X0\1]5Y&U&%4 "33"\1X1X0\1]5Y&U&50 T1T1X0\1\1X"T"10 T1T1X0\\XT"1 T d XT"!TdXTddXTdtXTeuYUR)--)YUQQQA@@@@@AAS*>>*ZVRQQWVUUS*??*55[ WVVW>*UUR*??*55[ WV*>?*UUQ*;;*55[ WV*?;*UUQ&''&5l5X UU*;&&UUUU&&YYUUUUYY"""""33"DDD D1@1@"@ "33"D!D!D D1D1D"D"!0 "33"D1D1D0D1D1D"D"10 T1T1T0T1T1T"D"10 T1T1T0TTTT!1 TTTTTTT!!TTTTTTTTTTTTTTTTTTTTTeeeeeUUReuuefVVS)e-u-u)vfWW#"S*>>*5f5W W"#3>"*R*??*5f5W W"*3>3?"*Q*??*5g5W W"*3?2;"*Q*;;*5V5V "*2;!&!&!&!&  !!"   !!"000!!!DDDDDDDDDDDDDDTTTTTTDTTTTTTTUUTTTTTReeeeVWWSeeefWWW#"S)e-u-u)f%g%W W"#3 "S*e>u>u*v%g%W W"3 3 "Q*??*%g%W W"3 2"($$ "2SaaabSS SaaabSS  Q aqqbSS FIFCIBG  $$ $ $ $ $ 4 4 > >J H H HHHHZLZLRRRRBBB@@@ ooooo ϐ     0 0 0 0` ` ` @ @ @ @ @ @ @@@@                 @@@@@@Ppp p p p` p` ` ` #?@9?k)_)?-wJ,?f~LӚׁۿ_2R* ؿ )@:7<881,     ,,,C,C,C,C<c|k|k|k|j|j|~|~t~T~T[~P[~Y~Y~I~I~IVITITHTHT@T@T@D@D@D@D`D````ddRdRdBdB$B$C$C$A$A$A$@@@@@@@@@@@@B@HN@HnHnInInInInIoIoIoKoK/B/+))))))))))))909090909090989<9<1,0,0,0,0,0,,, 4 $ $ $ $ $ $ , , , ,  1 1 1 qLYaLX`HP`HP`HP`HP`HP`HP`HP`@P`@P`@P`@P`@P@AP@AP@C`        $$$$$$$$ A)@, ?s?{DǴ?@ \ &lm4)D()? F @@y@@@FCECHBG        @@ACC&C&c&c'c'c'c'c/#/'+'i'i%i%i%i%i-i=i=====9999988((٨٨W٨W٨WYWYWYWYWXGGFFBBBB@@33 1 5L 5L L l l h h h h h h h h h x ̛̛̘̚țȓȓHHH   !!!#a#a#a#aaaAAAAAAA@@@HHHHHHHH$>$ $ $$$$$$%>%>'>'>':':':':':':':/:0:: { Zy@ y@ y@ i@ I@I@I@A@A@A@A@@A@A@A@A@A@             ! !  !  ! L! 3L! 3L! 3L! 3Lڡ Lʁ H@€€ƀĀDDDEEEĂAĂADAFDAFDAFL3AFL3a^L3a^ 3a 3a 3! 1! 1  1  1 1 1 0 0     QQQ Q Q Q P X"x"xxz********                fLFHDHADHADHADHADHAD@A@@A@@AAAAAAAabQQQQQUWN'-=S/} A._q_n]?_~%Cؖy(Q#ܿ{rRxV2 ݃"ځ\&\" 8(|HDXc/$( 'X{XUpt C  % @`m@.69@AAC@       $L$L$L$L$ D$ *D$ *$.Ĥ.Ĭ&FFNL栨L2栩L2栩 :根 : * * * * .n@S"n@S"AS82AS82AS82AS82QS82R82B02B02B02B00B00B"0*0*0*0*0*0*0*0**:89;;@+D+Dx+Lx+Lx+Lp+Lp+L`+nL`+L`*L`*L@*l@*l@* h@* h@* h@*h*h*`*`*`*`ppppp p p p p 0 0Xk0Xk0Xo0Xo0 X/0 X.0 X.0 h> (> (< (< (< (<  " " " """"""#        `0`0`2`2`2`2G`rFpsFpsFpsFpsFpsFpcFPcFP cFP cBP cB0C@@0CA@0AA@0AAP0AAP0C0ЅC0ЄC0ЄC0؄c1؄c1Xc1Xc1}Yօ3S=]0$zJ 0AzI4?ҕn1n*\ -=1|,o,~\mөL`,q,3$Z |.GHqv!,(l )CX/X Bؖ0h Pj(P3' ]! f%  CMGGLII    ((:::ň:ň:(ň:,Lj:,Lj:,Lj:,:,,,,,"""""""::::::88888F(D(D(Ds(DsDsDsDqDQDQD   ``bbb#######""  IIɘHHHHHHhhhh``````6`2 222222" CCCBBB*B: : : : : : : : : : :M8M8]8]8]8]]q p p p p p p p p p 0 0     $$$$$$4000U1U1U1U1qqQQQQSSSC&[߿&w w@_U!>K?_l[ڀd *"7zGԘ~z$ |p ׄa#X`{A#5 }1"8 }3#7 *APEIHER   PPPP%% % % % ! !  !"!"a2aԲcԲss8spsp貏rp貏rp躅Rp躅ppppc### !!!!!!!!@ !@ !@ 1@ 1@ 1@ 1` 1` 1` 1` ` `F `n `n `n `n`n"n"n"nC*C*C*R*R *R (R R# R ң СССР֜ƜƄƄ0„00  @ @ @ @@@~qC~qC|qCxqBxqBxqB8qB8qBBqB!PB!PB!PB!PB!PB!BBBBccccs4s%1%1%1'1'1''''''''###(( GGggPfPfPfPnPCnPCnpC~qCo@3 P3l{@IӘi's6?Y!1,ML1SVez6ؑBN1MـL-LqDL"?L~LF/D)L$ lL,lHW HN$ /* 9588:;8     @@ @ @ @ D D D DKDKTZVZ^Z^Z^Z^^^^^^^^^^^^^^~~>>>>!!!!!!!!!!!!!!ppppppppppp`````9`999999999999999yyyy y a a a ` ` @ @ @@@@@ H X X X X X X M\ E\ E E E E E E E A AAAAAAS######       (@@@@@@@ @3=_~R o~ ?V^~R?Rb#Rh' jl0^! yM+3 ~ )QKCBUJP   """" "   A AQuXPXPXPXPXXXXXxxxx|zl         U UQQQQQAAAAA@@@@@      ` ` ` ` ` ` ```h`h`h0 `h0 `H0 `H0 `Hpi`Ipa`I`Ca`I`Ca`I`Ca` `Ca` ca` `` ` ` ``       _  s" s"s"#"#&#$#$#>$*$"$""""         ^^^^_K_KKKKKXKXMXMXX PPPPPPP@@XXXXXX P] P" P" p" r" r" s" s" [qzIMҚwٿ;;J9ۿ;Flesׁ}@;X@;H;5@_( @O-3F-w))(d#>D#DXe8+zJ3~PKp P]`k`` )DEIREDQ    @@@@@@P@P@P@@X@HX@HX@HXPxPxPDxPDxPDxP6D6D&D&DfDfDfDfDfNfΦfϦǻϦǻϦǻ;;332111111000CCCCS[__     ++ **("(")")")b)b)`=`=`=`X=`=`=`=@7@ W@ W@0000000 @ @ ....** L (LI LI LI @LIPDIPDIP@P@P@P@P@P@P@P@PDPDRRRRgg@g@c@c!!!!!!3333434 44$$.....*3+3@93 }K+{&!Yޟ1Ԁ-x@csNWlO1R~L& @l(  @L( LD& L#L) |lrEїLׁ HpRH!aO %@@ #=:499A8     ""bb!b0!r0!rp!rp!r`!r`%v`%v`tat dddddDDDDDDDDDDDTxTx\x\z\~FFoFoFoBoBo@o@g@g@G CC@@@@@@@@@@@8@@8@@8@@8@@8A@AAAAAA!! ! ! ! ! ! ! ! # &$@&$@&$&$&$&,&,, )%,K(7 ?@_l"؂ۿO:IӘVW2g&)Z)(0+' Wٞ ? FIFFDOI    00000X0X0\0\0\0\0\0\0\\~~~ ~~~~~~ ~ f f  OOOOOMEE'E'D'D'D'D@@@@@PPPPPPPPPVRRRR   h  (  (  (  (>(<((= (= (=  =  =  =  =  5  5  4 444446666666<<00000     $"d"d"d"ddddEEEEEEE     76666666п__    Sa -_$ ? &r$R~brb,޼\o}+ [ rrέ 4  1   & <;=<=E<      "**A*!A*!A*!A*!A*aC*aC aC aC aC `c`><<<<<<<<<<<<:<:<:,:$: : : :222888888888888͜͜͜qHq`s`s`s`3`3`3`3`3`2`2` ` ` ` ` ` h3h#h#h#h#h#h#!`#!@!        \^NJJJJJJJJJJJB.nhhhhhhh? 8_KzLٳ=!O BL~4"0!/  # s8 Ҁ` a# !NJJIJ>N $%%%%%%%%5577 77 .gngnf'nJb'nJb'fJb'fHb'fHb&fH"fH"fH"fH @@ @ @ @@@ CC C hChBhBHBHBHBHBHBH    @@@@ @``hhhhhhhH`H`H`     h((((88800888((hhhhhhhH@@@@@@@@@444b4"4"t"p p p ` ` @ @ @D @D @D @D ADADADALCLhhhhhhh)%+[=,k@_H,u0\TDDDDDD@@@@@@@@@````aGaGaFaFaFaFaFaFaFaFaNa!"""###!!!!!!!)9909000 000]Y]GO*&A_ ?[_9 _&b "D"1(%pF0 M ݀ ~`\! 1112171         SSSSRRRRRBBBBBBBBBBBbbbbbbbbbbb` Kkkkkkkkkkk,`,@,@,@,@(@(((((((((((((((xxxxxxxxxXPPPPPPPTHTHTHTHTHTHTH\HHHH   33333"2"0"0"0"0"0"0"0"0" " " """6VT@T@T@T@T@T@T@=__?,u- ~ppZ _" [ ucį   7;46=@9  @)@)@)@ )@ )@ )`J)`J-hJ-xHxHxHxHxHxHXHHԞԞ0ԞpԞpTpPpPtRdRfRfRfBfB5f4f$&$&$&$$$$$$$$ @@@@@@$BB""[[[[[K@PP P P P P P P P> P> p> p> p> p> p> p> p> p> * *****":::::::!:!!!!!!!aaaaaaaquTRTPTPTP\P\\\AAAAAA AA@@@@DDlDlDlDlLLLLL$ L. L. L. L. L. . ** ?!6 {?__ڀ( O( b#O'vj`R*|@Pk(}] < ܁`E o l )  j( d ] =@>@?;AH@   ````C@`C@`C@dK@dK@dK@dK@dK@dO@dO@O@`````  022333300       """""" 2 ; ;";;;;;;;;89999999;;;;;;;PPPPPPPPP@@@@@@@@@ @ @ @ @ @ A A    ""1222266&&&&&&&&&.    ((((((( ( ( ( l  @@!?؀6% OVW' =5-B-(ݿ>>?9 9 9 0 0 0 046`     FFFFF      ) ) ) ) ) 9 9 9 #Ph }J. )).@?< h~MҚׁ( !C=@7C@;  33333333;88 8 < <.N.N.j*b*b*b8c8s8s8s8Ps8Ps0Psppspps`rS`bR`bR@bP@bP@bP@"P@"@"@"@ 9 9 98D8D8D8D:D>D~D~DfDfDfDơDƩDƩDQƩcƿcƿcƿcccccc?c?bbb"""" ~~~>:::;@b;@B@B@B@B@B@@@@@@@@@@@!aaaaiiiiiHHHHHH/H/H/'''@DDDDDfnnnn~~~?"-?"-7_ѿ,EH7=wj#2#^ON)k8h@pUl(0P}Ѐ $ 5`#@BJ@CHAOB   @@@`bbb Hhh l l | |9| 1| 1~ 1~ 1~ 1~ 1 1 3 @@AA@@@@?>><<<<,$$$$$$$$$$$$$ $ $ $ d 000į0į0/0+0+0+0+0 00H H H H H  44444    (((hhh`h`h`H`H`H``$$$$$$$$$$$44444~wܿA A-))-#?5?lSם ܿ K{A5)||cΈi++"LIJEIGI  ::::::::><,,$GGGG]FݤFݥLեL"L#eL'EL'AL'AL'ALwAL}AL}AL}A@]Ap]pYpY00Y00Y00Y00Y 0X 0X 0X  @@@@@@@hhhhhhhhhhƎČĈĈĈĈdd`````!! ! !II I IIIyyyxxxpppa0a a A A A A @ o o"O"O"M"M"M""     !!!!!%Y%Y%Y%Y%YgYfYfPfBPBPBBJjjjj****//------%0022222$w$g$g$g$o$o o o (_/ 3m(/U'!?O=|_kavD>avD>aVF>!WF<)WG<)WGL,)WGLl)WGLl(WLL(WLL(W@L(G@L(C@L(ADDD@@@@@@@@@@@``bbffQfQfUfE&E&D &D! &D! &D! @&D! `&D! `&D!&D!5D85D85x>5x~ z~ zz zr zr zb Zb Zb Zb Zb Z` Z` R` B` B@ B@ @ 1c1c1c1b1b1B!B!B&B & & $ $ $$$4FF F"F2F2F2F2F2D3D3D3L3L3L3L3LH3HHHHHH@AAAA@@HHHHLLL!L!L!L!Lc c c c c c ccٿ] %} D,1% Yq" y@`@} [fY*[v5 J'zh*"rq0-Dzg4Aӕt$hH0$0f P 9 q !@67779=8    00888888888 ______WGGGGCICICICICIKKKK # ##########!   @@@@@@@@@@@677?7>7>7>7>7>7>7>7(( PRPRPRPRPRPRPRPRPRPRPRPR@R@R@R@S@C      59_ !}{A75Ԁ? NG i y5 ` >ABA@GA    A A AAAAAIY qq111143434366666&&&&&"XXXGXgXgXgXggoo''''''''''%%%%%%%``%`%`%`% % %(%88888@@@@HXXXXXXXXX X X X     @@@@````ց[FӗgO"?@;o(_ݿ('~J׀)Xl0 [ a & 95-405     $$$$$$$$$$ $L$L$L$Lq$a$aaaccccccs3[3Y3Y322 2 2 2         LLLlllnnnnnnnooooo}}}}}}}}}}}}~^ܝ^ܝ^ܝ^ܝ^ܝ^ؕؐ!!!!!!!!###7#7+5*5*5*5*5 5 5 % % % $ $ $$$HPHPHPHPHPHPDHPDHTDHTDHTdHdHdHdHdHd@d@`pxxxx8888}7 {F "#M'+F;4ZN*vb"rr-*VN{B$<8@bmQn^!{B # 5:87>6:       1!1!81!81!8q!8q8q8q8p8p(h(h(h(h*h*h   Z ^ \ \ \\\\\TAAAA@@@@@   ЎЎЎPP``      6 m        Z`ȄȔȔȔ     ```````hhhhhhjjjJ    p z0 z0 Z0Z0Z Z Z`Z`D !-< ?!m-O!߻{_aOa 'Gd %%R }08P(uSd pz ZN  @~, 8@E@!<<<><E<          AIIIiihh223333311111111111  p P P P P PRVV__________       PPPP @ @@@@@@HH@H@H@H@H@@@@@@@@@@@!!!!!!4 4 $ $ $ $ $ $ ,0-0 - - - --//$ N #?` \a #M~]&txX1 ܀`^ zG ? $@"23232@3  PP P P P P P P P P P P X X X X x x y 9 9 ) )D )D )E )U+U+U QQQQQ2Q2Q2Q2Q2Q2Q2q2p202 2    @@@@ ! ! 1 1 1 1 1 1 1      4444????????//(((((((((ȹ BBBBBBBB@@@@@@@@@&&"4"4"4"4"4"4b$8,8,(,(,(,(,(((__a ?^%/O/M [ZP!Qp"P N ?`>J ~%   #DFDEFGD    )..>>>>>>>>>8888888ffffvvvvvvvvvvVRAA $4444<<|||llLLLLLHH H H H H H H(H(((((8;;;; & & & & & & &&HHHHHHIPIPIPAPAPAPAPAPAPAA}6.7W#__X Խ_[}_տe1%(C # _ "`gϦ  $azvaia pennsylvania california washingtonfl@%#Fj"Ai!3h ,g+f*e/d>c&bEa;`8_4^%]~(\}-[|=Z{@Yz)Xy1Wx.Vw0Uv 6Tu 9St G CRs 7QBPr?Oq5Np'MoDL$Kn<Jm:Il2Hk'I<HICMR;URAS@MOENEA7D7@HJCOLT=G9>E@GNULL 0 Joined 0 |Broken|0|1 0 0 73 1 60 2 72 3 73 4 67 5 77 6 82 7 59 8 85 9 82 A 65 B 83 C 64 D 77 E 79 F 69 G 78 H 69 I 65 J 55 K 68 L 55 M 64 N 72 O 74 P 67 Q 79 R 76 S 84 T 61 U 71 V 57 W 62 X 69 Y 64 Z 71 4 linear essential -0.250000 0.750000 linear non-essential 0.000000 1.000000 linear essential 0.000000 1.000000 linear essential 0.000000 1.000000 0 1 significant elliptical 557 0.368085 0.318955 0.224753 0.109719 0.000400 0.000400 0.000400 0.000400 1 1 significant elliptical 569 0.338903 0.191509 0.243815 0.066468 0.000572 0.000400 0.000400 0.000400 2 1 significant elliptical 558 0.364835 0.252789 0.250308 0.095304 0.000400 0.000400 0.000400 0.000400 3 1 significant elliptical 518 0.365325 0.257674 0.245302 0.098342 0.000400 0.000400 0.000400 0.000400 4 1 significant elliptical 578 0.340418 0.234428 0.207471 0.090743 0.000408 0.000400 0.000400 0.000400 5 1 significant elliptical 547 0.371115 0.273065 0.239952 0.101305 0.000400 0.000400 0.000400 0.000400 6 1 significant elliptical 616 0.341321 0.281630 0.222834 0.101429 0.000433 0.000400 0.000400 0.000400 7 1 significant elliptical 570 0.428289 0.204979 0.241495 0.080167 0.000400 0.000400 0.000400 0.000400 8 1 significant elliptical 592 0.369279 0.320417 0.225388 0.104558 0.000400 0.000400 0.000400 0.000400 9 1 significant elliptical 657 0.398271 0.280918 0.222989 0.102181 0.000529 0.000400 0.000400 0.000400 A 1 significant elliptical 548 0.317704 0.259466 0.220553 0.092645 0.000400 0.000519 0.000400 0.000400 B 1 significant elliptical 554 0.366384 0.330039 0.231576 0.107718 0.000400 0.000400 0.000400 0.000400 C 1 significant elliptical 483 0.369937 0.252676 0.251310 0.105364 0.000400 0.000400 0.000400 0.000400 D 1 significant elliptical 543 0.368454 0.325995 0.229577 0.108670 0.000400 0.000400 0.000400 0.000400 E 1 significant elliptical 464 0.369217 0.275288 0.253418 0.101436 0.000400 0.000526 0.000400 0.000400 F 1 significant elliptical 503 0.427241 0.231626 0.231905 0.093354 0.000400 0.000400 0.000400 0.000400 G 1 significant elliptical 484 0.359206 0.301634 0.232373 0.109948 0.000400 0.000400 0.000400 0.000400 H 1 significant elliptical 427 0.368752 0.328477 0.230148 0.117416 0.000400 0.000400 0.000400 0.000400 I 1 significant elliptical 445 0.369900 0.216901 0.259770 0.075790 0.000400 0.000658 0.000400 0.000400 J 1 significant elliptical 438 0.319501 0.207141 0.238272 0.090022 0.000654 0.000400 0.000400 0.000400 K 1 significant elliptical 421 0.364274 0.287947 0.246929 0.100848 0.000400 0.000430 0.000400 0.000400 L 1 significant elliptical 505 0.302537 0.205753 0.243170 0.091089 0.000400 0.000400 0.000400 0.000400 M 1 significant elliptical 442 0.362601 0.305813 0.235586 0.119370 0.000400 0.001184 0.000400 0.000400 N 1 significant elliptical 450 0.370816 0.320431 0.230425 0.114418 0.000400 0.000718 0.000400 0.000400 O 1 significant elliptical 281 0.370538 0.328275 0.229426 0.123165 0.000400 0.000400 0.000400 0.000400 P 1 significant elliptical 463 0.432759 0.263275 0.216894 0.106945 0.000400 0.000400 0.000400 0.000400 Q 1 significant elliptical 347 0.367424 0.329299 0.228262 0.115521 0.000400 0.000400 0.000400 0.000400 R 1 significant elliptical 512 0.374550 0.327612 0.234108 0.108780 0.000400 0.000400 0.000400 0.000400 S 1 significant elliptical 525 0.367805 0.279581 0.244167 0.103661 0.000400 0.000468 0.000400 0.000400 T 1 significant elliptical 487 0.431677 0.204972 0.244289 0.076352 0.000400 0.000400 0.000400 0.000400 U 1 significant elliptical 493 0.370761 0.321378 0.227355 0.114953 0.000400 0.000400 0.000400 0.000400 V 1 significant elliptical 398 0.429128 0.253687 0.219987 0.095448 0.000438 0.000499 0.000400 0.000400 W 1 significant elliptical 371 0.390172 0.284089 0.238481 0.110765 0.000400 0.001210 0.000400 0.000400 X 1 significant elliptical 427 0.372219 0.269799 0.256861 0.096979 0.000400 0.000400 0.000400 0.000400 Y 1 significant elliptical 438 0.434566 0.219676 0.235160 0.078928 0.000400 0.000400 0.000400 0.000400 Z 1 significant elliptical 313 0.370744 0.251672 0.264040 0.095347 0.000400 0.000400 0.000400 0.000400 &%$#"!      # ! & % $ " &%$#"!      &%$#!      # ! & % $ " &%$#"!      # ! & % $ " openalpr_2.2.4.orig/runtime_data/postprocess/000077500000000000000000000000001266464252400214335ustar00rootroot00000000000000openalpr_2.2.4.orig/runtime_data/postprocess/au.patterns000066400000000000000000000002451266464252400236230ustar00rootroot00000000000000act [Y]@@##@ act [T]##### nsw @@##@@ nsw @@@##@ nsw [T][AR]##@@ nt @@##@@ nt [T]@#### qld ###@@@ sa [S]###@@@ tas @##@@ vic @@@### vic #@@#@@ vic @##### wa [1]@@@###openalpr_2.2.4.orig/runtime_data/postprocess/eu.patterns000066400000000000000000000034361266464252400236340ustar00rootroot00000000000000al @@###@@ ad @#### am ###@@### am ##@@### am ###@@## am ##@@## at @@??? at @@???? at @@????? at @@?????? at @??? at @???? at @????? at @?????? az ##@@### by ####@@# be #@@@### be ###@@@ ba [AEJKMOT]##[AEJKMOT]### ba [T][A]###### ba [T][T]###### ba ######[AEJKT] bg @@####@@ bg @####@@ hr @@###@@ hr @@####@@ hr @@###@ hr @@####@ cy @@@### cz #@##### cz #@@#### dk @@##### ee ###@@@ fi @@@### fr @@###@@ ge @@###@@ gi [G]####@ gr @@@#### hu @@@### is @@@## ie ##[12][CDGLTW]###### ie ##[12][CDGKLMORSTW][DEHKLMNOSWXY]###### ie ##[12][CDGLTW]##### ie ##[12][CDGKLMORSTW][DEHKLMNOSWXY]##### ie ##[12][CDGLTW]#### ie ##[12][CDGKLMORSTW][DEHKLMNOSWXY]#### ie ##[12][CDGLTW]### ie ##[12][CDGKLMORSTW][DEHKLMNOSWXY]### ie ##[CDGLTW]###### ie ##[CDGKLMORSTW][DEHKLMNOSWXY]###### ie ##[CDGLTW]##### ie ##[CDGKLMORSTW][DEHKLMNOSWXY]##### ie ##[CDGLTW]#### ie ##[CDGKLMORSTW][DEHKLMNOSWXY]#### ie ##[CDGLTW]### ie ##[CDGKLMORSTW][DEHKLMNOSWXY]### it @@###@@ kz ###@@@ lv @@#### lv @@### lv @#### lv @### li @@##### li @@#### li @@### lt @@@### lu @@#### mk @@####@@ mt @@@### md @@@@### md [CK]@@### md @@@@## md [CK]@@## md @@@@# md [CK]@@# mc ???? me @@@@### nl @@#### nl ####@@ nl ##@@## nl @@##@@ nl @@@@## nl ##@@@@ nl ##@@@# nl #@@@## nl @@###@ nl @###@@ nl @@@##@ nl @##@@@ nl #@@### nl ###@@# no @@##### no ##### pl @@##### pl @@####@ pl @@###@@ pl @@#@### pl @@#@@## pl @@@@### pl @@@##@@ pl @@@#@## pl @@@##@# pl @@@#@@# pl @@@@@## pl @@@##### pl @@@####@ pl @@@###@@ pt @@#### pt ####@@ pt ##@@## pt @@##@@ pt @@@@## pt ##@@@@ ro @@###@@@ ro @@##@@@ ro @###@@@ ro @##@@@ ru @###@@## ru @###@@### sm @#### sm @### rs @@###@@ sk @@###@@ si @@@@### es ####@@@ es @####@@@ se @@@### ch @@###### ch [APM]###### tr ##@#### tr ##@##### tr ##@@### tr ##@@#### tr ##@@@## ua @@####@@ gb @@##@@@ va [S][C][V]##### va [C][V]##### openalpr_2.2.4.orig/runtime_data/postprocess/gb.patterns000066400000000000000000000000131266464252400235770ustar00rootroot00000000000000gb @@##@@@ openalpr_2.2.4.orig/runtime_data/postprocess/kr.patterns000066400000000000000000000000121266464252400236220ustar00rootroot00000000000000kr ##@####openalpr_2.2.4.orig/runtime_data/postprocess/mx.patterns000066400000000000000000000000651266464252400236420ustar00rootroot00000000000000mx @@@#### mx @##@@@ mx @@@###@ mx @@##### mx ####@@ openalpr_2.2.4.orig/runtime_data/postprocess/readme.txt000066400000000000000000000005541266464252400234350ustar00rootroot00000000000000Each line is a possible lp pattern organized by region/state and then likelihood. The parser goes through each line and tries to match @ = any letter # = any number ? = a skip position (can be anything, but remove it if encountered) [A-FGZ] is just a single char position with specific letter requirements. In this example, the regex defines characters ABCDEFGZ openalpr_2.2.4.orig/runtime_data/postprocess/sg.patterns000066400000000000000000000000601266464252400236220ustar00rootroot00000000000000sg @[A-HJ-NP-Z][A-HJ-NP-Z]####[A-EGHJ-MPR-UX-Z] openalpr_2.2.4.orig/runtime_data/postprocess/us.patterns000066400000000000000000000054121266464252400236460ustar00rootroot00000000000000base @@@#### base @@@### base ###@@@ al #@##@## al ##@##@# al @@##### al #####@@ al #@####@ al ##@###@ al ##@#@#@ ak @@@### as #### az @@@#### az ?@@@#### az ###@@@ ar ###@@@ ar @@@### ca #@@@### ca #@@@### ca #@##### ca #####@# ca ###@@@ co ###@@@ co @@#### co @@@### co @@@#### ct #@@@@# ct ###@@@ ct ##### ct ###### ct @### ct @@### ct @@#### de ###### de ##### de #### de ### dc @@#### dc ###### fl @@@@## fl ####[GH]@ fl ###[H-Y]@@ fl @###@@ fl @##@@@ fl @###@@ fl @@@?@## fl ###?#[GH]@ fl ###?[H-Y]@@ fl @##?#@@ fl @##?@@@ fl @##?#@@ ga @@@#### ga ####@@@ ga ####@@ ga #####[Q]@ ga ###@@@ gu @@#### gu @@@#### gu @@@###@ hi [A-HJKNMPR-Z]@@### id @###### id #@##### id #@@#### id #@@@### id [A]#####[T] id [A]@####[T] id #[A]####[T] id #[A]@###[T] id [BU]#### id ####[BEFGHIJKLMNPRSTUXYZ] id ##[S][A][S] id #@@#[S] id [J]@### id #####[BCS] id ###@[E] id ##@@[E] id ##### il @###### il ##### il ###### il @@#### il @@@### il @##### il ####### il ####@ il #####@ in ###@ in ###@@ in ###@@@ in #### ia @@@### ia ###@@@ ia ####@@ ks ###@@@ ks @@@### ky ###@@@ la @@@### me ####@@ me ###@@ me ##@@ me ###@@@ ms @@@@@# ms @@@### ms @@@?### ms ##[W]## md #@@@## md #[AB]#### md @@@### md [A]###### md ##### md ###[AB][A-N][A-MY] md [AB][A-E][A-Y]##@ md @##### ma ###@@# ma #@@### ma #@@@## ma ###### ma ###@@@ ma ####@@ ma ##@@## mi @@@#### mi #@@@## mn ###@@@ mn @@@### mo @@#@#@ mo ###@@@ mo #@@##@ mt ######[A] mt #####@ mt ####@ ne #@#### ne #@@### ne ##@### ne ##@@## nv ###@@@ nv @##### nv @@#### nv @@@### nh ###### nh ####### nh @@@### nj @##@@@ nj @@@##@ nj @@###@ nj @@@#### nm @@@### nm ###@@@ nm @@@### nm @@### nm @### ny @@@#### ny @@@### ny #@@### ny @#@### ny @###@@ ny @@###@ nc @@@#### nd @@@### mp @@@### oh @@@#### oh @@##@@ ok ###@@@ or ###@@@ or @@@### or ###?@@@ or @@@?### pa @@@#### pr @@@### ri @@### ri ###### ri ##### sc @@@### sc ####@@ sd #@@### sd ##@### sd ##@@## sd ##@@@# tn ###@@@ tx @@@#### tx @##@@@ tx ###@@@ tx @@@### tx @@#@### ut @###@@ ut @###@@ ut ###@# ut ###@@@ ut @@@### ut @##?#@@ ut @##?#@@ ut ###?@# ut ###?@@@ ut @@@?### vt @@@### vt ###@### vt ##[B]## vi @@@### va @@@#### va [J-Z]@@#### va @@@### va @@#### va ####@@ va #####[JY] wa ###@@@ wa @@@#### wi ###@@@ wi @@@### wv [1-9DON]@@### wv [1-9DON]@#### wy ###### wy ####### wy ##### wy ####@ wy ###@@ wy ##@@@ ab @@@#### ab @@@### bc @@###@ bc @@#### bc @@@### bc ####@@ bc ###@@@ mb @@@### nl @@@### ns @@@### nt @@@### nu [M][L][A]### nu @@@### on @@@@### on @@##### on [G][V]@@### on @@@### on ###@@@ pe @@### qc @##@@@ qc ###@### qc @@@### qc ###@@@ sk ###@@@ sk @@@### yt @@@## openalpr_2.2.4.orig/runtime_data/region/000077500000000000000000000000001266464252400203325ustar00rootroot00000000000000openalpr_2.2.4.orig/runtime_data/region/au.xml000066400000000000000000000725631266464252400214760ustar00rootroot00000000000000 BOOST LBP 16 44 GAB 9.9500000476837158e-01 4.4999998807907104e-01 9.4999999999999996e-01 1 100 256 1 12 <_> 3 -8.9822268486022949e-01 <_> 0 -1 49 -611788592 -573485032 -117253679 1594429461 -1971287878 -22527862 -1979021157 -1626733427 -7.5738126039505005e-01 7.0481771230697632e-01 <_> 0 -1 67 -6294750 -244874989 -2130526208 1938772839 -148114893 268845060 855894827 1937767359 -8.3813470602035522e-01 5.8474522829055786e-01 <_> 0 -1 11 -1677699056 -36618160 403725789 1595490577 -1207940968 -7794569 -1911899905 -1154826022 -6.8870306015014648e-01 6.9729328155517578e-01 <_> 5 -1.1421134471893311e+00 <_> 0 -1 57 -1119506 -708121466 -612065092 -743202 -1193508626 -23197524 -2118161 -2098177 -7.4267381429672241e-01 5.3053784370422363e-01 <_> 0 -1 7 -1677572080 -1074181040 506615293 1597720656 -1640363268 -1077966123 436928508 -1088695875 -7.4100035429000854e-01 4.8635581135749817e-01 <_> 0 -1 23 -84414770 583066006 -931127296 575594756 54857728 23068739 1263612160 -134220801 -6.7841631174087524e-01 5.1256632804870605e-01 <_> 0 -1 44 -787854184 -573318064 789683645 488446228 -1155351302 -2439010 -1375170676 -552942435 -6.5471035242080688e-01 5.3409981727600098e-01 <_> 0 -1 53 -800595898 -621366522 -620510633 -749743337 -1034952182 -928062974 6801775 1074262595 -7.0143872499465942e-01 4.8587721586227417e-01 <_> 5 -9.6150821447372437e-01 <_> 0 -1 40 -541371172 -589667 1602442749 -43619 -124991250 2008760573 -123154482 -72627255 -6.7388784885406494e-01 5.1606619358062744e-01 <_> 0 -1 8 -72646400 -80015225 394858760 -14727777 -1145332839 -1145335621 992677307 -1077936129 -6.5934449434280396e-01 4.8057270050048828e-01 <_> 0 -1 41 -1060918 -12000442 453118984 -549920786 1542418820 352678917 1610051308 -2098177 -6.0796922445297241e-01 5.1345926523208618e-01 <_> 0 -1 34 -1749417744 1364332732 1493720573 1595610484 -1700261638 1970278583 1040904142 -74943796 -6.7741453647613525e-01 4.4649848341941833e-01 <_> 0 -1 24 -1089741678 -6597552 1090668753 -1626320880 -548872518 -5633400 184583099 -636825123 -5.6575518846511841e-01 5.3567945957183838e-01 <_> 6 -1.7560020685195923e+00 <_> 0 -1 22 -167773201 -2360353 -541076993 -10289 -251920405 1082327050 -529793297 1083171531 -6.7891544103622437e-01 4.0690261125564575e-01 <_> 0 -1 76 -2 998962142 -1148940788 1070267870 2035936504 958419688 1975383548 2147483647 -6.8827730417251587e-01 4.0660688281059265e-01 <_> 0 -1 16 4276 1431657684 1035952127 1571231228 -1733272834 -15402508 446622687 -1163383595 -7.0099341869354248e-01 4.0688174962997437e-01 <_> 0 -1 61 1195896586 2145385823 -2129967732 1339936015 1338232714 1627381991 -82321841 2144329695 -6.2260180711746216e-01 4.3509158492088318e-01 <_> 0 -1 26 -285737074 580841251 -2006785528 584047371 1502441728 577284943 2135905038 1677721599 -6.5793162584304810e-01 3.7901020050048828e-01 <_> 0 -1 52 -676619022 -573206503 453763057 487281681 -276129030 -5726556 -1970630945 -1742028395 -5.5209952592849731e-01 4.6138554811477661e-01 <_> 7 -1.2151374816894531e+00 <_> 0 -1 66 -85 -211815617 -268444926 -739289265 -134221270 1082119455 -208666710 1476395007 -7.2162574529647827e-01 3.6626848578453064e-01 <_> 0 -1 1 -286265718 -999299098 -391593396 -523769430 -404792576 1157058627 -244452604 -167772161 -6.0357534885406494e-01 4.5322033762931824e-01 <_> 0 -1 60 -255793462 -70257709 -254351630 -70320397 1909288463 -45121 -721424433 -754984085 -5.4790908098220825e-01 4.7318786382675171e-01 <_> 0 -1 71 -33554436 -1812277562 2119891740 334164958 2147349968 287338979 1936016656 2139095039 -7.0448672771453857e-01 3.6576277017593384e-01 <_> 0 -1 78 -828176 -604415011 -1717332707 463297429 -361047880 -623162369 -1433736440 -1163985015 -5.1334244012832642e-01 5.0787007808685303e-01 <_> 0 -1 83 -68161830 799997919 -1356582888 -1113522566 734945480 364392665 1400706424 -1 -5.3373581171035767e-01 4.6751043200492859e-01 <_> 0 -1 20 -1103673124 -548322284 1074454045 1360249113 1310589182 1429537920 -1979578680 1932215757 -6.9125539064407349e-01 3.8719448447227478e-01 <_> 7 -1.3708573579788208e+00 <_> 0 -1 70 -524289 1431323989 -39460867 1437687765 -45228340 990927545 -270731060 -1 -5.7995080947875977e-01 4.9767187237739563e-01 <_> 0 -1 9 655623184 -10511395 -4259843 -34268773 -540755556 -1078503171 -604497410 -4583798 -6.1646574735641479e-01 3.9822673797607422e-01 <_> 0 -1 48 -538188118 1608358907 1025101263 1604179967 -597894689 224255963 1576926959 1574829791 -6.2925642728805542e-01 3.6492335796356201e-01 <_> 0 -1 21 -939276798 -257753089 -489952064 -169349429 1858071503 -94705729 -213130489 1088938791 -6.5357398986816406e-01 3.3915162086486816e-01 <_> 0 -1 19 -552936270 523391216 -100696577 -543732683 -11891462 523689429 -6100737 184703099 -6.0573673248291016e-01 3.5936927795410156e-01 <_> 0 -1 28 -422589434 -423691488 -938504690 -490755185 -318770933 1647010671 1467924480 1132459647 -6.4206576347351074e-01 3.4757497906684875e-01 <_> 0 -1 69 -783944702 -214179085 1931721521 858993458 -404040966 -17077 1668546218 -11028208 -5.5039256811141968e-01 3.7237152457237244e-01 <_> 7 -1.1579054594039917e+00 <_> 0 -1 13 -4272908 -571236 -1696923651 456132792 -83965188 -12545 -1363481468 -318769923 -6.9065040349960327e-01 2.7564468979835510e-01 <_> 0 -1 31 -581968114 -540299265 -1727069430 2142976778 -287318193 -1055025 -22282321 -32965 -5.8724719285964966e-01 3.7282180786132812e-01 <_> 0 -1 50 -6038341 -640165672 463208439 421485845 -269825286 -33620748 -1708470305 -620048939 -4.1684821248054504e-01 5.3800636529922485e-01 <_> 0 -1 3 -6430 -286266012 -287322898 -319895826 1606896711 1948587903 1467434225 -184549377 -5.9499925374984741e-01 3.7343171238899231e-01 <_> 0 -1 75 -1071192570 1138221878 -46664013 2005367587 -352330769 -97783513 -905969969 1075299911 -6.4896816015243530e-01 3.2506215572357178e-01 <_> 0 -1 85 -7340034 -1549099046 843056536 1000387532 1341479372 730346793 2110204140 -68421681 -4.5823952555656433e-01 4.5764750242233276e-01 <_> 0 -1 46 -402653794 -782245121 314947160 398323455 1436373236 293425369 372627928 1946156991 -5.9321647882461548e-01 3.5472938418388367e-01 <_> 8 -1.6782619953155518e+00 <_> 0 -1 10 873553151 2004180213 -2228225 -58913 -1125475105 -983116 -1090846721 -1157632065 -5.4920834302902222e-01 4.2633989453315735e-01 <_> 0 -1 80 -681844738 1056975922 561003192 2146500470 214478076 202376324 767075576 -131073 -5.7025521993637085e-01 3.7582042813301086e-01 <_> 0 -1 55 -125150774 -8730 -545267713 -10494133 720038827 1610612715 -270345237 -344997758 -5.1887893676757812e-01 4.0973290801048279e-01 <_> 0 -1 38 -584001028 489741808 -42207235 503252372 -1657349640 -1723926019 -1680196609 153796781 -6.3692367076873779e-01 3.1177058815956116e-01 <_> 0 -1 82 -286265654 547679875 -1400445950 -1059936821 930072066 1180585997 -258477310 1941960687 -6.5258508920669556e-01 3.1520101428031921e-01 <_> 0 -1 30 -542221264 -64290 -115418115 -542109068 -1884628452 -6644857 782818812 -1205688280 -5.6938469409942627e-01 3.5461488366127014e-01 <_> 0 -1 18 -15734801 1032825246 -687726584 297774843 -1498729557 66534254 1715609562 1946157055 -5.5432200431823730e-01 3.2321223616600037e-01 <_> 0 -1 35 -1065096062 -84943881 -570294785 -1045448925 -353914625 -128196145 -273751377 -2113150457 -4.6718406677246094e-01 4.1839256882667542e-01 <_> 9 -1.8586442470550537e+00 <_> 0 -1 62 -1039938489 -36489 -348127361 -513 -88084689 -3411553 -285475073 -230950913 -5.2433896064758301e-01 4.3869677186012268e-01 <_> 0 -1 47 -68160017 -67112461 -269750015 -1576601 -69209890 289042673 -271590433 2012741631 -5.0064951181411743e-01 4.2426300048828125e-01 <_> 0 -1 74 -1 2003850580 -1108279300 389044597 2136800716 1027633524 517520350 -4194305 -4.2682534456253052e-01 4.7421252727508545e-01 <_> 0 -1 58 -1213068112 -536915939 -1080057863 1595874817 -433959436 -5054980 -341144337 -550875515 -5.4924941062927246e-01 3.6996433138847351e-01 <_> 0 -1 17 -570425850 991534916 855818035 2063621960 1785546766 1062771928 -32834882 -11535521 -5.3338468074798584e-01 3.7742823362350464e-01 <_> 0 -1 65 1755451637 -1953609758 -4228609 -37791274 -1466384897 -1348535297 -1161896257 -1549632026 -4.7544640302658081e-01 3.8450938463211060e-01 <_> 0 -1 6 -232593430 -1307839544 -1343531197 1094448996 -90448405 -299175006 -348390929 1076097603 -6.0394722223281860e-01 3.0843117833137512e-01 <_> 0 -1 29 -8394754 1073531900 196423260 755082090 2111333900 2034519740 836572127 -9 -4.8120620846748352e-01 3.7419813871383667e-01 <_> 0 -1 25 -137374758 319557367 -568726642 64821167 861795555 14684406 -1354713171 464377851 -5.6392318010330200e-01 3.4628465771675110e-01 <_> 9 -1.1646637916564941e+00 <_> 0 -1 14 -1 -76292 -857749057 -593901347 -290799697 -1090401 -1999699413 -1963987201 -5.4174631834030151e-01 3.7251868844032288e-01 <_> 0 -1 64 -8705 -537207489 -4239937 -541254337 1531993580 -4956764 -269291571 -12884219 -3.9605358242988586e-01 5.0030308961868286e-01 <_> 0 -1 59 -1073495033 -76581641 -171966465 -8396801 -117455345 -67108865 -1062207745 -803473873 -3.6505150794982910e-01 4.9429062008857727e-01 <_> 0 -1 73 -514 1071182842 -269553700 196673532 -539004706 942702557 2138922334 2147483647 -5.8632147312164307e-01 2.8380626440048218e-01 <_> 0 -1 42 -168165409 956350580 1433794553 335084340 -1997487890 134781876 -841695318 226286586 -5.1184880733489990e-01 3.7335854768753052e-01 <_> 0 -1 36 -8417330 -201338881 -1615940728 254748635 -54790209 348076479 638561215 2013248319 -5.6350380182266235e-01 2.8398692607879639e-01 <_> 0 -1 81 -587260784 -576749575 -1669606123 453170521 -83953252 -623125537 -90477824 -1706049971 -4.9625855684280396e-01 3.4869700670242310e-01 <_> 0 -1 84 -9938 696398318 1070208736 967486022 670776829 434662137 872353116 1811674871 -5.8320373296737671e-01 2.6583945751190186e-01 <_> 0 -1 5 -363141622 -239875254 -1432323398 464676619 -656156600 -655048034 -391188993 -84412465 -5.2150756120681763e-01 3.4631469845771790e-01 <_> 10 -1.2043844461441040e+00 <_> 0 -1 43 -571488022 -570436133 -281030916 -583199483 -641536514 -1615143425 -116326401 -550844145 -6.6361653804779053e-01 1.9604672491550446e-01 <_> 0 -1 12 -262145 -705568772 -175415297 -178392083 -1437799681 -1258629332 1000844015 21979147 -5.6519657373428345e-01 3.0456781387329102e-01 <_> 0 -1 51 -1342172400 -191617 -643 -36603918 -91291650 -17053217 -1577783044 -1260963209 -5.8604788780212402e-01 2.9618346691131592e-01 <_> 0 -1 54 -806359297 -4121 -1513370873 736620259 196577104 -405277098 -34120841 -1048973 -3.6587005853652954e-01 4.7734427452087402e-01 <_> 0 -1 63 1967936212 -71692 -14222346 -1345844538 -11780 -1077960993 -1615157796 791500233 -6.5954190492630005e-01 2.5595691800117493e-01 <_> 0 -1 68 -531634558 865068942 -214445902 2138308467 81786719 -5247442 -1057227777 1080554471 -5.7511496543884277e-01 2.8192704916000366e-01 <_> 0 -1 4 -1042088222 -285753242 2012049383 -245161914 -314395669 -213939586 621805471 1114071675 -6.1220860481262207e-01 2.3731665313243866e-01 <_> 0 -1 56 -1443145485 1988102498 534740469 1327645410 -342172998 -1145315953 2064154367 -232077321 -3.7831282615661621e-01 4.3669494986534119e-01 <_> 0 -1 32 -10553354 1878801516 166209754 799550440 1033640828 757621993 206579948 1610612567 -5.7260602712631226e-01 2.6549491286277771e-01 <_> 0 -1 2 -335546834 -552104052 -158802433 1929566028 180870239 1601293336 -366221073 -2892043 -4.6849688887596130e-01 3.5707718133926392e-01 <_> 10 -1.7368365526199341e+00 <_> 0 -1 72 -113 -817 -140552306 -3145873 1732237303 1981281703 -134750257 -134217729 -5.4474252462387085e-01 3.1632161140441895e-01 <_> 0 -1 39 -72655696 2013224444 -1111523331 1606092792 -341115654 2146036667 -1129779250 -4456739 -5.3460186719894409e-01 3.2928371429443359e-01 <_> 0 -1 0 -2097169 -825229530 -269562298 -831523257 -301990081 1082130175 1937224823 -134219785 -4.0298810601234436e-01 4.2096018791198730e-01 <_> 0 -1 77 -30158608 -571467276 -1168352803 429370257 -631056196 -16855555 719990476 -1968224636 -4.8689436912536621e-01 3.4727278351783752e-01 <_> 0 -1 33 1433925623 354472528 1433785845 87126399 -570561537 186816912 1572383037 229432799 -5.1943379640579224e-01 3.3485844731330872e-01 <_> 0 -1 27 -1112651588 -1109424130 1597931517 825755554 -90440 -7087629 -553719032 -659554565 -5.0690883398056030e-01 3.0206778645515442e-01 <_> 0 -1 45 -537308019 401852740 2137390506 1474785670 1724184559 1733253107 -119477265 1942486011 -4.7200435400009155e-01 3.4845206141471863e-01 <_> 0 -1 15 -827403230 1380898820 -1171928688 1594721160 1794931804 142497885 -626327570 -88430851 -6.2450623512268066e-01 2.4470937252044678e-01 <_> 0 -1 37 1998842498 -639640488 1392994901 1600123220 -771092838 -51660481 176868063 -636760815 -4.9276769161224365e-01 3.0157554149627686e-01 <_> 0 -1 79 -1060970492 -201327841 -1883509605 -2049 -941883442 -90457258 1188654255 1096937603 -6.1707288026809692e-01 2.6061785221099854e-01 <_> 0 2 1 1 <_> 0 2 1 3 <_> 0 3 14 1 <_> 0 5 2 3 <_> 1 0 2 2 <_> 1 8 12 1 <_> 2 0 2 3 <_> 2 0 9 1 <_> 2 7 14 2 <_> 3 0 3 1 <_> 3 0 5 1 <_> 3 0 9 1 <_> 3 1 5 2 <_> 3 1 9 1 <_> 3 1 9 2 <_> 3 4 11 1 <_> 4 0 5 1 <_> 4 3 13 1 <_> 4 6 2 2 <_> 4 13 7 1 <_> 5 1 11 1 <_> 6 0 1 3 <_> 6 0 3 3 <_> 6 6 1 3 <_> 6 13 10 1 <_> 7 1 3 5 <_> 7 3 1 4 <_> 8 1 6 1 <_> 8 2 1 3 <_> 8 7 1 3 <_> 9 0 6 1 <_> 9 2 11 4 <_> 9 7 1 3 <_> 9 10 5 2 <_> 10 0 8 1 <_> 11 0 1 2 <_> 11 2 3 4 <_> 11 12 10 1 <_> 11 13 5 1 <_> 12 0 7 1 <_> 12 1 7 1 <_> 12 4 2 4 <_> 12 10 4 2 <_> 12 12 7 1 <_> 12 13 8 1 <_> 13 2 6 1 <_> 13 3 3 4 <_> 13 4 6 3 <_> 13 6 6 3 <_> 13 13 8 1 <_> 13 13 9 1 <_> 14 0 2 1 <_> 14 13 8 1 <_> 15 0 2 3 <_> 15 3 4 3 <_> 16 0 4 2 <_> 16 0 8 1 <_> 17 7 3 3 <_> 17 13 6 1 <_> 18 0 1 2 <_> 18 0 2 3 <_> 18 1 3 4 <_> 19 0 1 2 <_> 20 1 1 5 <_> 20 13 4 1 <_> 21 0 3 1 <_> 21 3 3 3 <_> 21 4 3 3 <_> 23 0 2 3 <_> 23 0 4 3 <_> 23 10 2 2 <_> 24 4 2 4 <_> 24 6 3 2 <_> 24 7 2 3 <_> 24 10 2 2 <_> 25 0 2 3 <_> 26 7 2 3 <_> 26 13 5 1 <_> 26 13 6 1 <_> 27 0 2 3 <_> 27 10 2 2 <_> 29 13 5 1 <_> 30 5 1 2 <_> 30 7 1 3 <_> 34 7 1 3 <_> 35 7 1 3 openalpr_2.2.4.orig/runtime_data/region/eu.xml000066400000000000000000000701431266464252400214720ustar00rootroot00000000000000 BOOST LBP 13 52 GAB 9.9500000476837158e-01 4.4999998807907104e-01 9.4999999999999996e-01 1 100 256 1 12 <_> 4 -1.8097745180130005e+00 <_> 0 -1 40 805311953 -691727 2113524735 -2108461 -1078407169 -4473889 -1146109953 -1074185492 -8.3389264345169067e-01 6.6482132673263550e-01 <_> 0 -1 14 -1624723464 -4443984 -64703235 -1216868228 -7684673 -1070151 -1618019585 -1433916280 -7.7608370780944824e-01 5.8700811862945557e-01 <_> 0 -1 19 614727832 -1612616257 1745255677 -6475305 -1366753605 -1079144802 1002113791 -1629746758 -6.9801986217498779e-01 5.1161581277847290e-01 <_> 0 -1 45 -2147269630 -2099757 -772579841 -547884401 -609488921 -76826409 -371196929 -1039424890 -6.3432163000106812e-01 4.9822175502777100e-01 <_> 6 -1.6889376640319824e+00 <_> 0 -1 37 823136753 -2542607 1599577599 -2237985 -1147536901 -71591686 -1148470273 -1075901777 -7.8587698936462402e-01 5.9190028905868530e-01 <_> 0 -1 22 1600978680 -33696264 -1084877327 -58928 -1146963009 -5579557 -546776577 -1618474808 -6.5951651334762573e-01 5.8263260126113892e-01 <_> 0 -1 21 -462951934 13628047 -509885886 -352329529 -899942545 1078690347 -202390009 -512232577 -6.3200807571411133e-01 5.2623480558395386e-01 <_> 0 -1 51 487594460 -619080456 -1718052525 -1075865123 -71540772 -76707172 -548220929 -128836 -5.7273632287979126e-01 5.5056226253509521e-01 <_> 0 -1 60 -479993342 -527044089 -626798585 1653596839 1110959381 575397931 -77506010 1937757023 -6.4247721433639526e-01 5.0577211380004883e-01 <_> 0 -1 46 268457040 -2138636 -6980099 -34121275 -1427580418 -335560209 -1734673665 -1719069704 -6.7786401510238647e-01 4.5851442217826843e-01 <_> 5 -1.3629199266433716e+00 <_> 0 -1 78 -1270 -385885489 -1072442624 -101202993 -205784318 50579755 -223875328 -134217729 -8.0022150278091431e-01 4.2767858505249023e-01 <_> 0 -1 43 805852408 -1240592 -1178348033 1566527989 -1188320001 -67132721 -1148604417 -1077994501 -7.1916246414184570e-01 5.3092259168624878e-01 <_> 0 -1 52 -2099542 279371931 570426146 25981188 -138186334 269492775 1459827403 2013265919 -7.1977400779724121e-01 4.2900100350379944e-01 <_> 0 -1 17 -538974994 148694470 -1944156952 -1982269969 1299832780 267128979 -74908676 -1025 -6.0405021905899048e-01 5.2153891324996948e-01 <_> 0 -1 39 928529624 -202827784 2018546933 -77671227 -1633105201 -106405207 420279249 252182572 -7.9407596588134766e-01 4.4149830937385559e-01 <_> 6 -1.6914845705032349e+00 <_> 0 -1 74 -117447418 -203424957 -1333007190 -136315905 -207628529 1527491074 -273419265 -469763089 -7.6240599155426025e-01 4.2571428418159485e-01 <_> 0 -1 10 -881930242 -1081718372 759341055 -1079899620 -9704705 -10834632 -80831553 -1366686534 -7.0847225189208984e-01 4.7101745009422302e-01 <_> 0 -1 56 -65538 1059902300 1649449468 495195900 -1188287571 719866089 413712380 -17409 -6.0372650623321533e-01 5.1609545946121216e-01 <_> 0 -1 34 -1073486846 -545265809 -2101249 -134217745 -276824065 -538968073 -704659973 -1072701149 -5.2723455429077148e-01 5.9482514858245850e-01 <_> 0 -1 59 -1744826288 -71879692 -1785233955 -323460 -1164198417 -4576611 -1879442465 -1098374692 -6.6947019100189209e-01 4.8889532685279846e-01 <_> 0 -1 5 -420483410 -1594899217 -532159314 -932782337 1943533343 1696295075 -134559905 -34078725 -6.8049335479736328e-01 4.5776519179344177e-01 <_> 5 -1.0514695644378662e+00 <_> 0 -1 0 -1899833345 -1060353 -545816577 -2369025 -1953517569 -4200225 -1412707329 -1884303875 -7.1151864528656006e-01 4.1955834627151489e-01 <_> 0 -1 26 470291672 1594456488 923230163 -2155043 -548005457 -1156854119 -1148534817 -1074156579 -6.6359061002731323e-01 4.6632391214370728e-01 <_> 0 -1 73 -2098180 1406937524 18879260 1072970683 1604073656 386138325 2141787068 -1 -6.0595369338989258e-01 4.8956662416458130e-01 <_> 0 -1 48 -1065107456 -72363733 -83362849 -536886273 -615521281 -73727349 -104368129 -1039669885 -5.7387554645538330e-01 5.1478314399719238e-01 <_> 0 -1 30 -804220688 -604380848 93818999 1903284625 -1837057622 -105322486 1001658814 146809632 -6.8071007728576660e-01 4.5826134085655212e-01 <_> 7 -1.2851260900497437e+00 <_> 0 -1 76 -469762302 -406855813 -900735066 -754977809 -480248062 1467738447 -947127609 -738198529 -7.7277177572250366e-01 1.8757019937038422e-01 <_> 0 -1 41 -1879043887 -697865 -8921089 -2661417 -1141637121 -6917 -1080640513 -1077996323 -6.5664166212081909e-01 4.0080460906028748e-01 <_> 0 -1 54 523766968 1071455224 930560885 -2098947 2106400509 -1078052162 1591720959 -1075896931 -6.1509358882904053e-01 4.2310744524002075e-01 <_> 0 -1 64 -8651778 -4277508 799804080 1006344958 -14402568 410268661 2147327960 -1 -4.9080836772918701e-01 5.2360612154006958e-01 <_> 0 -1 24 1088939042 1810221070 -279576713 -404492753 1618469823 718008371 -149431041 1614276131 -6.7838019132614136e-01 3.8422819972038269e-01 <_> 0 -1 58 -1073494493 -2129 -134254593 -134219777 -134217793 -1 -306473026 -1018443994 -4.2494711279869080e-01 5.7714998722076416e-01 <_> 0 -1 33 -5221000 -1114344044 -1099382275 -319523 1782890237 -17110082 1000266751 -1909977844 -6.6518503427505493e-01 3.9107745885848999e-01 <_> 7 -1.3347427845001221e+00 <_> 0 -1 35 -1064835289 -262657 -304612481 -10753 -68160513 -72365105 -337910785 -509876305 -6.8524187803268433e-01 3.7823274731636047e-01 <_> 0 -1 6 -285278470 550824651 -1094793490 -1427444482 -12229897 1936169977 -270537745 -1281 -6.9259029626846313e-01 3.6514124274253845e-01 <_> 0 -1 50 -2 1072971740 1469594812 2147236670 1072445948 1071398300 258306045 2138439679 -6.4422893524169922e-01 3.5341870784759521e-01 <_> 0 -1 77 -1065714 254791671 -731382782 -2101936449 1842282242 2013256162 1924135744 -469762069 -6.3399165868759155e-01 3.5231015086174011e-01 <_> 0 -1 12 -561580302 -605386104 -1675307299 -35200410 -1192691478 -7641158 -581569329 -1369166102 -5.7274460792541504e-01 4.2036578059196472e-01 <_> 0 -1 55 -2138864126 -4201985 -642265613 -210241921 -19989761 -3952177 -141558833 -2095331545 -5.2967888116836548e-01 4.3784245848655701e-01 <_> 0 -1 8 -830297412 -7802014 1277567223 1068347800 2064507871 -69260897 -70362181 -1083442246 -5.0855410099029541e-01 4.3978407979011536e-01 <_> 7 -1.1835207939147949e+00 <_> 0 -1 62 -1025 -4396129 1971420605 2147480543 -72745987 999057909 998865912 -1 -6.2991225719451904e-01 4.2762723565101624e-01 <_> 0 -1 11 -1499277837 -1056769 -575146113 -3147017 -1906574593 -4199525 -1409286145 -1360048899 -5.1901364326477051e-01 4.7817090153694153e-01 <_> 0 -1 65 -1 1069088700 -42160132 2147384700 -216067 183271421 1070607356 -1 -3.9919072389602661e-01 5.6214183568954468e-01 <_> 0 -1 7 -357904642 231662471 10134269 1534886015 1336724732 1600028668 939520767 -805610049 -5.3103089332580566e-01 4.1148453950881958e-01 <_> 0 -1 27 1078457858 -137102849 -1002967209 -671383873 1475047263 1759237042 2053111259 1079494146 -6.8956327438354492e-01 3.1366679072380066e-01 <_> 0 -1 69 1543485614 532452284 465371692 1071593917 805052841 114115064 801844479 2145353727 -6.5592241287231445e-01 3.1573730707168579e-01 <_> 0 -1 70 -1621418188 -12621959 -1156064295 -1619035693 -1147405636 -72399880 264178616 162469136 -6.0899454355239868e-01 3.2693719863891602e-01 <_> 8 -1.3792396783828735e+00 <_> 0 -1 63 -896547289 -69206021 -113517697 -1052225 -68420609 -67116645 -262145 -746600513 -6.4787888526916504e-01 3.4486734867095947e-01 <_> 0 -1 16 682098471 16776910 -37761185 -67637249 -10748929 -224138833 -11537429 -356519169 -5.3469586372375488e-01 4.4898313283920288e-01 <_> 0 -1 80 -2097156 -550503779 -136588804 -1507362 -71830028 356891563 -3596 -33554441 -4.7742679715156555e-01 4.9222531914710999e-01 <_> 0 -1 31 -9447425 -18531 1463749625 1467983669 -1619632146 -1088585865 1599745195 2066940426 -4.2506697773933411e-01 5.0970840454101562e-01 <_> 0 -1 9 -554774785 469641207 -1631651073 1064967857 -689152273 -591873 -71685 -10289665 -3.9916789531707764e-01 5.2942800521850586e-01 <_> 0 -1 32 -460868 -1149758532 -1955583982 999878622 995457144 100663605 769463536 1609564143 -6.4832550287246704e-01 3.3265480399131775e-01 <_> 0 -1 61 -525614592 -73410230 -1055140389 1397186271 1796729711 -81276894 -10489481 1393553927 -5.8127319812774658e-01 3.2805305719375610e-01 <_> 0 -1 71 -69206083 2140951352 1605407413 1073482444 -1166230804 984105561 265051901 -4202499 -4.3958845734596252e-01 4.4159027934074402e-01 <_> 9 -1.6337682008743286e+00 <_> 0 -1 49 -1 -254281737 -847263745 -704649317 -547359277 -214437129 -137368609 -1025 -5.8612054586410522e-01 5.3436428308486938e-01 <_> 0 -1 3 -1964573706 -2163202 -629999617 1724277282 -1345454081 -54273 -1947226113 -134494477 -5.9594768285751343e-01 3.4550479054450989e-01 <_> 0 -1 25 -33624106 1979243351 -1684228388 -1327580354 1869634120 1507656105 1004215260 -65537 -5.5408185720443726e-01 3.4455263614654541e-01 <_> 0 -1 20 216 -273853464 -1746981891 2147439095 -1342177362 -1882457730 535869183 -1892939286 -6.8447190523147583e-01 2.6072457432746887e-01 <_> 0 -1 79 -9470 -881859202 -472124544 1722743726 2040260640 88788209 -311968000 -420088102 -5.8676868677139282e-01 3.3556821942329407e-01 <_> 0 -1 47 2147466972 2069665750 405150428 1073037215 721199016 465318864 1859398396 2139054079 -6.0411739349365234e-01 2.8773531317710876e-01 <_> 0 -1 44 -1073955720 2142794040 2033980733 -1107354732 -1141629188 -5309192 -340984065 796396953 -7.0512706041336060e-01 2.0565895736217499e-01 <_> 0 -1 1 -285218257 641662811 -317140617 -1615220229 1397455865 1830527419 -480512675 1879048055 -4.5888698101043701e-01 3.7875139713287354e-01 <_> 0 -1 67 -136335093 1363122398 -1475206391 2001653675 -275119445 1368433380 831138571 2132278783 -4.4568619132041931e-01 3.9391869306564331e-01 <_> 9 -1.6722478866577148e+00 <_> 0 -1 13 -2097665 285007871 -14702337 -1073745441 -603979783 -12805 -7606276 -33 -6.4239740371704102e-01 3.0229949951171875e-01 <_> 0 -1 68 -1297 -605028505 1663525735 -4983053 -1132728133 -326122023 -1512309265 -1049601 -3.4561732411384583e-01 6.5836638212203979e-01 <_> 0 -1 28 -1879027627 -11273 -38282337 -69730305 -1192231939 -263686 -1109656581 -1142203922 -4.3300274014472961e-01 4.2989093065261841e-01 <_> 0 -1 75 -50337789 2147446389 -16778341 1374150655 -134224126 2013265911 -285214754 -202385409 -3.8676849007606506e-01 4.9856430292129517e-01 <_> 0 -1 18 120097976 1060086728 -1389487875 -1137790177 1602117610 -1619061910 -35668997 -1343251714 -5.7169276475906372e-01 3.2476642727851868e-01 <_> 0 -1 57 -293082161 1154481003 1111976386 1447558455 1677190926 69697407 1417113986 -1553 -4.1726982593536377e-01 4.2075014114379883e-01 <_> 0 -1 29 -1313845040 -4467728 1134850749 -175787547 -1194534214 -878738628 1573022699 883187712 -6.9330018758773804e-01 2.6707106828689575e-01 <_> 0 -1 38 -78190598 -19340938 -1491289896 1809372080 524079264 491799709 1996433399 -16778277 -4.9384438991546631e-01 3.3502304553985596e-01 <_> 0 -1 2 -1562189238 -691542934 -1197225897 -421099968 198047231 -273967949 954460927 -161480843 -5.9740668535232544e-01 2.6929464936256409e-01 <_> 9 -1.4638713598251343e+00 <_> 0 -1 53 -1 -4262913 2134212095 2145352703 -1058817 993552889 1055702527 -1 -5.8213829994201660e-01 4.4301766157150269e-01 <_> 0 -1 23 -528260318 -75500601 -579380737 -2099850 -1063233 -72614673 -69469185 -948439049 -4.8428696393966675e-01 3.6954393982887268e-01 <_> 0 -1 72 -62975984 -109063308 -220856875 -212370443 -1694834769 -4560166 872043843 -1157812201 -4.9901553988456726e-01 3.3146089315414429e-01 <_> 0 -1 42 497556920 532413304 -1102144617 501201365 1535916763 1594493624 2142156779 1876574201 -6.4244377613067627e-01 2.4512745440006256e-01 <_> 0 -1 4 1120136910 -521672978 111862860 -806363025 -516557833 -670045001 1709173499 -67114049 -5.2952063083648682e-01 3.0346292257308960e-01 <_> 0 -1 36 -997733374 -206319209 -415124517 -406932517 -746852645 -7087441 -395582722 1111744578 -5.4006469249725342e-01 3.0697867274284363e-01 <_> 0 -1 15 -720467974 -541134070 -1319464207 -1162493988 -922194945 -1146112565 -741476891 -1349606460 -5.7269197702407837e-01 2.6673358678817749e-01 <_> 0 -1 81 -100667637 657118705 -1242872032 2016867655 -541072749 63672337 -136122523 -182452739 -4.3601182103157043e-01 3.6583909392356873e-01 <_> 0 -1 66 -938523136 -69889 -1720331847 -2371401 -347348081 -81010021 -646974889 56092062 -5.2380156517028809e-01 2.9095169901847839e-01 <_> 0 0 4 1 <_> 0 1 2 3 <_> 0 1 5 2 <_> 0 1 6 1 <_> 0 3 10 1 <_> 0 5 3 2 <_> 0 6 2 2 <_> 0 9 5 1 <_> 0 9 11 1 <_> 0 10 4 1 <_> 0 10 8 1 <_> 1 0 3 1 <_> 1 1 14 1 <_> 1 4 2 3 <_> 2 10 11 1 <_> 2 10 14 1 <_> 3 1 1 2 <_> 3 4 2 3 <_> 3 9 12 1 <_> 4 0 8 1 <_> 4 0 13 1 <_> 4 2 1 2 <_> 4 10 13 1 <_> 5 0 1 2 <_> 5 0 2 3 <_> 5 7 1 2 <_> 7 9 13 1 <_> 10 0 2 3 <_> 10 0 3 1 <_> 10 1 8 1 <_> 10 1 13 1 <_> 10 2 12 1 <_> 11 4 1 3 <_> 11 10 5 1 <_> 12 0 1 2 <_> 13 0 1 2 <_> 13 0 1 3 <_> 13 0 9 1 <_> 14 4 2 3 <_> 15 10 10 1 <_> 16 0 7 1 <_> 17 0 4 1 <_> 18 9 8 1 <_> 19 0 8 1 <_> 19 10 4 1 <_> 20 0 1 2 <_> 20 0 4 1 <_> 20 7 2 2 <_> 21 0 1 2 <_> 21 4 2 2 <_> 21 7 2 2 <_> 21 9 7 1 <_> 22 3 2 3 <_> 24 7 1 2 <_> 24 9 8 1 <_> 25 0 1 2 <_> 25 7 1 2 <_> 26 3 2 1 <_> 27 0 1 2 <_> 27 0 6 1 <_> 27 1 1 3 <_> 28 0 1 3 <_> 28 7 1 2 <_> 30 0 1 2 <_> 30 4 2 3 <_> 30 7 1 2 <_> 31 0 1 2 <_> 31 3 3 3 <_> 33 3 4 2 <_> 34 6 3 2 <_> 34 10 6 1 <_> 35 7 1 2 <_> 37 0 5 1 <_> 37 4 2 3 <_> 49 0 1 2 <_> 49 1 1 1 <_> 49 1 1 2 <_> 49 2 1 1 <_> 49 3 1 2 <_> 49 4 1 2 <_> 49 4 1 3 <_> 49 8 1 1 openalpr_2.2.4.orig/runtime_data/region/us.xml000066400000000000000000002612621266464252400215140ustar00rootroot00000000000000 BOOST LBP 18 36 GAB 9.9500000476837158e-01 4.4999998807907104e-01 9.4999999999999996e-01 1 100 256 1 17 <_> 5 -1.6074185371398926e+00 <_> 0 -1 260 -286277120 174374 -487661056 -1058275700 1194804992 225095 -998772480 -202375169 -5.5654716491699219e-01 8.0171042680740356e-01 <_> 0 -1 20 -342891006 -1033195986 1856252450 -1062802910 661726532 1179932483 -177793536 -134219817 -6.0683506727218628e-01 6.9047766923904419e-01 <_> 0 -1 64 1358958608 -721415659 286261721 1603863003 -619134817 -1123538802 420086683 991758991 -6.2987571954727173e-01 5.9246963262557983e-01 <_> 0 -1 193 -1073512446 -777805822 -2139684581 -783301117 -2105302838 -2139667934 1078190215 -803212537 -5.2881276607513428e-01 6.4907532930374146e-01 <_> 0 -1 125 -105079848 419692680 268435456 386400776 956826300 268962496 402653388 -1069665 -5.8266061544418335e-01 5.6521910429000854e-01 <_> 6 -1.0134286880493164e+00 <_> 0 -1 94 268435472 285218064 464917949 360560017 -1631809362 -1074249812 212339386 -1079443448 -4.5969772338867188e-01 7.2184652090072632e-01 <_> 0 -1 264 -1055230 -2132094970 1522782208 -1865488446 -160460288 426831 -239083008 -184549393 -5.5715596675872803e-01 5.5740809440612793e-01 <_> 0 -1 89 -1073512446 -750262246 1612181323 -241712057 -536370706 -87562613 -1073356338 -783818237 -4.9830266833305359e-01 5.8845627307891846e-01 <_> 0 -1 40 75531512 -1080031088 -1924518403 -1660943824 68163832 -1649934168 201603577 251658408 -6.2323546409606934e-01 4.3935534358024597e-01 <_> 0 -1 153 -1073495038 -212339694 272084497 -683317309 -1070863634 -362310394 -1013976081 -1073233397 -4.9089384078979492e-01 5.5601859092712402e-01 <_> 0 -1 187 -1072466 961592942 -592705488 287353834 2099253432 269753198 1573261038 -3146001 -4.4843783974647522e-01 5.5326616764068604e-01 <_> 7 -1.4775381088256836e+00 <_> 0 -1 90 268440029 2136817663 459096063 2147292127 496541439 -6340609 465289215 462293642 -4.9297222495079041e-01 6.8286538124084473e-01 <_> 0 -1 5 -428890622 1089466031 -1032976798 -1023422750 1114064710 1148187463 -134744065 -134219785 -4.8018595576286316e-01 5.6948053836822510e-01 <_> 0 -1 121 469767184 487588084 289153021 521930004 -1433892612 -1074227012 -1635771972 403179528 -7.1417349576950073e-01 3.9334431290626526e-01 <_> 0 -1 185 -1073511934 -742133625 1093132935 -705716410 -523770994 -521672861 -930616433 -790109557 -4.5168292522430420e-01 5.3130024671554565e-01 <_> 0 -1 80 1358954512 -3073699 285214908 -35898484 -209417729 -2616386 -1197962246 -1148441974 -5.5315750837326050e-01 4.3438988924026489e-01 <_> 0 -1 256 -486543614 15463303 1271390210 -352321538 -479201533 42978919 -135268606 -218628353 -4.9413478374481201e-01 4.6485596895217896e-01 <_> 0 -1 102 285216249 486815568 -6799425 494394865 -1885305139 -1651728472 -1633603955 -1080819456 -4.4841548800468445e-01 5.0453257560729980e-01 <_> 9 -1.5383964776992798e+00 <_> 0 -1 18 -353374678 1085269614 -292625886 -487658514 2001172311 1147598679 -680011913 -134217729 -3.9491996169090271e-01 6.2135654687881470e-01 <_> 0 -1 249 3122690 37739375 -2070166735 -741345321 -749734397 1718866259 -472912958 -419430401 -5.0506174564361572e-01 4.6618077158927917e-01 <_> 0 -1 123 1244 -1611582991 -550281217 -4384259 -1193231618 -1080312899 -1631932417 -1431828440 -5.2341967821121216e-01 4.4499680399894714e-01 <_> 0 -1 173 -1070406 998248624 224141340 993009672 931922364 471863736 1182795928 2145385471 -5.5938553810119629e-01 3.7860521674156189e-01 <_> 0 -1 147 -1073512446 -705173933 1082185555 -182463589 -408944641 -49182969 -189800481 -792205781 -4.2763561010360718e-01 4.8702400922775269e-01 <_> 0 -1 104 -19522 1003492282 1532888968 461644738 2100304008 218375113 1604668604 2147482623 -4.9799257516860962e-01 3.6454525589942932e-01 <_> 0 -1 44 285212672 -581614700 1359493625 -548332547 134266620 -37709632 2043202253 -586138712 -5.6203496456146240e-01 3.4002208709716797e-01 <_> 0 -1 128 1034951165 2105349244 -1309598211 -1120070435 -31409729 -38756688 1588345855 1065883852 -5.2008801698684692e-01 3.3988323807716370e-01 <_> 0 -1 200 -218110210 2000425110 558260 2006753352 1499734716 487590088 468989064 -3146289 -4.3743440508842468e-01 4.3907517194747925e-01 <_> 9 -1.4532921314239502e+00 <_> 0 -1 262 -1278 1124068607 -1494488320 -1056964673 -67111165 1115682655 -134224128 -134217729 -3.8763198256492615e-01 5.7781529426574707e-01 <_> 0 -1 56 -1073225214 -742661509 1082291375 -143132909 -1072969042 -1574413 -1058747766 -253237617 -3.7089401483535767e-01 5.3697389364242554e-01 <_> 0 -1 140 268435632 858788752 523386879 -1208936463 -1091764737 -4461123 704556735 -1141702479 -5.2869093418121338e-01 3.7775269150733948e-01 <_> 0 -1 230 -603992354 1471449720 -1921775488 -712594264 1598590108 206591385 1292634312 -9217 -3.9741289615631104e-01 4.6708774566650391e-01 <_> 0 -1 159 -1073496061 -246159373 -2126132421 -682133909 -338430209 -54568013 -894697569 -1056710645 -3.4375002980232239e-01 5.2743333578109741e-01 <_> 0 -1 96 -1071350 457060632 931268864 321430144 826485888 67247908 2102198728 2105540603 -4.9998486042022705e-01 3.3325645327568054e-01 <_> 0 -1 49 80 -11992040 412270047 425795985 787613322 -1085856977 -2004303873 710936064 -6.0664796829223633e-01 2.5685796141624451e-01 <_> 0 -1 194 -1037574142 -204006398 829156199 -177753533 1112262023 -232373213 1115155935 1074249730 -5.0947064161300659e-01 3.2005622982978821e-01 <_> 0 -1 79 269484248 -16708916 269484120 2013084168 8699580 522459800 -71812466 -70403128 -5.4420560598373413e-01 2.8768730163574219e-01 <_> 11 -1.3969734907150269e+00 <_> 0 -1 16 -269488594 -1527781586 -420581878 -420548914 1736406903 1199570807 -142608537 -134217729 -3.8963079452514648e-01 5.6084793806076050e-01 <_> 0 -1 51 269811920 -1085195183 425006589 1060705723 -663184132 -38933009 -1469268483 -2142977 -4.4839790463447571e-01 4.1743433475494385e-01 <_> 0 -1 228 -1039162878 -753932542 -1518107646 -139466238 -265297522 -1027372277 -420502646 1114620418 -5.1765841245651245e-01 3.3084028959274292e-01 <_> 0 -1 253 -889196793 9369379 -407120128 -235405325 -67111162 1088929783 -490739968 -218104065 -3.7889918684959412e-01 4.3052202463150024e-01 <_> 0 -1 190 -100665346 354197178 489693724 999564452 802745048 419956669 485268696 -2049 -4.2029377818107605e-01 3.9566606283187866e-01 <_> 0 -1 100 -1073233918 -136333515 1096513373 -2639093 -716180502 -1339822428 1365762947 1359476551 -4.5561933517456055e-01 3.3654430508613586e-01 <_> 0 -1 87 296230396 -575143599 -1645471619 1073517564 -1130554900 -1076347144 2124945068 -1079504776 -4.4698345661163330e-01 3.5266625881195068e-01 <_> 0 -1 112 -1073512309 -612898185 -630869569 -114037589 -622288129 -564411838 -336594433 -1056456565 -3.3337074518203735e-01 4.6491983532905579e-01 <_> 0 -1 148 143134872 -2583556 -45872131 -611282540 -2001982580 -3434534 604048076 -1094829557 -4.9170136451721191e-01 3.0917447805404663e-01 <_> 0 -1 160 -1073233918 -201853499 2136473557 -1787301069 -700452677 -818420694 -202390597 1073996290 -4.4051462411880493e-01 3.1225615739822388e-01 <_> 0 -1 113 269490512 -2517667 522163703 -537454823 -1689747461 -1074037346 -1997340673 -96204792 -4.6469467878341675e-01 2.9259225726127625e-01 <_> 13 -1.1383904218673706e+00 <_> 0 -1 23 -286265618 -286363926 -289093086 -420550110 2000123717 1886877559 2002089840 -134742185 -3.9212599396705627e-01 5.1098263263702393e-01 <_> 0 -1 129 -1073504254 -264809 -184165057 -137364109 -243010581 -17059405 -138940421 -782765113 -3.6656334996223450e-01 4.3630459904670715e-01 <_> 0 -1 263 -254 -975177841 -287868416 -454562817 -68947194 7855995 -1574144 -167773185 -5.1354819536209106e-01 3.0799564719200134e-01 <_> 0 -1 75 353377757 -36151880 -105319713 -13290732 -4419665 -3626840 -542331973 -1148712960 -3.0346295237541199e-01 5.0388520956039429e-01 <_> 0 -1 182 -67139074 997767672 671353020 1036812588 1541149116 210770921 156045544 2147483359 -4.9035164713859558e-01 2.9925996065139771e-01 <_> 0 -1 82 -1073512445 -774385513 -699687041 -716968609 -741868625 -83951421 -766260517 -1052261909 -3.2826542854309082e-01 4.3343764543533325e-01 <_> 0 -1 158 494149052 1064834428 1072696313 1062998301 980434168 -1078457388 -1075036164 462430488 -6.2883645296096802e-01 1.9601677358150482e-01 <_> 0 -1 7 -366007761 718007086 -957642206 -227808730 826762323 1149178927 2011674103 -150997001 -4.2092534899711609e-01 3.2627391815185547e-01 <_> 0 -1 219 -1068507134 1404819342 -1292973354 -2081703262 -1062212049 1521194594 1120134826 1081065738 -4.9449604749679565e-01 2.5739732384681702e-01 <_> 0 -1 109 1002320056 -1141363980 247988368 496806910 2140155836 503368365 2143886332 -1029 -4.6534663438796997e-01 2.7641868591308594e-01 <_> 0 -1 37 -1558044672 -272795331 -541372483 -138980931 -69481992 -73401925 -892597096 -1473642496 -4.3698188662528992e-01 2.9382380843162537e-01 <_> 0 -1 155 -290461950 -827336921 -1966168542 -744227044 1800381711 1112758063 818804610 -201861137 -3.7232404947280884e-01 3.4170129895210266e-01 <_> 0 -1 54 285249680 1934966666 25433949 488973060 1478098938 -1094677832 227065823 1599080840 -4.8338237404823303e-01 2.5156795978546143e-01 <_> 15 -1.4386829137802124e+00 <_> 0 -1 261 -268701949 242745343 -5243136 1660944351 -268435642 1115680639 -152043776 -134217729 -2.7799502015113831e-01 5.4982155561447144e-01 <_> 0 -1 132 8593 990033664 -576621569 -1074318441 -1158758913 -1074026283 -16908305 -1091291517 -4.0600129961967468e-01 3.6996468901634216e-01 <_> 0 -1 25 -288428034 -71050 -1362440962 -554135814 1549553644 -34515644 1676953849 -566273 -3.7318351864814758e-01 3.5906440019607544e-01 <_> 0 -1 116 -84482 999865790 642392280 430453020 486019228 176175289 503058908 1608510447 -4.9817672371864319e-01 2.6974949240684509e-01 <_> 0 -1 107 -1073495933 -69733121 -1018873637 -579344995 -989072181 -883437510 -1072890405 -1056194293 -2.8550347685813904e-01 4.5101368427276611e-01 <_> 0 -1 189 -806359506 1074709095 -486758912 1351286574 -154475059 1076360787 -184699776 -771751937 -4.4456201791763306e-01 2.7919811010360718e-01 <_> 0 -1 169 -1070874622 1933267362 -196106221 -251150048 -500175889 -357637246 -1011890229 1074511882 -4.5736011862754822e-01 2.6500344276428223e-01 <_> 0 -1 6 -276828369 -1024987189 -286285073 -159518738 1602053975 1442273271 -565281 -1 -3.1965401768684387e-01 4.1932246088981628e-01 <_> 0 -1 111 486544828 -537059988 -1751312897 -1613226148 -658465284 -543379988 -1093091841 1067977880 -4.8029872775077820e-01 2.6561823487281799e-01 <_> 0 -1 161 -1945631744 -5296883 -1268883969 -14726113 -1174757464 -1074007512 -1667299075 -1474158576 -4.5131915807723999e-01 2.6787233352661133e-01 <_> 0 -1 217 -1062748021 -241972242 1358675959 -137365053 1614802383 -85199626 -521677122 -774905909 -2.8362974524497986e-01 4.3354424834251404e-01 <_> 0 -1 240 -306184416 270597662 -610796288 283958071 -1183996 739027842 -50988400 -50855945 -4.0036740899085999e-01 2.9442140460014343e-01 <_> 0 -1 95 -554255472 1603836918 -1621489008 296493866 1348278524 419714073 1699230668 2147280872 -5.4947453737258911e-01 2.0912265777587891e-01 <_> 0 -1 206 -86788422 2131273658 776208432 -576513782 458753272 17302057 236982460 1610345454 -4.1417434811592102e-01 2.6627203822135925e-01 <_> 0 -1 225 -1998419393 1970208692 -416092289 -1078486094 -293746689 -1073807393 -1091252289 -1432214942 -3.0541145801544189e-01 3.6917164921760559e-01 <_> 16 -1.0340467691421509e+00 <_> 0 -1 21 -286527834 -1068831058 -294761950 -898699542 2004313959 1098346311 -147095568 -134742025 -2.8367474675178528e-01 4.9616107344627380e-01 <_> 0 -1 70 1346441429 -8700675 2031427039 932524345 -129231619 -57857 -899441153 -513 -3.2046657800674438e-01 4.4359135627746582e-01 <_> 0 -1 245 -2137791829 -201854321 -1042423873 -671089185 -824181265 -67108929 -1056968721 -1065106517 -2.7950826287269592e-01 4.4218274950981140e-01 <_> 0 -1 157 -77602820 2138585950 514852884 1066941396 838622680 945058236 866392280 -4129 -3.8313990831375122e-01 3.1780952215194702e-01 <_> 0 -1 243 -4721096 1023467420 -134291660 1033761695 -4210772 221878372 -50725504 -1087510340 -3.9204674959182739e-01 2.7886122465133667e-01 <_> 0 -1 167 402655256 -59502095 1016903901 968722453 413964974 -1147520884 -1157853505 -1375203816 -3.9511302113533020e-01 2.7908802032470703e-01 <_> 0 -1 259 -842019321 29087711 -1197759456 -1024271642 -84434170 10396867 -205391616 -505413633 -4.4214919209480286e-01 2.4015739560127258e-01 <_> 0 -1 83 -1072970229 -704650065 -986451969 -671629065 -491795794 -83894506 -152305989 -1044123057 -2.5662669539451599e-01 4.2095991969108582e-01 <_> 0 -1 213 -289488478 -785848987 1080164352 -752132086 1648030656 4932041 1125613966 1462761471 -4.2129451036453247e-01 2.5393015146255493e-01 <_> 0 -1 141 -2111831408 1058027000 -1412416713 -1112244144 -1196438369 -1075139649 413677738 -1421518927 -4.3579095602035522e-01 2.4805732071399689e-01 <_> 0 -1 117 974826424 2140991454 -1551889736 2106757052 974174716 525151932 2132131800 -2229265 -4.3843334913253784e-01 2.4296501278877258e-01 <_> 0 -1 48 -1073232885 -143007068 -773324933 -169909025 -276826117 -4609 -1055470354 -1073487358 -2.8845074772834778e-01 3.7775728106498718e-01 <_> 0 -1 130 4194384 -11062023 -1646789123 -10208559 696171518 -4603727 461304799 -1971060584 -4.4695761799812317e-01 2.4860441684722900e-01 <_> 0 -1 110 -8667656 523941428 96732444 1026570044 986888700 402658556 1262748664 -278561 -3.7495428323745728e-01 2.7029833197593689e-01 <_> 0 -1 174 -13638750 1864560619 -1370314720 1999282058 -817131236 4473885 -134896400 -713556481 -3.6440634727478027e-01 2.9757344722747803e-01 <_> 0 -1 233 -2036986093 -1051337 -575185507 -269560521 -1210388481 -33638407 -1460748289 -1348220030 -2.4903561174869537e-01 4.1136464476585388e-01 <_> 17 -1.0704072713851929e+00 <_> 0 -1 266 -254 -2135949377 -805569536 -1871315074 -8390908 411041775 -294650624 -521142273 -2.3864482343196869e-01 5.1211661100387573e-01 <_> 0 -1 10 -298850769 720366571 -890837393 -83888594 1601664891 1162342359 -403439649 -134217737 -3.2492494583129883e-01 3.9914098381996155e-01 <_> 0 -1 45 1426084316 -552595000 289756637 -10649621 -649527380 -1744813944 -879801361 -70522229 -3.6111223697662354e-01 3.4889730811119080e-01 <_> 0 -1 152 -805060605 -673851745 -214641903 -204344129 -355468849 -109330249 -336593441 -754728693 -2.7390027046203613e-01 4.0517389774322510e-01 <_> 0 -1 197 -553218 2080025566 15600272 1067365024 406823868 502294020 1964967166 2144335806 -4.1399970650672913e-01 2.4464462697505951e-01 <_> 0 -1 85 -538984454 492342896 799550936 423145104 1541193180 420504824 1828503544 -1050625 -3.0913692712783813e-01 3.3819082379341125e-01 <_> 0 -1 255 201271047 17141657 -371197159 -1326777353 1944023302 649588447 -1229011072 -520880129 -3.9375796914100647e-01 2.4731478095054626e-01 <_> 0 -1 205 -23878 -145515102 731382156 2144016106 1607833771 1056969892 1185290222 1602224127 -3.6536556482315063e-01 2.7690681815147400e-01 <_> 0 -1 38 -65269776 1071392190 -275117764 -1081071592 2146766812 -1107681807 2139883704 -1086447804 -3.7846565246582031e-01 2.7309983968734741e-01 <_> 0 -1 93 1543509013 -1340423 1028472191 -1081510144 887624920 -35095665 -2135409219 148906016 -4.1629123687744141e-01 2.4189476668834686e-01 <_> 0 -1 229 -269752574 -477904061 1248223232 1988291330 836748036 18970387 -415567680 -772802817 -4.1729050874710083e-01 2.2995656728744507e-01 <_> 0 -1 57 805765258 825992962 539756060 -786587074 1086266510 936117914 1994991852 938313640 -5.1461368799209595e-01 1.7466257512569427e-01 <_> 0 -1 19 -1364201690 -991697714 -433131806 -923877650 662140228 1685545751 1934037844 1702328183 -6.7833232879638672e-01 1.4472034573554993e-01 <_> 0 -1 126 -596878983 -8574020 890080733 2144151295 1294489804 -1104539940 -1880318229 -1366815844 -2.9432180523872375e-01 3.2720017433166504e-01 <_> 0 -1 177 -794106197 -616564426 -464594145 -671358534 2049886875 -285491337 1346093803 1074254850 -3.2180884480476379e-01 2.9128241539001465e-01 <_> 0 -1 143 268444179 1060451826 231817213 1896813565 825999358 234786998 -1194542085 -1137764542 -3.7007480859756470e-01 2.5912946462631226e-01 <_> 0 -1 252 -553648881 46136703 -838963448 1358618355 -159383801 1442840309 -428343678 -788529157 -2.8239414095878601e-01 3.4309053421020508e-01 <_> 19 -1.2764363288879395e+00 <_> 0 -1 9 -298848529 -353370417 -1368723733 -487657882 1731417983 1466398519 2146924503 -136316937 -1.3626074790954590e-01 5.3867846727371216e-01 <_> 0 -1 124 -1207913485 -552553001 766677247 -1145867265 -278529 -4862721 1072304059 -1410613313 -2.8374841809272766e-01 4.2133846879005432e-01 <_> 0 -1 55 -1073692672 -1314401 -1811288065 -684984521 -800407349 -88435541 -2147439990 -615789361 -3.6561635136604309e-01 3.0520385503768921e-01 <_> 0 -1 247 -2132826321 -214438017 -201326793 -389 -51400849 -69653 -1527911430 -1056967185 -2.9651319980621338e-01 3.6694592237472534e-01 <_> 0 -1 35 4194512 -2638928 -2044722799 2139958178 596410875 -1074918424 1466248191 -537957654 -3.9065396785736084e-01 2.6272973418235779e-01 <_> 0 -1 166 -71845966 2073723792 497158160 2143945512 1041642428 403710357 872230872 1607980799 -3.8319590687751770e-01 2.6842683553695679e-01 <_> 0 -1 268 -2097157 -1087391056 -5251176 -1148338189 -262727 -1969291265 -168429408 -47188481 -2.3368743062019348e-01 4.6013623476028442e-01 <_> 0 -1 223 -67653378 859369722 -1712813960 -1827098028 -1818690562 405849580 1524404938 -536872737 -2.6423501968383789e-01 3.5824209451675415e-01 <_> 0 -1 92 -750550494 1932244802 1426271509 -136875033 -526392657 -72620446 -813185125 1380447810 -3.5788068175315857e-01 2.5566586852073669e-01 <_> 0 -1 22 -294919089 1122946018 -1434007938 -28119070 1400077876 1781744245 -12065055 -526377 -3.2082986831665039e-01 2.7075409889221191e-01 <_> 0 -1 127 355473876 533732892 -1082376741 1073220956 966269432 456665256 974404281 1033897352 -5.8279770612716675e-01 1.6808734834194183e-01 <_> 0 -1 186 1108320930 1913312195 1956727103 1405871873 640671914 -1700015324 -359675449 1124845386 -4.3383055925369263e-01 1.9900636374950409e-01 <_> 0 -1 42 1152581648 -551588004 -811938313 -13125865 -369139204 -22061077 -1461089093 -935721983 -3.0062291026115417e-01 2.9034814238548279e-01 <_> 0 -1 199 -269489370 1932518286 -362551552 -246745498 1744783768 1124222739 -408492416 -772276529 -3.5581216216087341e-01 2.4337428808212280e-01 <_> 0 -1 115 -671055358 -11312698 2145976308 -742141112 -141826933 -1094435776 -1614350450 1090764867 -4.2795911431312561e-01 2.3045140504837036e-01 <_> 0 -1 210 -1448411117 1945829181 -1285333769 -5000414 1199458509 -71870659 249890952 -4243337 -2.8348302841186523e-01 3.1098461151123047e-01 <_> 0 -1 179 -903454720 -744425358 -1023026384 -745872950 -1276125254 40537699 313451211 1342948107 -4.2663165926933289e-01 2.0645493268966675e-01 <_> 0 -1 50 1359284444 -585531077 429466362 1901934459 -371400450 -166606184 1484769755 1226030794 -3.3885788917541504e-01 2.5499990582466125e-01 <_> 0 -1 59 1483004925 -1090388084 -1464555305 297539532 -8906500 -5079587 583675598 170689042 -2.8159880638122559e-01 3.0345180630683899e-01 <_> 22 -1.3054379224777222e+00 <_> 0 -1 265 -254 -2145583105 -1938034944 -2130840321 -553655040 2031487 -136905472 -452984833 -2.7614569664001465e-01 4.5691058039665222e-01 <_> 0 -1 28 -12545 -67190790 -889264898 -18821378 -598319912 -545824932 -543942692 -539304449 -2.3074461519718170e-01 4.5855847001075745e-01 <_> 0 -1 114 -1073708414 -3312390 -581578753 -585237253 -538847237 -7671817 -615792689 -582618181 -3.7242972850799561e-01 2.6750817894935608e-01 <_> 0 -1 0 -435425501 -805313558 2030041983 2104849954 1734342395 -268435729 1669330943 -280496809 -2.2564554214477539e-01 4.1358834505081177e-01 <_> 0 -1 184 141330492 -581105012 2232 -6488023 -1933800258 -1439956440 -1431525122 -14832066 -4.0444701910018921e-01 2.0259374380111694e-01 <_> 0 -1 154 -502300158 -142650766 -425559141 -772543129 -521426209 -1930702029 -353902641 1080279618 -3.9792767167091370e-01 2.1542146801948547e-01 <_> 0 -1 74 1498741759 -540935422 -147991877 -2245855 -1643344209 -74675288 -604002065 -75030520 -2.0798775553703308e-01 4.1534551978111267e-01 <_> 0 -1 14 779037479 678413167 -1360990494 -926427546 2136434543 394229691 1937503671 -135269513 -3.8600119948387146e-01 2.1943412721157074e-01 <_> 0 -1 78 2067726474 -136991756 755436448 -2516098 365527994 1000094552 1529674638 -4198449 -3.3906060457229614e-01 2.4326251447200775e-01 <_> 0 -1 208 -94704418 -577880596 1012403216 502857864 -1450010402 134218160 73403384 2070935801 -3.6030718684196472e-01 2.2849518060684204e-01 <_> 0 -1 41 20 -1476983145 -570726433 -700837 532647935 -1076147219 -3302914 751304704 -5.0285857915878296e-01 1.7315587401390076e-01 <_> 0 -1 250 -327160061 550493039 1837889286 5198839 -442765562 61852909 -1533416158 -1061163713 -3.2188731431961060e-01 2.5676867365837097e-01 <_> 0 -1 236 237827 -1625566337 -684738721 -708838809 -1031870705 -79761285 -75497793 -1001930198 -3.2259994745254517e-01 2.6575720310211182e-01 <_> 0 -1 67 -872413360 352326992 527438201 1058613553 1249525754 -1090901768 -1192209672 -1884680476 -3.9298188686370850e-01 2.1306723356246948e-01 <_> 0 -1 212 -8737144 2074281186 7026092 1372771984 -361188420 680686614 88896761 1405867375 -3.8286519050598145e-01 2.1039620041847229e-01 <_> 0 -1 221 -1162939742 1386357926 1317941504 1938564578 -579168328 685403501 1526351822 1404036351 -3.7337547540664673e-01 2.3099578917026520e-01 <_> 0 -1 101 285348049 -11430284 968767967 -1078062983 407110908 -12830052 706951641 -19647952 -3.6690136790275574e-01 2.2291134297847748e-01 <_> 0 -1 63 -2065527166 -5011633 -151545441 1033233464 -1901425192 -67122241 -1150550790 -359407070 -2.6886180043220520e-01 3.1598880887031555e-01 <_> 0 -1 239 -272630014 7263167 -675318270 2086844 -381944064 9104875 -41757440 1357905831 -6.4544874429702759e-01 1.3946346938610077e-01 <_> 0 -1 99 -804290429 -543997893 -105636419 -683035777 -878802513 -312840040 -1051884901 -250101621 -2.4588571488857269e-01 3.4430846571922302e-01 <_> 0 -1 8 -339546369 -1412792581 1615777023 -54629182 1226143607 1921996013 1440111615 2113370743 -3.0302020907402039e-01 2.6522767543792725e-01 <_> 0 -1 165 -1907792 995242656 222434520 1024884896 -425286920 188359786 1260570527 2135949294 -3.5448068380355835e-01 2.3688268661499023e-01 <_> 20 -1.1824889183044434e+00 <_> 0 -1 235 16 -1141556511 223442943 -1178299563 -2120171521 -70275846 -1130849281 0 -4.3084502220153809e-01 4.0112102031707764e-01 <_> 0 -1 3 -290459857 1691339118 -833917338 -320446462 1646752359 1853571447 1936424807 -135793289 -3.6515438556671143e-01 2.9652595520019531e-01 <_> 0 -1 216 -1059553040 -753596268 805306416 -572717025 -1999863638 -1162302592 -1362318401 -102765569 -3.0720838904380798e-01 3.1679639220237732e-01 <_> 0 -1 251 -1946165489 390070111 -1179279847 -1612709889 -21667325 737673215 -1573417332 -1073741893 -2.7648302912712097e-01 3.4295567870140076e-01 <_> 0 -1 202 -1022368734 -586554954 -848870069 -70032832 1258989227 -1075904854 1095746255 1091551490 -3.7661415338516235e-01 2.3859095573425293e-01 <_> 0 -1 58 -669035267 -2146853 428561887 -1681980139 -931332868 -67110465 1078586367 -617873729 -2.4220712482929230e-01 3.9469438791275024e-01 <_> 0 -1 172 -269568840 2074779832 2143224388 2146841138 -1411342152 710153642 375502988 2135947215 -3.8428553938865662e-01 2.2938421368598938e-01 <_> 0 -1 27 -352335105 -924422186 -321853204 1083754446 1346657621 1359424271 -78481065 -211296393 -3.3762323856353760e-01 2.4234491586685181e-01 <_> 0 -1 66 -74730830 -1719892262 -1925244624 925677442 440769672 422641780 743449532 2146957287 -3.4075874090194702e-01 2.3807466030120850e-01 <_> 0 -1 176 1601190364 331089657 352984893 1004016536 -1097580804 -1078437672 -1196287489 -1099430164 -3.7614050507545471e-01 2.2108320891857147e-01 <_> 0 -1 211 -1022639734 -610610142 -846554339 -1683757005 -1706381105 -89921561 -1070089217 1395312138 -3.3527806401252747e-01 2.3671005666255951e-01 <_> 0 -1 122 196611 -1152142945 -1621293031 2039173647 -1412433217 -360952176 -1019001861 -1056735205 -3.7603583931922913e-01 2.1857734024524689e-01 <_> 0 -1 238 -268458234 13536831 -86770910 12106874 1777826563 5182807 -536873214 31457275 -6.7599576711654663e-01 1.2590792775154114e-01 <_> 0 -1 13 335489835 -2643656 199426574 -1084804598 988528383 1503145984 1087005402 4501192 -3.8562673330307007e-01 1.8832445144653320e-01 <_> 0 -1 60 -552616270 2006492848 -1491548008 831274626 2103542712 408176996 266439336 1594227130 -3.6538988351821899e-01 2.1386267244815826e-01 <_> 0 -1 232 -1616214020 381206526 -1380008483 -1149718988 -117949220 -1109642243 -1431620166 -1395922789 -2.4237436056137085e-01 3.1383481621742249e-01 <_> 0 -1 108 -995385310 -251140753 1313566227 -642384614 -1416911446 -383105408 1936676078 1131406090 -3.3444473147392273e-01 2.3265764117240906e-01 <_> 0 -1 150 -148142336 -1969716433 1196531029 -1744832112 -2029179607 -2011647352 -1438180599 -318770533 -3.2824718952178955e-01 2.3133316636085510e-01 <_> 0 -1 136 -643080969 1461129532 834687225 901055537 448793752 -1704373864 412357071 725441173 -2.9123142361640930e-01 2.6894247531890869e-01 <_> 0 -1 1 -416832992 -13127980 -303303043 -9341920 -402666209 -4194305 -302059521 -949000704 -2.5401115417480469e-01 3.0308523774147034e-01 <_> 24 -1.1056766510009766e+00 <_> 0 -1 257 -932252672 69009174 -274731216 -1246036485 1467479344 341049303 -176687136 -50331649 -2.0472958683967590e-01 4.4878211617469788e-01 <_> 0 -1 151 32819 1058478609 356204543 -1078257961 526777855 -1682103621 1073725435 -1144048133 -3.3589541912078857e-01 2.8405088186264038e-01 <_> 0 -1 29 -78082 -77826 -269291522 -40235266 1260129788 -763028 -538161153 -805679106 -2.7084660530090332e-01 3.1221374869346619e-01 <_> 0 -1 192 -788275069 -70040829 -705953801 -586702026 -745545985 -74747888 -933764645 -263989757 -2.2504505515098572e-01 3.5947087407112122e-01 <_> 0 -1 73 -120123236 2132770214 -35818179 1908110615 -121884484 -1414604912 818846378 137098400 -3.5432848334312439e-01 2.0587421953678131e-01 <_> 0 -1 15 -433951198 -819277842 -290550674 -1495268700 1734361719 2117276675 669475411 1979151479 -5.8497852087020874e-01 1.4614424109458923e-01 <_> 0 -1 98 -211311702 -156525938 -1508441816 1002025790 -1747079220 -1971316467 902235131 -45400665 -3.0112090706825256e-01 2.6494047045707703e-01 <_> 0 -1 175 -4540164 2066654612 223217726 1023881400 -335898184 168564521 54796540 2140929499 -3.9817783236503601e-01 1.8089868128299713e-01 <_> 0 -1 43 270254167 939505587 -472385025 -167779321 603961850 2138655772 -1806172161 1095753738 -2.7771857380867004e-01 2.6211205124855042e-01 <_> 0 -1 131 188 890515783 -1928298757 1032347002 946387198 -44553749 -1148281121 201859080 -5.2568686008453369e-01 1.4650578796863556e-01 <_> 0 -1 254 -603984625 180320127 -637804776 -1802184225 -118623484 78303441 -223629692 -1060380693 -2.9106634855270386e-01 2.5167012214660645e-01 <_> 0 -1 198 -420486590 1916956719 -2075873214 -1293260342 1409777995 1074266893 -2103578117 1427624199 -4.6088227629661560e-01 1.4865124225616455e-01 <_> 0 -1 71 1560287376 1501872576 93330197 -149922831 -6426675 1497631401 1236609707 1028467595 -4.3222665786743164e-01 1.5400531888008118e-01 <_> 0 -1 204 -427301504 -204573308 -278003456 1368326670 -1675434308 38732356 37807304 1369431486 -3.7656742334365845e-01 1.8921722471714020e-01 <_> 0 -1 120 899160056 965489400 861681915 536155580 597171592 505688760 945037276 799547400 -5.5298405885696411e-01 1.3539013266563416e-01 <_> 0 -1 26 -294654417 -357580018 -456331548 -121186622 1626432311 1978081807 -500149913 -489160841 -3.3190330862998962e-01 2.2509172558784485e-01 <_> 0 -1 76 -72703744 993173512 -1413994056 434274880 2065667486 169890832 15967450 2070403071 -3.6797395348548889e-01 1.9423931837081909e-01 <_> 0 -1 156 1811669538 1083351686 -1835457886 268618330 2111253504 121815302 -941760343 1934852079 -5.0998413562774658e-01 1.3428133726119995e-01 <_> 0 -1 164 -133250046 -736681242 178479257 -772903362 -64228357 -1360092638 1128132254 1078673546 -3.5058033466339111e-01 2.0806281268596649e-01 <_> 0 -1 242 -1983656160 537984560 -576718280 -255984996 -801968 749519076 2056507520 4185519 -4.2675125598907471e-01 1.6700066626071930e-01 <_> 0 -1 62 -919302320 -82812016 1049935357 -1143920043 -1613181556 -1386341991 264245176 -1956298560 -3.1545698642730713e-01 2.4012765288352966e-01 <_> 0 -1 86 -297796297 1563704656 -1347412235 2108997601 -2007646019 -1398784592 804001469 1878972927 -2.1169832348823547e-01 3.2915911078453064e-01 <_> 0 -1 188 -1956730190 2139632728 797444224 535265612 718943472 179072316 1177307790 1497229291 -3.9158722758293152e-01 1.7950470745563507e-01 <_> 0 -1 12 -1423824222 -347933442 -473527329 -367885210 1951885175 2080068088 -23110657 2004083703 -4.2160919308662415e-01 1.6656816005706787e-01 <_> 25 -1.0476776361465454e+00 <_> 0 -1 248 -931197947 2139053339 -29360641 -134217729 -50673106 -1048609 -2136277334 -1459880677 -2.0601851865649223e-02 5.8408087491989136e-01 <_> 0 -1 241 -8390896 872469788 -75498736 1010106133 -93323368 -1937197636 -67117652 -1136721921 -3.3375686407089233e-01 2.8437638282775879e-01 <_> 0 -1 144 402661681 -8388673 2100678583 -2238601 602925055 -13914198 1068230655 -98610345 -2.8332126140594482e-01 3.0587852001190186e-01 <_> 0 -1 65 1359009236 -1114605582 -1214450753 2063676794 1549031932 -5408068 2023930863 1051026088 -3.4133437275886536e-01 2.3669779300689697e-01 <_> 0 -1 6 -299372689 -1505047345 -292656826 -957421105 1718830967 1097560413 2140605295 -135790637 -3.7768480181694031e-01 1.7987045645713806e-01 <_> 0 -1 97 -1073154 1004322294 -1147662916 -10115876 1834274296 759698865 1273497048 -1048630 -2.6975196599960327e-01 2.8011119365692139e-01 <_> 0 -1 81 -335546486 -150033533 105915952 -138218202 -666660472 -2143208063 -243365219 -581436677 -2.9663780331611633e-01 2.5062835216522217e-01 <_> 0 -1 227 -1073233917 -267476830 -2122911785 -177815006 -800076401 -352392257 -2065963301 -793255169 -2.5752902030944824e-01 2.9005941748619080e-01 <_> 0 -1 178 387383978 1968913403 1067585192 -12769396 962213582 1601127733 -1278178594 1024198792 -4.5044946670532227e-01 1.7661906778812408e-01 <_> 0 -1 36 16786801 -1644201986 -653400577 -4230181 -1460667943 -74949445 -1349652517 -1918893952 -3.2200109958648682e-01 2.2200360894203186e-01 <_> 0 -1 201 -873492986 1332382499 -287153536 1365960970 1570751688 86400343 294895824 389283743 -4.3344420194625854e-01 1.6486145555973053e-01 <_> 0 -1 39 -550462977 497814994 165283325 939408050 272107772 -1661714196 1559500029 1557690863 -3.0110406875610352e-01 2.3946200311183929e-01 <_> 0 -1 24 -320674066 -353966197 -354228484 -621876062 1547785717 1505060372 1978947829 2002219895 -4.4369262456893921e-01 1.7294771969318390e-01 <_> 0 -1 162 -360529920 -79617826 848794457 331310642 -1987053061 -1666055573 -1209271298 1074513418 -3.7377515435218811e-01 1.9515429437160492e-01 <_> 0 -1 33 -521936894 1718228966 -1030226758 -28903354 1207396130 656656962 1940608999 2012739543 -4.9193805456161499e-01 1.2772387266159058e-01 <_> 0 -1 31 -237788756 -36891774 756162060 -3341429 -216220931 290458792 -460804133 2068699022 -2.8526151180267334e-01 2.4898144602775574e-01 <_> 0 -1 137 -851507192 -49414322 390020095 1973369886 -337723713 -1048769 -1940650054 -927994838 -2.5763612985610962e-01 2.7653712034225464e-01 <_> 0 -1 135 1372360956 862725368 730609136 -1553429230 819508732 453807332 1829633021 1568536543 -4.5178580284118652e-01 1.5893152356147766e-01 <_> 0 -1 145 -1274805488 -9927920 -1376462213 -1078424186 603396895 -1090832097 107616655 440664192 -4.1856658458709717e-01 1.5220971405506134e-01 <_> 0 -1 231 -288652797 1608697646 1224139776 1396858638 1558660860 27394093 -438065980 -714604577 -2.8446722030639648e-01 2.5237077474594116e-01 <_> 0 -1 226 -1951680709 1048748839 934756149 -1214142800 854407598 -1073775393 747499512 -1427114237 -2.1165955066680908e-01 3.3453106880187988e-01 <_> 0 -1 106 890514736 521148756 -5461060 -15927272 625774572 252750161 -891770388 985401424 -4.9193653464317322e-01 1.3465493917465210e-01 <_> 0 -1 220 -291781117 1931978283 -1006772142 1937440367 1660932014 1140286983 -532876302 -105978118 -2.1797108650207520e-01 3.2472217082977295e-01 <_> 0 -1 237 142568448 5694285 267957505 432006067 -5019387 82008315 -50595072 553515895 -5.4030531644821167e-01 1.3703715801239014e-01 <_> 0 -1 103 -499261184 -361790357 131140111 -303573517 1180757915 -1205085265 1326942120 1365504522 -3.4566181898117065e-01 1.9119048118591309e-01 <_> 27 -1.0864274501800537e+00 <_> 0 -1 11 -891007484 1211035262 -823210124 -654381318 1564990964 1683447679 -8960692 -50331681 -1.7767603695392609e-01 4.3950533866882324e-01 <_> 0 -1 53 1494504925 -545673287 -609913089 -15682660 -1202970706 -6512216 -540169493 -1152645112 -2.3206019401550293e-01 3.5371619462966919e-01 <_> 0 -1 258 -889194746 175616267 -277357050 317915103 2124145408 1123013115 -85207488 -520355841 -4.2506697773933411e-01 1.9782558083534241e-01 <_> 0 -1 146 -1 -21002 -1282140165 -560551 -1429426772 -1363539800 737717486 -17697 -1.6592836380004883e-01 4.8377290368080139e-01 <_> 0 -1 218 -1023770102 -134512145 -539363397 -203424453 1272623102 -271847642 -1595890998 -1595743638 -2.7133983373641968e-01 2.7267450094223022e-01 <_> 0 -1 88 -805076990 -149425455 -985448645 -712822435 -765819210 -620768021 -1832196982 -754716533 -2.9903864860534668e-01 2.4232909083366394e-01 <_> 0 -1 46 -1074249731 1063010200 886120183 1054646218 -1074188360 -8614708 496775097 -1073988100 -3.1212934851646423e-01 2.2097712755203247e-01 <_> 0 -1 214 -68779992 -205123576 536228408 2106794930 782941342 442508840 123864506 1608515547 -3.5215419530868530e-01 1.9548596441745758e-01 <_> 0 -1 138 134414867 -82756098 907087317 -12699939 1052716991 -1081669588 797642751 -362887177 -3.0123272538185120e-01 2.3132325708866119e-01 <_> 0 -1 72 1480642776 1997667273 282210484 -607120144 -86663538 491273210 -1258550900 1994914794 -2.9138937592506409e-01 2.3277030885219574e-01 <_> 0 -1 139 -2136960272 -1712184942 1342143477 -1242228367 -1669857285 -1157984263 -1970783861 720044086 -3.4432685375213623e-01 1.9603538513183594e-01 <_> 0 -1 6 -273682938 1617226187 -318870010 -892410812 1448570470 1380122631 -480551051 -169347209 -3.5725796222686768e-01 1.9195778667926788e-01 <_> 0 -1 181 -285563872 1938338084 -284622176 1369605388 788149694 685363 1733297344 1371537119 -4.4718763232231140e-01 1.3961075246334076e-01 <_> 0 -1 171 -277887485 -500718545 1321160726 -207444821 -1956666417 1114068100 1408541940 -215746625 -2.5066006183624268e-01 2.7519401907920837e-01 <_> 0 -1 195 1935143315 993445570 610010493 -150654117 2122195213 258214406 1195727869 1598549829 -2.5814446806907654e-01 2.5379449129104614e-01 <_> 0 -1 191 473206268 465341840 275257821 499657168 1043082232 -1078087986 -638054916 1051468524 -4.7531163692474365e-01 1.4217083156108856e-01 <_> 0 -1 118 -474312942 -216353000 672931579 965216924 -1694308964 679631248 65288426 1072693195 -3.4220626950263977e-01 1.9385008513927460e-01 <_> 0 -1 222 -281285118 1670008163 -496738816 1408028942 -1077961972 12189815 -1226841458 -773849649 -3.1611573696136475e-01 2.1220512688159943e-01 <_> 0 -1 47 67109888 -1141813928 -229012613 -5148635 761734654 -1076389365 -1933831800 142606340 -5.6943005323410034e-01 1.2012590467929840e-01 <_> 0 -1 84 -1431396224 -1725172082 1078388256 -1850738226 730587530 169925378 -811871287 1099953835 -3.8054189085960388e-01 1.7058716714382172e-01 <_> 0 -1 77 260768394 495072811 233439232 28973086 575602872 181406662 445404133 1334704062 -4.8239827156066895e-01 1.3680285215377808e-01 <_> 0 -1 209 -1066220015 -4474061 2076076799 -8947757 1888966619 -1 1786727151 -356580861 -1.9837911427021027e-01 3.4651774168014526e-01 <_> 0 -1 266 -1087373425 -2131526698 -27274240 678559092 -1239681243 21160657 -198446336 -521666585 -3.1992667913436890e-01 2.1379309892654419e-01 <_> 0 -1 183 1411949790 1533029780 537141276 -645595131 -2015977302 -70766372 80521851 1862591198 -3.4819486737251282e-01 1.9132232666015625e-01 <_> 0 -1 163 159409168 453523316 268185503 939303412 754542844 -1094811257 783024670 460076576 -4.6279802918434143e-01 1.3720697164535522e-01 <_> 0 -1 17 -270012637 -422582426 -859576340 -290994558 1611619109 1984156183 1973384935 -671910025 -3.7753671407699585e-01 1.7108070850372314e-01 <_> 0 -1 61 1390453016 -148850831 -1946933193 487664274 1167036398 -1356085046 -918287665 -1085865773 -2.5055482983589172e-01 2.6808515191078186e-01 <_> 28 -1.1071751117706299e+00 <_> 0 -1 234 -352328126 553381479 -520359742 49741823 -402656305 15201071 -404752917 16777215 -2.6885536313056946e-01 3.9257419109344482e-01 <_> 0 -1 69 -2010207566 992095882 -923650279 788664832 -2001285654 -1627768873 -33641557 -286265589 -2.6879036426544189e-01 2.9501637816429138e-01 <_> 0 -1 91 -784068086 -108473857 2146818527 -573505573 -875627894 -121565032 -69223473 -69862017 -3.2706248760223389e-01 2.2975476086139679e-01 <_> 0 -1 30 -1048578 -604866 -1074132226 -859444 -42054404 1572803865 -537376564 -45313 -2.4356228113174438e-01 3.0726879835128784e-01 <_> 0 -1 203 546301 -3364714 1037842677 -1141359636 -1730392580 -1073873060 -1195099652 -1197997560 -2.6030963659286499e-01 2.7339822053909302e-01 <_> 0 -1 244 340007221 -715777 1976958847 -560289 -645227653 -5137 -1395851585 -1475338720 -3.3757317066192627e-01 1.8169505894184113e-01 <_> 0 -1 252 -536876277 6287771 -1619133169 -2073298197 1942747142 347331667 -456196348 -187697169 -2.8153365850448608e-01 2.3552483320236206e-01 <_> 0 -1 133 -1895619053 1594076987 213346175 1940061794 -1084264449 -836767838 1113580542 -71317581 -1.8090774118900299e-01 3.6471092700958252e-01 <_> 0 -1 196 -939278205 -1038940544 358138843 -241883949 -1991554675 -593763646 72742875 -639905253 -2.0349347591400146e-01 3.1664288043975830e-01 <_> 0 -1 32 -10467632 -1218534866 -140499459 -873455368 -84072199 -1396790337 -1298891060 174661666 -2.9732549190521240e-01 2.0526884496212006e-01 <_> 0 -1 168 -1014857696 -580779488 1466422647 2000216098 -999622994 -9829728 -1067589142 1090961922 -4.1238275170326233e-01 1.5365768969058990e-01 <_> 0 -1 34 -2744452 -386768130 -423167714 -1765347118 -638249896 1761690417 1675646108 -16942736 -3.5506930947303772e-01 1.7395976185798645e-01 <_> 0 -1 180 -297810429 2010621711 1795057440 1471141759 -298336274 7532327 2012214010 -1968439297 -2.2353799641132355e-01 2.8162729740142822e-01 <_> 0 -1 52 1370465178 -138027107 1106779442 1039434826 1994768862 1132100550 -62026870 1892530874 -2.9113209247589111e-01 2.0969158411026001e-01 <_> 0 -1 119 1785457410 1074396198 -722866136 1894924863 -2092621052 1078068033 849286481 1350039159 -4.8594748973846436e-01 1.4445739984512329e-01 <_> 0 -1 267 -307232973 739178939 -109578240 -1467065064 -1953519100 543159451 2067785728 1156576639 -3.6847913265228271e-01 1.6343115270137787e-01 <_> 0 -1 105 -304479188 -653906046 458232444 362283648 743997852 1023435825 678702296 2141353427 -4.1794654726982117e-01 1.4696790277957916e-01 <_> 0 -1 149 -1474295600 583142900 796931261 636946882 -1677723202 -1141257324 999885740 146933924 -4.6846660971641541e-01 1.2554962933063507e-01 <_> 0 -1 142 -1056920456 2144904732 1557674361 454068048 162041240 982532569 1165742776 -71968580 -3.3009743690490723e-01 1.8906202912330627e-01 <_> 0 -1 215 -586218806 2064193627 276825240 420610717 290201738 838881504 -1650814757 1587269849 -3.3714732527732849e-01 1.8466538190841675e-01 <_> 0 -1 2 386867406 1939059381 424173738 -46096988 -1177697538 251995272 1344360935 1962986616 -3.3208054304122925e-01 1.8585780262947083e-01 <_> 0 -1 134 -298857425 1997964573 1077568116 2012533912 1361920524 1090935617 1193755076 -200286465 -2.5905856490135193e-01 2.4680580198764801e-01 <_> 0 -1 68 -1056780345 -4719777 2134372031 -1759921541 -553802593 -16822282 -421013525 -374683390 -1.5229237079620361e-01 3.9819708466529846e-01 <_> 0 -1 224 -608446464 -1867328277 -790902508 421068383 -394279672 141484519 445484292 408940511 -3.8842281699180603e-01 1.5214422345161438e-01 <_> 0 -1 4 1810834979 545384940 -2033506042 1522937006 1661430389 1146300953 2110680405 -152732730 -3.6140063405036926e-01 1.7674677073955536e-01 <_> 0 -1 170 -1660680999 455349924 -1074521701 -1076380789 1166607768 -88280905 1539185402 -1680998264 -2.5342223048210144e-01 2.5323724746704102e-01 <_> 0 -1 207 -85819176 -574059136 -1925111674 1596920354 1782615976 437916680 386308588 2132014521 -3.5360771417617798e-01 1.8576373159885406e-01 <_> 0 -1 246 493625329 -1650686224 -1305653903 -1780149510 1033453485 -90368775 -1899817483 -1610865684 -2.7493086457252502e-01 2.2744202613830566e-01 <_> 0 0 1 1 <_> 0 0 3 1 <_> 0 0 12 6 <_> 0 1 1 1 <_> 0 1 1 2 <_> 0 1 1 4 <_> 0 2 1 1 <_> 0 2 1 2 <_> 0 2 2 1 <_> 0 3 1 1 <_> 0 3 1 2 <_> 0 3 1 5 <_> 0 3 2 1 <_> 0 3 12 4 <_> 0 4 1 1 <_> 0 4 2 1 <_> 0 6 1 1 <_> 0 6 2 1 <_> 0 7 1 2 <_> 0 7 2 2 <_> 0 8 1 2 <_> 0 9 1 2 <_> 0 11 1 2 <_> 0 12 1 1 <_> 0 12 2 1 <_> 0 12 2 2 <_> 0 13 1 1 <_> 0 14 1 1 <_> 0 15 1 1 <_> 0 15 2 1 <_> 0 15 3 1 <_> 0 15 12 1 <_> 1 4 5 1 <_> 1 6 2 1 <_> 1 9 1 3 <_> 1 11 10 2 <_> 2 0 1 1 <_> 2 0 2 1 <_> 2 15 2 1 <_> 2 15 4 1 <_> 2 15 8 1 <_> 3 0 1 1 <_> 3 0 2 1 <_> 3 0 3 1 <_> 3 0 10 3 <_> 3 2 10 2 <_> 3 15 1 1 <_> 4 0 1 1 <_> 4 0 1 4 <_> 4 0 7 1 <_> 4 0 9 2 <_> 4 1 5 2 <_> 4 1 9 1 <_> 4 4 9 1 <_> 4 12 9 2 <_> 5 0 1 3 <_> 5 0 1 4 <_> 5 1 9 1 <_> 5 2 5 2 <_> 5 4 6 1 <_> 5 6 1 4 <_> 5 12 8 1 <_> 5 13 6 1 <_> 6 0 4 1 <_> 6 1 9 2 <_> 6 3 6 1 <_> 6 7 1 3 <_> 6 12 4 2 <_> 7 0 3 1 <_> 7 1 2 1 <_> 7 1 4 2 <_> 7 1 9 2 <_> 7 2 8 1 <_> 7 4 4 1 <_> 7 4 7 1 <_> 7 4 8 1 <_> 7 6 1 3 <_> 7 7 7 3 <_> 7 8 7 3 <_> 7 9 8 3 <_> 7 11 7 2 <_> 8 10 6 1 <_> 9 0 1 3 <_> 9 0 1 4 <_> 9 4 1 3 <_> 9 9 1 3 <_> 9 11 3 2 <_> 9 15 3 1 <_> 10 0 1 3 <_> 10 0 1 4 <_> 10 0 5 2 <_> 10 1 5 3 <_> 10 3 1 3 <_> 10 3 3 1 <_> 10 3 8 1 <_> 10 6 1 4 <_> 10 7 1 3 <_> 10 9 1 3 <_> 10 11 5 1 <_> 11 0 1 3 <_> 11 1 1 4 <_> 11 2 3 1 <_> 11 3 6 1 <_> 11 4 1 2 <_> 11 6 1 4 <_> 11 10 1 2 <_> 11 13 2 1 <_> 12 0 1 3 <_> 12 3 1 3 <_> 12 6 3 4 <_> 12 9 1 3 <_> 12 15 4 1 <_> 13 0 1 3 <_> 13 0 3 1 <_> 13 1 2 3 <_> 13 3 3 2 <_> 13 8 1 3 <_> 13 8 2 3 <_> 13 10 1 2 <_> 13 10 3 1 <_> 13 15 1 1 <_> 13 15 5 1 <_> 14 0 1 3 <_> 14 0 4 1 <_> 14 1 5 2 <_> 14 9 1 3 <_> 14 13 4 1 <_> 14 15 2 1 <_> 14 15 3 1 <_> 15 0 1 4 <_> 15 0 2 1 <_> 15 0 3 1 <_> 15 3 3 1 <_> 15 5 1 1 <_> 15 9 1 1 <_> 15 9 2 3 <_> 15 10 4 2 <_> 16 0 5 1 <_> 16 3 1 1 <_> 16 3 3 1 <_> 16 3 4 1 <_> 16 3 5 1 <_> 16 10 1 2 <_> 17 3 1 1 <_> 17 4 1 1 <_> 17 4 4 1 <_> 17 12 2 2 <_> 18 0 1 4 <_> 18 1 5 2 <_> 18 2 3 1 <_> 18 2 6 1 <_> 18 3 1 1 <_> 19 0 1 3 <_> 19 0 1 4 <_> 19 2 1 3 <_> 19 7 1 1 <_> 19 8 2 1 <_> 19 9 1 3 <_> 19 15 2 1 <_> 20 0 1 3 <_> 20 0 1 4 <_> 20 0 3 1 <_> 20 3 1 2 <_> 20 3 3 1 <_> 20 4 1 2 <_> 20 7 1 3 <_> 20 8 1 3 <_> 20 13 3 1 <_> 21 1 1 4 <_> 21 3 1 3 <_> 21 4 3 1 <_> 21 7 1 1 <_> 21 7 1 3 <_> 21 8 1 3 <_> 21 10 1 1 <_> 21 10 1 2 <_> 21 15 2 1 <_> 22 2 1 3 <_> 22 3 2 5 <_> 22 4 1 3 <_> 22 6 1 1 <_> 22 8 1 2 <_> 22 8 1 3 <_> 22 10 2 2 <_> 22 11 2 2 <_> 23 0 1 4 <_> 23 3 1 3 <_> 23 6 1 4 <_> 23 7 1 3 <_> 23 9 1 1 <_> 23 9 1 3 <_> 23 15 1 1 <_> 24 0 1 3 <_> 24 0 1 4 <_> 24 1 1 4 <_> 24 1 3 1 <_> 24 2 1 2 <_> 24 7 1 3 <_> 24 7 2 1 <_> 24 9 1 1 <_> 24 9 1 3 <_> 24 10 1 1 <_> 25 4 1 2 <_> 25 4 3 1 <_> 25 7 1 2 <_> 25 7 1 3 <_> 25 8 1 3 <_> 25 9 1 2 <_> 25 10 1 2 <_> 26 0 1 1 <_> 26 1 1 1 <_> 26 3 1 3 <_> 26 6 1 3 <_> 26 7 1 2 <_> 26 7 1 3 <_> 26 8 2 2 <_> 26 11 2 2 <_> 27 0 1 4 <_> 27 0 3 1 <_> 27 3 1 3 <_> 27 6 1 1 <_> 27 6 1 3 <_> 27 9 1 1 <_> 27 9 1 3 <_> 27 9 3 2 <_> 28 0 2 1 <_> 28 1 1 1 <_> 28 1 1 3 <_> 28 3 1 3 <_> 28 9 1 1 <_> 28 9 1 3 <_> 28 10 1 1 <_> 28 15 1 1 <_> 29 0 1 1 <_> 29 7 2 1 <_> 30 0 1 1 <_> 30 0 1 4 <_> 30 4 2 1 <_> 30 8 2 1 <_> 30 10 2 1 <_> 30 11 2 2 <_> 30 12 2 2 <_> 30 14 2 1 <_> 30 15 2 1 <_> 31 0 1 1 <_> 31 0 1 4 <_> 31 15 1 1 <_> 32 0 1 4 <_> 33 0 1 1 <_> 33 0 1 4 <_> 33 1 1 1 <_> 33 1 1 2 <_> 33 2 1 1 <_> 33 2 1 2 <_> 33 3 1 1 <_> 33 3 1 2 <_> 33 3 1 3 <_> 33 3 1 5 <_> 33 4 1 1 <_> 33 5 1 1 <_> 33 5 1 3 <_> 33 6 1 1 <_> 33 9 1 1 <_> 33 10 1 1 <_> 33 10 1 2 <_> 33 12 1 1 <_> 33 13 1 1 <_> 33 14 1 1 <_> 33 15 1 1 openalpr_2.2.4.orig/src/000077500000000000000000000000001266464252400151625ustar00rootroot00000000000000openalpr_2.2.4.orig/src/CMakeLists.txt000066400000000000000000000170471266464252400177330ustar00rootroot00000000000000project(src) #set(CMAKE_BUILD_TYPE Debug) cmake_minimum_required (VERSION 2.6) # Set the OpenALPR version in cmake, and also add it as a DEFINE for the code to access SET(OPENALPR_MAJOR_VERSION "2") SET(OPENALPR_MINOR_VERSION "2") SET(OPENALPR_PATCH_VERSION "4") SET(OPENALPR_VERSION ${OPENALPR_MAJOR_VERSION}.${OPENALPR_MINOR_VERSION}.${OPENALPR_PATCH_VERSION}) add_definitions( -DOPENALPR_MAJOR_VERSION=${OPENALPR_MAJOR_VERSION}) add_definitions( -DOPENALPR_MINOR_VERSION=${OPENALPR_MINOR_VERSION}) add_definitions( -DOPENALPR_PATCH_VERSION=${OPENALPR_PATCH_VERSION}) SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules/") # TODO: switch to http://www.cmake.org/cmake/help/v2.8.5/cmake.html#module:GNUInstallDirs ? IF (NOT CMAKE_INSTALL_SYSCONFDIR) SET(CMAKE_INSTALL_SYSCONFDIR "/etc") ENDIF() IF (NOT CMAKE_INSTALL_VARDIR) SET(CMAKE_INSTALL_VARDIR "${CMAKE_INSTALL_PREFIX}/var") ENDIF() IF ( NOT DEFINED WITH_DAEMON ) SET(WITH_DAEMON ON) ENDIF() IF ( NOT DEFINED WITH_STATEDETECTION ) SET(WITH_STATEDETECTION ON) ENDIF() if ( NOT DEFINED WITH_GPU_DETECTOR ) SET(WITH_GPU_DETECTOR OFF) ENDIF() if ( NOT DEFINED WITH_TESTS ) SET(WITH_TESTS ON) ENDIF() if ( NOT DEFINED WITH_BINDING_JAVA ) SET(WITH_BINDING_JAVA ON) ENDIF() if ( NOT DEFINED WITH_BINDING_PYTHON ) SET(WITH_BINDING_PYTHON ON) ENDIF() if ( NOT DEFINED WITH_BINDING_GO ) SET(WITH_BINDING_GO ON) ENDIF() if ( NOT DEFINED WITH_UTILITIES ) SET(WITH_UTILITIES ON) ENDIF() IF (WIN32 AND WITH_DAEMON) MESSAGE(WARNING "Skipping alprd daemon installation, as it is not supported in Windows.") SET(WITH_DAEMON OFF) ENDIF() IF(Tesseract_FRAMEWORK_PATH AND Leptonica_FRAMEWORK_PATH) MESSAGE(STATUS "Using Tesseract iOS framework: ${Tesseract_FRAMEWORK_PATH}") MESSAGE(STATUS "Using Leptonica iOS framework: ${Leptonica_FRAMEWORK_PATH}") # http://www.vtk.org/Wiki/CMake:HowToUseExistingOSXFrameworks SET(Tesseract_LIBRARIES "${Tesseract_FRAMEWORK_PATH};${Leptonica_FRAMEWORK_PATH}") SET(Tesseract_INCLUDE_DIRS "${Tesseract_FRAMEWORK_PATH}/Headers") ELSE() FIND_PACKAGE( Tesseract REQUIRED ) ENDIF() include_directories(${Tesseract_INCLUDE_DIRS}) IF(OpenCV_FRAMEWORK_PATH) MESSAGE(STATUS "Using OpenCV iOS framework: ${OpenCV_FRAMEWORK_PATH}") SET(OpenCV_INCLUDE_DIRS "${OpenCV_FRAMEWORK_PATH}/Headers") SET(OpenCV_LIBS ${OpenCV_FRAMEWORK_PATH}) # OpenCV's released framework has this disabled, so we must too. # http://stackoverflow.com/a/32710441/868173 SET(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE NO) ELSE() # Discover OpenCV directory automatically find_path(OpenCV_DIR NAMES OpenCVConfig.cmake HINTS ${CMAKE_SOURCE_DIR}/../libraries/opencv/ /storage/projects/alpr/libraries/opencv/ ) # Opencv Package FIND_PACKAGE( OpenCV REQUIRED ) ENDIF() IF (${OpenCV_VERSION} VERSION_LESS 2.4.7) MESSAGE(FATAL_ERROR "OpenCV version is not compatible : ${OpenCV_VERSION}") ENDIF() include_directories(${OpenCV_INCLUDE_DIRS}) add_definitions( -DOPENCV_MAJOR_VERSION=${OpenCV_VERSION_MAJOR}) include_directories(./openalpr ) IF (WIN32) add_definitions( -DWINDOWS) add_definitions( -DNOMINMAX) # Extra linker dependencies for Windows SET (Tesseract_LIBRARIES ${Tesseract_LIBRARIES} ws2_32.lib ) ELSE() SET (Extra_LIBS pthread ) ENDIF() set(CMAKE_CSS_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall ") if (NOT IOS) ADD_EXECUTABLE( alpr main.cpp ) ENDIF() if (WIN32) SET(OPENALPR_LIB openalpr-static) ELSE() SET(OPENALPR_LIB openalpr) ENDIF() IF (WITH_STATEDETECTION) SET(STATE_DETECTION_LIB statedetection) add_subdirectory(statedetection) ELSE() SET(STATE_DETECTION_LIB "") add_definitions( -DSKIP_STATE_DETECTION=1 ) ENDIF() if (NOT IOS) TARGET_LINK_LIBRARIES(alpr ${OPENALPR_LIB} ${STATE_DETECTION_LIB} support video ${OpenCV_LIBS} ${Tesseract_LIBRARIES} ${Extra_LIBS} ) ENDIF() # Compile the alprd library on Unix-based OS IF (WITH_DAEMON) ADD_EXECUTABLE( alprd daemon.cpp daemon/beanstalk.c daemon/beanstalk.cc ) FIND_PACKAGE( log4cplus REQUIRED ) TARGET_LINK_LIBRARIES(alprd ${OPENALPR_LIB} support video curl ${OpenCV_LIBS} ${Tesseract_LIBRARIES} ${log4cplus_LIBRARIES} ${Extra_LIBS} ) ENDIF() if(WITH_UTILITIES) add_subdirectory(misc_utilities) ENDIF() if (WITH_TESTS) add_subdirectory(tests) ENDIF() add_subdirectory(openalpr) add_subdirectory(video) if (WITH_BINDING_JAVA) add_subdirectory(bindings/java) ENDIF() if (WITH_BINDING_PYTHON) add_subdirectory(bindings/python) ENDIF() if (WITH_BINDING_GO) set(OPENALPR_LIB_GO openalprgo) set(TAG_OPENALPR_LIB_GO "-l${OPENALPR_LIB_GO}") add_subdirectory(bindings/go) ENDIF() if (NOT IOS) install (TARGETS alpr DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) install (FILES ${CMAKE_SOURCE_DIR}/../doc/man/alpr.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1 COMPONENT doc) ENDIF() install (DIRECTORY ${CMAKE_SOURCE_DIR}/../runtime_data DESTINATION ${CMAKE_INSTALL_PREFIX}/share/openalpr) # set runtime_data to reflect the current CMAKE_INSTALL_PREFIX CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/../config/openalpr.conf.in ${CMAKE_CURRENT_BINARY_DIR}/config/openalpr.conf) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/config/openalpr.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/openalpr/ COMPONENT config) IF (WITH_DAEMON) install (TARGETS alprd DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) install (FILES ${CMAKE_SOURCE_DIR}/../config/alprd.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/openalpr COMPONENT config) ENDIF() SET (CPACK_PACKAGE_VERSION ${OPENALPR_VERSION}) SET (CPACK_SET_DESTDIR "on") SET (CPACK_GENERATOR "DEB;TGZ") SET (CPACK_PACKAGE_NAME "openalpr") SET (CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_SOURCE_DIR}/build;src;ALL;/") SET (CPACK_CMAKE_GENERATOR "Unix Makefiles") SET (CPACK_STRIP_FILES "1") SET (CPACK_DEBIAN_PACKAGE_PRIORITY "optional") SET (CPACK_DEBIAN_PACKAGE_SECTION "video") SET (CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) SET (CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.1.3), libgcc1 (>= 4.1.1), libtesseract3 (>= 3.0.3), libopencv-core2.4 (>= 2.4.8), libopencv-objdetect2.4 (>= 2.4.8), libopencv-highgui2.4 (>= 2.4.8), libopencv-imgproc2.4 (>= 2.4.8), libopencv-flann2.4 (>= 2.4.8), libopencv-features2d2.4 (>= 2.4.8), libopencv-video2.4 (>= 2.4.8), libopencv-gpu2.4 (>=2.4.8), liblog4cplus-1.0-4, libcurl3, beanstalkd") SET (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/../LICENSE") SET (CPACK_PACKAGE_DESCRIPTION "OpenALPR - Open Source Automatic License Plate Recognition") SET (CPACK_PACKAGE_DESCRIPTION_SUMMARY "OpenALPR is an open source Automatic License Plate Recognition library written in C++. The library analyzes images and identifies license plates. The output is the text representation of any license plate characters found in the image. Check out a live online demo here: http://www.openalpr.com/demo.html" ) SET (CPACK_PACKAGE_CONTACT "Matt Hill ") SET (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_ARCHITECTURE}") SET (CPACK_COMPONENTS_ALL Libraries ApplicationData) INCLUDE(CPack) # ---------------------------------------------------------------------------- # Uninstall target, for "make uninstall" # http://www.cmake.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F # ---------------------------------------------------------------------------- CONFIGURE_FILE( "${CMAKE_MODULE_PATH}/templates/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" @ONLY) ADD_CUSTOM_TARGET(uninstall COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") openalpr_2.2.4.orig/src/bindings/000077500000000000000000000000001266464252400167575ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/csharp/000077500000000000000000000000001266464252400202375ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net.sln000066400000000000000000000104731266464252400233660ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openalpr-net", "openalpr-net\openalpr-net.vcxproj", "{4044340C-C435-4A1F-8F12-0806C38AE3B6}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "openalprnet-cli", "openalprnet-cli\openalprnet-cli.csproj", "{BD50C6C1-EEB9-48D2-A87C-70F5342579DD}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "openalprnet-windemo", "openalprnet-windemo\openalprnet-windemo.csproj", "{C7863A14-55D2-4389-9072-04AA6E30AAD1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Mixed Platforms = Debug|Mixed Platforms Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Mixed Platforms = Release|Mixed Platforms Release|Win32 = Release|Win32 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Debug|Mixed Platforms.Build.0 = Debug|Win32 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Debug|Win32.ActiveCfg = Debug|Win32 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Debug|Win32.Build.0 = Debug|Win32 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Debug|x64.ActiveCfg = Debug|x64 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Debug|x64.Build.0 = Debug|x64 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Debug|x86.ActiveCfg = Debug|Win32 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Release|Mixed Platforms.ActiveCfg = Release|Win32 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Release|Mixed Platforms.Build.0 = Release|Win32 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Release|Win32.ActiveCfg = Release|Win32 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Release|Win32.Build.0 = Release|Win32 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Release|x64.ActiveCfg = Release|x64 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Release|x64.Build.0 = Release|x64 {4044340C-C435-4A1F-8F12-0806C38AE3B6}.Release|x86.ActiveCfg = Release|Win32 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Debug|Mixed Platforms.Build.0 = Debug|x86 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Debug|Win32.ActiveCfg = Debug|x86 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Debug|x64.ActiveCfg = Debug|x64 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Debug|x64.Build.0 = Debug|x64 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Debug|x86.ActiveCfg = Debug|x86 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Debug|x86.Build.0 = Debug|x86 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Release|Mixed Platforms.ActiveCfg = Release|x86 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Release|Mixed Platforms.Build.0 = Release|x86 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Release|Win32.ActiveCfg = Release|x86 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Release|x64.ActiveCfg = Release|x64 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Release|x64.Build.0 = Release|x64 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Release|x86.ActiveCfg = Release|x86 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD}.Release|x86.Build.0 = Release|x86 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Debug|Mixed Platforms.Build.0 = Debug|x86 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Debug|Win32.ActiveCfg = Debug|x86 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Debug|x64.ActiveCfg = Debug|x64 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Debug|x64.Build.0 = Debug|x64 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Debug|x86.ActiveCfg = Debug|x86 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Debug|x86.Build.0 = Debug|x86 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Release|Mixed Platforms.ActiveCfg = Release|x86 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Release|Mixed Platforms.Build.0 = Release|x86 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Release|Win32.ActiveCfg = Release|x86 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Release|x64.ActiveCfg = Release|x64 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Release|x64.Build.0 = Release|x64 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Release|x86.ActiveCfg = Release|x86 {C7863A14-55D2-4389-9072-04AA6E30AAD1}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/000077500000000000000000000000001266464252400226435ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/AssemblyInfo.cpp000066400000000000000000000024511266464252400257440ustar00rootroot00000000000000#include "stdafx.h" using namespace System; using namespace System::Reflection; using namespace System::Runtime::CompilerServices; using namespace System::Runtime::InteropServices; using namespace System::Security::Permissions; // // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. // [assembly:AssemblyTitleAttribute("openalprnet")]; [assembly:AssemblyDescriptionAttribute("")]; [assembly:AssemblyConfigurationAttribute("")]; [assembly:AssemblyCompanyAttribute("")]; [assembly:AssemblyProductAttribute("openalprnet")]; [assembly:AssemblyCopyrightAttribute("Copyright (c) 2015")]; [assembly:AssemblyTrademarkAttribute("")]; [assembly:AssemblyCultureAttribute("")]; // // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the value or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersionAttribute("2.1.0")] [assembly: AssemblyFileVersionAttribute("2.1.0")] [assembly: AssemblyInformationalVersionAttribute("2.1.0")] [assembly:ComVisible(false)]; [assembly:CLSCompliantAttribute(true)]; openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/Stdafx.cpp000066400000000000000000000003131266464252400245750ustar00rootroot00000000000000// stdafx.cpp : source file that includes just the standard includes // openalpr-net.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/Stdafx.h000066400000000000000000000002551266464252400242470ustar00rootroot00000000000000// stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, // but are changed infrequently #pragma once openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/app.ico000066400000000000000000000020661266464252400241230ustar00rootroot00000000000000 &(( @wwwwwwwwwwwwwwpDDDDDDDDDDDDDDppppppppppppppppppppDDDDDDDDDDDDDDpLLLLLLLLLNItpDDDDDDDDDDDDD@( wwwwwwwDDDDDDDGOGOGOGOGOGOGOGOGHGLGDDDDDDopenalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/app.rc000066400000000000000000000047761266464252400237670ustar00rootroot00000000000000// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (United States) resources ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon placed first or with lowest ID value becomes application icon LANGUAGE 9, 1 1 ICON "app.ico" #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" "\0" END 2 TEXTINCLUDE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/bitmapmat-net.cpp000066400000000000000000000020271266464252400261120ustar00rootroot00000000000000#include "stdafx.h" #include "bitmapmat-net.h" using namespace openalprnet; BitmapMat::BitmapMat(array^ byteArray) { m_bitmap = new cv::Mat(); std::vector buffer = AlprHelper::ToVector(byteArray); cv::imdecode(buffer, CV_LOAD_IMAGE_COLOR, this->m_bitmap); } BitmapMat::BitmapMat(Bitmap^ bitmap) { m_bitmap = new cv::Mat(); MemoryStream^ ms = gcnew MemoryStream(); bitmap->Save(ms, ImageFormat::Png); std::vector buffer = AlprHelper::ToVector(ms->ToArray()); cv::imdecode(buffer, CV_LOAD_IMAGE_COLOR, this->m_bitmap); delete ms; } BitmapMat::BitmapMat(MemoryStream^ memoryStream) { m_bitmap = new cv::Mat(); std::vector buffer = AlprHelper::ToVector(memoryStream->ToArray()); cv::imdecode(buffer, CV_LOAD_IMAGE_COLOR, this->m_bitmap); } BitmapMat::BitmapMat(String^ filename) { m_bitmap = new cv::Mat(); array^ byteArray = File::ReadAllBytes(filename); std::vector buffer = AlprHelper::ToVector(byteArray); cv::imdecode(buffer, CV_LOAD_IMAGE_COLOR, this->m_bitmap); delete byteArray; } openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/bitmapmat-net.h000066400000000000000000000014301266464252400255540ustar00rootroot00000000000000#pragma once #include #include #include "helper-net.h" using namespace System; using namespace System::Drawing; using namespace System::Drawing::Imaging; using namespace System::IO; namespace openalprnet { private ref class BitmapMat : IDisposable { private: cv::Mat* m_bitmap; bool m_disposed; public: BitmapMat(array^ byteArray); BitmapMat(Bitmap^ bitmap); BitmapMat(MemoryStream^ memoryStream); BitmapMat(String^ filename); ~BitmapMat() { if (this->m_disposed) { return; } this->!BitmapMat(); this->m_disposed = true; } !BitmapMat() { m_bitmap->release(); delete m_bitmap; } property cv::Mat Value { cv::Mat get() { return *m_bitmap; } } }; }openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/config-net.h000066400000000000000000000271461266464252400250570ustar00rootroot00000000000000#pragma once #include "config.h" // alpr #include "types-net.h" #include "helper-net.h" using namespace System; using namespace openalprnet::types; namespace openalprnet { public ref class AlprConfigNet sealed { internal: AlprConfigNet(Config* config) : m_config(config) { } public: property bool IsLoaded { bool get() { return this->m_config->loaded; } } property AlprDetectorTypeNet Detector { AlprDetectorTypeNet get() { return static_cast(this->m_config->detector); } void set(AlprDetectorTypeNet value) { this->m_config->detector = static_cast(value); } } property float DetectionIterationIncrease { float get() { return this->m_config->detection_iteration_increase; } void set(float value) { this->m_config->detection_iteration_increase = value; } } property int DetectionStrictness { int get() { return this->m_config->detectionStrictness; } void set(int value) { this->m_config->detectionStrictness = value; } } property float MaxPlateWidthPercent { float get() { return this->m_config->maxPlateWidthPercent; } void set(float value) { this->m_config->maxPlateWidthPercent = value; } } property float MaxPlateHeightPercent { float get() { return this->m_config->maxPlateHeightPercent; } void set(float value) { this->m_config->maxPlateHeightPercent = value; } } property int MaxDetectionInputWidth { int get() { return this->m_config->maxDetectionInputWidth; } void set(int value) { this->m_config->maxDetectionInputWidth = value; } } property int MaxDetectionInputHeight { int get() { return this->m_config->maxDetectionInputHeight; } void set(int value) { this->m_config->maxDetectionInputHeight = value; } } property bool SkipDetection { bool get() { return this->m_config->skipDetection; } void set(bool value) { this->m_config->skipDetection = true; } } property String^ PreWarp { String^ get() { return AlprHelper::ToManagedString(this->m_config->prewarp); } void set(String^ value) { if(String::IsNullOrWhiteSpace(value)) { this->m_config->prewarp = ""; return; } this->m_config->prewarp = marshal_as(value); } } property int MaxPlateAngleDegrees { int get() { return this->m_config->maxPlateAngleDegrees; } void set(int value) { this->m_config->maxPlateAngleDegrees = value; } } property float MinPlateSizeWidthPx { float get() { return this->m_config->minPlateSizeWidthPx; } void set(float value) { this->m_config->minPlateSizeWidthPx = value; } } property float MinPlateSizeHeightPx { float get() { return this->m_config->minPlateSizeHeightPx; } void set(float value) { this->m_config->minPlateSizeHeightPx = value; } } property bool Multiline { bool get() { return this->m_config->multiline; } void set(bool value) { this->m_config->multiline = value; } } property float PlateWidthMM { float get() { return this->m_config->plateWidthMM; } void set(float value) { this->m_config->plateWidthMM = value; } } property float PlateHeightMM { float get() { return this->m_config->plateHeightMM; } void set(float value) { this->m_config->plateHeightMM = value; } } property float CharWhitespaceTopMM { float get() { return this->m_config->charWhitespaceTopMM; } void set(float value) { this->m_config->charWhitespaceTopMM = value; } } property float CharWhitespaceBotMM { float get() { return this->m_config->charWhitespaceBotMM; } void set(float value) { this->m_config->charWhitespaceBotMM = value; } } property int TemplateWidthPx { int get() { return this->m_config->templateWidthPx; } void set(int value) { this->m_config->templateWidthPx = value; } } property int TemplateHeightPx { int get() { return this->m_config->templateHeightPx; } void set(int value) { this->m_config->templateHeightPx = value; } } property int OcrImageWidthPx { int get() { return this->m_config->ocrImageWidthPx; } void set(int value) { this->m_config->ocrImageWidthPx = value; } } property int OcrImageHeightPx { int get() { return this->m_config->ocrImageHeightPx; } void set(int value) { this->m_config->ocrImageHeightPx = value; } } property int StateIdImageWidthPx { int get() { return this->m_config->stateIdImageWidthPx; } void set(int value) { this->m_config->stateIdImageWidthPx = value; } } property int StateIdimageHeightPx { int get() { return this->m_config->stateIdimageHeightPx; } void set(int value) { this->m_config->stateIdimageHeightPx = value; } } property float CharAnalysisMinPercent { float get() { return this->m_config->charAnalysisMinPercent; } void set(float value) { this->m_config->charAnalysisMinPercent = value; } } property float CharAnalysisHeightRange { float get() { return this->m_config->charAnalysisHeightRange; } void set(float value) { this->m_config->charAnalysisHeightRange = value; } } property float CharAnalysisHeightStepSize { float get() { return this->m_config->charAnalysisHeightStepSize; } void set(float value) { this->m_config->charAnalysisHeightStepSize = value; } } property int CharAnalysisNumSteps { int get() { return this->m_config->charAnalysisNumSteps; } void set(int value) { this->m_config->charAnalysisNumSteps = value; } } property float PlateLinesSensitivityVertical { float get() { return this->m_config->plateLinesSensitivityVertical; } void set(float value) { this->m_config->plateLinesSensitivityVertical = value; } } property float PlateLinesSensitivityHorizontal { float get() { return this->m_config->plateLinesSensitivityHorizontal; } void set(float value) { this->m_config->plateLinesSensitivityHorizontal = value; } } property int SegmentationMinBoxWidthPx { int get() { return this->m_config->segmentationMinBoxWidthPx; } void set(int value) { this->m_config->segmentationMinBoxWidthPx = value; } } property float SegmentationMinCharHeightPercent { float get() { return this->m_config->segmentationMinCharHeightPercent; } void set(float value) { this->m_config->segmentationMinCharHeightPercent = value; } } property float SegmentationMaxCharWidthvsAverage { float get() { return this->m_config->segmentationMaxCharWidthvsAverage; } void set(float value) { this->m_config->segmentationMaxCharWidthvsAverage = value; } } property bool MustMatchPattern { bool get() { return this->m_config->mustMatchPattern; } void set(bool value) { this->m_config->mustMatchPattern = value; } } property String^ OcrLanguage { String^ get() { return AlprHelper::ToManagedString(this->m_config->ocrLanguage); } void set(String^ value) { if (String::IsNullOrWhiteSpace(value)) { return; } this->m_config->ocrLanguage = marshal_as(value); } } property int OcrMinFontSize { int get() { return this->m_config->ocrMinFontSize; } void set(int value) { this->m_config->ocrMinFontSize = value; } } property float PostProcessMinConfidence { float get() { return this->m_config->postProcessMinConfidence; } void set(float value) { this->m_config->postProcessMinConfidence = value; } } property float PostProcessConfidenceSkipLevel { float get() { return this->m_config->postProcessConfidenceSkipLevel; } void set(float value) { this->m_config->postProcessConfidenceSkipLevel = value; } } property unsigned int PostProcessMinCharacters { unsigned int get() { return this->m_config->postProcessMinCharacters; } void set(unsigned int value) { this->m_config->postProcessMinCharacters = value; } } property unsigned int PostProcessMaxCharacters { unsigned int get() { return this->m_config->postProcessMaxCharacters; } void set(unsigned int value) { this->m_config->postProcessMaxCharacters = value; } } property bool DebugGeneral { bool get() { return this->m_config->debugGeneral; } void set(bool value) { this->m_config->debugGeneral = value; } } property bool DebugTiming { bool get() { return this->m_config->debugTiming; } void set(bool value) { this->m_config->debugTiming = value; } } property bool DebugPrewarp { bool get() { return this->m_config->debugPrewarp; } void set(bool value) { this->m_config->debugPrewarp = value; } } property bool DebugDetector { bool get() { return this->m_config->debugDetector; } void set(bool value) { this->m_config->debugDetector = value; } } property bool DebugStateId { bool get() { return this->m_config->debugStateId; } void set(bool value) { this->m_config->debugStateId = value; } } property bool DebugPlateLines { bool get() { return this->m_config->debugPlateLines; } void set(bool value) { this->m_config->debugPlateLines = value; } } property bool DebugPlateCorners { bool get() { return this->m_config->debugPlateCorners; } void set(bool value) { this->m_config->debugPlateCorners = value; } } property bool DebugCharSegmenter { bool get() { return this->m_config->debugCharSegmenter; } void set(bool value) { this->m_config->debugCharSegmenter = value; } } property bool DebugCharAnalysis { bool get() { return this->m_config->debugCharAnalysis; } void set(bool value) { this->m_config->debugCharAnalysis = value; } } property bool DebugColorFiler { bool get() { return this->m_config->debugColorFiler; } void set(bool value) { this->m_config->debugColorFiler = value; } } property bool DebugOcr { bool get() { return this->m_config->debugOcr; } void set(bool value) { this->m_config->debugOcr = value; } } property bool DebugPostProcess { bool get() { return this->m_config->debugPostProcess; } void set(bool value) { this->m_config->debugPostProcess = value; } } property bool DebugShowImages { bool get() { return this->m_config->debugShowImages; } void set(bool value) { this->m_config->debugShowImages = value; } } property bool DebugPauseOnFrame { bool get() { return this->m_config->debugPauseOnFrame; } void set(bool value) { this->m_config->debugPauseOnFrame = value; } } void SetDebug(bool value) { this->m_config->setDebug(value); } String^ GetKeypointsRuntimeDir() { return AlprHelper::ToManagedString(this->m_config->getKeypointsRuntimeDir()); } String^ GetCascadeRuntimeDir() { return AlprHelper::ToManagedString(this->m_config->getCascadeRuntimeDir()); } String^ GetPostProcessRuntimeDir() { return AlprHelper::ToManagedString(this->m_config->getPostProcessRuntimeDir()); } String^ GetTessdataPrefix() { return AlprHelper::ToManagedString(this->m_config->getTessdataPrefix()); } ~AlprConfigNet() { // void } private: Config *m_config; }; }openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/helper-net.h000066400000000000000000000042321266464252400250600ustar00rootroot00000000000000#pragma once #using #include #include #include #include #include "alpr.h" #include "opencv2/imgproc/imgproc.hpp" #include using namespace alpr; using namespace msclr::interop; using namespace System; using namespace System::Runtime::InteropServices; using namespace System::Drawing; using namespace System::Drawing::Imaging; using namespace System::IO; using namespace System::Collections::Generic; namespace openalprnet { private ref class AlprHelper sealed { public: static std::vector ToVector(array^ src) { std::vector result(src->Length); pin_ptr pin(&src[0]); char *first(pin), *last(pin + src->Length); std::copy(first, last, result.begin()); return result; } static std::vector ToVector(array^ src) { std::vector result(src->Length); pin_ptr pin(&src[0]); char* pch = reinterpret_cast(pin); char *first(pch), *last(pch + src->Length); std::copy(first, last, result.begin()); return result; } static std::vector ToVector(List^ src) { std::vector result; for each(System::Drawing::Rectangle^ rect in src) { AlprRegionOfInterest roi(rect->X, rect->Y, rect->Width, rect->Height); result.push_back(roi); } return result; } static unsigned char* ToCharPtr(array^ src) { //unsigned char* result = (unsigned char*) new unsigned char[src->Length]; pin_ptr pin(&src[0]); unsigned char* pc = pin; return pc; } static System::String^ ToManagedString(std::string s) { return gcnew String(s.c_str()); } static System::Drawing::Rectangle ToRectangle(cv::Rect rect) { return System::Drawing::Rectangle(rect.x, rect.y, rect.width, rect.height); } static List^ ToRectangleList(std::vector srcRects) { List^ rects = gcnew List(); for each(cv::Rect rect in srcRects) { rects->Add(ToRectangle(rect)); } return rects; } }; };openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/lock-net.h000066400000000000000000000004101266464252400245230ustar00rootroot00000000000000#pragma once using namespace System::Threading; namespace openalprnet { private ref class Lock { Object^ m_pObject; public: Lock(Object ^ pObject) : m_pObject(pObject) { Monitor::Enter(m_pObject); } ~Lock() { Monitor::Exit(m_pObject); } }; } openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/motiondetector-net.cpp000066400000000000000000000037261266464252400272020ustar00rootroot00000000000000#include "stdafx.h" #include "motiondetector-net.h" #include "lock-net.h" #include "bitmapmat-net.h" using namespace openalprnet; void AlprMotionDetectionNet::ResetMotionDetection(Bitmap^ bitmap) { BitmapMat^ wrapper = gcnew BitmapMat(bitmap); ResetMotionDetection(wrapper->Value); delete wrapper; } void AlprMotionDetectionNet::ResetMotionDetection(String^ filename) { cv::Mat mat = cv::imread(marshal_as(filename)); ResetMotionDetection(mat); } void AlprMotionDetectionNet::ResetMotionDetection(MemoryStream^ memoryStream) { return ResetMotionDetection(memoryStream->ToArray()); } void AlprMotionDetectionNet::ResetMotionDetection(array^ byteArray) { std::vector buffer = AlprHelper::ToVector(byteArray); cv::Mat mat = cv::imdecode(buffer, CV_LOAD_IMAGE_COLOR); ResetMotionDetection(mat); } System::Drawing::Rectangle AlprMotionDetectionNet::MotionDetect(Bitmap^ bitmap) { BitmapMat^ wrapper = gcnew BitmapMat(bitmap); System::Drawing::Rectangle motion = MotionDetect(wrapper->Value); delete wrapper; return motion; } System::Drawing::Rectangle AlprMotionDetectionNet::MotionDetect(String^ filename) { cv::Mat mat = cv::imread(marshal_as(filename)); System::Drawing::Rectangle motion = MotionDetect(mat); return motion; } System::Drawing::Rectangle AlprMotionDetectionNet::MotionDetect(MemoryStream^ memoryStream) { return MotionDetect(memoryStream->ToArray()); } System::Drawing::Rectangle AlprMotionDetectionNet::MotionDetect(array^ byteArray) { std::vector buffer = AlprHelper::ToVector(byteArray); cv::Mat mat = cv::imdecode(buffer, CV_LOAD_IMAGE_COLOR); return MotionDetect(mat); } void AlprMotionDetectionNet::ResetMotionDetection(cv::Mat mat) { Lock lock(this); this->m_motionDetector->ResetMotionDetection(&mat); } System::Drawing::Rectangle AlprMotionDetectionNet::MotionDetect(cv::Mat mat) { Lock lock(this); cv::Rect rect = this->m_motionDetector->MotionDetect(&mat); return AlprHelper::ToRectangle(rect); }openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/motiondetector-net.h000066400000000000000000000024531266464252400266430ustar00rootroot00000000000000#pragma once #include "motiondetector.h" // alpr #include "helper-net.h" using namespace alpr; namespace openalprnet { public ref class AlprMotionDetectionNet : IDisposable { public: AlprMotionDetectionNet() : m_motionDetector(new MotionDetector()) { } void AlprMotionDetectionNet::ResetMotionDetection(Bitmap^ bitmap); void AlprMotionDetectionNet::ResetMotionDetection(String^ filename); void AlprMotionDetectionNet::ResetMotionDetection(MemoryStream^ memoryStream); void AlprMotionDetectionNet::ResetMotionDetection(array^ byteArray); System::Drawing::Rectangle AlprMotionDetectionNet::MotionDetect(Bitmap^ bitmap); System::Drawing::Rectangle AlprMotionDetectionNet::MotionDetect(String^ filename); System::Drawing::Rectangle AlprMotionDetectionNet::MotionDetect(MemoryStream^ memoryStream); System::Drawing::Rectangle AlprMotionDetectionNet::MotionDetect(array^ byteArray); private: ~AlprMotionDetectionNet() { if (this->m_disposed) { return; } this->!AlprMotionDetectionNet(); this->m_disposed = true; } !AlprMotionDetectionNet() { delete m_motionDetector; } private: void ResetMotionDetection(cv::Mat mat); System::Drawing::Rectangle MotionDetect(cv::Mat mat); private: MotionDetector* m_motionDetector; bool m_disposed; }; }openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/openalpr-net.cpp000066400000000000000000000311311266464252400257520ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "stdafx.h" #include "openalpr-net.h" using namespace System; using namespace alpr; namespace openalprnet { public ref class AlprPlateNet sealed { public: AlprPlateNet(AlprPlate plate){ m_characters = AlprHelper::ToManagedString(plate.characters); m_overall_confidence=plate.overall_confidence; m_matches_template=plate.matches_template; } property System::String^ Characters { System::String^ get() { return m_characters; } } property float OverallConfidence { float get() { return m_overall_confidence; } } property bool MatchesTemplate { bool get() { return m_matches_template; } } private: System::String^ m_characters; float m_overall_confidence; bool m_matches_template; }; public ref class AlprPlateResultNet sealed { public: AlprPlateResultNet(AlprPlateResult result) { m_plate_index = result.plate_index; m_processing_time_ms = result.processing_time_ms; m_regionConfidence = result.regionConfidence; m_region = AlprHelper::ToManagedString(result.region); m_bestPlate = gcnew AlprPlateNet(result.bestPlate); m_plate_points = gcnew List(4); for (int i = 0; i < 4; i++) { m_plate_points->Add(System::Drawing::Point(result.plate_points[i].x, result.plate_points[i].y)); } int num = result.topNPlates.size(); m_topNPlates = gcnew List(num); for (int i = 0; i < num; i++) { m_topNPlates->Add(gcnew AlprPlateNet(result.topNPlates[i])); } } property int RequestedTopN { int get() { return m_requested_topn; } } property int RegionConfidence { int get() { return m_regionConfidence; } } property int PlateIndex { int get() { return m_plate_index; } } property System::String^ Region { System::String^ get() { return m_region; } } property AlprPlateNet^ BestPlate { AlprPlateNet^ get() { return m_bestPlate; } } property List^ PlatePoints { List^ get() { return m_plate_points; } } property List^ TopNPlates { List^ get() { return m_topNPlates; } } property float ProcessingTimeMs { float get() { return m_processing_time_ms; } } private: int m_requested_topn; int m_regionConfidence; int m_plate_index; System::String^ m_region; float m_processing_time_ms; List^ m_topNPlates; List^ m_plate_points; AlprPlateNet^ m_bestPlate; }; public ref class AlprResultsNet sealed { public: AlprResultsNet(AlprResults results) { m_epoch_time = results.epoch_time; m_img_width = results.img_width; m_img_height = results.img_height; m_total_processing_time_ms = results.total_processing_time_ms; int num_rois = results.regionsOfInterest.size(); m_regionsOfInterest = gcnew List(num_rois); for (int i = 0; i < num_rois; i++) { m_regionsOfInterest->Add(System::Drawing::Rectangle( results.regionsOfInterest[i].x, results.regionsOfInterest[i].y, results.regionsOfInterest[i].width, results.regionsOfInterest[i].height)); } int num_plates = results.plates.size(); m_plates = gcnew List(num_plates); for (int i = 0; i < num_plates; i++) { m_plates->Add(gcnew AlprPlateResultNet(results.plates[i])); } std::string json = Alpr::toJson(results); m_json = AlprHelper::ToManagedString(json); } property long EpochTime { long get() { return m_epoch_time; } } property int ImageWidth { int get() { return m_img_width; } } property int ImageHeight { int get() { return m_img_height; } } property float TotalProcessingTimeMs { float get() { return m_total_processing_time_ms; } } property List^ RegionsOfInterest { List^ get() { return m_regionsOfInterest; } } property List^ Plates { List^ get() { return m_plates; } } property System::String^ Json { System::String^ get() { return m_json; } } private: long m_epoch_time; int m_img_width; int m_img_height; float m_total_processing_time_ms; List^ m_regionsOfInterest; List^ m_plates; System::String^ m_json; }; public ref class AlprFrameEventArgs : public EventArgs { public: AlprFrameEventArgs(int frameNumber, System::Drawing::Image^ frame, AlprResultsNet^ results) { m_frameNumber = frameNumber; m_frame = frame; m_results = results; m_cancel = false; } property int FrameNumber { int get() { return m_frameNumber; } } property System::Drawing::Image^ Frame { System::Drawing::Image^ get() { return m_frame; } } property AlprResultsNet^ Results { AlprResultsNet^ get() { return m_results; } } property bool Cancel { bool get() { return m_cancel; } void set( bool cancel ) { m_cancel = cancel; } } private: int m_frameNumber; System::Drawing::Image^ m_frame; AlprResultsNet^ m_results; bool m_cancel; }; public ref class AlprNet sealed : IDisposable { public: // Allocate the native object on the C++ Heap via a constructor AlprNet(System::String^ country, System::String^ configFile, System::String^ runtimeDir) : m_Impl( new Alpr(marshal_as(country), marshal_as(configFile), marshal_as(runtimeDir)) ) { this->m_config = gcnew AlprConfigNet(this->m_Impl->getConfig()); } ~AlprNet() { if(this->m_disposed) { return; } this->!AlprNet(); this->m_disposed = true; } property AlprConfigNet^ Configuration { AlprConfigNet^ get() { return this->m_config; } } property int TopN { int get() { return m_topN; } void set( int topn ){ m_topN = topn; m_Impl->setTopN(topn); } } property bool DetectRegion { bool get() { return m_detectRegion; } void set( bool detectRegion ) { m_detectRegion = detectRegion; m_Impl->setDetectRegion(detectRegion); } } property System::String^ DefaultRegion { System::String^ get() { return m_defaultRegion; } void set( System::String^ region ){ m_defaultRegion = region; m_Impl->setDefaultRegion(marshal_as(region)); } } event EventHandler^ FrameProcessed; void RecognizeFromVideo(System::String^ videoPath) { if (System::IO::File::Exists(videoPath)) { int framenum = 0; cv::VideoCapture cap = cv::VideoCapture(); cap.open(marshal_as(videoPath)); cv::Mat frame; while (cap.read(frame)) { std::vector regionsOfInterest; regionsOfInterest.push_back(AlprRegionOfInterest(0, 0, frame.cols, frame.rows)); AlprResults results = m_Impl->recognize(frame.data, frame.elemSize(), frame.cols, frame.rows, regionsOfInterest); int framecolsorig = frame.cols; if (framecolsorig % 4 != 0) copyMakeBorder(frame, frame, 0, 0, 0, 4 - (framecolsorig % 4), IPL_BORDER_REPLICATE); //Stride has to be multiple of 4 Image^ frameImage = gcnew Bitmap(framecolsorig, frame.rows, frame.step, Imaging::PixelFormat::Format24bppRgb, IntPtr(frame.data)); AlprFrameEventArgs^ alprFrameEventArgs = gcnew AlprFrameEventArgs(framenum, frameImage, gcnew AlprResultsNet(results)); FrameProcessed(this, alprFrameEventArgs); delete frameImage; if (alprFrameEventArgs->Cancel) { break; } framenum++; } } else { throw gcnew System::IO::FileNotFoundException("No video was not found at " + videoPath, videoPath); } } ///

/// Recognize from an image on disk /// AlprResultsNet^ Recognize(System::String^ filepath) { return Recognize(filepath, gcnew List()); } /// /// Recognize from an image on disk /// AlprResultsNet^ Recognize(System::String^ filepath, List^ regionsOfInterest) { array^ byteArray = File::ReadAllBytes(filepath); return Recognize(byteArray, regionsOfInterest); } /// /// Recognize from a bitmap /// AlprResultsNet^ Recognize(Bitmap^ bitmap) { return Recognize(bitmap, gcnew List()); } /// /// Recognize from MemoryStream representing an encoded image (e.g., BMP, PNG, JPG, GIF etc). /// AlprResultsNet^ Recognize(MemoryStream^ memoryStream) { return Recognize(memoryStream, gcnew List()); } /// /// Recognize from MemoryStream representing an encoded image (e.g., BMP, PNG, JPG, GIF etc). /// AlprResultsNet^ Recognize(MemoryStream^ memoryStream, List^ regionsOfInterest) { return Recognize(memoryStream->ToArray(), regionsOfInterest); } /// /// Recognize from byte data representing an encoded image (e.g., BMP, PNG, JPG, GIF etc). /// AlprResultsNet^ Recognize(array^ imageBuffer) { return Recognize(imageBuffer, gcnew List()); } /// /// Recognize from a bitmap /// AlprResultsNet^ Recognize(Bitmap^ bitmap, List^ regionsOfInterest) { BitmapMat^ wrapper = gcnew BitmapMat(bitmap); AlprResultsNet^ results = Recognize(wrapper, regionsOfInterest); delete wrapper; return results; } /// /// Recognize from byte data representing an encoded image (e.g., BMP, PNG, JPG, GIF etc). /// AlprResultsNet^ Recognize(array^ imageBuffer, List^ regionsOfInterest) { BitmapMat^ wrapper = gcnew BitmapMat(imageBuffer); AlprResultsNet^ results = Recognize(wrapper, regionsOfInterest); delete wrapper; return results; } /// /// Pre-warp from raw pixel data. /// array^ PreWarp(array^ imageBuffer) { std::vector buffer = AlprHelper::ToVector(imageBuffer); cv::Mat src = cv::imdecode(buffer, CV_LOAD_IMAGE_COLOR); alpr::PreWarp *preWarp = new alpr::PreWarp(m_Impl->getConfig()); cv::Mat warpedImage = preWarp->warpImage(src); std::vector warpedImageVector; cv::imencode(".jpg", warpedImage, warpedImageVector); const size_t warpedImageSize = warpedImageVector.size(); array^ warpedImageByteArray = gcnew array(warpedImageSize); pin_ptr pin(&warpedImageByteArray[0]); std::memcpy(pin, &warpedImageVector[0], warpedImageSize); delete preWarp; return warpedImageByteArray; } bool IsLoaded() { return m_Impl->isLoaded(); } static System::String^ GetVersion() { return AlprHelper::ToManagedString(Alpr::getVersion()); } protected: AlprResultsNet^ Recognize(BitmapMat^ bitmapMat, List^ regionsOfInterest) { cv::Mat frame = bitmapMat->Value; std::vector rois = AlprHelper::ToVector(regionsOfInterest); AlprResults results = m_Impl->recognize(frame.data, frame.elemSize(), frame.cols, frame.rows, rois); return gcnew AlprResultsNet(results); } // Deallocate the native object on the finalizer just in case no destructor is called !AlprNet() { delete m_Impl; delete m_config; } private: Alpr * m_Impl; AlprConfigNet^ m_config; int m_topN; bool m_detectRegion; System::String^ m_defaultRegion; bool m_disposed; }; } openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/openalpr-net.h000066400000000000000000000006031266464252400254170ustar00rootroot00000000000000#pragma once #include "alpr.h" #include "prewarp.h" #include #include #include #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #using #include #include "helper-net.h" #include "bitmapmat-net.h" #include "motiondetector-net.h" #include "config-net.h" namespace openalprnet { } openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/openalpr-net.vcxproj000066400000000000000000000410611266464252400266660ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {4044340C-C435-4A1F-8F12-0806C38AE3B6} v4.0 ManagedCProj openalprnet 2.1.0 v120 v140 ..\..\..\..\windows None $(OpenALPRWindowsDir)\build\dist\$(OpenALPRVersion)\$(PlatformToolset)\$(Configuration)\$(Platform) $(OpenALPRWindowsDir)\build\dist\$(OpenALPRVersion)\$(PlatformToolset)\$(Configuration)\$(Platform)_CUDA_Fermi $(OpenALPRWindowsDir)\build\dist\$(OpenALPRVersion)\$(PlatformToolset)\$(Configuration)\$(Platform)_CUDA_Kepler 300 303 170 d -debug DynamicLibrary true true Unicode DynamicLibrary true true Unicode DynamicLibrary false true Unicode false DynamicLibrary false true Unicode false true true false false Level3 Disabled WIN32;_DEBUG;WINDOWS;%(PreprocessorDefinitions) Use $(OpenALPRWindowsDir)\tesseract-ocr\src\api;$(OpenALPRWindowsDir)\tesseract-ocr\src\ccstruct;$(OpenALPRWindowsDir)\tesseract-ocr\src\ccmain;$(OpenALPRWindowsDir)\tesseract-ocr\src\ccutil;$(OpenALPRWindowsDir)\opencv;$(OpenALPRWindowsDir)\opencv\include;$(OpenALPRWindowsDir)\opencv\include\opencv;$(OpenALPRWindowsDir)\opencv\modules\core\include;$(OpenALPRWindowsDir)\opencv\modules\flann\include;$(OpenALPRWindowsDir)\opencv\modules\imgproc\include;$(OpenALPRWindowsDir)\opencv\modules\highgui\include;$(OpenALPRWindowsDir)\opencv\modules\features2d\include;$(OpenALPRWindowsDir)\opencv\modules\calib3d\include;$(OpenALPRWindowsDir)\opencv\modules\ml\include;$(OpenALPRWindowsDir)\opencv\modules\video\include;$(OpenALPRWindowsDir)\opencv\modules\legacy\include;$(OpenALPRWindowsDir)\opencv\modules\objdetect\include;$(OpenALPRWindowsDir)\opencv\modules\photo\include;$(OpenALPRWindowsDir)\opencv\modules\gpu\include;$(OpenALPRWindowsDir)\opencv\modules\nonfree\include;$(OpenALPRWindowsDir)\opencv\modules\contrib\include;$(OpenALPRWindowsDir)\opencv\modules\stitching\include;$(OpenALPRWindowsDir)\opencv\modules\ts\include;$(OpenALPRWindowsDir)\opencv\modules\videostab\include;$(OpenALPRWindowsDir)\..\src\openalpr;$(OpenALPRWindowsDir)\opencv\modules\hal\include;$(OpenALPRWindowsDir)\opencv\modules\imgcodecs\include;$(OpenALPRWindowsDir)\opencv\modules\videoio\include;%(AdditionalIncludeDirectories) true kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;$(OpenALPRDistDir)\libtesseract$(TesseractVersion)-static$(TesseractDebugPrefix).lib;$(OpenALPRDistDir)\liblept$(LeptonicaVersion)$(DebugPrefix).lib;ws2_32.lib;$(OpenALPRDistDir)\support.lib;$(OpenALPRDistDir)\openalpr-static.lib;$(OpenALPRDistDir)\statedetection.lib;$(OpenALPRDistDir)\opencv_world$(OpenCVVersion)$(DebugPrefix).lib Level3 Disabled WIN32;_DEBUG;WINDOWS;%(PreprocessorDefinitions) Use $(OpenALPRWindowsDir)\tesseract-ocr\src\api;$(OpenALPRWindowsDir)\tesseract-ocr\src\ccstruct;$(OpenALPRWindowsDir)\tesseract-ocr\src\ccmain;$(OpenALPRWindowsDir)\tesseract-ocr\src\ccutil;$(OpenALPRWindowsDir)\opencv;$(OpenALPRWindowsDir)\opencv\include;$(OpenALPRWindowsDir)\opencv\include\opencv;$(OpenALPRWindowsDir)\opencv\modules\core\include;$(OpenALPRWindowsDir)\opencv\modules\flann\include;$(OpenALPRWindowsDir)\opencv\modules\imgproc\include;$(OpenALPRWindowsDir)\opencv\modules\highgui\include;$(OpenALPRWindowsDir)\opencv\modules\features2d\include;$(OpenALPRWindowsDir)\opencv\modules\calib3d\include;$(OpenALPRWindowsDir)\opencv\modules\ml\include;$(OpenALPRWindowsDir)\opencv\modules\video\include;$(OpenALPRWindowsDir)\opencv\modules\legacy\include;$(OpenALPRWindowsDir)\opencv\modules\objdetect\include;$(OpenALPRWindowsDir)\opencv\modules\photo\include;$(OpenALPRWindowsDir)\opencv\modules\gpu\include;$(OpenALPRWindowsDir)\opencv\modules\nonfree\include;$(OpenALPRWindowsDir)\opencv\modules\contrib\include;$(OpenALPRWindowsDir)\opencv\modules\stitching\include;$(OpenALPRWindowsDir)\opencv\modules\ts\include;$(OpenALPRWindowsDir)\opencv\modules\videostab\include;$(OpenALPRWindowsDir)\..\src\openalpr;$(OpenALPRWindowsDir)\opencv\modules\hal\include;$(OpenALPRWindowsDir)\opencv\modules\imgcodecs\include;$(OpenALPRWindowsDir)\opencv\modules\videoio\include;%(AdditionalIncludeDirectories) true kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;$(OpenALPRDistDir)\libtesseract$(TesseractVersion)-static$(TesseractDebugPrefix).lib;$(OpenALPRDistDir)\liblept$(LeptonicaVersion)$(DebugPrefix).lib;ws2_32.lib;$(OpenALPRDistDir)\support.lib;$(OpenALPRDistDir)\openalpr-static.lib;$(OpenALPRDistDir)\statedetection.lib;$(OpenALPRDistDir)\opencv_world$(OpenCVVersion)$(DebugPrefix).lib Level3 WIN32;NDEBUG;WINDOWS;%(PreprocessorDefinitions) Use $(OpenALPRWindowsDir)\tesseract-ocr\src\api;$(OpenALPRWindowsDir)\tesseract-ocr\src\ccstruct;$(OpenALPRWindowsDir)\tesseract-ocr\src\ccmain;$(OpenALPRWindowsDir)\tesseract-ocr\src\ccutil;$(OpenALPRWindowsDir)\opencv;$(OpenALPRWindowsDir)\opencv\include;$(OpenALPRWindowsDir)\opencv\include\opencv;$(OpenALPRWindowsDir)\opencv\modules\core\include;$(OpenALPRWindowsDir)\opencv\modules\flann\include;$(OpenALPRWindowsDir)\opencv\modules\imgproc\include;$(OpenALPRWindowsDir)\opencv\modules\highgui\include;$(OpenALPRWindowsDir)\opencv\modules\features2d\include;$(OpenALPRWindowsDir)\opencv\modules\calib3d\include;$(OpenALPRWindowsDir)\opencv\modules\ml\include;$(OpenALPRWindowsDir)\opencv\modules\video\include;$(OpenALPRWindowsDir)\opencv\modules\legacy\include;$(OpenALPRWindowsDir)\opencv\modules\objdetect\include;$(OpenALPRWindowsDir)\opencv\modules\photo\include;$(OpenALPRWindowsDir)\opencv\modules\gpu\include;$(OpenALPRWindowsDir)\opencv\modules\nonfree\include;$(OpenALPRWindowsDir)\opencv\modules\contrib\include;$(OpenALPRWindowsDir)\opencv\modules\stitching\include;$(OpenALPRWindowsDir)\opencv\modules\ts\include;$(OpenALPRWindowsDir)\opencv\modules\videostab\include;$(OpenALPRWindowsDir)\..\src\openalpr;$(OpenALPRWindowsDir)\opencv\modules\hal\include;$(OpenALPRWindowsDir)\opencv\modules\imgcodecs\include;$(OpenALPRWindowsDir)\opencv\modules\videoio\include;%(AdditionalIncludeDirectories) true kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;$(OpenALPRDistDir)\opencv_world$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\libtesseract$(TesseractVersion)-static$(TesseractDebugPrefix).lib;$(OpenALPRDistDir)\liblept$(LeptonicaVersion)$(DebugPrefix).lib;ws2_32.lib;$(OpenALPRDistDir)\support.lib;$(OpenALPRDistDir)\openalpr-static.lib;$(OpenALPRDistDir)\statedetection.lib Level3 WIN32;NDEBUG;WINDOWS;%(PreprocessorDefinitions) Use $(OpenALPRWindowsDir)\tesseract-ocr\src\api;$(OpenALPRWindowsDir)\tesseract-ocr\src\ccstruct;$(OpenALPRWindowsDir)\tesseract-ocr\src\ccmain;$(OpenALPRWindowsDir)\tesseract-ocr\src\ccutil;$(OpenALPRWindowsDir)\opencv;$(OpenALPRWindowsDir)\opencv\include;$(OpenALPRWindowsDir)\opencv\include\opencv;$(OpenALPRWindowsDir)\opencv\modules\core\include;$(OpenALPRWindowsDir)\opencv\modules\flann\include;$(OpenALPRWindowsDir)\opencv\modules\imgproc\include;$(OpenALPRWindowsDir)\opencv\modules\highgui\include;$(OpenALPRWindowsDir)\opencv\modules\features2d\include;$(OpenALPRWindowsDir)\opencv\modules\calib3d\include;$(OpenALPRWindowsDir)\opencv\modules\ml\include;$(OpenALPRWindowsDir)\opencv\modules\video\include;$(OpenALPRWindowsDir)\opencv\modules\legacy\include;$(OpenALPRWindowsDir)\opencv\modules\objdetect\include;$(OpenALPRWindowsDir)\opencv\modules\photo\include;$(OpenALPRWindowsDir)\opencv\modules\gpu\include;$(OpenALPRWindowsDir)\opencv\modules\nonfree\include;$(OpenALPRWindowsDir)\opencv\modules\contrib\include;$(OpenALPRWindowsDir)\opencv\modules\stitching\include;$(OpenALPRWindowsDir)\opencv\modules\ts\include;$(OpenALPRWindowsDir)\opencv\modules\videostab\include;$(OpenALPRWindowsDir)\..\src\openalpr;$(OpenALPRWindowsDir)\opencv\modules\hal\include;$(OpenALPRWindowsDir)\opencv\modules\imgcodecs\include;$(OpenALPRWindowsDir)\opencv\modules\videoio\include;%(AdditionalIncludeDirectories) true kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;$(OpenALPRDistDir)\opencv_world$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\libtesseract$(TesseractVersion)-static$(TesseractDebugPrefix).lib;$(OpenALPRDistDir)\liblept$(LeptonicaVersion)$(DebugPrefix).lib;ws2_32.lib;$(OpenALPRDistDir)\support.lib;$(OpenALPRDistDir)\openalpr-static.lib;$(OpenALPRDistDir)\statedetection.lib Create Create Create Create openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/openalpr-net.vcxproj.filters000066400000000000000000000045721266464252400303430ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Resource Files Resource Files openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/openalpr-net.vcxproj.user000066400000000000000000000002151266464252400276370ustar00rootroot00000000000000 openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/resource.h000066400000000000000000000001301266464252400246350ustar00rootroot00000000000000//{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by app.rc openalpr_2.2.4.orig/src/bindings/csharp/openalpr-net/types-net.h000066400000000000000000000005751266464252400247530ustar00rootroot00000000000000#pragma once #include "alpr.h" // alpr #include "config.h" // alpr using namespace alpr; namespace openalprnet { namespace types { public enum class AlprDetectorTypeNet : int { DetectorLbpCpu = alpr::DETECTOR_LBP_CPU, DetectorLbpGpu = alpr::DETECTOR_LBP_GPU, DetectorLbpMorphCpu = alpr::DETECTOR_MORPH_CPU, DetectorLbpOpencl = alpr::DETECTOR_LBP_OPENCL }; } }openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-cli/000077500000000000000000000000001266464252400233335ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-cli/CommandLine.cs000066400000000000000000000064151266464252400260560ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; namespace openalprnet_cli { internal static class CommandLine { private const string NameGroup = "name"; // Names of capture groups private const string ValueGroup = "value"; /* The regex that extracts names and comma-separated values for switches in the form ([="value 1",value2,...])+ */ private static readonly Regex RexPattern = new Regex(@"(?[^=]+)=?((?\""?)(?(?(quoted)[^\""]+|[^,]+))\""?,?)*", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase); public static void Process(this string[] args, Action printUsage, params Switch[] switches) { /* Run through all matches in the argument list and if any of the switches match, get the values and invoke the handler we were given. We do a Sum() here for 2 reasons; a) To actually run the handlers and b) see if any were invoked at all (each returns 1 if invoked). If none were invoked, we simply invoke the printUsage handler. */ if ((from arg in args from Match match in RexPattern.Matches(arg) from s in switches where match.Success && ((string.Compare(match.Groups[NameGroup].Value, s.Name, true) == 0) || (string.Compare(match.Groups[NameGroup].Value, s.ShortForm, true) == 0)) select s.InvokeHandler(match.Groups[ValueGroup].Value.Split(','))).Sum() == 0) printUsage(); // We didn't find any switches } public class Switch // Class that encapsulates switch data. { public Switch(string name, Action> handler, string shortForm) { Name = name; Handler = handler; ShortForm = shortForm; } public Switch(string name, Action> handler) { Name = name; Handler = handler; ShortForm = null; } public string Name { get; private set; } public string ShortForm { get; private set; } public Action> Handler { get; private set; } public int InvokeHandler(string[] values) { Handler(values); return 1; } } } }openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-cli/Program.cs000066400000000000000000000121761266464252400253000ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using openalprnet; namespace openalprnet_cli { internal class Program { public static string AssemblyDirectory { get { var codeBase = Assembly.GetExecutingAssembly().CodeBase; var uri = new UriBuilder(codeBase); var path = Uri.UnescapeDataString(uri.Path); return Path.GetDirectoryName(path); } } private static bool StrToBool(string s) { return !string.IsNullOrEmpty(s) && s.Trim() == "1"; } private static void Main(string[] args) { var region = "us"; var detectRegion = false; var benchmark = false; var json = false; var filename = string.Empty; args.Process( () => Console.WriteLine("Usage: r=us/eu b=0/1 j=0/1 d=0/1 f="), new CommandLine.Switch("r", val => { if (val.Any()) region = val.First().Trim().ToLower(); }), new CommandLine.Switch("b", val => { if (val.Any()) benchmark = StrToBool(val.First()); }), new CommandLine.Switch("j", val => { if (val.Any()) json = StrToBool(val.First()); }), new CommandLine.Switch("d", val => { if (val.Any()) detectRegion = StrToBool(val.First()); }), new CommandLine.Switch("f", val => { if (val.Any()) filename = val.First().Trim(); }) ); Console.WriteLine("OpenAlpr Version: {0}", AlprNet.GetVersion()); var config = Path.Combine(AssemblyDirectory, "openalpr.conf"); var runtime_data = Path.Combine(AssemblyDirectory, "runtime_data"); var alpr = new AlprNet(region, config, runtime_data); if (!alpr.IsLoaded()) { Console.WriteLine("OpenAlpr failed to load!"); return; } //var samplePath = Path.Combine(AssemblyDirectory, @"samples\eu-1.jpg"); //alpr.TopN = 3; alpr.DefaultRegion = region; alpr.DetectRegion = detectRegion; if (Directory.Exists(filename)) { var files = Directory.GetFiles(filename, "*.jpg", SearchOption.TopDirectoryOnly); foreach (var fname in files) { PerformAlpr(alpr, fname, benchmark, json); } return; } if (!File.Exists(filename)) { Console.WriteLine("The file doesn't exist!"); return; } var buffer = File.ReadAllBytes(filename); PerformAlpr(alpr, buffer, benchmark, json); } private static void PerformAlpr(AlprNet alpr, string filename, bool benchmark, bool writeJson) { Console.WriteLine("Processing '{0}'...\n------------------", Path.GetFileName(filename)); var buffer = File.ReadAllBytes(filename); PerformAlpr(alpr, buffer, benchmark, writeJson); } private static void PerformAlpr(AlprNet alpr, byte[] buffer, bool benchmark, bool writeJson) { var sw = Stopwatch.StartNew(); var results = alpr.Recognize(buffer); sw.Stop(); if (benchmark) { Console.WriteLine("Total Time to process image(s): {0} msec(s)", sw.ElapsedMilliseconds); } if (writeJson) { //Console.WriteLine(alpr.toJson()); } else { var i = 0; foreach (var result in results.Plates) { Console.WriteLine("Plate {0}: {1} result(s)", i++, result.TopNPlates.Count); Console.WriteLine(" Processing Time: {0} msec(s)", result.ProcessingTimeMs); foreach (var plate in result.TopNPlates) { Console.WriteLine(" - {0}\t Confidence: {1}\tMatches Template: {2}", plate.Characters, plate.OverallConfidence, plate.MatchesTemplate); } } } } } }openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-cli/Properties/000077500000000000000000000000001266464252400254675ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-cli/Properties/AssemblyInfo.cs000066400000000000000000000025761266464252400304230ustar00rootroot00000000000000using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("openalprnet-cli")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("openalprnet-cli")] [assembly: AssemblyCopyright("Copyright © 2015")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("42477de2-b13e-4959-a03e-ae43c65e68f3")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-cli/openalprnet-cli.csproj000066400000000000000000000115641266464252400276600ustar00rootroot00000000000000 Debug x86 8.0.30703 2.0 {BD50C6C1-EEB9-48D2-A87C-70F5342579DD} Exe Properties openalprnet_cli openalprnet-cli v4.0 Client 512 x86 true full false bin\Debug\ DEBUG;TRACE prompt 4 x86 pdbonly true bin\Release\ TRACE prompt 4 true bin\x64\Debug\ DEBUG;TRACE full x64 bin\Debug\openalprnet-cli.exe.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false false bin\x64\Release\ TRACE true pdbonly x64 bin\Release\openalprnet-cli.exe.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules {4044340C-C435-4A1F-8F12-0806C38AE3B6} openalpr-net openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/000077500000000000000000000000001266464252400242265ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/Form1.Designer.cs000066400000000000000000000203301266464252400272760ustar00rootroot00000000000000namespace openalprnet_windemo { partial class Form1 { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.btnDetect = new System.Windows.Forms.Button(); this.picLicensePlate = new System.Windows.Forms.PictureBox(); this.picOriginal = new System.Windows.Forms.PictureBox(); this.lbxPlates = new System.Windows.Forms.ListBox(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.openFileDialog = new System.Windows.Forms.OpenFileDialog(); this.label4 = new System.Windows.Forms.Label(); this.rbUSA = new System.Windows.Forms.RadioButton(); this.rbEU = new System.Windows.Forms.RadioButton(); ((System.ComponentModel.ISupportInitialize)(this.picLicensePlate)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.picOriginal)).BeginInit(); this.SuspendLayout(); // // btnDetect // this.btnDetect.Location = new System.Drawing.Point(830, 25); this.btnDetect.Name = "btnDetect"; this.btnDetect.Size = new System.Drawing.Size(291, 37); this.btnDetect.TabIndex = 0; this.btnDetect.Text = "Detect License Plate"; this.btnDetect.UseVisualStyleBackColor = true; this.btnDetect.Click += new System.EventHandler(this.button1_Click); // // picLicensePlate // this.picLicensePlate.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.picLicensePlate.Location = new System.Drawing.Point(827, 403); this.picLicensePlate.Name = "picLicensePlate"; this.picLicensePlate.Size = new System.Drawing.Size(294, 123); this.picLicensePlate.TabIndex = 1; this.picLicensePlate.TabStop = false; // // picOriginal // this.picOriginal.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.picOriginal.Location = new System.Drawing.Point(12, 25); this.picOriginal.Name = "picOriginal"; this.picOriginal.Size = new System.Drawing.Size(809, 501); this.picOriginal.TabIndex = 2; this.picOriginal.TabStop = false; // // lbxPlates // this.lbxPlates.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lbxPlates.FormattingEnabled = true; this.lbxPlates.Location = new System.Drawing.Point(827, 81); this.lbxPlates.Name = "lbxPlates"; this.lbxPlates.Size = new System.Drawing.Size(294, 303); this.lbxPlates.TabIndex = 3; // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(12, 9); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(76, 13); this.label1.TabIndex = 4; this.label1.Text = "Source Image:"; // // label2 // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(827, 387); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(96, 13); this.label2.TabIndex = 5; this.label2.Text = "License Plate ROI:"; // // label3 // this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(827, 65); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(124, 13); this.label3.TabIndex = 6; this.label3.Text = "Matched License Plates:"; // // openFileDialog // this.openFileDialog.Filter = "Image files (*.jpg, *.jpeg, *.jpe, *.jfif, *.png) | *.jpg; *.jpeg; *.jpe; *.jfif;" + " *.png|All files (*.*)|*.*"; // // label4 // this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(827, 9); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(44, 13); this.label4.TabIndex = 7; this.label4.Text = "Region:"; // // rbUSA // this.rbUSA.AutoSize = true; this.rbUSA.Checked = true; this.rbUSA.Location = new System.Drawing.Point(878, 7); this.rbUSA.Name = "rbUSA"; this.rbUSA.Size = new System.Drawing.Size(40, 17); this.rbUSA.TabIndex = 8; this.rbUSA.TabStop = true; this.rbUSA.Text = "US"; this.rbUSA.UseVisualStyleBackColor = true; // // rbEU // this.rbEU.AutoSize = true; this.rbEU.Location = new System.Drawing.Point(924, 7); this.rbEU.Name = "rbEU"; this.rbEU.Size = new System.Drawing.Size(40, 17); this.rbEU.TabIndex = 9; this.rbEU.Text = "EU"; this.rbEU.UseVisualStyleBackColor = true; // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.White; this.ClientSize = new System.Drawing.Size(1128, 538); this.Controls.Add(this.rbEU); this.Controls.Add(this.rbUSA); this.Controls.Add(this.label4); this.Controls.Add(this.label3); this.Controls.Add(this.label2); this.Controls.Add(this.label1); this.Controls.Add(this.lbxPlates); this.Controls.Add(this.picOriginal); this.Controls.Add(this.picLicensePlate); this.Controls.Add(this.btnDetect); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "Form1"; this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "OpenALPR-Net Demo"; this.Load += new System.EventHandler(this.Form1_Load); ((System.ComponentModel.ISupportInitialize)(this.picLicensePlate)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.picOriginal)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Button btnDetect; private System.Windows.Forms.PictureBox picLicensePlate; private System.Windows.Forms.PictureBox picOriginal; private System.Windows.Forms.ListBox lbxPlates; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.OpenFileDialog openFileDialog; private System.Windows.Forms.Label label4; private System.Windows.Forms.RadioButton rbUSA; private System.Windows.Forms.RadioButton rbEU; } } openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/Form1.cs000066400000000000000000000140361266464252400255450ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; using System.Windows.Forms; using openalprnet; namespace openalprnet_windemo { public partial class Form1 : Form { public Form1() { InitializeComponent(); } public static string AssemblyDirectory { get { var codeBase = Assembly.GetExecutingAssembly().CodeBase; var uri = new UriBuilder(codeBase); var path = Uri.UnescapeDataString(uri.Path); return Path.GetDirectoryName(path); } } public Rectangle boundingRectangle(List points) { // Add checks here, if necessary, to make sure that points is not null, // and that it contains at least one (or perhaps two?) elements var minX = points.Min(p => p.X); var minY = points.Min(p => p.Y); var maxX = points.Max(p => p.X); var maxY = points.Max(p => p.Y); return new Rectangle(new Point(minX, minY), new Size(maxX - minX, maxY - minY)); } private static Image cropImage(Image img, Rectangle cropArea) { var bmpImage = new Bitmap(img); return bmpImage.Clone(cropArea, bmpImage.PixelFormat); } public static Bitmap combineImages(List images) { //read all images into memory Bitmap finalImage = null; try { var width = 0; var height = 0; foreach (var bmp in images) { width += bmp.Width; height = bmp.Height > height ? bmp.Height : height; } //create a bitmap to hold the combined image finalImage = new Bitmap(width, height); //get a graphics object from the image so we can draw on it using (var g = Graphics.FromImage(finalImage)) { //set background color g.Clear(Color.Black); //go through each image and draw it on the final image var offset = 0; foreach (Bitmap image in images) { g.DrawImage(image, new Rectangle(offset, 0, image.Width, image.Height)); offset += image.Width; } } return finalImage; } catch (Exception ex) { if (finalImage != null) finalImage.Dispose(); throw ex; } finally { //clean up memory foreach (var image in images) { image.Dispose(); } } } private void button1_Click(object sender, EventArgs e) { if (openFileDialog.ShowDialog(this) == DialogResult.OK) { processImageFile(openFileDialog.FileName); } } private void processImageFile(string fileName) { resetControls(); var region = rbUSA.Checked ? "us" : "eu"; String config_file = Path.Combine(AssemblyDirectory, "openalpr.conf"); String runtime_data_dir = Path.Combine(AssemblyDirectory, "runtime_data"); using (var alpr = new AlprNet(region, config_file, runtime_data_dir)) { if (!alpr.IsLoaded()) { lbxPlates.Items.Add("Error initializing OpenALPR"); return; } picOriginal.ImageLocation = fileName; picOriginal.Load(); var results = alpr.Recognize(fileName); var images = new List(results.Plates.Count()); var i = 1; foreach (var result in results.Plates) { var rect = boundingRectangle(result.PlatePoints); var img = Image.FromFile(fileName); var cropped = cropImage(img, rect); images.Add(cropped); lbxPlates.Items.Add("\t\t-- Plate #" + i++ + " --"); foreach (var plate in result.TopNPlates) { lbxPlates.Items.Add(string.Format(@"{0} {1}% {2}", plate.Characters.PadRight(12), plate.OverallConfidence.ToString("N1").PadLeft(8), plate.MatchesTemplate.ToString().PadLeft(8))); } } if (images.Any()) { picLicensePlate.Image = combineImages(images); } } } private void resetControls() { picOriginal.Image = null; picLicensePlate.Image = null; lbxPlates.Items.Clear(); } private void Form1_Load(object sender, EventArgs e) { resetControls(); } } }openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/Form1.resx000066400000000000000000000134111266464252400261150ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/Program.cs000066400000000000000000000022361266464252400261670ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace openalprnet_windemo { static class Program { /// /// The main entry point for the application. /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } } openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/Properties/000077500000000000000000000000001266464252400263625ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/Properties/AssemblyInfo.cs000066400000000000000000000026061266464252400313100ustar00rootroot00000000000000using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("openalprnet-windemo")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("openalprnet-windemo")] [assembly: AssemblyCopyright("Copyright © 2015")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("b1ab96ca-afe9-497d-9aa0-74ace195dfca")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/Properties/Resources.Designer.cs000066400000000000000000000053631266464252400324310ustar00rootroot00000000000000//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.34209 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace openalprnet_windemo.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("openalprnet_windemo.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } } } openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/Properties/Resources.resx000066400000000000000000000125701266464252400312440ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/Properties/Settings.Designer.cs000066400000000000000000000020641266464252400322520ustar00rootroot00000000000000//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.34209 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace openalprnet_windemo.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); public static Settings Default { get { return defaultInstance; } } } } openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/Properties/Settings.settings000066400000000000000000000003621266464252400317450ustar00rootroot00000000000000 openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/app.config000066400000000000000000000002341266464252400261740ustar00rootroot00000000000000 openalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/appicon.ico000066400000000000000000000021761266464252400263610ustar00rootroot00000000000000 h(  @aaas`s`s`s`s`s`s`s`s`s`s`s`s`s`ÌIDBmtropenalpr_2.2.4.orig/src/bindings/csharp/openalprnet-windemo/openalprnet-windemo.csproj000066400000000000000000000143061266464252400314430ustar00rootroot00000000000000 Debug x86 8.0.30703 2.0 {C7863A14-55D2-4389-9072-04AA6E30AAD1} WinExe Properties openalprnet_windemo openalprnet-windemo v4.0 Client 512 x86 true full false bin\Debug\ DEBUG;TRACE prompt 4 x86 pdbonly true bin\Release\ TRACE prompt 4 true bin\x64\Debug\ DEBUG;TRACE full x64 bin\Debug\openalprnet-windemo.exe.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true bin\x64\Release\ TRACE true pdbonly x64 bin\Release\openalprnet-windemo.exe.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true appicon.ico Form Form1.cs Form1.cs ResXFileCodeGenerator Resources.Designer.cs Designer True Resources.resx True SettingsSingleFileGenerator Settings.Designer.cs True Settings.settings True {4044340C-C435-4A1F-8F12-0806C38AE3B6} openalpr-net openalpr_2.2.4.orig/src/bindings/go/000077500000000000000000000000001266464252400173645ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/go/CMakeLists.txt000066400000000000000000000016541266464252400221320ustar00rootroot00000000000000cmake_minimum_required (VERSION 2.6) set(go_source_files openalprgo.cpp ) add_library(openalprgo SHARED ${go_source_files}) set_target_properties(openalprgo PROPERTIES SOVERSION ${OPENALPR_MAJOR_VERSION}) TARGET_LINK_LIBRARIES(openalprgo openalpr) if (NOT WIN32) #generate the .pc file SET(prefix "${CMAKE_INSTALL_PREFIX}") SET(exec_prefix "\${prefix}") SET(libdir "\${exec_prefix}/lib") SET(includedir "\${exec_prefix}/include") SET(VERSION "${OPENALPR_VERSION}") CONFIGURE_FILE( "${CMAKE_MODULE_PATH}/templates/openalpr.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/openalpr.pc" @ONLY) install (FILES "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/openalpr.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig") ENDIF() install (TARGETS openalprgo DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) install (FILES openalprgo.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include) openalpr_2.2.4.orig/src/bindings/go/main.go000066400000000000000000000013151266464252400206370ustar00rootroot00000000000000package main import ( "fmt" "io/ioutil" ) import "github.com/openalpr/openalpr/src/bindings/go/openalpr" func main() { alpr := openalpr.NewAlpr("us", "", "../../../runtime_data") defer alpr.Unload() if !alpr.IsLoaded() { fmt.Println("OpenAlpr failed to load!") return } alpr.SetTopN(20) fmt.Println(alpr.IsLoaded()) fmt.Println(openalpr.GetVersion()) resultFromFilePath, err := alpr.RecognizeByFilePath("lp.jpg") if err != nil { fmt.Println(err) } fmt.Printf("%+v\n", resultFromFilePath) fmt.Printf("\n\n\n") imageBytes, err := ioutil.ReadFile("lp.jpg") if err != nil { fmt.Println(err) } resultFromBlob, err := alpr.RecognizeByBlob(imageBytes) fmt.Printf("%+v\n", resultFromBlob) } openalpr_2.2.4.orig/src/bindings/go/make.sh000066400000000000000000000005101266464252400206310ustar00rootroot00000000000000#!/bin/bash OPENALPR_INCLUDE_DIR=$(pwd)/../../openalpr OPENALPR_LIB_DIR=$(pwd)/../../build/openalpr export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.:${OPENALPR_LIB_DIR} g++ -Wall -L${OPENALPR_LIB_DIR} -I${OPENALPR_INCLUDE_DIR} -shared -fPIC -o libopenalprgo.so openalprgo.cpp -lopenalpr (cd openalpr && go install) go run main.go openalpr_2.2.4.orig/src/bindings/go/openalpr/000077500000000000000000000000001266464252400212045ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/go/openalpr/openalpr.go000066400000000000000000000066011266464252400233560ustar00rootroot00000000000000package openalpr /* #cgo pkg-config: openalpr #include #include */ import "C" import ( "encoding/json" "fmt" "unsafe" ) type Alpr struct { //Country string //configFile string //runtimeDir string cAlpr C.Alpr } type AlprResults struct { EpochTime int64 `json:"epoch_time"` ImgWidth int `json:"img_witdh"` ImgHeight int `json:"img_height"` TotalProcessingTimeMs float32 `json:"processing_time_ms"` Plates []AlprPlateResult `json:"results"` RegionsOfInterest []AlprRegionOfInterest `json:"regions_of_interest"` } type AlprPlate struct { Characters string `json:"plate"` OverallConfidence float32 `json:"confidence"` MatchesTemplate bool } type AlprRegionOfInterest struct { X int `json:"x"` Y int `json:"y"` Width int `json:"width"` Height int `json:"height"` } type AlprCoordinate struct { X int `json:"x"` Y int `json:"y"` } type AlprPlateResult struct { RequestedTopN int `json:"requested_topn"` BestPlate string `json:"plate"` TopNPlates []AlprPlate `json:"candidates"` ProcessingTimeMs float32 `json:"processing_time_ms"` PlatePoints []AlprCoordinate `json:"coordinates"` PlateIndex int `json:"plate_index"` RegionConfidence int `json:"region_confidence"` Region string `json:"region"` } func bool2Cint(b bool) C.int { if b { return 1 } else { return 0 } } func cint2Bool(i C.int) bool { if i == 0 { return false } else { return true } } func NewAlpr(country string, configFile string, runtimeDir string) *Alpr { cstrCountry := C.CString(country) cstrConfigFile := C.CString(configFile) cstrRuntimeDir := C.CString(runtimeDir) defer C.free(unsafe.Pointer(cstrCountry)) defer C.free(unsafe.Pointer(cstrConfigFile)) defer C.free(unsafe.Pointer(cstrRuntimeDir)) alpr := C.AlprInit(cstrCountry, cstrConfigFile, cstrRuntimeDir) return &Alpr{cAlpr: alpr} } func (alpr *Alpr) SetDetectRegion(detectRegion bool) { C.SetDetectRegion(alpr.cAlpr, bool2Cint(detectRegion)) } func (alpr *Alpr) SetTopN(topN int) { C.SetTopN(alpr.cAlpr, C.int(topN)) } func (alpr *Alpr) SetDefaultRegion(region string) { cstrRegion := C.CString(region) defer C.free(unsafe.Pointer(cstrRegion)) C.SetDefaultRegion(alpr.cAlpr, cstrRegion) } func (alpr *Alpr) IsLoaded() bool { return cint2Bool(C.IsLoaded(alpr.cAlpr)) } func GetVersion() string { return C.GoString(C.GetVersion()) } func (alpr *Alpr) RecognizeByFilePath(filePath string) (AlprResults, error) { cstrFilePath := C.CString(filePath) defer C.free(unsafe.Pointer(cstrFilePath)) stringResult := C.GoString(C.RecognizeByFilePath(alpr.cAlpr, cstrFilePath)) fmt.Println(stringResult) var results AlprResults err := json.Unmarshal([]byte(stringResult), &results) return results, err } func (alpr *Alpr) RecognizeByBlob(imageBytes []byte) (AlprResults, error) { stringImageBytes := string(imageBytes) cstrImageBytes := C.CString(stringImageBytes) defer C.free(unsafe.Pointer(cstrImageBytes)) stringResult := C.GoString(C.RecognizeByBlob(alpr.cAlpr, cstrImageBytes, C.int(len(imageBytes)))) var results AlprResults err := json.Unmarshal([]byte(stringResult), &results) return results, err } func (alpr *Alpr) Unload() { C.Unload(alpr.cAlpr) } openalpr_2.2.4.orig/src/bindings/go/openalprgo.cpp000066400000000000000000000043631266464252400222440ustar00rootroot00000000000000#include #include #include #include #include "openalprgo.h" extern "C" { #if defined(_MSC_VER) // Microsoft #define OPENALPR_EXPORT __declspec(dllexport) #else // do nothing #define OPENALPR_EXPORT #endif //using namespace alpr; OPENALPR_EXPORT Alpr AlprInit(char* country, char* configFile, char* runtimeDir) { alpr::Alpr* alpr = new alpr::Alpr(country, configFile, runtimeDir); return (void*)alpr; } OPENALPR_EXPORT void SetDetectRegion(Alpr alpr, int detectRegion) { alpr::Alpr* cxxalpr = (alpr::Alpr*) alpr; cxxalpr->setDetectRegion(detectRegion); } OPENALPR_EXPORT void SetTopN(Alpr alpr, int topN) { alpr::Alpr* cxxalpr = (alpr::Alpr*) alpr; cxxalpr->setTopN(topN); } OPENALPR_EXPORT void SetDefaultRegion(Alpr alpr, char* region) { alpr::Alpr* cxxalpr = (alpr::Alpr*) alpr; cxxalpr->setDefaultRegion(region); } OPENALPR_EXPORT int IsLoaded(Alpr alpr) { alpr::Alpr* cxxalpr = (alpr::Alpr*) alpr; return cxxalpr->isLoaded(); } OPENALPR_EXPORT void Unload(Alpr alpr) { alpr::Alpr* cxxalpr = (alpr::Alpr*) alpr; delete cxxalpr; } OPENALPR_EXPORT char* RecognizeByFilePath(Alpr alpr, char* filePath) { alpr::Alpr* cxxalpr = (alpr::Alpr*) alpr; alpr::AlprResults result = cxxalpr->recognize(filePath); std::string resultString = alpr::Alpr::toJson(result); char *cstr = new char[resultString.length() + 1]; strcpy(cstr, resultString.c_str()); return cstr; } OPENALPR_EXPORT char* RecognizeByBlob(Alpr alpr, char* imageBytes, int len) { alpr::Alpr* cxxalpr = (alpr::Alpr*) alpr; std::vector vec(imageBytes, imageBytes + len); alpr::AlprResults result = cxxalpr->recognize(vec); std::string resultString = alpr::Alpr::toJson(result); char *cstr = new char[resultString.length() + 1]; strcpy(cstr, resultString.c_str()); return cstr; } OPENALPR_EXPORT char* GetVersion() { std::string version = alpr::Alpr::getVersion(); char *cstr = new char[version.length() + 1]; strcpy(cstr, version.c_str()); return cstr; } } openalpr_2.2.4.orig/src/bindings/go/openalprgo.h000066400000000000000000000010001266464252400216720ustar00rootroot00000000000000#ifdef __cplusplus extern "C" { #endif typedef void* Alpr; Alpr AlprInit(char* country, char* configFile, char* runtimeDir); void SetDetectRegion(Alpr alpr, int detectRegion); void SetTopN(Alpr alpr, int topN); void SetDefaultRegion(Alpr alpr, char* region); int IsLoaded(Alpr alpr); void Unload(Alpr alpr); char* RecognizeByFilePath(Alpr alpr, char* filePath); char* RecognizeByBlob(Alpr alpr, char* imageBytes, int len); char* GetVersion(); #ifdef __cplusplus } #endif openalpr_2.2.4.orig/src/bindings/java/000077500000000000000000000000001266464252400177005ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/java/CMakeLists.txt000066400000000000000000000011221266464252400224340ustar00rootroot00000000000000 cmake_minimum_required (VERSION 2.6) find_package(JNI) if (${JNI_FOUND} MATCHES "TRUE") include_directories( ../../openalpr/ ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2} ) set(jni_source_files openalprjni.cpp ) add_library(openalprjni SHARED ${jni_source_files}) set_target_properties(openalprjni PROPERTIES SOVERSION ${OPENALPR_MAJOR_VERSION}) TARGET_LINK_LIBRARIES(openalprjni openalpr) install (TARGETS openalprjni DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) else() MESSAGE("JAVA JNI library not found, skipping openalprjni compilation") endif()openalpr_2.2.4.orig/src/bindings/java/com_openalpr_jni_Alpr.h000066400000000000000000000041021266464252400243420ustar00rootroot00000000000000/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_openalpr_jni_Alpr */ #ifndef _Included_com_openalpr_jni_Alpr #define _Included_com_openalpr_jni_Alpr #ifdef __cplusplus extern "C" { #endif /* * Class: com_openalpr_jni_Alpr * Method: initialize * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_openalpr_jni_Alpr_initialize (JNIEnv *, jobject, jstring, jstring, jstring); /* * Class: com_openalpr_jni_Alpr * Method: dispose * Signature: ()V */ JNIEXPORT void JNICALL Java_com_openalpr_jni_Alpr_dispose (JNIEnv *, jobject); /* * Class: com_openalpr_jni_Alpr * Method: is_loaded * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_com_openalpr_jni_Alpr_is_1loaded (JNIEnv *, jobject); /* * Class: com_openalpr_jni_Alpr * Method: native_recognize * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_openalpr_jni_Alpr_native_1recognize__Ljava_lang_String_2 (JNIEnv *, jobject, jstring); /* * Class: com_openalpr_jni_Alpr * Method: native_recognize * Signature: ([B)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_openalpr_jni_Alpr_native_1recognize___3B (JNIEnv *, jobject, jbyteArray); /* * Class: com_openalpr_jni_Alpr * Method: set_default_region * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_openalpr_jni_Alpr_set_1default_1region (JNIEnv *, jobject, jstring); /* * Class: com_openalpr_jni_Alpr * Method: detect_region * Signature: (Z)V */ JNIEXPORT void JNICALL Java_com_openalpr_jni_Alpr_detect_1region (JNIEnv *, jobject, jboolean); /* * Class: com_openalpr_jni_Alpr * Method: set_top_n * Signature: (I)V */ JNIEXPORT void JNICALL Java_com_openalpr_jni_Alpr_set_1top_1n (JNIEnv *, jobject, jint); /* * Class: com_openalpr_jni_Alpr * Method: get_version * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_openalpr_jni_Alpr_get_1version (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif openalpr_2.2.4.orig/src/bindings/java/make.sh000077500000000000000000000012671266464252400211620ustar00rootroot00000000000000#!/bin/sh # openbsd 4.9 # gcc 4.2.1 # openjdk 1.7.0 OPENALPR_INCLUDE_DIR=/storage/projects/alpr/src/openalpr/ OPENALPR_LIB_DIR=/storage/projects/alpr/src/build/openalpr/ JAVA_PATH=/usr/lib/jvm/java-1.7.0-openjdk-amd64 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.:${OPENALPR_LIB_DIR} # Compile java javac -Xlint:unchecked src/com/openalpr/jni/json/*.java src/com/openalpr/jni/*.java src/Main.java # Create native header from Alpr java file javah -classpath src com.openalpr.jni.Alpr # Compile/link native interface g++ -Wall -L${OPENALPR_LIB_DIR} -I${JAVA_PATH}/include/ -I${OPENALPR_INCLUDE_DIR} -shared -fPIC -o libopenalprjni.so openalprjni.cpp -lopenalpr # Test java -classpath src Mainopenalpr_2.2.4.orig/src/bindings/java/openalprjni.cpp000066400000000000000000000066551266464252400227410ustar00rootroot00000000000000#include #include "com_openalpr_jni_Alpr.h" using namespace alpr; bool initialized = false; static Alpr* nativeAlpr; JNIEXPORT void JNICALL Java_com_openalpr_jni_Alpr_initialize (JNIEnv *env, jobject thisObj, jstring jcountry, jstring jconfigFile, jstring jruntimeDir) { //printf("Initialize"); // Convert strings from java to C++ and release resources const char *ccountry = env->GetStringUTFChars(jcountry, NULL); std::string country(ccountry); env->ReleaseStringUTFChars(jcountry, ccountry); const char *cconfigFile = env->GetStringUTFChars(jconfigFile, NULL); std::string configFile(cconfigFile); env->ReleaseStringUTFChars(jconfigFile, cconfigFile); const char *cruntimeDir = env->GetStringUTFChars(jruntimeDir, NULL); std::string runtimeDir(cruntimeDir); env->ReleaseStringUTFChars(jruntimeDir, cruntimeDir); nativeAlpr = new alpr::Alpr(country, configFile, runtimeDir); initialized = true; return; } JNIEXPORT void JNICALL Java_com_openalpr_jni_Alpr_dispose (JNIEnv *env, jobject thisObj) { //printf("Dispose"); initialized = false; delete nativeAlpr; } JNIEXPORT jboolean JNICALL Java_com_openalpr_jni_Alpr_is_1loaded (JNIEnv *env, jobject thisObj) { //printf("IS LOADED"); if (!initialized) return false; return (jboolean) nativeAlpr->isLoaded(); } JNIEXPORT jstring JNICALL Java_com_openalpr_jni_Alpr_native_1recognize__Ljava_lang_String_2 (JNIEnv *env, jobject thisObj, jstring jimageFile) { //printf("Recognize file"); // Convert strings from java to C++ and release resources const char *cimageFile = env->GetStringUTFChars(jimageFile, NULL); std::string imageFile(cimageFile); env->ReleaseStringUTFChars(jimageFile, cimageFile); AlprResults results = nativeAlpr->recognize(imageFile); std::string json = Alpr::toJson(results); return env->NewStringUTF(json.c_str()); } JNIEXPORT jstring JNICALL Java_com_openalpr_jni_Alpr_native_1recognize___3B (JNIEnv *env, jobject thisObj, jbyteArray jimageBytes) { //printf("Recognize byte array"); int len = env->GetArrayLength (jimageBytes); unsigned char* buf = new unsigned char[len]; env->GetByteArrayRegion (jimageBytes, 0, len, reinterpret_cast(buf)); std::vector cvec(buf, buf+len); AlprResults results = nativeAlpr->recognize(cvec); std::string json = Alpr::toJson(results); return env->NewStringUTF(json.c_str()); } JNIEXPORT void JNICALL Java_com_openalpr_jni_Alpr_set_1default_1region (JNIEnv *env, jobject thisObj, jstring jdefault_region) { // Convert strings from java to C++ and release resources const char *cdefault_region = env->GetStringUTFChars(jdefault_region, NULL); std::string default_region(cdefault_region); env->ReleaseStringUTFChars(jdefault_region, cdefault_region); nativeAlpr->setDefaultRegion(default_region); } JNIEXPORT void JNICALL Java_com_openalpr_jni_Alpr_detect_1region (JNIEnv *env, jobject thisObj, jboolean detect_region) { nativeAlpr->setDetectRegion(detect_region); } JNIEXPORT void JNICALL Java_com_openalpr_jni_Alpr_set_1top_1n (JNIEnv *env, jobject thisObj, jint top_n) { nativeAlpr->setTopN(top_n); } JNIEXPORT jstring JNICALL Java_com_openalpr_jni_Alpr_get_1version (JNIEnv *env, jobject thisObj) { std::string version = nativeAlpr->getVersion(); return env->NewStringUTF(version.c_str()); }openalpr_2.2.4.orig/src/bindings/java/src/000077500000000000000000000000001266464252400204675ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/java/src/Main.java000066400000000000000000000041111266464252400222130ustar00rootroot00000000000000import com.openalpr.jni.Alpr; import com.openalpr.jni.AlprPlate; import com.openalpr.jni.AlprPlateResult; import com.openalpr.jni.AlprResults; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.Files; public class Main { public static void main(String[] args) throws Exception { String country = "", configfile = "", runtimeDataDir = "", licensePlate = ""; if (args.length == 4) { country = args[0]; configfile = args[1]; runtimeDataDir = args[2]; licensePlate = args[3]; } else { System.err.println("Program requires 4 arguments: Country, Config File, runtime_data dir, and license plate image"); System.exit(1); } Alpr alpr = new Alpr(country, configfile, runtimeDataDir); alpr.setTopN(10); alpr.setDefaultRegion("wa"); // Read an image into a byte array and send it to OpenALPR Path path = Paths.get(licensePlate); byte[] imagedata = Files.readAllBytes(path); AlprResults results = alpr.recognize(imagedata); System.out.println("OpenALPR Version: " + alpr.getVersion()); System.out.println("Image Size: " + results.getImgWidth() + "x" + results.getImgHeight()); System.out.println("Processing Time: " + results.getTotalProcessingTimeMs() + " ms"); System.out.println("Found " + results.getPlates().size() + " results"); System.out.format(" %-15s%-8s\n", "Plate Number", "Confidence"); for (AlprPlateResult result : results.getPlates()) { for (AlprPlate plate : result.getTopNPlates()) { if (plate.isMatchesTemplate()) System.out.print(" * "); else System.out.print(" - "); System.out.format("%-15s%-8f\n", plate.getCharacters(), plate.getOverallConfidence()); } } // Make sure to call this to release memory alpr.unload(); } } openalpr_2.2.4.orig/src/bindings/java/src/com/000077500000000000000000000000001266464252400212455ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/000077500000000000000000000000001266464252400230655ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/000077500000000000000000000000001266464252400236455ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/Alpr.java000066400000000000000000000037541266464252400254170ustar00rootroot00000000000000package com.openalpr.jni; import com.openalpr.jni.json.JSONException; public class Alpr { static { // Load the OpenALPR library at runtime // openalprjni.dll (Windows) or libopenalprjni.so (Linux/Mac) System.loadLibrary("openalprjni"); } private native void initialize(String country, String configFile, String runtimeDir); private native void dispose(); private native boolean is_loaded(); private native String native_recognize(String imageFile); private native String native_recognize(byte[] imageBytes); private native void set_default_region(String region); private native void detect_region(boolean detectRegion); private native void set_top_n(int topN); private native String get_version(); public Alpr(String country, String configFile, String runtimeDir) { initialize(country, configFile, runtimeDir); } public void unload() { dispose(); } public boolean isLoaded() { return is_loaded(); } public AlprResults recognize(String imageFile) throws AlprException { try { String json = native_recognize(imageFile); return new AlprResults(json); } catch (JSONException e) { throw new AlprException("Unable to parse ALPR results"); } } public AlprResults recognize(byte[] imageBytes) throws AlprException { try { String json = native_recognize(imageBytes); return new AlprResults(json); } catch (JSONException e) { throw new AlprException("Unable to parse ALPR results"); } } public void setTopN(int topN) { set_top_n(topN); } public void setDefaultRegion(String region) { set_default_region(region); } public void setDetectRegion(boolean detectRegion) { detect_region(detectRegion); } public String getVersion() { return get_version(); } } openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/AlprCoordinate.java000066400000000000000000000007031266464252400274160ustar00rootroot00000000000000package com.openalpr.jni; import com.openalpr.jni.json.JSONException; import com.openalpr.jni.json.JSONObject; public class AlprCoordinate { private final int x; private final int y; AlprCoordinate(JSONObject coordinateObj) throws JSONException { x = coordinateObj.getInt("x"); y = coordinateObj.getInt("y"); } public int getX() { return x; } public int getY() { return y; } } openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/AlprException.java000066400000000000000000000002131266464252400272610ustar00rootroot00000000000000package com.openalpr.jni; public class AlprException extends Exception { public AlprException(String s) { super(s); } } openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/AlprPlate.java000066400000000000000000000013701266464252400263750ustar00rootroot00000000000000package com.openalpr.jni; import com.openalpr.jni.json.JSONException; import com.openalpr.jni.json.JSONObject; public class AlprPlate { private final String characters; private final float overall_confidence; private final boolean matches_template; AlprPlate(JSONObject plateObj) throws JSONException { characters = plateObj.getString("plate"); overall_confidence = (float) plateObj.getDouble("confidence"); matches_template = plateObj.getInt("matches_template") != 0; } public String getCharacters() { return characters; } public float getOverallConfidence() { return overall_confidence; } public boolean isMatchesTemplate() { return matches_template; } } openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/AlprPlateResult.java000066400000000000000000000057401266464252400276010ustar00rootroot00000000000000package com.openalpr.jni; import com.openalpr.jni.json.JSONArray; import com.openalpr.jni.json.JSONException; import com.openalpr.jni.json.JSONObject; import java.util.ArrayList; import java.util.List; public class AlprPlateResult { // The number requested is always >= the topNPlates count private final int requested_topn; // the best plate is the topNPlate with the highest confidence private final AlprPlate bestPlate; // A list of possible plate number permutations private List topNPlates; // The processing time for this plate private final float processing_time_ms; // the X/Y coordinates of the corners of the plate (clock-wise from top-left) private List plate_points; // The index of the plate if there were multiple plates returned private final int plate_index; // When region detection is enabled, this returns the region. Region detection is experimental private final int regionConfidence; private final String region; AlprPlateResult(JSONObject plateResult) throws JSONException { requested_topn = plateResult.getInt("requested_topn"); JSONArray candidatesArray = plateResult.getJSONArray("candidates"); if (candidatesArray.length() > 0) bestPlate = new AlprPlate((JSONObject) candidatesArray.get(0)); else bestPlate = null; topNPlates = new ArrayList(candidatesArray.length()); for (int i = 0; i < candidatesArray.length(); i++) { JSONObject candidateObj = (JSONObject) candidatesArray.get(i); AlprPlate newPlate = new AlprPlate(candidateObj); topNPlates.add(newPlate); } JSONArray coordinatesArray = plateResult.getJSONArray("coordinates"); plate_points = new ArrayList(coordinatesArray.length()); for (int i = 0; i < coordinatesArray.length(); i++) { JSONObject coordinateObj = (JSONObject) coordinatesArray.get(i); AlprCoordinate coordinate = new AlprCoordinate(coordinateObj); plate_points.add(coordinate); } processing_time_ms = (float) plateResult.getDouble("processing_time_ms"); plate_index = plateResult.getInt("plate_index"); regionConfidence = plateResult.getInt("region_confidence"); region = plateResult.getString("region"); } public int getRequestedTopn() { return requested_topn; } public AlprPlate getBestPlate() { return bestPlate; } public List getTopNPlates() { return topNPlates; } public float getProcessingTimeMs() { return processing_time_ms; } public List getPlatePoints() { return plate_points; } public int getPlateIndex() { return plate_index; } public int getRegionConfidence() { return regionConfidence; } public String getRegion() { return region; } } openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/AlprRegionOfInterest.java000066400000000000000000000012731266464252400305600ustar00rootroot00000000000000package com.openalpr.jni; import com.openalpr.jni.json.JSONException; import com.openalpr.jni.json.JSONObject; public class AlprRegionOfInterest { private final int x; private final int y; private final int width; private final int height; AlprRegionOfInterest(JSONObject roiObj) throws JSONException { x = roiObj.getInt("x"); y = roiObj.getInt("y"); width = roiObj.getInt("width"); height = roiObj.getInt("height"); } public int getX() { return x; } public int getY() { return y; } public int getWidth() { return width; } public int getHeight() { return height; } } openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/AlprResults.java000066400000000000000000000040431266464252400267710ustar00rootroot00000000000000package com.openalpr.jni; import com.openalpr.jni.json.JSONArray; import com.openalpr.jni.json.JSONException; import com.openalpr.jni.json.JSONObject; import java.util.ArrayList; import java.util.List; public class AlprResults { private final long epoch_time; private final int img_width; private final int img_height; private final float total_processing_time_ms; private List plates; private List regionsOfInterest; AlprResults(String json) throws JSONException { JSONObject jobj = new JSONObject(json); epoch_time = jobj.getLong("epoch_time"); img_width = jobj.getInt("img_width"); img_height = jobj.getInt("img_height"); total_processing_time_ms = (float) jobj.getDouble("processing_time_ms"); JSONArray resultsArray = jobj.getJSONArray("results"); plates = new ArrayList(resultsArray.length()); for (int i = 0; i < resultsArray.length(); i++) { JSONObject plateObj = (JSONObject) resultsArray.get(i); AlprPlateResult result = new AlprPlateResult(plateObj); plates.add(result); } JSONArray roisArray = jobj.getJSONArray("regions_of_interest"); regionsOfInterest = new ArrayList(roisArray.length()); for (int i = 0; i < roisArray.length(); i++) { JSONObject roiObj = (JSONObject) roisArray.get(i); AlprRegionOfInterest roi = new AlprRegionOfInterest(roiObj); regionsOfInterest.add(roi); } } public long getEpochTime() { return epoch_time; } public int getImgWidth() { return img_width; } public int getImgHeight() { return img_height; } public float getTotalProcessingTimeMs() { return total_processing_time_ms; } public List getPlates() { return plates; } public List getRegionsOfInterest() { return regionsOfInterest; } } openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/json/000077500000000000000000000000001266464252400246165ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/json/JSON.java000066400000000000000000000074711266464252400262430ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.openalpr.jni.json; class JSON { /** * Returns the input if it is a JSON-permissible value; throws otherwise. */ static double checkDouble(double d) throws JSONException { if (Double.isInfinite(d) || Double.isNaN(d)) { throw new JSONException("Forbidden numeric value: " + d); } return d; } static Boolean toBoolean(Object value) { if (value instanceof Boolean) { return (Boolean) value; } else if (value instanceof String) { String stringValue = (String) value; if ("true".equalsIgnoreCase(stringValue)) { return true; } else if ("false".equalsIgnoreCase(stringValue)) { return false; } } return null; } static Double toDouble(Object value) { if (value instanceof Double) { return (Double) value; } else if (value instanceof Number) { return ((Number) value).doubleValue(); } else if (value instanceof String) { try { return Double.valueOf((String) value); } catch (NumberFormatException ignored) { } } return null; } static Integer toInteger(Object value) { if (value instanceof Integer) { return (Integer) value; } else if (value instanceof Number) { return ((Number) value).intValue(); } else if (value instanceof String) { try { return (int) Double.parseDouble((String) value); } catch (NumberFormatException ignored) { } } return null; } static Long toLong(Object value) { if (value instanceof Long) { return (Long) value; } else if (value instanceof Number) { return ((Number) value).longValue(); } else if (value instanceof String) { try { return (long) Double.parseDouble((String) value); } catch (NumberFormatException ignored) { } } return null; } static String toString(Object value) { if (value instanceof String) { return (String) value; } else if (value != null) { return String.valueOf(value); } return null; } public static JSONException typeMismatch(Object indexOrName, Object actual, String requiredType) throws JSONException { if (actual == null) { throw new JSONException("Value at " + indexOrName + " is null."); } else { throw new JSONException("Value " + actual + " at " + indexOrName + " of type " + actual.getClass().getName() + " cannot be converted to " + requiredType); } } public static JSONException typeMismatch(Object actual, String requiredType) throws JSONException { if (actual == null) { throw new JSONException("Value is null."); } else { throw new JSONException("Value " + actual + " of type " + actual.getClass().getName() + " cannot be converted to " + requiredType); } } } openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/json/JSONArray.java000066400000000000000000000457101266464252400272400ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.openalpr.jni.json; import java.util.ArrayList; import java.util.Collection; import java.util.List; // Note: this class was written without inspecting the non-free org.json sourcecode. /** * A dense indexed sequence of values. Values may be any mix of * {@link JSONObject JSONObjects}, other {@link JSONArray JSONArrays}, Strings, * Booleans, Integers, Longs, Doubles, {@code null} or {@link JSONObject#NULL}. * Values may not be {@link Double#isNaN() NaNs}, {@link Double#isInfinite() * infinities}, or of any type not listed here. * *

{@code JSONArray} has the same type coercion behavior and * optional/mandatory accessors as {@link JSONObject}. See that class' * documentation for details. * *

Warning: this class represents null in two incompatible * ways: the standard Java {@code null} reference, and the sentinel value {@link * JSONObject#NULL}. In particular, {@code get} fails if the requested index * holds the null reference, but succeeds if it holds {@code JSONObject.NULL}. * *

Instances of this class are not thread safe. Although this class is * nonfinal, it was not designed for inheritance and should not be subclassed. * In particular, self-use by overridable methods is not specified. See * Effective Java Item 17, "Design and Document or inheritance or else * prohibit it" for further information. */ public class JSONArray { private final List values; /** * Creates a {@code JSONArray} with no values. */ public JSONArray() { values = new ArrayList(); } /** * Creates a new {@code JSONArray} by copying all values from the given * collection. * * @param copyFrom a collection whose values are of supported types. * Unsupported values are not permitted and will yield an array in an * inconsistent state. */ /* Accept a raw type for API compatibility */ public JSONArray(Collection copyFrom) { this(); Collection copyFromTyped = (Collection) copyFrom; values.addAll(copyFromTyped); } /** * Creates a new {@code JSONArray} with values from the next array in the * tokener. * * @param readFrom a tokener whose nextValue() method will yield a * {@code JSONArray}. * @throws JSONException if the parse fails or doesn't yield a * {@code JSONArray}. */ public JSONArray(JSONTokener readFrom) throws JSONException { /* * Getting the parser to populate this could get tricky. Instead, just * parse to temporary JSONArray and then steal the data from that. */ Object object = readFrom.nextValue(); if (object instanceof JSONArray) { values = ((JSONArray) object).values; } else { throw JSON.typeMismatch(object, "JSONArray"); } } /** * Creates a new {@code JSONArray} with values from the JSON string. * * @param json a JSON-encoded string containing an array. * @throws JSONException if the parse fails or doesn't yield a {@code * JSONArray}. */ public JSONArray(String json) throws JSONException { this(new JSONTokener(json)); } /** * Returns the number of values in this array. */ public int length() { return values.size(); } /** * Appends {@code value} to the end of this array. * * @return this array. */ public JSONArray put(boolean value) { values.add(value); return this; } /** * Appends {@code value} to the end of this array. * * @param value a finite value. May not be {@link Double#isNaN() NaNs} or * {@link Double#isInfinite() infinities}. * @return this array. */ public JSONArray put(double value) throws JSONException { values.add(JSON.checkDouble(value)); return this; } /** * Appends {@code value} to the end of this array. * * @return this array. */ public JSONArray put(int value) { values.add(value); return this; } /** * Appends {@code value} to the end of this array. * * @return this array. */ public JSONArray put(long value) { values.add(value); return this; } /** * Appends {@code value} to the end of this array. * * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May * not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite() * infinities}. Unsupported values are not permitted and will cause the * array to be in an inconsistent state. * @return this array. */ public JSONArray put(Object value) { values.add(value); return this; } /** * Sets the value at {@code index} to {@code value}, null padding this array * to the required length if necessary. If a value already exists at {@code * index}, it will be replaced. * * @return this array. */ public JSONArray put(int index, boolean value) throws JSONException { return put(index, (Boolean) value); } /** * Sets the value at {@code index} to {@code value}, null padding this array * to the required length if necessary. If a value already exists at {@code * index}, it will be replaced. * * @param value a finite value. May not be {@link Double#isNaN() NaNs} or * {@link Double#isInfinite() infinities}. * @return this array. */ public JSONArray put(int index, double value) throws JSONException { return put(index, (Double) value); } /** * Sets the value at {@code index} to {@code value}, null padding this array * to the required length if necessary. If a value already exists at {@code * index}, it will be replaced. * * @return this array. */ public JSONArray put(int index, int value) throws JSONException { return put(index, (Integer) value); } /** * Sets the value at {@code index} to {@code value}, null padding this array * to the required length if necessary. If a value already exists at {@code * index}, it will be replaced. * * @return this array. */ public JSONArray put(int index, long value) throws JSONException { return put(index, (Long) value); } /** * Sets the value at {@code index} to {@code value}, null padding this array * to the required length if necessary. If a value already exists at {@code * index}, it will be replaced. * * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May * not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite() * infinities}. * @return this array. */ public JSONArray put(int index, Object value) throws JSONException { if (value instanceof Number) { // deviate from the original by checking all Numbers, not just floats & doubles JSON.checkDouble(((Number) value).doubleValue()); } while (values.size() <= index) { values.add(null); } values.set(index, value); return this; } /** * Returns true if this array has no value at {@code index}, or if its value * is the {@code null} reference or {@link JSONObject#NULL}. */ public boolean isNull(int index) { Object value = opt(index); return value == null || value == JSONObject.NULL; } /** * Returns the value at {@code index}. * * @throws JSONException if this array has no value at {@code index}, or if * that value is the {@code null} reference. This method returns * normally if the value is {@code JSONObject#NULL}. */ public Object get(int index) throws JSONException { try { Object value = values.get(index); if (value == null) { throw new JSONException("Value at " + index + " is null."); } return value; } catch (IndexOutOfBoundsException e) { throw new JSONException("Index " + index + " out of range [0.." + values.size() + ")"); } } /** * Returns the value at {@code index}, or null if the array has no value * at {@code index}. */ public Object opt(int index) { if (index < 0 || index >= values.size()) { return null; } return values.get(index); } /** * Returns the value at {@code index} if it exists and is a boolean or can * be coerced to a boolean. * * @throws JSONException if the value at {@code index} doesn't exist or * cannot be coerced to a boolean. */ public boolean getBoolean(int index) throws JSONException { Object object = get(index); Boolean result = JSON.toBoolean(object); if (result == null) { throw JSON.typeMismatch(index, object, "boolean"); } return result; } /** * Returns the value at {@code index} if it exists and is a boolean or can * be coerced to a boolean. Returns false otherwise. */ public boolean optBoolean(int index) { return optBoolean(index, false); } /** * Returns the value at {@code index} if it exists and is a boolean or can * be coerced to a boolean. Returns {@code fallback} otherwise. */ public boolean optBoolean(int index, boolean fallback) { Object object = opt(index); Boolean result = JSON.toBoolean(object); return result != null ? result : fallback; } /** * Returns the value at {@code index} if it exists and is a double or can * be coerced to a double. * * @throws JSONException if the value at {@code index} doesn't exist or * cannot be coerced to a double. */ public double getDouble(int index) throws JSONException { Object object = get(index); Double result = JSON.toDouble(object); if (result == null) { throw JSON.typeMismatch(index, object, "double"); } return result; } /** * Returns the value at {@code index} if it exists and is a double or can * be coerced to a double. Returns {@code NaN} otherwise. */ public double optDouble(int index) { return optDouble(index, Double.NaN); } /** * Returns the value at {@code index} if it exists and is a double or can * be coerced to a double. Returns {@code fallback} otherwise. */ public double optDouble(int index, double fallback) { Object object = opt(index); Double result = JSON.toDouble(object); return result != null ? result : fallback; } /** * Returns the value at {@code index} if it exists and is an int or * can be coerced to an int. * * @throws JSONException if the value at {@code index} doesn't exist or * cannot be coerced to a int. */ public int getInt(int index) throws JSONException { Object object = get(index); Integer result = JSON.toInteger(object); if (result == null) { throw JSON.typeMismatch(index, object, "int"); } return result; } /** * Returns the value at {@code index} if it exists and is an int or * can be coerced to an int. Returns 0 otherwise. */ public int optInt(int index) { return optInt(index, 0); } /** * Returns the value at {@code index} if it exists and is an int or * can be coerced to an int. Returns {@code fallback} otherwise. */ public int optInt(int index, int fallback) { Object object = opt(index); Integer result = JSON.toInteger(object); return result != null ? result : fallback; } /** * Returns the value at {@code index} if it exists and is a long or * can be coerced to a long. * * @throws JSONException if the value at {@code index} doesn't exist or * cannot be coerced to a long. */ public long getLong(int index) throws JSONException { Object object = get(index); Long result = JSON.toLong(object); if (result == null) { throw JSON.typeMismatch(index, object, "long"); } return result; } /** * Returns the value at {@code index} if it exists and is a long or * can be coerced to a long. Returns 0 otherwise. */ public long optLong(int index) { return optLong(index, 0L); } /** * Returns the value at {@code index} if it exists and is a long or * can be coerced to a long. Returns {@code fallback} otherwise. */ public long optLong(int index, long fallback) { Object object = opt(index); Long result = JSON.toLong(object); return result != null ? result : fallback; } /** * Returns the value at {@code index} if it exists, coercing it if * necessary. * * @throws JSONException if no such value exists. */ public String getString(int index) throws JSONException { Object object = get(index); String result = JSON.toString(object); if (result == null) { throw JSON.typeMismatch(index, object, "String"); } return result; } /** * Returns the value at {@code index} if it exists, coercing it if * necessary. Returns the empty string if no such value exists. */ public String optString(int index) { return optString(index, ""); } /** * Returns the value at {@code index} if it exists, coercing it if * necessary. Returns {@code fallback} if no such value exists. */ public String optString(int index, String fallback) { Object object = opt(index); String result = JSON.toString(object); return result != null ? result : fallback; } /** * Returns the value at {@code index} if it exists and is a {@code * JSONArray}. * * @throws JSONException if the value doesn't exist or is not a {@code * JSONArray}. */ public JSONArray getJSONArray(int index) throws JSONException { Object object = get(index); if (object instanceof JSONArray) { return (JSONArray) object; } else { throw JSON.typeMismatch(index, object, "JSONArray"); } } /** * Returns the value at {@code index} if it exists and is a {@code * JSONArray}. Returns null otherwise. */ public JSONArray optJSONArray(int index) { Object object = opt(index); return object instanceof JSONArray ? (JSONArray) object : null; } /** * Returns the value at {@code index} if it exists and is a {@code * JSONObject}. * * @throws JSONException if the value doesn't exist or is not a {@code * JSONObject}. */ public JSONObject getJSONObject(int index) throws JSONException { Object object = get(index); if (object instanceof JSONObject) { return (JSONObject) object; } else { throw JSON.typeMismatch(index, object, "JSONObject"); } } /** * Returns the value at {@code index} if it exists and is a {@code * JSONObject}. Returns null otherwise. */ public JSONObject optJSONObject(int index) { Object object = opt(index); return object instanceof JSONObject ? (JSONObject) object : null; } /** * Returns a new object whose values are the values in this array, and whose * names are the values in {@code names}. Names and values are paired up by * index from 0 through to the shorter array's length. Names that are not * strings will be coerced to strings. This method returns null if either * array is empty. */ public JSONObject toJSONObject(JSONArray names) throws JSONException { JSONObject result = new JSONObject(); int length = Math.min(names.length(), values.size()); if (length == 0) { return null; } for (int i = 0; i < length; i++) { String name = JSON.toString(names.opt(i)); result.put(name, opt(i)); } return result; } /** * Returns a new string by alternating this array's values with {@code * separator}. This array's string values are quoted and have their special * characters escaped. For example, the array containing the strings '12" * pizza', 'taco' and 'soda' joined on '+' returns this: *
"12\" pizza"+"taco"+"soda"
*/ public String join(String separator) throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.open(JSONStringer.Scope.NULL, ""); for (int i = 0, size = values.size(); i < size; i++) { if (i > 0) { stringer.out.append(separator); } stringer.value(values.get(i)); } stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); return stringer.out.toString(); } /** * Encodes this array as a compact JSON string, such as: *
[94043,90210]
*/ @Override public String toString() { try { JSONStringer stringer = new JSONStringer(); writeTo(stringer); return stringer.toString(); } catch (JSONException e) { return null; } } /** * Encodes this array as a human readable JSON string for debugging, such * as: *
     * [
     *     94043,
     *     90210
     * ]
* * @param indentSpaces the number of spaces to indent for each level of * nesting. */ public String toString(int indentSpaces) throws JSONException { JSONStringer stringer = new JSONStringer(indentSpaces); writeTo(stringer); return stringer.toString(); } void writeTo(JSONStringer stringer) throws JSONException { stringer.array(); for (Object value : values) { stringer.value(value); } stringer.endArray(); } @Override public boolean equals(Object o) { return o instanceof JSONArray && ((JSONArray) o).values.equals(values); } @Override public int hashCode() { // diverge from the original, which doesn't implement hashCode return values.hashCode(); } } openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/json/JSONException.java000066400000000000000000000032011266464252400301050ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.openalpr.jni.json; // Note: this class was written without inspecting the non-free org.json sourcecode. /** * Thrown to indicate a problem with the JSON API. Such problems include: *
    *
  • Attempts to parse or construct malformed documents *
  • Use of null as a name *
  • Use of numeric types not available to JSON, such as {@link * Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. *
  • Lookups using an out of range index or nonexistent name *
  • Type mismatches on lookups *
* *

Although this is a checked exception, it is rarely recoverable. Most * callers should simply wrap this exception in an unchecked exception and * rethrow: *

  public JSONArray toJSONObject() {
 *     try {
 *         JSONObject result = new JSONObject();
 *         ...
 *     } catch (JSONException e) {
 *         throw new RuntimeException(e);
 *     }
 * }
*/ public class JSONException extends Exception { public JSONException(String s) { super(s); } } openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/json/JSONObject.java000066400000000000000000000606611266464252400273720ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.openalpr.jni.json; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; // Note: this class was written without inspecting the non-free org.json sourcecode. /** * A modifiable set of name/value mappings. Names are unique, non-null strings. * Values may be any mix of {@link JSONObject JSONObjects}, {@link JSONArray * JSONArrays}, Strings, Booleans, Integers, Longs, Doubles or {@link #NULL}. * Values may not be {@code null}, {@link Double#isNaN() NaNs}, {@link * Double#isInfinite() infinities}, or of any type not listed here. * *

This class can coerce values to another type when requested. *

    *
  • When the requested type is a boolean, strings will be coerced using a * case-insensitive comparison to "true" and "false". *
  • When the requested type is a double, other {@link Number} types will * be coerced using {@link Number#doubleValue() doubleValue}. Strings * that can be coerced using {@link Double#valueOf(String)} will be. *
  • When the requested type is an int, other {@link Number} types will * be coerced using {@link Number#intValue() intValue}. Strings * that can be coerced using {@link Double#valueOf(String)} will be, * and then cast to int. *
  • When the requested type is a long, other {@link Number} types will * be coerced using {@link Number#longValue() longValue}. Strings * that can be coerced using {@link Double#valueOf(String)} will be, * and then cast to long. This two-step conversion is lossy for very * large values. For example, the string "9223372036854775806" yields the * long 9223372036854775807. *
  • When the requested type is a String, other non-null values will be * coerced using {@link String#valueOf(Object)}. Although null cannot be * coerced, the sentinel value {@link JSONObject#NULL} is coerced to the * string "null". *
* *

This class can look up both mandatory and optional values: *

    *
  • Use getType() to retrieve a mandatory value. This * fails with a {@code JSONException} if the requested name has no value * or if the value cannot be coerced to the requested type. *
  • Use optType() to retrieve an optional value. This * returns a system- or user-supplied default if the requested name has no * value or if the value cannot be coerced to the requested type. *
* *

Warning: this class represents null in two incompatible * ways: the standard Java {@code null} reference, and the sentinel value {@link * JSONObject#NULL}. In particular, calling {@code put(name, null)} removes the * named entry from the object but {@code put(name, JSONObject.NULL)} stores an * entry whose value is {@code JSONObject.NULL}. * *

Instances of this class are not thread safe. Although this class is * nonfinal, it was not designed for inheritance and should not be subclassed. * In particular, self-use by overrideable methods is not specified. See * Effective Java Item 17, "Design and Document or inheritance or else * prohibit it" for further information. */ public class JSONObject { private static final Double NEGATIVE_ZERO = -0d; /** * A sentinel value used to explicitly define a name with no value. Unlike * {@code null}, names with this value: *

    *
  • show up in the {@link #names} array *
  • show up in the {@link #keys} iterator *
  • return {@code true} for {@link #has(String)} *
  • do not throw on {@link #get(String)} *
  • are included in the encoded JSON string. *
* *

This value violates the general contract of {@link Object#equals} by * returning true when compared to {@code null}. Its {@link #toString} * method returns "null". */ public static final Object NULL = new Object() { @Override public boolean equals(Object o) { return o == this || o == null; // API specifies this broken equals implementation } @Override public String toString() { return "null"; } }; private final Map nameValuePairs; /** * Creates a {@code JSONObject} with no name/value mappings. */ public JSONObject() { nameValuePairs = new HashMap(); } /** * Creates a new {@code JSONObject} by copying all name/value mappings from * the given map. * * @param copyFrom a map whose keys are of type {@link String} and whose * values are of supported types. * @throws NullPointerException if any of the map's keys are null. */ /* (accept a raw type for API compatibility) */ public JSONObject(Map copyFrom) { this(); Map contentsTyped = (Map) copyFrom; for (Map.Entry entry : contentsTyped.entrySet()) { /* * Deviate from the original by checking that keys are non-null and * of the proper type. (We still defer validating the values). */ String key = (String) entry.getKey(); if (key == null) { throw new NullPointerException("key == null"); } nameValuePairs.put(key, entry.getValue()); } } /** * Creates a new {@code JSONObject} with name/value mappings from the next * object in the tokener. * * @param readFrom a tokener whose nextValue() method will yield a * {@code JSONObject}. * @throws JSONException if the parse fails or doesn't yield a * {@code JSONObject}. */ public JSONObject(JSONTokener readFrom) throws JSONException { /* * Getting the parser to populate this could get tricky. Instead, just * parse to temporary JSONObject and then steal the data from that. */ Object object = readFrom.nextValue(); if (object instanceof JSONObject) { this.nameValuePairs = ((JSONObject) object).nameValuePairs; } else { throw JSON.typeMismatch(object, "JSONObject"); } } /** * Creates a new {@code JSONObject} with name/value mappings from the JSON * string. * * @param json a JSON-encoded string containing an object. * @throws JSONException if the parse fails or doesn't yield a {@code * JSONObject}. */ public JSONObject(String json) throws JSONException { this(new JSONTokener(json)); } /** * Creates a new {@code JSONObject} by copying mappings for the listed names * from the given object. Names that aren't present in {@code copyFrom} will * be skipped. */ public JSONObject(JSONObject copyFrom, String[] names) throws JSONException { this(); for (String name : names) { Object value = copyFrom.opt(name); if (value != null) { nameValuePairs.put(name, value); } } } /** * Returns the number of name/value mappings in this object. */ public int length() { return nameValuePairs.size(); } /** * Maps {@code name} to {@code value}, clobbering any existing name/value * mapping with the same name. * * @return this object. */ public JSONObject put(String name, boolean value) throws JSONException { nameValuePairs.put(checkName(name), value); return this; } /** * Maps {@code name} to {@code value}, clobbering any existing name/value * mapping with the same name. * * @param value a finite value. May not be {@link Double#isNaN() NaNs} or * {@link Double#isInfinite() infinities}. * @return this object. */ public JSONObject put(String name, double value) throws JSONException { nameValuePairs.put(checkName(name), JSON.checkDouble(value)); return this; } /** * Maps {@code name} to {@code value}, clobbering any existing name/value * mapping with the same name. * * @return this object. */ public JSONObject put(String name, int value) throws JSONException { nameValuePairs.put(checkName(name), value); return this; } /** * Maps {@code name} to {@code value}, clobbering any existing name/value * mapping with the same name. * * @return this object. */ public JSONObject put(String name, long value) throws JSONException { nameValuePairs.put(checkName(name), value); return this; } /** * Maps {@code name} to {@code value}, clobbering any existing name/value * mapping with the same name. If the value is {@code null}, any existing * mapping for {@code name} is removed. * * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Integer, Long, Double, {@link #NULL}, or {@code null}. May not be * {@link Double#isNaN() NaNs} or {@link Double#isInfinite() * infinities}. * @return this object. */ public JSONObject put(String name, Object value) throws JSONException { if (value == null) { nameValuePairs.remove(name); return this; } if (value instanceof Number) { // deviate from the original by checking all Numbers, not just floats & doubles JSON.checkDouble(((Number) value).doubleValue()); } nameValuePairs.put(checkName(name), value); return this; } /** * Equivalent to {@code put(name, value)} when both parameters are non-null; * does nothing otherwise. */ public JSONObject putOpt(String name, Object value) throws JSONException { if (name == null || value == null) { return this; } return put(name, value); } /** * Appends {@code value} to the array already mapped to {@code name}. If * this object has no mapping for {@code name}, this inserts a new mapping. * If the mapping exists but its value is not an array, the existing * and new values are inserted in order into a new array which is itself * mapped to {@code name}. In aggregate, this allows values to be added to a * mapping one at a time. * * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Integer, Long, Double, {@link #NULL} or null. May not be {@link * Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. */ public JSONObject accumulate(String name, Object value) throws JSONException { Object current = nameValuePairs.get(checkName(name)); if (current == null) { return put(name, value); } // check in accumulate, since array.put(Object) doesn't do any checking if (value instanceof Number) { JSON.checkDouble(((Number) value).doubleValue()); } if (current instanceof JSONArray) { JSONArray array = (JSONArray) current; array.put(value); } else { JSONArray array = new JSONArray(); array.put(current); array.put(value); nameValuePairs.put(name, array); } return this; } String checkName(String name) throws JSONException { if (name == null) { throw new JSONException("Names must be non-null"); } return name; } /** * Removes the named mapping if it exists; does nothing otherwise. * * @return the value previously mapped by {@code name}, or null if there was * no such mapping. */ public Object remove(String name) { return nameValuePairs.remove(name); } /** * Returns true if this object has no mapping for {@code name} or if it has * a mapping whose value is {@link #NULL}. */ public boolean isNull(String name) { Object value = nameValuePairs.get(name); return value == null || value == NULL; } /** * Returns true if this object has a mapping for {@code name}. The mapping * may be {@link #NULL}. */ public boolean has(String name) { return nameValuePairs.containsKey(name); } /** * Returns the value mapped by {@code name}. * * @throws JSONException if no such mapping exists. */ public Object get(String name) throws JSONException { Object result = nameValuePairs.get(name); if (result == null) { throw new JSONException("No value for " + name); } return result; } /** * Returns the value mapped by {@code name}, or null if no such mapping * exists. */ public Object opt(String name) { return nameValuePairs.get(name); } /** * Returns the value mapped by {@code name} if it exists and is a boolean or * can be coerced to a boolean. * * @throws JSONException if the mapping doesn't exist or cannot be coerced * to a boolean. */ public boolean getBoolean(String name) throws JSONException { Object object = get(name); Boolean result = JSON.toBoolean(object); if (result == null) { throw JSON.typeMismatch(name, object, "boolean"); } return result; } /** * Returns the value mapped by {@code name} if it exists and is a boolean or * can be coerced to a boolean. Returns false otherwise. */ public boolean optBoolean(String name) { return optBoolean(name, false); } /** * Returns the value mapped by {@code name} if it exists and is a boolean or * can be coerced to a boolean. Returns {@code fallback} otherwise. */ public boolean optBoolean(String name, boolean fallback) { Object object = opt(name); Boolean result = JSON.toBoolean(object); return result != null ? result : fallback; } /** * Returns the value mapped by {@code name} if it exists and is a double or * can be coerced to a double. * * @throws JSONException if the mapping doesn't exist or cannot be coerced * to a double. */ public double getDouble(String name) throws JSONException { Object object = get(name); Double result = JSON.toDouble(object); if (result == null) { throw JSON.typeMismatch(name, object, "double"); } return result; } /** * Returns the value mapped by {@code name} if it exists and is a double or * can be coerced to a double. Returns {@code NaN} otherwise. */ public double optDouble(String name) { return optDouble(name, Double.NaN); } /** * Returns the value mapped by {@code name} if it exists and is a double or * can be coerced to a double. Returns {@code fallback} otherwise. */ public double optDouble(String name, double fallback) { Object object = opt(name); Double result = JSON.toDouble(object); return result != null ? result : fallback; } /** * Returns the value mapped by {@code name} if it exists and is an int or * can be coerced to an int. * * @throws JSONException if the mapping doesn't exist or cannot be coerced * to an int. */ public int getInt(String name) throws JSONException { Object object = get(name); Integer result = JSON.toInteger(object); if (result == null) { throw JSON.typeMismatch(name, object, "int"); } return result; } /** * Returns the value mapped by {@code name} if it exists and is an int or * can be coerced to an int. Returns 0 otherwise. */ public int optInt(String name) { return optInt(name, 0); } /** * Returns the value mapped by {@code name} if it exists and is an int or * can be coerced to an int. Returns {@code fallback} otherwise. */ public int optInt(String name, int fallback) { Object object = opt(name); Integer result = JSON.toInteger(object); return result != null ? result : fallback; } /** * Returns the value mapped by {@code name} if it exists and is a long or * can be coerced to a long. * * @throws JSONException if the mapping doesn't exist or cannot be coerced * to a long. */ public long getLong(String name) throws JSONException { Object object = get(name); Long result = JSON.toLong(object); if (result == null) { throw JSON.typeMismatch(name, object, "long"); } return result; } /** * Returns the value mapped by {@code name} if it exists and is a long or * can be coerced to a long. Returns 0 otherwise. */ public long optLong(String name) { return optLong(name, 0L); } /** * Returns the value mapped by {@code name} if it exists and is a long or * can be coerced to a long. Returns {@code fallback} otherwise. */ public long optLong(String name, long fallback) { Object object = opt(name); Long result = JSON.toLong(object); return result != null ? result : fallback; } /** * Returns the value mapped by {@code name} if it exists, coercing it if * necessary. * * @throws JSONException if no such mapping exists. */ public String getString(String name) throws JSONException { Object object = get(name); String result = JSON.toString(object); if (result == null) { throw JSON.typeMismatch(name, object, "String"); } return result; } /** * Returns the value mapped by {@code name} if it exists, coercing it if * necessary. Returns the empty string if no such mapping exists. */ public String optString(String name) { return optString(name, ""); } /** * Returns the value mapped by {@code name} if it exists, coercing it if * necessary. Returns {@code fallback} if no such mapping exists. */ public String optString(String name, String fallback) { Object object = opt(name); String result = JSON.toString(object); return result != null ? result : fallback; } /** * Returns the value mapped by {@code name} if it exists and is a {@code * JSONArray}. * * @throws JSONException if the mapping doesn't exist or is not a {@code * JSONArray}. */ public JSONArray getJSONArray(String name) throws JSONException { Object object = get(name); if (object instanceof JSONArray) { return (JSONArray) object; } else { throw JSON.typeMismatch(name, object, "JSONArray"); } } /** * Returns the value mapped by {@code name} if it exists and is a {@code * JSONArray}. Returns null otherwise. */ public JSONArray optJSONArray(String name) { Object object = opt(name); return object instanceof JSONArray ? (JSONArray) object : null; } /** * Returns the value mapped by {@code name} if it exists and is a {@code * JSONObject}. * * @throws JSONException if the mapping doesn't exist or is not a {@code * JSONObject}. */ public JSONObject getJSONObject(String name) throws JSONException { Object object = get(name); if (object instanceof JSONObject) { return (JSONObject) object; } else { throw JSON.typeMismatch(name, object, "JSONObject"); } } /** * Returns the value mapped by {@code name} if it exists and is a {@code * JSONObject}. Returns null otherwise. */ public JSONObject optJSONObject(String name) { Object object = opt(name); return object instanceof JSONObject ? (JSONObject) object : null; } /** * Returns an array with the values corresponding to {@code names}. The * array contains null for names that aren't mapped. This method returns * null if {@code names} is either null or empty. */ public JSONArray toJSONArray(JSONArray names) throws JSONException { JSONArray result = new JSONArray(); if (names == null) { return null; } int length = names.length(); if (length == 0) { return null; } for (int i = 0; i < length; i++) { String name = JSON.toString(names.opt(i)); result.put(opt(name)); } return result; } /** * Returns an iterator of the {@code String} names in this object. The * returned iterator supports {@link Iterator#remove() remove}, which will * remove the corresponding mapping from this object. If this object is * modified after the iterator is returned, the iterator's behavior is * undefined. The order of the keys is undefined. */ /* Return a raw type for API compatibility */ public Iterator keys() { return nameValuePairs.keySet().iterator(); } /** * Returns an array containing the string names in this object. This method * returns null if this object contains no mappings. */ public JSONArray names() { return nameValuePairs.isEmpty() ? null : new JSONArray(new ArrayList(nameValuePairs.keySet())); } /** * Encodes this object as a compact JSON string, such as: *

{"query":"Pizza","locations":[94043,90210]}
*/ @Override public String toString() { try { JSONStringer stringer = new JSONStringer(); writeTo(stringer); return stringer.toString(); } catch (JSONException e) { return null; } } /** * Encodes this object as a human readable JSON string for debugging, such * as: *
     * {
     *     "query": "Pizza",
     *     "locations": [
     *         94043,
     *         90210
     *     ]
     * }
* * @param indentSpaces the number of spaces to indent for each level of * nesting. */ public String toString(int indentSpaces) throws JSONException { JSONStringer stringer = new JSONStringer(indentSpaces); writeTo(stringer); return stringer.toString(); } void writeTo(JSONStringer stringer) throws JSONException { stringer.object(); for (Map.Entry entry : nameValuePairs.entrySet()) { stringer.key(entry.getKey()).value(entry.getValue()); } stringer.endObject(); } /** * Encodes the number as a JSON string. * * @param number a finite value. May not be {@link Double#isNaN() NaNs} or * {@link Double#isInfinite() infinities}. */ public static String numberToString(Number number) throws JSONException { if (number == null) { throw new JSONException("Number must be non-null"); } double doubleValue = number.doubleValue(); JSON.checkDouble(doubleValue); // the original returns "-0" instead of "-0.0" for negative zero if (number.equals(NEGATIVE_ZERO)) { return "-0"; } long longValue = number.longValue(); if (doubleValue == (double) longValue) { return Long.toString(longValue); } return number.toString(); } /** * Encodes {@code data} as a JSON string. This applies quotes and any * necessary character escaping. * * @param data the string to encode. Null will be interpreted as an empty * string. */ public static String quote(String data) { if (data == null) { return "\"\""; } try { JSONStringer stringer = new JSONStringer(); stringer.open(JSONStringer.Scope.NULL, ""); stringer.value(data); stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); return stringer.toString(); } catch (JSONException e) { throw new AssertionError(); } } } openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/json/JSONStringer.java000066400000000000000000000316171266464252400277600ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.openalpr.jni.json; import java.util.ArrayList; import java.util.Arrays; import java.util.List; // Note: this class was written without inspecting the non-free org.json sourcecode. /** * Implements {@link JSONObject#toString} and {@link JSONArray#toString}. Most * application developers should use those methods directly and disregard this * API. For example:
 * JSONObject object = ...
 * String json = object.toString();
* *

Stringers only encode well-formed JSON strings. In particular: *

    *
  • The stringer must have exactly one top-level array or object. *
  • Lexical scopes must be balanced: every call to {@link #array} must * have a matching call to {@link #endArray} and every call to {@link * #object} must have a matching call to {@link #endObject}. *
  • Arrays may not contain keys (property names). *
  • Objects must alternate keys (property names) and values. *
  • Values are inserted with either literal {@link #value(Object) value} * calls, or by nesting arrays or objects. *
* Calls that would result in a malformed JSON string will fail with a * {@link JSONException}. * *

This class provides no facility for pretty-printing (ie. indenting) * output. To encode indented output, use {@link JSONObject#toString(int)} or * {@link JSONArray#toString(int)}. * *

Some implementations of the API support at most 20 levels of nesting. * Attempts to create more than 20 levels of nesting may fail with a {@link * JSONException}. * *

Each stringer may be used to encode a single top level value. Instances of * this class are not thread safe. Although this class is nonfinal, it was not * designed for inheritance and should not be subclassed. In particular, * self-use by overrideable methods is not specified. See Effective Java * Item 17, "Design and Document or inheritance or else prohibit it" for further * information. */ public class JSONStringer { /** The output data, containing at most one top-level array or object. */ final StringBuilder out = new StringBuilder(); /** * Lexical scoping elements within this stringer, necessary to insert the * appropriate separator characters (ie. commas and colons) and to detect * nesting errors. */ enum Scope { /** * An array with no elements requires no separators or newlines before * it is closed. */ EMPTY_ARRAY, /** * A array with at least one value requires a comma and newline before * the next element. */ NONEMPTY_ARRAY, /** * An object with no keys or values requires no separators or newlines * before it is closed. */ EMPTY_OBJECT, /** * An object whose most recent element is a key. The next element must * be a value. */ DANGLING_KEY, /** * An object with at least one name/value pair requires a comma and * newline before the next element. */ NONEMPTY_OBJECT, /** * A special bracketless array needed by JSONStringer.join() and * JSONObject.quote() only. Not used for JSON encoding. */ NULL, } /** * Unlike the original implementation, this stack isn't limited to 20 * levels of nesting. */ private final List stack = new ArrayList(); /** * A string containing a full set of spaces for a single level of * indentation, or null for no pretty printing. */ private final String indent; public JSONStringer() { indent = null; } JSONStringer(int indentSpaces) { char[] indentChars = new char[indentSpaces]; Arrays.fill(indentChars, ' '); indent = new String(indentChars); } /** * Begins encoding a new array. Each call to this method must be paired with * a call to {@link #endArray}. * * @return this stringer. */ public JSONStringer array() throws JSONException { return open(Scope.EMPTY_ARRAY, "["); } /** * Ends encoding the current array. * * @return this stringer. */ public JSONStringer endArray() throws JSONException { return close(Scope.EMPTY_ARRAY, Scope.NONEMPTY_ARRAY, "]"); } /** * Begins encoding a new object. Each call to this method must be paired * with a call to {@link #endObject}. * * @return this stringer. */ public JSONStringer object() throws JSONException { return open(Scope.EMPTY_OBJECT, "{"); } /** * Ends encoding the current object. * * @return this stringer. */ public JSONStringer endObject() throws JSONException { return close(Scope.EMPTY_OBJECT, Scope.NONEMPTY_OBJECT, "}"); } /** * Enters a new scope by appending any necessary whitespace and the given * bracket. */ JSONStringer open(Scope empty, String openBracket) throws JSONException { if (stack.isEmpty() && out.length() > 0) { throw new JSONException("Nesting problem: multiple top-level roots"); } beforeValue(); stack.add(empty); out.append(openBracket); return this; } /** * Closes the current scope by appending any necessary whitespace and the * given bracket. */ JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSONException { Scope context = peek(); if (context != nonempty && context != empty) { throw new JSONException("Nesting problem"); } stack.remove(stack.size() - 1); if (context == nonempty) { newline(); } out.append(closeBracket); return this; } /** * Returns the value on the top of the stack. */ private Scope peek() throws JSONException { if (stack.isEmpty()) { throw new JSONException("Nesting problem"); } return stack.get(stack.size() - 1); } /** * Replace the value on the top of the stack with the given value. */ private void replaceTop(Scope topOfStack) { stack.set(stack.size() - 1, topOfStack); } /** * Encodes {@code value}. * * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Integer, Long, Double or null. May not be {@link Double#isNaN() NaNs} * or {@link Double#isInfinite() infinities}. * @return this stringer. */ public JSONStringer value(Object value) throws JSONException { if (stack.isEmpty()) { throw new JSONException("Nesting problem"); } if (value instanceof JSONArray) { ((JSONArray) value).writeTo(this); return this; } else if (value instanceof JSONObject) { ((JSONObject) value).writeTo(this); return this; } beforeValue(); if (value == null || value instanceof Boolean || value == JSONObject.NULL) { out.append(value); } else if (value instanceof Number) { out.append(JSONObject.numberToString((Number) value)); } else { string(value.toString()); } return this; } /** * Encodes {@code value} to this stringer. * * @return this stringer. */ public JSONStringer value(boolean value) throws JSONException { if (stack.isEmpty()) { throw new JSONException("Nesting problem"); } beforeValue(); out.append(value); return this; } /** * Encodes {@code value} to this stringer. * * @param value a finite value. May not be {@link Double#isNaN() NaNs} or * {@link Double#isInfinite() infinities}. * @return this stringer. */ public JSONStringer value(double value) throws JSONException { if (stack.isEmpty()) { throw new JSONException("Nesting problem"); } beforeValue(); out.append(JSONObject.numberToString(value)); return this; } /** * Encodes {@code value} to this stringer. * * @return this stringer. */ public JSONStringer value(long value) throws JSONException { if (stack.isEmpty()) { throw new JSONException("Nesting problem"); } beforeValue(); out.append(value); return this; } private void string(String value) { out.append("\""); for (int i = 0, length = value.length(); i < length; i++) { char c = value.charAt(i); /* * From RFC 4627, "All Unicode characters may be placed within the * quotation marks except for the characters that must be escaped: * quotation mark, reverse solidus, and the control characters * (U+0000 through U+001F)." */ switch (c) { case '"': case '\\': case '/': out.append('\\').append(c); break; case '\t': out.append("\\t"); break; case '\b': out.append("\\b"); break; case '\n': out.append("\\n"); break; case '\r': out.append("\\r"); break; case '\f': out.append("\\f"); break; default: if (c <= 0x1F) { out.append(String.format("\\u%04x", (int) c)); } else { out.append(c); } break; } } out.append("\""); } private void newline() { if (indent == null) { return; } out.append("\n"); for (int i = 0; i < stack.size(); i++) { out.append(indent); } } /** * Encodes the key (property name) to this stringer. * * @param name the name of the forthcoming value. May not be null. * @return this stringer. */ public JSONStringer key(String name) throws JSONException { if (name == null) { throw new JSONException("Names must be non-null"); } beforeKey(); string(name); return this; } /** * Inserts any necessary separators and whitespace before a name. Also * adjusts the stack to expect the key's value. */ private void beforeKey() throws JSONException { Scope context = peek(); if (context == Scope.NONEMPTY_OBJECT) { // first in object out.append(','); } else if (context != Scope.EMPTY_OBJECT) { // not in an object! throw new JSONException("Nesting problem"); } newline(); replaceTop(Scope.DANGLING_KEY); } /** * Inserts any necessary separators and whitespace before a literal value, * inline array, or inline object. Also adjusts the stack to expect either a * closing bracket or another element. */ private void beforeValue() throws JSONException { if (stack.isEmpty()) { return; } Scope context = peek(); if (context == Scope.EMPTY_ARRAY) { // first in array replaceTop(Scope.NONEMPTY_ARRAY); newline(); } else if (context == Scope.NONEMPTY_ARRAY) { // another in array out.append(','); newline(); } else if (context == Scope.DANGLING_KEY) { // value for key out.append(indent == null ? ":" : ": "); replaceTop(Scope.NONEMPTY_OBJECT); } else if (context != Scope.NULL) { throw new JSONException("Nesting problem"); } } /** * Returns the encoded JSON string. * *

If invoked with unterminated arrays or unclosed objects, this method's * return value is undefined. * *

Warning: although it contradicts the general contract * of {@link Object#toString}, this method returns null if the stringer * contains no data. */ @Override public String toString() { return out.length() == 0 ? null : out.toString(); } } openalpr_2.2.4.orig/src/bindings/java/src/com/openalpr/jni/json/JSONTokener.java000066400000000000000000000503111266464252400275620ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.openalpr.jni.json; // Note: this class was written without inspecting the non-free org.json sourcecode. /** * Parses a JSON (RFC 4627) * encoded string into the corresponding object. Most clients of * this class will use only need the {@link #JSONTokener(String) constructor} * and {@link #nextValue} method. Example usage:

 * String json = "{"
 *         + "  \"query\": \"Pizza\", "
 *         + "  \"locations\": [ 94043, 90210 ] "
 *         + "}";
 *
 * JSONObject object = (JSONObject) new JSONTokener(json).nextValue();
 * String query = object.getString("query");
 * JSONArray locations = object.getJSONArray("locations");
* *

For best interoperability and performance use JSON that complies with * RFC 4627, such as that generated by {@link JSONStringer}. For legacy reasons * this parser is lenient, so a successful parse does not indicate that the * input string was valid JSON. All of the following syntax errors will be * ignored: *

    *
  • End of line comments starting with {@code //} or {@code #} and ending * with a newline character. *
  • C-style comments starting with {@code /*} and ending with * {@code *}{@code /}. Such comments may not be nested. *
  • Strings that are unquoted or {@code 'single quoted'}. *
  • Hexadecimal integers prefixed with {@code 0x} or {@code 0X}. *
  • Octal integers prefixed with {@code 0}. *
  • Array elements separated by {@code ;}. *
  • Unnecessary array separators. These are interpreted as if null was the * omitted value. *
  • Key-value pairs separated by {@code =} or {@code =>}. *
  • Key-value pairs separated by {@code ;}. *
* *

Each tokener may be used to parse a single JSON string. Instances of this * class are not thread safe. Although this class is nonfinal, it was not * designed for inheritance and should not be subclassed. In particular, * self-use by overrideable methods is not specified. See Effective Java * Item 17, "Design and Document or inheritance or else prohibit it" for further * information. */ public class JSONTokener { /** The input JSON. */ private final String in; /** * The index of the next character to be returned by {@link #next}. When * the input is exhausted, this equals the input's length. */ private int pos; /** * @param in JSON encoded string. Null is not permitted and will yield a * tokener that throws {@code NullPointerExceptions} when methods are * called. */ public JSONTokener(String in) { // consume an optional byte order mark (BOM) if it exists if (in != null && in.startsWith("\ufeff")) { in = in.substring(1); } this.in = in; } /** * Returns the next value from the input. * * @return a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Integer, Long, Double or {@link JSONObject#NULL}. * @throws JSONException if the input is malformed. */ public Object nextValue() throws JSONException { int c = nextCleanInternal(); switch (c) { case -1: throw syntaxError("End of input"); case '{': return readObject(); case '[': return readArray(); case '\'': case '"': return nextString((char) c); default: pos--; return readLiteral(); } } private int nextCleanInternal() throws JSONException { while (pos < in.length()) { int c = in.charAt(pos++); switch (c) { case '\t': case ' ': case '\n': case '\r': continue; case '/': if (pos == in.length()) { return c; } char peek = in.charAt(pos); switch (peek) { case '*': // skip a /* c-style comment */ pos++; int commentEnd = in.indexOf("*/", pos); if (commentEnd == -1) { throw syntaxError("Unterminated comment"); } pos = commentEnd + 2; continue; case '/': // skip a // end-of-line comment pos++; skipToEndOfLine(); continue; default: return c; } case '#': /* * Skip a # hash end-of-line comment. The JSON RFC doesn't * specify this behavior, but it's required to parse * existing documents. See http://b/2571423. */ skipToEndOfLine(); continue; default: return c; } } return -1; } /** * Advances the position until after the next newline character. If the line * is terminated by "\r\n", the '\n' must be consumed as whitespace by the * caller. */ private void skipToEndOfLine() { for (; pos < in.length(); pos++) { char c = in.charAt(pos); if (c == '\r' || c == '\n') { pos++; break; } } } /** * Returns the string up to but not including {@code quote}, unescaping any * character escape sequences encountered along the way. The opening quote * should have already been read. This consumes the closing quote, but does * not include it in the returned string. * * @param quote either ' or ". * @throws NumberFormatException if any unicode escape sequences are * malformed. */ public String nextString(char quote) throws JSONException { /* * For strings that are free of escape sequences, we can just extract * the result as a substring of the input. But if we encounter an escape * sequence, we need to use a StringBuilder to compose the result. */ StringBuilder builder = null; /* the index of the first character not yet appended to the builder. */ int start = pos; while (pos < in.length()) { int c = in.charAt(pos++); if (c == quote) { if (builder == null) { // a new string avoids leaking memory return new String(in.substring(start, pos - 1)); } else { builder.append(in, start, pos - 1); return builder.toString(); } } if (c == '\\') { if (pos == in.length()) { throw syntaxError("Unterminated escape sequence"); } if (builder == null) { builder = new StringBuilder(); } builder.append(in, start, pos - 1); builder.append(readEscapeCharacter()); start = pos; } } throw syntaxError("Unterminated string"); } /** * Unescapes the character identified by the character or characters that * immediately follow a backslash. The backslash '\' should have already * been read. This supports both unicode escapes "u000A" and two-character * escapes "\n". * * @throws NumberFormatException if any unicode escape sequences are * malformed. */ private char readEscapeCharacter() throws JSONException { char escaped = in.charAt(pos++); switch (escaped) { case 'u': if (pos + 4 > in.length()) { throw syntaxError("Unterminated escape sequence"); } String hex = in.substring(pos, pos + 4); pos += 4; return (char) Integer.parseInt(hex, 16); case 't': return '\t'; case 'b': return '\b'; case 'n': return '\n'; case 'r': return '\r'; case 'f': return '\f'; case '\'': case '"': case '\\': default: return escaped; } } /** * Reads a null, boolean, numeric or unquoted string literal value. Numeric * values will be returned as an Integer, Long, or Double, in that order of * preference. */ private Object readLiteral() throws JSONException { String literal = nextToInternal("{}[]/\\:,=;# \t\f"); if (literal.length() == 0) { throw syntaxError("Expected literal value"); } else if ("null".equalsIgnoreCase(literal)) { return JSONObject.NULL; } else if ("true".equalsIgnoreCase(literal)) { return Boolean.TRUE; } else if ("false".equalsIgnoreCase(literal)) { return Boolean.FALSE; } /* try to parse as an integral type... */ if (literal.indexOf('.') == -1) { int base = 10; String number = literal; if (number.startsWith("0x") || number.startsWith("0X")) { number = number.substring(2); base = 16; } else if (number.startsWith("0") && number.length() > 1) { number = number.substring(1); base = 8; } try { long longValue = Long.parseLong(number, base); if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) { return (int) longValue; } else { return longValue; } } catch (NumberFormatException e) { /* * This only happens for integral numbers greater than * Long.MAX_VALUE, numbers in exponential form (5e-10) and * unquoted strings. Fall through to try floating point. */ } } /* ...next try to parse as a floating point... */ try { return Double.valueOf(literal); } catch (NumberFormatException ignored) { } /* ... finally give up. We have an unquoted string */ return new String(literal); // a new string avoids leaking memory } /** * Returns the string up to but not including any of the given characters or * a newline character. This does not consume the excluded character. */ private String nextToInternal(String excluded) { int start = pos; for (; pos < in.length(); pos++) { char c = in.charAt(pos); if (c == '\r' || c == '\n' || excluded.indexOf(c) != -1) { return in.substring(start, pos); } } return in.substring(start); } /** * Reads a sequence of key/value pairs and the trailing closing brace '}' of * an object. The opening brace '{' should have already been read. */ private JSONObject readObject() throws JSONException { JSONObject result = new JSONObject(); /* Peek to see if this is the empty object. */ int first = nextCleanInternal(); if (first == '}') { return result; } else if (first != -1) { pos--; } while (true) { Object name = nextValue(); if (!(name instanceof String)) { if (name == null) { throw syntaxError("Names cannot be null"); } else { throw syntaxError("Names must be strings, but " + name + " is of type " + name.getClass().getName()); } } /* * Expect the name/value separator to be either a colon ':', an * equals sign '=', or an arrow "=>". The last two are bogus but we * include them because that's what the original implementation did. */ int separator = nextCleanInternal(); if (separator != ':' && separator != '=') { throw syntaxError("Expected ':' after " + name); } if (pos < in.length() && in.charAt(pos) == '>') { pos++; } result.put((String) name, nextValue()); switch (nextCleanInternal()) { case '}': return result; case ';': case ',': continue; default: throw syntaxError("Unterminated object"); } } } /** * Reads a sequence of values and the trailing closing brace ']' of an * array. The opening brace '[' should have already been read. Note that * "[]" yields an empty array, but "[,]" returns a two-element array * equivalent to "[null,null]". */ private JSONArray readArray() throws JSONException { JSONArray result = new JSONArray(); /* to cover input that ends with ",]". */ boolean hasTrailingSeparator = false; while (true) { switch (nextCleanInternal()) { case -1: throw syntaxError("Unterminated array"); case ']': if (hasTrailingSeparator) { result.put(null); } return result; case ',': case ';': /* A separator without a value first means "null". */ result.put(null); hasTrailingSeparator = true; continue; default: pos--; } result.put(nextValue()); switch (nextCleanInternal()) { case ']': return result; case ',': case ';': hasTrailingSeparator = true; continue; default: throw syntaxError("Unterminated array"); } } } /** * Returns an exception containing the given message plus the current * position and the entire input string. */ public JSONException syntaxError(String message) { return new JSONException(message + this); } /** * Returns the current position and the entire input string. */ @Override public String toString() { // consistent with the original implementation return " at character " + pos + " of " + in; } /* * Legacy APIs. * * None of the methods below are on the critical path of parsing JSON * documents. They exist only because they were exposed by the original * implementation and may be used by some clients. */ /** * Returns true until the input has been exhausted. */ public boolean more() { return pos < in.length(); } /** * Returns the next available character, or the null character '\0' if all * input has been exhausted. The return value of this method is ambiguous * for JSON strings that contain the character '\0'. */ public char next() { return pos < in.length() ? in.charAt(pos++) : '\0'; } /** * Returns the next available character if it equals {@code c}. Otherwise an * exception is thrown. */ public char next(char c) throws JSONException { char result = next(); if (result != c) { throw syntaxError("Expected " + c + " but was " + result); } return result; } /** * Returns the next character that is not whitespace and does not belong to * a comment. If the input is exhausted before such a character can be * found, the null character '\0' is returned. The return value of this * method is ambiguous for JSON strings that contain the character '\0'. */ public char nextClean() throws JSONException { int nextCleanInt = nextCleanInternal(); return nextCleanInt == -1 ? '\0' : (char) nextCleanInt; } /** * Returns the next {@code length} characters of the input. * *

The returned string shares its backing character array with this * tokener's input string. If a reference to the returned string may be held * indefinitely, you should use {@code new String(result)} to copy it first * to avoid memory leaks. * * @throws JSONException if the remaining input is not long enough to * satisfy this request. */ public String next(int length) throws JSONException { if (pos + length > in.length()) { throw syntaxError(length + " is out of bounds"); } String result = in.substring(pos, pos + length); pos += length; return result; } /** * Returns the {@link String#trim trimmed} string holding the characters up * to but not including the first of: *

    *
  • any character in {@code excluded} *
  • a newline character '\n' *
  • a carriage return '\r' *
* *

The returned string shares its backing character array with this * tokener's input string. If a reference to the returned string may be held * indefinitely, you should use {@code new String(result)} to copy it first * to avoid memory leaks. * * @return a possibly-empty string */ public String nextTo(String excluded) { if (excluded == null) { throw new NullPointerException("excluded == null"); } return nextToInternal(excluded).trim(); } /** * Equivalent to {@code nextTo(String.valueOf(excluded))}. */ public String nextTo(char excluded) { return nextToInternal(String.valueOf(excluded)).trim(); } /** * Advances past all input up to and including the next occurrence of * {@code through}. If the remaining input doesn't contain {@code through}, the * input is exhausted. */ public void skipPast(String through) { int throughStart = in.indexOf(through, pos); pos = throughStart == -1 ? in.length() : (throughStart + through.length()); } /** * Advances past all input up to but not including the next occurrence of * {@code to}. If the remaining input doesn't contain {@code to}, the input * is unchanged. */ public char skipTo(char to) { int index = in.indexOf(to, pos); if (index != -1) { pos = index; return to; } else { return '\0'; } } /** * Unreads the most recent character of input. If no input characters have * been read, the input is unchanged. */ public void back() { if (--pos == -1) { pos = 0; } } /** * Returns the integer [0..15] value for the given hex character, or -1 * for non-hex input. * * @param hex a character in the ranges [0-9], [A-F] or [a-f]. Any other * character will yield a -1 result. */ public static int dehexchar(char hex) { if (hex >= '0' && hex <= '9') { return hex - '0'; } else if (hex >= 'A' && hex <= 'F') { return hex - 'A' + 10; } else if (hex >= 'a' && hex <= 'f') { return hex - 'a' + 10; } else { return -1; } } } openalpr_2.2.4.orig/src/bindings/python/000077500000000000000000000000001266464252400203005ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/python/CMakeLists.txt000066400000000000000000000010061266464252400230350ustar00rootroot00000000000000 cmake_minimum_required (VERSION 2.6) include_directories(../../openalpr/) set(python_source_files openalprpy.cpp ) add_library(openalprpy SHARED ${python_source_files}) set_target_properties(openalprpy PROPERTIES SOVERSION ${OPENALPR_MAJOR_VERSION}) TARGET_LINK_LIBRARIES(openalprpy openalpr) install (TARGETS openalprpy DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) install (DIRECTORY openalpr DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/python2.7/dist-packages USE_SOURCE_PERMISSIONS ) openalpr_2.2.4.orig/src/bindings/python/make.sh000077500000000000000000000005101266464252400215500ustar00rootroot00000000000000#!/bin/bash OPENALPR_INCLUDE_DIR=/storage/projects/alpr/src/openalpr/ OPENALPR_LIB_DIR=/storage/projects/alpr/src/build/openalpr/ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.:${OPENALPR_LIB_DIR} g++ -Wall -L${OPENALPR_LIB_DIR} -I${OPENALPR_INCLUDE_DIR} -shared -fPIC -o libopenalprpy.so openalprpy.cpp -lopenalpr python test.pyopenalpr_2.2.4.orig/src/bindings/python/openalpr/000077500000000000000000000000001266464252400221205ustar00rootroot00000000000000openalpr_2.2.4.orig/src/bindings/python/openalpr/__init__.py000066400000000000000000000001671266464252400242350ustar00rootroot00000000000000import sys as _sys if _sys.version_info.major >= 3: from .openalpr import Alpr else: from openalpr import Alpropenalpr_2.2.4.orig/src/bindings/python/openalpr/openalpr.py000066400000000000000000000211161266464252400243130ustar00rootroot00000000000000import ctypes import json import platform # We need to do things slightly differently for Python 2 vs. 3 # ... because the way str/unicode have changed to bytes/str if platform.python_version_tuple()[0] == '2': # Using Python 2 bytes = str _PYTHON_3 = False else: # Assume using Python 3+ unicode = str _PYTHON_3 = True def _convert_to_charp(string): # Prepares function input for use in c-functions as char* if type(string) == unicode: return string.encode("UTF-8") elif type(string) == bytes: return string else: raise TypeError("Expected unicode string values or ascii/bytes values. Got: %r" % type(string)) def _convert_from_charp(charp): # Prepares char* output from c-functions into Python strings if _PYTHON_3 and type(charp) == bytes: return charp.decode("UTF-8") else: return charp class Alpr(): def __init__(self, country, config_file, runtime_dir): """ Initializes an OpenALPR instance in memory. :param country: The default region for license plates. E.g., "us" or "eu" :param config_file: The path to the OpenALPR config file :param runtime_dir: The path to the OpenALPR runtime data directory :return: An OpenALPR instance """ country = _convert_to_charp(country) config_file = _convert_to_charp(config_file) runtime_dir = _convert_to_charp(runtime_dir) try: # Load the .dll for Windows and the .so for Unix-based if platform.system().lower().find("windows") != -1: self._openalprpy_lib = ctypes.cdll.LoadLibrary("openalprpy.dll") elif platform.system().lower().find("darwin") != -1: self._openalprpy_lib = ctypes.cdll.LoadLibrary("libopenalprpy.dylib") else: self._openalprpy_lib = ctypes.cdll.LoadLibrary("libopenalprpy.so") except OSError as e: nex = OSError("Unable to locate the OpenALPR library. Please make sure that OpenALPR is properly " "installed on your system and that the libraries are in the appropriate paths.") if _PYTHON_3: nex.__cause__ = e; raise nex self._initialize_func = self._openalprpy_lib.initialize self._initialize_func.restype = ctypes.c_void_p self._initialize_func.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p] self._dispose_func = self._openalprpy_lib.dispose self._dispose_func.argtypes = [ctypes.c_void_p] self._is_loaded_func = self._openalprpy_lib.isLoaded self._is_loaded_func.argtypes = [ctypes.c_void_p] self._is_loaded_func.restype = ctypes.c_bool self._recognize_file_func = self._openalprpy_lib.recognizeFile self._recognize_file_func.restype = ctypes.c_void_p self._recognize_file_func.argtypes = [ctypes.c_void_p, ctypes.c_char_p] self._recognize_array_func = self._openalprpy_lib.recognizeArray self._recognize_array_func.restype = ctypes.c_void_p self._recognize_array_func.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_ubyte), ctypes.c_uint] self._free_json_mem_func = self._openalprpy_lib.freeJsonMem self._set_country_func = self._openalprpy_lib.setCountry self._set_country_func.argtypes = [ctypes.c_void_p, ctypes.c_char_p] self._set_prewarp_func = self._openalprpy_lib.setPrewarp self._set_prewarp_func.argtypes = [ctypes.c_void_p, ctypes.c_char_p] self._set_default_region_func = self._openalprpy_lib.setDefaultRegion self._set_default_region_func.argtypes = [ctypes.c_void_p, ctypes.c_char_p] self._set_detect_region_func = self._openalprpy_lib.setDetectRegion self._set_detect_region_func.argtypes = [ctypes.c_void_p, ctypes.c_bool] self._set_top_n_func = self._openalprpy_lib.setTopN self._set_top_n_func.argtypes = [ctypes.c_void_p, ctypes.c_int] self._get_version_func = self._openalprpy_lib.getVersion self._get_version_func.argtypes = [ctypes.c_void_p] self._get_version_func.restype = ctypes.c_void_p self.alpr_pointer = self._initialize_func(country, config_file, runtime_dir) def unload(self): """ Unloads OpenALPR from memory. :return: None """ self._openalprpy_lib.dispose(self.alpr_pointer) def is_loaded(self): """ Checks if OpenALPR is loaded. :return: A bool representing if OpenALPR is loaded or not """ return self._is_loaded_func(self.alpr_pointer) def recognize_file(self, file_path): """ This causes OpenALPR to attempt to recognize an image by opening a file on disk. :param file_path: The path to the image that will be analyzed :return: An OpenALPR analysis in the form of a response dictionary """ file_path = _convert_to_charp(file_path) ptr = self._recognize_file_func(self.alpr_pointer, file_path) json_data = ctypes.cast(ptr, ctypes.c_char_p).value json_data = _convert_from_charp(json_data) response_obj = json.loads(json_data) self._free_json_mem_func(ctypes.c_void_p(ptr)) return response_obj def recognize_array(self, byte_array): """ This causes OpenALPR to attempt to recognize an image passed in as a byte array. :param byte_array: This should be a string (Python 2) or a bytes object (Python 3) :return: An OpenALPR analysis in the form of a response dictionary """ if type(byte_array) != bytes: raise TypeError("Expected a byte array (string in Python 2, bytes in Python 3)") pb = ctypes.cast(byte_array, ctypes.POINTER(ctypes.c_ubyte)) ptr = self._recognize_array_func(self.alpr_pointer, pb, len(byte_array)) json_data = ctypes.cast(ptr, ctypes.c_char_p).value json_data = _convert_from_charp(json_data) response_obj = json.loads(json_data) self._free_json_mem_func(ctypes.c_void_p(ptr)) return response_obj def get_version(self): """ This gets the version of OpenALPR :return: Version information """ ptr = self._get_version_func(self.alpr_pointer) version_number = ctypes.cast(ptr, ctypes.c_char_p).value version_number = _convert_from_charp(version_number) self._free_json_mem_func(ctypes.c_void_p(ptr)) return version_number def set_top_n(self, topn): """ Sets the number of returned results when analyzing an image. For example, setting topn = 5 returns the top 5 results. :param topn: An integer that represents the number of returned results. :return: None """ self._set_top_n_func(self.alpr_pointer, topn) def set_country(self, country): """ This sets the country for detecting license plates. For example, setting country to "us" for United States or "eu" for Europe. :param country: A unicode/ascii string (Python 2/3) or bytes array (Python 3) :return: None """ country = _convert_to_charp(country) self._set_country_func(self.alpr_pointer, country) def set_prewarp(self, prewarp): """ Updates the prewarp configuration used to skew images in OpenALPR before processing. :param prewarp: A unicode/ascii string (Python 2/3) or bytes array (Python 3) :return: None """ prewarp = _convert_to_charp(prewarp) self._set_prewarp_func(self.alpr_pointer, prewarp) def set_default_region(self, region): """ This sets the default region for detecting license plates. For example, setting region to "md" for Maryland or "fr" for France. :param region: A unicode/ascii string (Python 2/3) or bytes array (Python 3) :return: None """ region = _convert_to_charp(region) self._set_default_region_func(self.alpr_pointer, region) def set_detect_region(self, enabled): """ This allows OpenALPR to attempt to detect the region of a license plate automatically. By default this is disabled, but you can enable it here. :param enabled: A boolean representing whether or not auto-detection is enabled :return: None """ self._set_detect_region_func(self.alpr_pointer, enabled) def __del__(self): if self.is_loaded(): self.unload() def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if self.is_loaded(): self.unload() openalpr_2.2.4.orig/src/bindings/python/openalprpy.cpp000066400000000000000000000071461266464252400232050ustar00rootroot00000000000000#include #include #include #include extern "C" { #if defined(_MSC_VER) // Microsoft #define OPENALPR_EXPORT __declspec(dllexport) #else // do nothing #define OPENALPR_EXPORT #endif using namespace alpr; OPENALPR_EXPORT Alpr* initialize(char* ccountry, char* cconfigFile, char* cruntimeDir) { //printf("Initialize"); // Convert strings from char* to string std::string country(ccountry); std::string configFile(cconfigFile); std::string runtimeDir(cruntimeDir); //std::cout << country << std::endl << configFile << std::endl << runtimeDir << std::endl; Alpr* nativeAlpr = new alpr::Alpr(country, configFile, runtimeDir); return nativeAlpr; } OPENALPR_EXPORT void dispose(Alpr* nativeAlpr) { delete nativeAlpr; } OPENALPR_EXPORT bool isLoaded(Alpr* nativeAlpr) { //printf("IS LOADED"); return nativeAlpr->isLoaded(); } OPENALPR_EXPORT char* recognizeFile(Alpr* nativeAlpr, char* cimageFile) { //printf("Recognize file"); // Convert strings from java to C++ and release resources std::string imageFile(cimageFile); AlprResults results = nativeAlpr->recognize(imageFile); std::string json = Alpr::toJson(results); int strsize = sizeof(char) * (strlen(json.c_str()) + 1); char* membuffer = (char*)malloc(strsize); strcpy(membuffer, json.c_str()); //printf("allocated address: %p\n", membuffer); return membuffer; } OPENALPR_EXPORT void freeJsonMem(char* ptr) { //printf("freeing address: %p\n", ptr); free( ptr ); } OPENALPR_EXPORT char* recognizeArray(Alpr* nativeAlpr, unsigned char* buf, int len) { //printf("Recognize byte array"); //printf("buffer pointer: %p\n", buf); //printf("buffer length: %d\n", len); //std::cout << "Using instance: " << nativeAlpr << std::endl; std::vector cvec(buf, buf+len); AlprResults results = nativeAlpr->recognize(cvec); std::string json = Alpr::toJson(results); int strsize = sizeof(char) * (strlen(json.c_str()) + 1); char* membuffer = (char*)malloc(strsize); strcpy(membuffer, json.c_str()); //printf("allocated address: %p\n", membuffer); return membuffer; } OPENALPR_EXPORT void setCountry(Alpr* nativeAlpr, char* ccountry) { // Convert strings from java to C++ and release resources std::string country(ccountry); nativeAlpr->setCountry(country); } OPENALPR_EXPORT void setPrewarp(Alpr* nativeAlpr, char* cprewarp) { // Convert strings from java to C++ and release resources std::string prewarp(cprewarp); nativeAlpr->setPrewarp(prewarp); } OPENALPR_EXPORT void setDefaultRegion(Alpr* nativeAlpr, char* cdefault_region) { // Convert strings from java to C++ and release resources std::string default_region(cdefault_region); nativeAlpr->setDefaultRegion(default_region); } OPENALPR_EXPORT void setDetectRegion(Alpr* nativeAlpr, bool detect_region) { nativeAlpr->setDetectRegion(detect_region); } OPENALPR_EXPORT void setTopN(Alpr* nativeAlpr, int top_n) { nativeAlpr->setTopN(top_n); } OPENALPR_EXPORT char* getVersion(Alpr* nativeAlpr) { std::string version = nativeAlpr->getVersion(); int strsize = sizeof(char) * (strlen(version.c_str()) + 1); char* membuffer = (char*)malloc(strsize); strcpy(membuffer, version.c_str()); //printf("allocated address: %p\n", membuffer); return membuffer; } }openalpr_2.2.4.orig/src/bindings/python/setup.py000066400000000000000000000004421266464252400220120ustar00rootroot00000000000000#!/usr/bin/env python from distutils.core import setup setup(name='openalpr', version='1.0', description='OpenALPR Python Bindings', author='Matt Hill', author_email='matthill@openalpr.com', url='http://www.openalpr.com/', packages=['openalpr'] ) openalpr_2.2.4.orig/src/bindings/python/test.py000066400000000000000000000035451266464252400216400ustar00rootroot00000000000000from openalpr import Alpr from argparse import ArgumentParser parser = ArgumentParser(description='OpenALPR Python Test Program') parser.add_argument("-c", "--country", dest="country", action="store", default="us", help="License plate Country" ) parser.add_argument("--config", dest="config", action="store", default="/etc/openalpr/openalpr.conf", help="Path to openalpr.conf config file" ) parser.add_argument("--runtime_data", dest="runtime_data", action="store", default="/usr/share/openalpr/runtime_data", help="Path to OpenALPR runtime_data directory" ) parser.add_argument('plate_image', help='License plate image file') options = parser.parse_args() alpr = None try: alpr = Alpr(options.country, options.config, options.runtime_data) if not alpr.is_loaded(): print("Error loading OpenALPR") else: print("Using OpenALPR " + alpr.get_version()) alpr.set_top_n(7) alpr.set_default_region("wa") alpr.set_detect_region(False) jpeg_bytes = open(options.plate_image, "rb").read() results = alpr.recognize_array(jpeg_bytes) # Uncomment to see the full results structure # import pprint # pprint.pprint(results) print("Image size: %dx%d" %(results['img_width'], results['img_height'])) print("Processing Time: %f" % results['processing_time_ms']) i = 0 for plate in results['results']: i += 1 print("Plate #%d" % i) print(" %12s %12s" % ("Plate", "Confidence")) for candidate in plate['candidates']: prefix = "-" if candidate['matches_template']: prefix = "*" print(" %s %12s%12f" % (prefix, candidate['plate'], candidate['confidence'])) finally: if alpr: alpr.unload() openalpr_2.2.4.orig/src/cmake_modules/000077500000000000000000000000001266464252400177725ustar00rootroot00000000000000openalpr_2.2.4.orig/src/cmake_modules/FindTesseract.cmake000066400000000000000000000055241266464252400235400ustar00rootroot00000000000000# - Try to find Tesseract-OCR # Once done, this will define # # Tesseract_FOUND - system has Tesseract # Tesseract_INCLUDE_DIRS - the Tesseract include directories # Tesseract_LIBRARIES - link these to use Tesseract include(LibFindMacros) # Use pkg-config to get hints about paths #libfind_pkg_check_modules(Tesseract_PKGCONF Tesseract) # Include dir find_path(Tesseract_INCLUDE_BASEAPI_DIR NAMES tesseract/baseapi.h HINTS "/usr/include" "/usr/include/tesseract" "/usr/local/include" "/usr/local/include/tesseract" "/opt/local/include" "/opt/local/include/tesseract" ${Tesseract_PKGCONF_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/../libraries/tesseract-ocr/api/ ) find_path(Tesseract_INCLUDE_CCSTRUCT_DIR NAMES publictypes.h HINTS "/usr/include" "/usr/include/tesseract" "/usr/local/include" "/usr/local/include/tesseract" "/opt/local/include" "/opt/local/include/tesseract" ${Tesseract_PKGCONF_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/../libraries/tesseract-ocr/ccstruct/ ) find_path(Tesseract_INCLUDE_CCMAIN_DIR NAMES thresholder.h HINTS "/usr/include" "/usr/include/tesseract" "/usr/local/include" "/usr/local/include/tesseract" "/opt/local/include" "/opt/local/include/tesseract" ${Tesseract_PKGCONF_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/../libraries/tesseract-ocr/ccmain/ ) find_path(Tesseract_INCLUDE_CCUTIL_DIR NAMES platform.h HINTS "/usr/include" "/usr/include/tesseract" "/usr/local/include" "/usr/local/include/tesseract" "/opt/local/include" "/opt/local/include/tesseract" ${Tesseract_PKGCONF_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/../libraries/tesseract-ocr/ccutil/ ) # Finally the library itself find_library(Tesseract_LIB NAMES tesseract tesseract-static libtesseract303-static HINTS "/usr/lib" "/usr/local/lib" "/opt/local/lib" ${Tesseract_PKGCONF_LIBRARY_DIRS} ${CMAKE_SOURCE_DIR}/../libraries/tesseract-ocr/api/.libs ${CMAKE_SOURCE_DIR}/../libraries/tesseract-ocr/vs2010/LIB_Release ) find_library(Leptonica_LIB NAMES liblept170 liblept lept HINTS "/usr/lib" "/usr/local/lib" "/opt/local/lib" ${Tesseract_PKGCONF_LIBRARY_DIRS} ${CMAKE_SOURCE_DIR}/../libraries/tesseract-ocr/api/.libs ${CMAKE_SOURCE_DIR}/../libraries/tesseract-ocr/vs2010/LIB_Release ) # Set the include dir variables and the libraries and let libfind_process do the rest. # NOTE: Singular variables for this library, plural for libraries this this lib depends on. set(Tesseract_PROCESS_INCLUDES Tesseract_INCLUDE_BASEAPI_DIR Tesseract_INCLUDE_CCSTRUCT_DIR Tesseract_INCLUDE_CCMAIN_DIR Tesseract_INCLUDE_CCUTIL_DIR Tesseract_INCLUDE_DIRS) set(Tesseract_PROCESS_LIBS Tesseract_LIB Leptonica_LIB Tesseract_LIBRARIES) libfind_process(Tesseract)openalpr_2.2.4.orig/src/cmake_modules/Findlog4cplus.cmake000066400000000000000000000016421266464252400235140ustar00rootroot00000000000000# - Try to find log4cplus # Once done, this will define # # log4cplus_FOUND - system has log4cplus # log4cplus_INCLUDE_DIRS - the log4cplus include directories # log4cplus_LIBRARIES - link these to use log4cplus #libfind_pkg_check_modules(log4cplus_PKGCONF log4cplus) # Include dir find_path(log4cplus_INCLUDE_DIR NAMES log4cplus/version.h HINTS "/usr/include" "/usr/include/log4cplus" "/usr/local/include" "/usr/local/include/log4cplus" "/opt/local/include" "/opt/local/include/log4cplus" ${log4cplus_PKGCONF_INCLUDE_DIRS} ) # Finally the library itself find_library(log4cplus_LIB NAMES log4cplus liblog4cplus liblog4cplus-static HINTS "/usr/lib" "/usr/local/lib" "/opt/local/lib" ${log4cplus_PKGCONF_LIBRARY_DIRS} ) set(log4cplus_PROCESS_INCLUDES log4cplus_INCLUDE_DIR) set(log4cplus_PROCESS_LIBS log4cplus_LIB) libfind_process(log4cplus) openalpr_2.2.4.orig/src/cmake_modules/LibFindMacros.cmake000066400000000000000000000100401266464252400234430ustar00rootroot00000000000000# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments # used for the current package. For this to work, the first parameter must be the # prefix of the current package, then the prefix of the new package etc, which are # passed to find_package. macro (libfind_package PREFIX) set (LIBFIND_PACKAGE_ARGS ${ARGN}) if (${PREFIX}_FIND_QUIETLY) set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) endif (${PREFIX}_FIND_QUIETLY) if (${PREFIX}_FIND_REQUIRED) set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) endif (${PREFIX}_FIND_REQUIRED) find_package(${LIBFIND_PACKAGE_ARGS}) endmacro (libfind_package) # CMake developers made the UsePkgConfig system deprecated in the same release (2.6) # where they added pkg_check_modules. Consequently I need to support both in my scripts # to avoid those deprecated warnings. Here's a helper that does just that. # Works identically to pkg_check_modules, except that no checks are needed prior to use. macro (libfind_pkg_check_modules PREFIX PKGNAME) if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) include(UsePkgConfig) pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(${PREFIX} ${PKGNAME}) endif (PKG_CONFIG_FOUND) endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) endmacro (libfind_pkg_check_modules) # Do the final processing once the paths have been detected. # If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain # all the variables, each of which contain one include directory. # Ditto for ${PREFIX}_PROCESS_LIBS and library files. # Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. # Also handles errors in case library detection was required, etc. macro (libfind_process PREFIX) # Skip processing if already processed during this run if (NOT ${PREFIX}_FOUND) # Start with the assumption that the library was found set (${PREFIX}_FOUND TRUE) # Process all includes and set _FOUND to false if any are missing foreach (i ${${PREFIX}_PROCESS_INCLUDES}) if (${i}) set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) mark_as_advanced(${i}) else (${i}) set (${PREFIX}_FOUND FALSE) endif (${i}) endforeach (i) # Process all libraries and set _FOUND to false if any are missing foreach (i ${${PREFIX}_PROCESS_LIBS}) if (${i}) set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) mark_as_advanced(${i}) else (${i}) set (${PREFIX}_FOUND FALSE) endif (${i}) endforeach (i) # Print message and/or exit on fatal error if (${PREFIX}_FOUND) if (NOT ${PREFIX}_FIND_QUIETLY) message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") endif (NOT ${PREFIX}_FIND_QUIETLY) else (${PREFIX}_FOUND) if (${PREFIX}_FIND_REQUIRED) foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) message("${i}=${${i}}") endforeach (i) message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") endif (${PREFIX}_FIND_REQUIRED) endif (${PREFIX}_FOUND) endif (NOT ${PREFIX}_FOUND) endmacro (libfind_process) macro(libfind_library PREFIX basename) set(TMP "") if(MSVC80) set(TMP -vc80) endif(MSVC80) if(MSVC90) set(TMP -vc90) endif(MSVC90) set(${PREFIX}_LIBNAMES ${basename}${TMP}) if(${ARGC} GREATER 2) set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) endif(${ARGC} GREATER 2) find_library(${PREFIX}_LIBRARY NAMES ${${PREFIX}_LIBNAMES} PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} ) endmacro(libfind_library)openalpr_2.2.4.orig/src/cmake_modules/templates/000077500000000000000000000000001266464252400217705ustar00rootroot00000000000000openalpr_2.2.4.orig/src/cmake_modules/templates/cmake_uninstall.cmake.in000066400000000000000000000020131266464252400265440ustar00rootroot00000000000000if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif(NOT "${rm_retval}" STREQUAL 0) else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") endforeach(file) openalpr_2.2.4.orig/src/cmake_modules/templates/openalpr.pc.in000066400000000000000000000004051266464252400245400ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: OpenALPR Description: Open source Automatic License Plate Recognition library Version: @VERSION@ Libs: -L${libdir} -lopenalpr @TAG_OPENALPR_LIB_GO@ Cflags: -I${includedir} openalpr_2.2.4.orig/src/daemon.cpp000066400000000000000000000320121266464252400171270ustar00rootroot00000000000000 #include #include #include #include "daemon/beanstalk.hpp" #include "video/logging_videobuffer.h" #include "tclap/CmdLine.h" #include "alpr.h" #include "openalpr/simpleini/simpleini.h" #include "openalpr/cjson.h" #include "support/tinythread.h" #include #include "support/timing.h" #include #include #include #include #include using namespace alpr; // prototypes void streamRecognitionThread(void* arg); bool writeToQueue(std::string jsonResult); bool uploadPost(CURL* curl, std::string url, std::string data); void dataUploadThread(void* arg); // Constants const std::string ALPRD_CONFIG_FILE_NAME="alprd.conf"; const std::string OPENALPR_CONFIG_FILE_NAME="openalpr.conf"; const std::string DEFAULT_LOG_FILE_PATH="/var/log/alprd.log"; const std::string BEANSTALK_QUEUE_HOST="127.0.0.1"; const int BEANSTALK_PORT=11300; const std::string BEANSTALK_TUBE_NAME="alprd"; struct CaptureThreadData { std::string company_id; std::string stream_url; std::string site_id; int camera_id; bool clock_on; std::string config_file; std::string country_code; bool output_images; std::string output_image_folder; int top_n; }; struct UploadThreadData { std::string upload_url; }; void segfault_handler(int sig) { void *array[10]; size_t size; // get void*'s for all entries on the stack size = backtrace(array, 10); // print out all the frames to stderr backtrace_symbols_fd(array, size, STDERR_FILENO); exit(1); } bool daemon_active; static log4cplus::Logger logger; int main( int argc, const char** argv ) { signal(SIGSEGV, segfault_handler); // install our segfault handler daemon_active = true; bool noDaemon = false; bool clockOn = false; std::string logFile; std::string configDir; TCLAP::CmdLine cmd("OpenAlpr Daemon", ' ', Alpr::getVersion()); TCLAP::ValueArg configDirArg("","config","Path to the openalpr config directory that contains alprd.conf and openalpr.conf. (Default: /etc/openalpr/)",false, "/etc/openalpr/" ,"config_file"); TCLAP::ValueArg logFileArg("l","log","Log file to write to. Default=" + DEFAULT_LOG_FILE_PATH,false, DEFAULT_LOG_FILE_PATH ,"topN"); TCLAP::SwitchArg daemonOffSwitch("f","foreground","Set this flag for debugging. Disables forking the process as a daemon and runs in the foreground. Default=off", cmd, false); TCLAP::SwitchArg clockSwitch("","clock","Display timing information to log. Default=off", cmd, false); try { cmd.add( configDirArg ); cmd.add( logFileArg ); if (cmd.parse( argc, argv ) == false) { // Error occurred while parsing. Exit now. return 1; } // Make sure configDir ends in a slash configDir = configDirArg.getValue(); if (hasEnding(configDir, "/") == false) configDir = configDir + "/"; logFile = logFileArg.getValue(); noDaemon = daemonOffSwitch.getValue(); clockOn = clockSwitch.getValue(); } catch (TCLAP::ArgException &e) // catch any exceptions { std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; return 1; } std::string openAlprConfigFile = configDir + OPENALPR_CONFIG_FILE_NAME; std::string daemonConfigFile = configDir + ALPRD_CONFIG_FILE_NAME; // Validate that the configuration files exist if (fileExists(openAlprConfigFile.c_str()) == false) { std::cerr << "error, openalpr.conf file does not exist at: " << openAlprConfigFile << std::endl; return 1; } if (fileExists(daemonConfigFile.c_str()) == false) { std::cerr << "error, alprd.conf file does not exist at: " << daemonConfigFile << std::endl; return 1; } log4cplus::BasicConfigurator config; config.configure(); if (noDaemon == false) { // Fork off into a separate daemon daemon(0, 0); log4cplus::SharedAppenderPtr myAppender(new log4cplus::RollingFileAppender(logFile)); myAppender->setName("alprd_appender"); // Redirect std out to log file logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd")); logger.addAppender(myAppender); LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in daemon mode."); } else { //log4cplus::SharedAppenderPtr myAppender(new log4cplus::ConsoleAppender()); //myAppender->setName("alprd_appender"); // Redirect std out to log file logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd")); //logger.addAppender(myAppender); LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in the foreground."); } CSimpleIniA ini; ini.SetMultiKey(); ini.LoadFile(daemonConfigFile.c_str()); std::vector stream_urls; CSimpleIniA::TNamesDepend values; ini.GetAllValues("daemon", "stream", values); // sort the values into the original load order values.sort(CSimpleIniA::Entry::LoadOrder()); // output all of the items CSimpleIniA::TNamesDepend::const_iterator i; for (i = values.begin(); i != values.end(); ++i) { stream_urls.push_back(i->pItem); } if (stream_urls.size() == 0) { LOG4CPLUS_FATAL(logger, "No video streams defined in the configuration."); return 1; } std::string country = ini.GetValue("daemon", "country", "us"); int topn = ini.GetLongValue("daemon", "topn", 20); bool storePlates = ini.GetBoolValue("daemon", "store_plates", false); std::string imageFolder = ini.GetValue("daemon", "store_plates_location", "/tmp/"); bool uploadData = ini.GetBoolValue("daemon", "upload_data", false); std::string upload_url = ini.GetValue("daemon", "upload_address", ""); std::string company_id = ini.GetValue("daemon", "company_id", ""); std::string site_id = ini.GetValue("daemon", "site_id", ""); LOG4CPLUS_INFO(logger, "Using: " << daemonConfigFile << " for daemon configuration"); LOG4CPLUS_INFO(logger, "Using: " << imageFolder << " for storing valid plate images"); pid_t pid; for (int i = 0; i < stream_urls.size(); i++) { pid = fork(); if (pid == (pid_t) 0) { // This is the child process, kick off the capture data and upload threads CaptureThreadData* tdata = new CaptureThreadData(); tdata->stream_url = stream_urls[i]; tdata->camera_id = i + 1; tdata->config_file = openAlprConfigFile; tdata->output_images = storePlates; tdata->output_image_folder = imageFolder; tdata->country_code = country; tdata->company_id = company_id; tdata->site_id = site_id; tdata->top_n = topn; tdata->clock_on = clockOn; tthread::thread* thread_recognize = new tthread::thread(streamRecognitionThread, (void*) tdata); if (uploadData) { // Kick off the data upload thread UploadThreadData* udata = new UploadThreadData(); udata->upload_url = upload_url; tthread::thread* thread_upload = new tthread::thread(dataUploadThread, (void*) udata ); } break; } // Parent process will continue and spawn more children } while (daemon_active) { usleep(30000); } } void streamRecognitionThread(void* arg) { CaptureThreadData* tdata = (CaptureThreadData*) arg; LOG4CPLUS_INFO(logger, "country: " << tdata->country_code << " -- config file: " << tdata->config_file ); LOG4CPLUS_INFO(logger, "Stream " << tdata->camera_id << ": " << tdata->stream_url); Alpr alpr(tdata->country_code, tdata->config_file); alpr.setTopN(tdata->top_n); int framenum = 0; LoggingVideoBuffer videoBuffer(logger); videoBuffer.connect(tdata->stream_url, 5); cv::Mat latestFrame; std::vector buffer; LOG4CPLUS_INFO(logger, "Starting camera " << tdata->camera_id); while (daemon_active) { std::vector regionsOfInterest; int response = videoBuffer.getLatestFrame(&latestFrame, regionsOfInterest); if (response != -1) { timespec startTime; getTimeMonotonic(&startTime); std::vector regionsOfInterest; regionsOfInterest.push_back(AlprRegionOfInterest(0,0, latestFrame.cols, latestFrame.rows)); AlprResults results = alpr.recognize(latestFrame.data, latestFrame.elemSize(), latestFrame.cols, latestFrame.rows, regionsOfInterest); timespec endTime; getTimeMonotonic(&endTime); double totalProcessingTime = diffclock(startTime, endTime); if (tdata->clock_on) { LOG4CPLUS_INFO(logger, "Camera " << tdata->camera_id << " processed frame in: " << totalProcessingTime << " ms."); } if (results.plates.size() > 0) { std::stringstream uuid_ss; uuid_ss << tdata->site_id << "-cam" << tdata->camera_id << "-" << getEpochTimeMs(); std::string uuid = uuid_ss.str(); // Save the image to disk (using the UUID) if (tdata->output_images) { std::stringstream ss; ss << tdata->output_image_folder << "/" << uuid << ".jpg"; cv::imwrite(ss.str(), latestFrame); } // Update the JSON content to include UUID and camera ID std::string json = alpr.toJson(results); cJSON *root = cJSON_Parse(json.c_str()); cJSON_AddStringToObject(root, "uuid", uuid.c_str()); cJSON_AddNumberToObject(root, "camera_id", tdata->camera_id); cJSON_AddStringToObject(root, "site_id", tdata->site_id.c_str()); cJSON_AddNumberToObject(root, "img_width", latestFrame.cols); cJSON_AddNumberToObject(root, "img_height", latestFrame.rows); // Add the company ID to the output if configured if (tdata->company_id.length() > 0) cJSON_AddStringToObject(root, "company_id", tdata->company_id.c_str()); char *out; out=cJSON_PrintUnformatted(root); cJSON_Delete(root); std::string response(out); free(out); // Push the results to the Beanstalk queue for (int j = 0; j < results.plates.size(); j++) { LOG4CPLUS_DEBUG(logger, "Writing plate " << results.plates[j].bestPlate.characters << " (" << uuid << ") to queue."); } writeToQueue(response); } } usleep(10000); } videoBuffer.disconnect(); LOG4CPLUS_INFO(logger, "Video processing ended"); delete tdata; } bool writeToQueue(std::string jsonResult) { try { Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT); client.use(BEANSTALK_TUBE_NAME); int id = client.put(jsonResult); if (id <= 0) { LOG4CPLUS_ERROR(logger, "Failed to write data to queue"); return false; } LOG4CPLUS_DEBUG(logger, "put job id: " << id ); } catch (const std::runtime_error& error) { LOG4CPLUS_WARN(logger, "Error connecting to Beanstalk. Result has not been saved."); return false; } return true; } void dataUploadThread(void* arg) { CURL *curl; /* In windows, this will init the winsock stuff */ curl_global_init(CURL_GLOBAL_ALL); UploadThreadData* udata = (UploadThreadData*) arg; while(daemon_active) { try { /* get a curl handle */ curl = curl_easy_init(); Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT); client.watch(BEANSTALK_TUBE_NAME); while (daemon_active) { Beanstalk::Job job; client.reserve(job); if (job.id() > 0) { //LOG4CPLUS_DEBUG(logger, job.body() ); if (uploadPost(curl, udata->upload_url, job.body())) { client.del(job.id()); LOG4CPLUS_INFO(logger, "Job: " << job.id() << " successfully uploaded" ); // Wait 10ms sleep_ms(10); } else { client.release(job); LOG4CPLUS_WARN(logger, "Job: " << job.id() << " failed to upload. Will retry." ); // Wait 2 seconds sleep_ms(2000); } } } /* always cleanup */ curl_easy_cleanup(curl); } catch (const std::runtime_error& error) { LOG4CPLUS_WARN(logger, "Error connecting to Beanstalk. Will retry." ); } // wait 5 seconds usleep(5000000); } curl_global_cleanup(); } bool uploadPost(CURL* curl, std::string url, std::string data) { bool success = true; CURLcode res; struct curl_slist *headers=NULL; // init to NULL is important /* Add the required headers */ headers = curl_slist_append(headers, "Accept: application/json"); headers = curl_slist_append( headers, "Content-Type: application/json"); headers = curl_slist_append( headers, "charsets: utf-8"); if(curl) { /* Add the headers */ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); /* First set the URL that is about to receive our POST. This URL can just as well be a https:// URL if that is what should receive the data. */ curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); /* Now specify the POST data */ //char* escaped_data = curl_easy_escape(curl, data.c_str(), data.length()); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); //curl_free(escaped_data); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ if(res != CURLE_OK) { success = false; } } return success; }openalpr_2.2.4.orig/src/daemon/000077500000000000000000000000001266464252400164255ustar00rootroot00000000000000openalpr_2.2.4.orig/src/daemon/beanstalk.c000066400000000000000000000450441266464252400205440ustar00rootroot00000000000000#include "beanstalk.h" #include #include #include #include #include #include #include #define BS_STATUS_IS(message, code) strncmp(message, code, strlen(code)) == 0 #define BS_MESSAGE_NO_BODY 0 #define BS_MESSAGE_HAS_BODY 1 #ifndef BS_READ_CHUNK_SIZE #define BS_READ_CHUNK_SIZE 4096 #endif #define DATA_PENDING (errno == EAGAIN || errno == EWOULDBLOCK) const char *bs_status_verbose[] = { "Success", "Operation failed", "Expected CRLF", "Job too big", "Queue draining", "Timed out", "Not found", "Deadline soon", "Buried", "Not ignored" }; const char bs_resp_using[] = "USING"; const char bs_resp_watching[] = "WATCHING"; const char bs_resp_inserted[] = "INSERTED"; const char bs_resp_buried[] = "BURIED"; const char bs_resp_expected_crlf[] = "EXPECTED_CRLF"; const char bs_resp_job_too_big[] = "JOB_TOO_BIG"; const char bs_resp_draining[] = "DRAINING"; const char bs_resp_reserved[] = "RESERVED"; const char bs_resp_deadline_soon[] = "DEADLINE_SOON"; const char bs_resp_timed_out[] = "TIMED_OUT"; const char bs_resp_deleted[] = "DELETED"; const char bs_resp_not_found[] = "NOT_FOUND"; const char bs_resp_released[] = "RELEASED"; const char bs_resp_touched[] = "TOUCHED"; const char bs_resp_not_ignored[] = "NOT_IGNORED"; const char bs_resp_found[] = "FOUND"; const char bs_resp_kicked[] = "KICKED"; const char bs_resp_ok[] = "OK"; const char* bs_status_text(int code) { unsigned int cindex = (unsigned int) abs(code); return (cindex > sizeof(bs_status_verbose) / sizeof(char*)) ? 0 : bs_status_verbose[cindex]; } int bs_resolve_address(char *host, int port, struct sockaddr_in *server) { char service[64]; struct addrinfo *addr, *rec; snprintf(service, 64, "%d", port); if (getaddrinfo(host, service, 0, &addr) != 0) return BS_STATUS_FAIL; for (rec = addr; rec != 0; rec = rec->ai_next) { if (rec->ai_family == AF_INET) { memcpy(server, rec->ai_addr, sizeof(*server)); break; } } freeaddrinfo(addr); return BS_STATUS_OK; } int bs_connect(char *host, int port) { int fd, state = 1; struct sockaddr_in server; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0 || bs_resolve_address(host, port, &server) < 0) return BS_STATUS_FAIL; if (connect(fd, (struct sockaddr*)&server, sizeof(server)) != 0) { close(fd); return BS_STATUS_FAIL; } /* disable nagle - we buffer in the application layer */ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &state, sizeof(state)); return fd; } int bs_connect_with_timeout(char *host, int port, float secs) { struct sockaddr_in server; int fd, res, option, state = 1; socklen_t option_length; struct pollfd pfd; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0 || bs_resolve_address(host, port, &server) < 0) return BS_STATUS_FAIL; // Set non-blocking fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, NULL) | O_NONBLOCK); res = connect(fd, (struct sockaddr*)&server, sizeof(server)); if (res < 0) { if (errno == EINPROGRESS) { // Init poll structure pfd.fd = fd; pfd.events = POLLOUT; if (poll(&pfd, 1, (int)(secs*1000)) > 0) { option_length = sizeof(int); getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)(&option), &option_length); if (option) { close(fd); return BS_STATUS_FAIL; } } else { close(fd); return BS_STATUS_FAIL; } } else { close(fd); return BS_STATUS_FAIL; } } // Set to blocking mode fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, NULL) & ~(O_NONBLOCK)); /* disable nagle - we buffer in the application layer */ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &state, sizeof(state)); return fd; } int bs_disconnect(int fd) { close(fd); return BS_STATUS_OK; } void bs_free_message(BSM* m) { if (m->status) free(m->status); if (m->data) free(m->data); free(m); } void bs_free_job(BSJ *job) { if (job->data) free(job->data); free(job); } // optional polling bs_poll_function bs_poll = 0; void bs_start_polling(bs_poll_function f) { bs_poll = f; } void bs_reset_polling() { bs_poll = 0; } BSM* bs_recv_message(int fd, int expect_data) { char *token, *data; size_t bytes, data_size, status_size, status_max = 512, expect_data_bytes = 0; ssize_t ret; BSM *message = (BSM*)calloc(1, sizeof(BSM)); if (!message) return 0; message->status = (char*)calloc(1, status_max); if (!message->status) { bs_free_message(message); return 0; } // poll until ready to read if (bs_poll) bs_poll(1, fd); ret = recv(fd, message->status, status_max - 1, 0); if (ret < 0) { bs_free_message(message); return 0; } else { bytes = (size_t) ret; } token = strstr(message->status, "\r\n"); if (!token) { bs_free_message(message); return 0; } *token = 0; status_size = (size_t) (token - message->status); if (expect_data) { token = rindex(message->status, ' '); expect_data_bytes = token ? strtoul(token + 1, NULL, 10) : 0; } if (!expect_data || expect_data_bytes == 0) return message; message->size = bytes - status_size - 2; data_size = message->size > BS_READ_CHUNK_SIZE ? message->size + BS_READ_CHUNK_SIZE : BS_READ_CHUNK_SIZE; message->data = (char*)malloc(data_size); if (!message->data) { bs_free_message(message); return 0; } memcpy(message->data, message->status + status_size + 2, message->size); data = message->data + message->size; // already read the body along with status, all good. if ((expect_data_bytes + 2) <= message->size) { message->size = expect_data_bytes; return message; } while (1) { // poll until ready to read. if (bs_poll) bs_poll(1, fd); ret = recv(fd, data, data_size - message->size, 0); if (ret < 0) { if (bs_poll && DATA_PENDING) continue; else { bs_free_message(message); return 0; } } else { bytes = (size_t) ret; } // doneski, we have read enough bytes + \r\n if (message->size + bytes >= expect_data_bytes + 2) { message->size = expect_data_bytes; break; } data_size += BS_READ_CHUNK_SIZE; message->size += bytes; message->data = (char*)realloc(message->data, data_size); if (!message->data) { bs_free_message(message); return 0; } // move ahead pointer for reading more. data = message->data + message->size; } return message; } ssize_t bs_send_message(int fd, char *message, size_t size) { // poll until ready to write. if (bs_poll) bs_poll(2, fd); return send(fd, message, size, bs_poll ? MSG_DONTWAIT : 0); } typedef struct bs_message_packet { char *data; size_t offset; size_t size; } BSMP; BSMP* bs_message_packet_new(size_t bytes) { BSMP *packet = (BSMP*)malloc(sizeof(BSMP)); assert(packet); packet->data = (char*)malloc(bytes); assert(packet->data); packet->offset = 0; packet->size = bytes; return packet; } void bs_message_packet_append(BSMP *packet, char *data, size_t bytes) { if (packet->offset + bytes > packet->size) { packet->data = (char*)realloc(packet->data, packet->size + bytes); assert(packet->data); packet->size += bytes; } memcpy(packet->data + packet->offset, data, bytes); packet->offset += bytes; } void bs_message_packet_free(BSMP *packet) { free(packet->data); free(packet); } #define BS_SEND(fd, command, size) { \ if (bs_send_message(fd, command, size) < 0) \ return BS_STATUS_FAIL; \ } #define BS_CHECK_MESSAGE(message) { \ if (!message) \ return BS_STATUS_FAIL; \ } #define BS_RETURN_OK_WHEN(message, okstatus) { \ if (BS_STATUS_IS(message->status, okstatus)) { \ bs_free_message(message); \ return BS_STATUS_OK; \ } \ } #define BS_RETURN_FAIL_WHEN(message, nokstatus, nokcode) { \ if (BS_STATUS_IS(message->status, nokstatus)) { \ bs_free_message(message); \ return nokcode; \ } \ } #define BS_RETURN_INVALID(message) { \ bs_free_message(message); \ return BS_STATUS_FAIL; \ } int bs_use(int fd, char *tube) { BSM *message; char command[1024]; snprintf(command, 1024, "use %s\r\n", tube); BS_SEND(fd, command, strlen(command)); message = bs_recv_message(fd, BS_MESSAGE_NO_BODY); BS_CHECK_MESSAGE(message); BS_RETURN_OK_WHEN(message, bs_resp_using); BS_RETURN_INVALID(message); } int bs_watch(int fd, char *tube) { BSM *message; char command[1024]; snprintf(command, 1024, "watch %s\r\n", tube); BS_SEND(fd, command, strlen(command)); message = bs_recv_message(fd, BS_MESSAGE_NO_BODY); BS_CHECK_MESSAGE(message); BS_RETURN_OK_WHEN(message, bs_resp_watching); BS_RETURN_INVALID(message); } int bs_ignore(int fd, char *tube) { BSM *message; char command[1024]; snprintf(command, 1024, "ignore %s\r\n", tube); BS_SEND(fd, command, strlen(command)); message = bs_recv_message(fd, BS_MESSAGE_NO_BODY); BS_CHECK_MESSAGE(message); BS_RETURN_OK_WHEN(message, bs_resp_watching); BS_RETURN_INVALID(message); } int64_t bs_put(int fd, uint32_t priority, uint32_t delay, uint32_t ttr, char *data, size_t bytes) { int64_t id; BSMP *packet; BSM *message; char command[1024]; size_t command_bytes; snprintf(command, 1024, "put %"PRIu32" %"PRIu32" %"PRIu32" %lu\r\n", priority, delay, ttr, bytes); command_bytes = strlen(command); packet = bs_message_packet_new(command_bytes + bytes + 3); bs_message_packet_append(packet, command, command_bytes); bs_message_packet_append(packet, data, bytes); bs_message_packet_append(packet, "\r\n", 2); // Can't use BS_SEND here, allocated memory needs to // be cleared on error int ret_code = bs_send_message(fd, packet->data, packet->offset); bs_message_packet_free(packet); if (ret_code <0) { return BS_STATUS_FAIL; } message = bs_recv_message(fd, BS_MESSAGE_NO_BODY); BS_CHECK_MESSAGE(message); if (BS_STATUS_IS(message->status, bs_resp_inserted)) { id = strtoll(message->status + strlen(bs_resp_inserted) + 1, NULL, 10); bs_free_message(message); return id; } if (BS_STATUS_IS(message->status, bs_resp_buried)) { id = strtoll(message->status + strlen(bs_resp_buried) + 1, NULL, 10); bs_free_message(message); return id; } BS_RETURN_FAIL_WHEN(message, bs_resp_expected_crlf, BS_STATUS_EXPECTED_CRLF); BS_RETURN_FAIL_WHEN(message, bs_resp_job_too_big, BS_STATUS_JOB_TOO_BIG); BS_RETURN_FAIL_WHEN(message, bs_resp_draining, BS_STATUS_DRAINING); BS_RETURN_INVALID(message); } int bs_delete(int fd, int64_t job) { BSM *message; char command[512]; snprintf(command, 512, "delete %"PRId64"\r\n", job); BS_SEND(fd, command, strlen(command)); message = bs_recv_message(fd, BS_MESSAGE_NO_BODY); BS_CHECK_MESSAGE(message); BS_RETURN_OK_WHEN(message, bs_resp_deleted); BS_RETURN_FAIL_WHEN(message, bs_resp_not_found, BS_STATUS_NOT_FOUND); BS_RETURN_INVALID(message); } int bs_reserve_job(int fd, char *command, BSJ **result) { BSJ *job; BSM *message; // XXX: debug // struct timeval start, end; // gettimeofday(&start, 0); BS_SEND(fd, command, strlen(command)); message = bs_recv_message(fd, BS_MESSAGE_HAS_BODY); BS_CHECK_MESSAGE(message); if (BS_STATUS_IS(message->status, bs_resp_reserved)) { *result = job = (BSJ*)malloc(sizeof(BSJ)); if (!job) { bs_free_message(message); return BS_STATUS_FAIL; } sscanf(message->status + strlen(bs_resp_reserved) + 1, "%"PRId64" %lu", &job->id, &job->size); job->data = message->data; message->data = 0; bs_free_message(message); // XXX: debug // gettimeofday(&end, 0); // printf("elapsed: %lu\n", (end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec)); return BS_STATUS_OK; } // i don't think we'll ever hit this status code here. BS_RETURN_FAIL_WHEN(message, bs_resp_timed_out, BS_STATUS_TIMED_OUT); BS_RETURN_FAIL_WHEN(message, bs_resp_deadline_soon, BS_STATUS_DEADLINE_SOON); BS_RETURN_INVALID(message); } int bs_reserve(int fd, BSJ **result) { char *command = "reserve\r\n"; return bs_reserve_job(fd, command, result); } int bs_reserve_with_timeout(int fd, uint32_t ttl, BSJ **result) { char command[512]; snprintf(command, 512, "reserve-with-timeout %"PRIu32"\r\n", ttl); return bs_reserve_job(fd, command, result); } int bs_release(int fd, int64_t id, uint32_t priority, uint32_t delay) { BSM *message; char command[512]; snprintf(command, 512, "release %"PRId64" %"PRIu32" %"PRIu32"\r\n", id, priority, delay); BS_SEND(fd, command, strlen(command)); message = bs_recv_message(fd, BS_MESSAGE_NO_BODY); BS_CHECK_MESSAGE(message); BS_RETURN_OK_WHEN(message, bs_resp_released); BS_RETURN_FAIL_WHEN(message, bs_resp_buried, BS_STATUS_BURIED); BS_RETURN_FAIL_WHEN(message, bs_resp_not_found, BS_STATUS_NOT_FOUND); BS_RETURN_INVALID(message); } int bs_bury(int fd, int64_t id, uint32_t priority) { BSM *message; char command[512]; snprintf(command, 512, "bury %"PRId64" %"PRIu32"\r\n", id, priority); BS_SEND(fd, command, strlen(command)); message = bs_recv_message(fd, BS_MESSAGE_NO_BODY); BS_CHECK_MESSAGE(message); BS_RETURN_OK_WHEN(message, bs_resp_buried); BS_RETURN_FAIL_WHEN(message, bs_resp_not_found, BS_STATUS_NOT_FOUND); BS_RETURN_INVALID(message); } int bs_touch(int fd, int64_t id) { BSM *message; char command[512]; snprintf(command, 512, "touch %"PRId64"\r\n", id); BS_SEND(fd, command, strlen(command)); message = bs_recv_message(fd, BS_MESSAGE_NO_BODY); BS_CHECK_MESSAGE(message); BS_RETURN_OK_WHEN(message, bs_resp_touched); BS_RETURN_FAIL_WHEN(message, bs_resp_not_found, BS_STATUS_NOT_FOUND); BS_RETURN_INVALID(message); } int bs_peek_job(int fd, char *command, BSJ **result) { BSJ *job; BSM *message; BS_SEND(fd, command, strlen(command)); message = bs_recv_message(fd, BS_MESSAGE_HAS_BODY); BS_CHECK_MESSAGE(message); if (BS_STATUS_IS(message->status, bs_resp_found)) { *result = job = (BSJ*)malloc(sizeof(BSJ)); if (!job) { bs_free_message(message); return BS_STATUS_FAIL; } sscanf(message->status + strlen(bs_resp_found) + 1, "%"PRId64" %lu", &job->id, &job->size); job->data = message->data; message->data = 0; bs_free_message(message); return BS_STATUS_OK; } BS_RETURN_FAIL_WHEN(message, bs_resp_not_found, BS_STATUS_NOT_FOUND); BS_RETURN_INVALID(message); } int bs_peek(int fd, int64_t id, BSJ **job) { char command[512]; snprintf(command, 512, "peek %"PRId64"\r\n", id); return bs_peek_job(fd, command, job); } int bs_peek_ready(int fd, BSJ **job) { return bs_peek_job(fd, "peek-ready\r\n", job); } int bs_peek_delayed(int fd, BSJ **job) { return bs_peek_job(fd, "peek-delayed\r\n", job); } int bs_peek_buried(int fd, BSJ **job) { return bs_peek_job(fd, "peek-buried\r\n", job); } int bs_kick(int fd, int bound) { BSM *message; char command[512]; snprintf(command, 512, "kick %d\r\n", bound); BS_SEND(fd, command, strlen(command)); message = bs_recv_message(fd, BS_MESSAGE_NO_BODY); BS_CHECK_MESSAGE(message); BS_RETURN_OK_WHEN(message, bs_resp_kicked); BS_RETURN_INVALID(message); } int bs_list_tube_used(int fd, char **tube) { BSM *message; char command[64]; snprintf(command, 64, "list-tube-used\r\n"); BS_SEND(fd, command, strlen(command)); message = bs_recv_message(fd, BS_MESSAGE_NO_BODY); if (BS_STATUS_IS(message->status, bs_resp_using)) { *tube = (char*)calloc(1, strlen(message->status) - strlen(bs_resp_using) + 1); strcpy(*tube, message->status + strlen(bs_resp_using) + 1); bs_free_message(message); return BS_STATUS_OK; } BS_RETURN_INVALID(message); } int bs_get_info(int fd, char *command, char **yaml) { BSM *message; size_t size; BS_SEND(fd, command, strlen(command)); message = bs_recv_message(fd, BS_MESSAGE_HAS_BODY); BS_CHECK_MESSAGE(message); if (BS_STATUS_IS(message->status, bs_resp_ok)) { sscanf(message->status + strlen(bs_resp_ok) + 1, "%lu", &size); *yaml = message->data; (*yaml)[size] = 0; message->data = 0; bs_free_message(message); return BS_STATUS_OK; } BS_RETURN_INVALID(message); } int bs_list_tubes(int fd, char **yaml) { char command[64]; snprintf(command, 64, "list-tubes\r\n"); return bs_get_info(fd, command, yaml); } int bs_list_tubes_watched(int fd, char **yaml) { char command[64]; snprintf(command, 64, "list-tubes-watched\r\n"); return bs_get_info(fd, command, yaml); } int bs_stats(int fd, char **yaml) { char command[64]; snprintf(command, 64, "stats\r\n"); return bs_get_info(fd, command, yaml); } int bs_stats_job(int fd, int64_t id, char **yaml) { char command[128]; snprintf(command, 128, "stats-job %"PRId64"\r\n", id); return bs_get_info(fd, command, yaml); } int bs_stats_tube(int fd, char *tube, char **yaml) { char command[512]; snprintf(command, 512, "stats-tube %s\r\n", tube); return bs_get_info(fd, command, yaml); } void bs_version(int *major, int *minor, int *patch) { *major = BS_MAJOR_VERSION; *minor = BS_MINOR_VERSION; *patch = BS_PATCH_VERSION; } openalpr_2.2.4.orig/src/daemon/beanstalk.cc000066400000000000000000000176271266464252400207150ustar00rootroot00000000000000#include "beanstalk.hpp" #include #include #include using namespace std; namespace Beanstalk { Job::Job() { _id = 0; } Job::Job(int64_t id, char *data, size_t size) { _body.assign(data, size); _id = id; } Job::Job(BSJ *job) { if (job) { _body.assign(job->data, job->size); _id = job->id; bs_free_job(job); } else { _id = 0; } } string& Job::body() { return _body; } int64_t Job::id() { return _id; } /* start helpers */ void parsedict(stringstream &stream, info_hash_t &dict) { string key, value; while(true) { stream >> key; if (stream.eof()) break; if (key[0] == '-') continue; stream >> value; key.erase(--key.end()); dict[key] = value; } } void parselist(stringstream &stream, info_list_t &list) { string value; while(true) { stream >> value; if (stream.eof()) break; if (value[0] == '-') continue; list.push_back(value); } } /* end helpers */ Client::~Client() { if (handle > 0) bs_disconnect(handle); handle = -1; } Client::Client() { handle = -1; host = ""; port = 0; timeout_secs = 0; } Client::Client(string host, int port, float secs) { handle = -1; timeout_secs = secs; connect(host, port, timeout_secs); } void Client::connect(string _host, int _port, float secs) { if (handle > 0) throw runtime_error("already connected to beanstalkd at " + host); host = _host; port = _port; timeout_secs = secs; handle = secs > 0 ? bs_connect_with_timeout((char *)host.c_str(), port, secs) : bs_connect((char*)host.c_str(), port); if (handle < 0) throw runtime_error("unable to connect to beanstalkd at " + host); } bool Client::is_connected() { return handle > 0; } bool Client::disconnect() { if (handle > 0 && bs_disconnect(handle) == BS_STATUS_OK) { handle = -1; return true; } return false; } void Client::version(int *major, int *minor, int *patch) { bs_version(major, minor, patch); } void Client::reconnect() { disconnect(); connect(host, port, timeout_secs); } bool Client::use(string tube) { return bs_use(handle, (char*)tube.c_str()) == BS_STATUS_OK; } bool Client::watch(string tube) { return bs_watch(handle, (char*)tube.c_str()) == BS_STATUS_OK; } bool Client::ignore(string tube) { return bs_ignore(handle, (char*)tube.c_str()) == BS_STATUS_OK; } int64_t Client::put(string body, uint32_t priority, uint32_t delay, uint32_t ttr) { int64_t id = bs_put(handle, priority, delay, ttr, (char*)body.data(), body.size()); return (id > 0 ? id : 0); } int64_t Client::put(char *body, size_t bytes, uint32_t priority, uint32_t delay, uint32_t ttr) { int64_t id = bs_put(handle, priority, delay, ttr, body, bytes); return (id > 0 ? id : 0); } bool Client::del(Job &job) { return bs_delete(handle, job.id()) == BS_STATUS_OK; } bool Client::del(int64_t id) { return bs_delete(handle, id) == BS_STATUS_OK; } bool Client::reserve(Job &job) { BSJ *bsj; if (bs_reserve(handle, &bsj) == BS_STATUS_OK) { job = bsj; return true; } return false; } bool Client::reserve(Job &job, uint32_t timeout) { BSJ *bsj; if (bs_reserve_with_timeout(handle, timeout, &bsj) == BS_STATUS_OK) { job = bsj; return true; } return false; } bool Client::release(Job &job, uint32_t priority, uint32_t delay) { return bs_release(handle, job.id(), priority, delay) == BS_STATUS_OK; } bool Client::release(int64_t id, uint32_t priority, uint32_t delay) { return bs_release(handle, id, priority, delay) == BS_STATUS_OK; } bool Client::bury(Job &job, uint32_t priority) { return bs_bury(handle, job.id(), priority) == BS_STATUS_OK; } bool Client::bury(int64_t id, uint32_t priority) { return bs_bury(handle, id, priority) == BS_STATUS_OK; } bool Client::touch(Job &job) { return bs_touch(handle, job.id()) == BS_STATUS_OK; } bool Client::touch(int64_t id) { return bs_touch(handle, id) == BS_STATUS_OK; } bool Client::peek(Job &job, int64_t id) { BSJ *bsj; if (bs_peek(handle, id, &bsj) == BS_STATUS_OK) { job = bsj; return true; } return false; } bool Client::peek_ready(Job &job) { BSJ *bsj; if (bs_peek_ready(handle, &bsj) == BS_STATUS_OK) { job = bsj; return true; } return false; } bool Client::peek_delayed(Job &job) { BSJ *bsj; if (bs_peek_delayed(handle, &bsj) == BS_STATUS_OK) { job = bsj; return true; } return false; } bool Client::peek_buried(Job &job) { BSJ *bsj; if (bs_peek_buried(handle, &bsj) == BS_STATUS_OK) { job = bsj; return true; } return false; } bool Client::kick(int bound) { return bs_kick(handle, bound) == BS_STATUS_OK; } string Client::list_tube_used() { char *name; string tube; if (bs_list_tube_used(handle, &name) == BS_STATUS_OK) { tube.assign(name); free(name); } return tube; } info_list_t Client::list_tubes() { char *yaml, *data; info_list_t tubes; if (bs_list_tubes(handle, &yaml) == BS_STATUS_OK) { if ((data = strstr(yaml, "---"))) { stringstream stream(data); parselist(stream, tubes); } free(yaml); } return tubes; } info_list_t Client::list_tubes_watched() { char *yaml, *data; info_list_t tubes; if (bs_list_tubes_watched(handle, &yaml) == BS_STATUS_OK) { if ((data = strstr(yaml, "---"))) { stringstream stream(data); parselist(stream, tubes); } free(yaml); } return tubes; } info_hash_t Client::stats() { char *yaml, *data; info_hash_t stats; string key, value; if (bs_stats(handle, &yaml) == BS_STATUS_OK) { if ((data = strstr(yaml, "---"))) { stringstream stream(data); parsedict(stream, stats); } free(yaml); } return stats; } info_hash_t Client::stats_job(int64_t id) { char *yaml, *data; info_hash_t stats; string key, value; if (bs_stats_job(handle, id, &yaml) == BS_STATUS_OK) { if ((data = strstr(yaml, "---"))) { stringstream stream(data); parsedict(stream, stats); } free(yaml); } return stats; } info_hash_t Client::stats_tube(string name) { char *yaml, *data; info_hash_t stats; string key, value; if (bs_stats_tube(handle, (char*)name.c_str(), &yaml) == BS_STATUS_OK) { if ((data = strstr(yaml, "---"))) { stringstream stream(data); parsedict(stream, stats); } free(yaml); } return stats; } bool Client::ping() { char *yaml; if (bs_list_tubes(handle, &yaml) == BS_STATUS_OK) { free(yaml); return true; } return false; } } openalpr_2.2.4.orig/src/daemon/beanstalk.h000066400000000000000000000066721266464252400205550ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include #define BS_MAJOR_VERSION 1 #define BS_MINOR_VERSION 2 #define BS_PATCH_VERSION 0 #define BS_STATUS_OK 0 #define BS_STATUS_FAIL -1 #define BS_STATUS_EXPECTED_CRLF -2 #define BS_STATUS_JOB_TOO_BIG -3 #define BS_STATUS_DRAINING -4 #define BS_STATUS_TIMED_OUT -5 #define BS_STATUS_NOT_FOUND -6 #define BS_STATUS_DEADLINE_SOON -7 #define BS_STATUS_BURIED -8 #define BS_STATUS_NOT_IGNORED -9 #ifdef __cplusplus extern "C" { #endif typedef struct bs_message { char *data; char *status; size_t size; } BSM; typedef struct bs_job { int64_t id; char *data; size_t size; } BSJ; // optional polling call, returns 1 if the socket is ready of the rw operation specified. // rw: 1 => read, 2 => write, 3 => read/write // fd: file descriptor of the socket typedef int (*bs_poll_function)(int rw, int fd); /* Handle DSO symbol visibility - Stolen from zmq.h */ #if defined _WIN32 # if defined DLL_EXPORT # define BSC_EXPORT __declspec(dllexport) # else # define BSC_EXPORT __declspec(dllimport) # endif #else # if defined __SUNPRO_C || defined __SUNPRO_CC # define BSC_EXPORT __global # elif (defined __GNUC__ && __GNUC__ >= 4) || defined __INTEL_COMPILER # define BSC_EXPORT __attribute__ ((visibility("default"))) # else # define BSC_EXPORT # endif #endif // export version BSC_EXPORT void bs_version(int *major, int *minor, int *patch); // polling setup BSC_EXPORT void bs_start_polling(bs_poll_function f); BSC_EXPORT void bs_reset_polling(void); // returns a descriptive text of the error code. BSC_EXPORT const char* bs_status_text(int code); BSC_EXPORT void bs_free_message(BSM* m); BSC_EXPORT void bs_free_job(BSJ *job); // returns socket descriptor or BS_STATUS_FAIL BSC_EXPORT int bs_connect(char *host, int port); BSC_EXPORT int bs_connect_with_timeout(char *host, int port, float secs); // returns job id or one of the negative failure codes. BSC_EXPORT int64_t bs_put(int fd, uint32_t priority, uint32_t delay, uint32_t ttr, char *data, size_t bytes); // rest return BS_STATUS_OK or one of the failure codes. BSC_EXPORT int bs_disconnect(int fd); BSC_EXPORT int bs_use(int fd, char *tube); BSC_EXPORT int bs_watch(int fd, char *tube); BSC_EXPORT int bs_ignore(int fd, char *tube); BSC_EXPORT int bs_delete(int fd, int64_t job); BSC_EXPORT int bs_reserve(int fd, BSJ **job); BSC_EXPORT int bs_reserve_with_timeout(int fd, uint32_t ttl, BSJ **job); BSC_EXPORT int bs_release(int fd, int64_t id, uint32_t priority, uint32_t delay); BSC_EXPORT int bs_bury(int fd, int64_t id, uint32_t priority); BSC_EXPORT int bs_touch(int fd, int64_t id); BSC_EXPORT int bs_peek(int fd, int64_t id, BSJ **job); BSC_EXPORT int bs_peek_ready(int fd, BSJ **job); BSC_EXPORT int bs_peek_delayed(int fd, BSJ **job); BSC_EXPORT int bs_peek_buried(int fd, BSJ **job); BSC_EXPORT int bs_kick(int fd, int bound); BSC_EXPORT int bs_list_tube_used(int fd, char **tube); BSC_EXPORT int bs_list_tubes(int fd, char **yaml); BSC_EXPORT int bs_list_tubes_watched(int fd, char **yaml); BSC_EXPORT int bs_stats(int fd, char **yaml); BSC_EXPORT int bs_stats_job(int fd, int64_t id, char **yaml); BSC_EXPORT int bs_stats_tube(int fd, char *tube, char **yaml); #ifdef __cplusplus } #endif openalpr_2.2.4.orig/src/daemon/beanstalk.hpp000066400000000000000000000043601266464252400211050ustar00rootroot00000000000000#pragma once #include "beanstalk.h" #include #include #include namespace Beanstalk { typedef std::vector info_list_t; typedef std::map info_hash_t; class Job { public: int64_t id(); std::string& body(); Job(int64_t, char*, size_t); Job(BSJ*); Job(); operator bool() { return _id > 0; } protected: int64_t _id; std::string _body; }; class Client { public: ~Client(); Client(); Client(std::string host, int port, float timeout_secs = 0); bool ping(); bool use(std::string); bool watch(std::string); bool ignore(std::string); int64_t put(std::string, uint32_t priority = 0, uint32_t delay = 0, uint32_t ttr = 60); int64_t put(char *data, size_t bytes, uint32_t priority, uint32_t delay, uint32_t ttr); bool del(int64_t id); bool del(Job&); bool reserve(Job &); bool reserve(Job &, uint32_t timeout); bool release(Job &, uint32_t priority = 1, uint32_t delay = 0); bool release(int64_t id, uint32_t priority = 1, uint32_t delay = 0); bool bury(Job &, uint32_t priority = 1); bool bury(int64_t id, uint32_t priority = 1); bool touch(Job &); bool touch(int64_t id); bool peek(Job &, int64_t id); bool peek_ready(Job &); bool peek_delayed(Job &); bool peek_buried(Job &); bool kick(int bound); void connect(std::string host, int port, float timeout_secs = 0); void reconnect(); bool disconnect(); void version(int *major, int *minor, int *patch); bool is_connected(); std::string list_tube_used(); info_list_t list_tubes(); info_list_t list_tubes_watched(); info_hash_t stats(); info_hash_t stats_job(int64_t); info_hash_t stats_tube(std::string); protected: float timeout_secs; int handle, port; std::string host; }; } openalpr_2.2.4.orig/src/main.cpp000066400000000000000000000267041266464252400166230ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "tclap/CmdLine.h" #include "support/filesystem.h" #include "support/timing.h" #include "support/platform.h" #include "video/videobuffer.h" #include "motiondetector.h" #include "alpr.h" using namespace alpr; const std::string MAIN_WINDOW_NAME = "ALPR main window"; const bool SAVE_LAST_VIDEO_STILL = false; const std::string LAST_VIDEO_STILL_LOCATION = "/tmp/laststill.jpg"; MotionDetector motiondetector; bool do_motiondetection = true; /** Function Headers */ bool detectandshow(Alpr* alpr, cv::Mat frame, std::string region, bool writeJson); bool is_supported_image(std::string image_file); bool measureProcessingTime = false; std::string templatePattern; // This boolean is set to false when the user hits terminates (e.g., CTRL+C ) // so we can end infinite loops for things like video processing. bool program_active = true; int main( int argc, const char** argv ) { std::vector filenames; std::string configFile = ""; bool outputJson = false; int seektoms = 0; bool detectRegion = false; std::string country; int topn; bool debug_mode = false; TCLAP::CmdLine cmd("OpenAlpr Command Line Utility", ' ', Alpr::getVersion()); TCLAP::UnlabeledMultiArg fileArg( "image_file", "Image containing license plates", true, "", "image_file_path" ); TCLAP::ValueArg countryCodeArg("c","country","Country code to identify (either us for USA or eu for Europe). Default=us",false, "us" ,"country_code"); TCLAP::ValueArg seekToMsArg("","seek","Seek to the specified millisecond in a video file. Default=0",false, 0 ,"integer_ms"); TCLAP::ValueArg configFileArg("","config","Path to the openalpr.conf file",false, "" ,"config_file"); TCLAP::ValueArg templatePatternArg("p","pattern","Attempt to match the plate number against a plate pattern (e.g., md for Maryland, ca for California)",false, "" ,"pattern code"); TCLAP::ValueArg topNArg("n","topn","Max number of possible plate numbers to return. Default=10",false, 10 ,"topN"); TCLAP::SwitchArg jsonSwitch("j","json","Output recognition results in JSON format. Default=off", cmd, false); TCLAP::SwitchArg debugSwitch("","debug","Enable debug output. Default=off", cmd, false); TCLAP::SwitchArg detectRegionSwitch("d","detect_region","Attempt to detect the region of the plate image. [Experimental] Default=off", cmd, false); TCLAP::SwitchArg clockSwitch("","clock","Measure/print the total time to process image and all plates. Default=off", cmd, false); TCLAP::SwitchArg motiondetect("", "motion", "Use motion detection on video file or stream. Default=off", cmd, false); try { cmd.add( templatePatternArg ); cmd.add( seekToMsArg ); cmd.add( topNArg ); cmd.add( configFileArg ); cmd.add( fileArg ); cmd.add( countryCodeArg ); if (cmd.parse( argc, argv ) == false) { // Error occurred while parsing. Exit now. return 1; } filenames = fileArg.getValue(); country = countryCodeArg.getValue(); seektoms = seekToMsArg.getValue(); outputJson = jsonSwitch.getValue(); debug_mode = debugSwitch.getValue(); configFile = configFileArg.getValue(); detectRegion = detectRegionSwitch.getValue(); templatePattern = templatePatternArg.getValue(); topn = topNArg.getValue(); measureProcessingTime = clockSwitch.getValue(); do_motiondetection = motiondetect.getValue(); } catch (TCLAP::ArgException &e) // catch any exceptions { std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; return 1; } cv::Mat frame; Alpr alpr(country, configFile); alpr.setTopN(topn); if (debug_mode) { alpr.getConfig()->setDebug(true); } if (detectRegion) alpr.setDetectRegion(detectRegion); if (templatePattern.empty() == false) alpr.setDefaultRegion(templatePattern); if (alpr.isLoaded() == false) { std::cerr << "Error loading OpenALPR" << std::endl; return 1; } for (unsigned int i = 0; i < filenames.size(); i++) { std::string filename = filenames[i]; if (filename == "stdin") { std::string filename; while (std::getline(std::cin, filename)) { if (fileExists(filename.c_str())) { frame = cv::imread(filename); detectandshow(&alpr, frame, "", outputJson); } else { std::cerr << "Image file not found: " << filename << std::endl; } } } else if (filename == "webcam") { int framenum = 0; cv::VideoCapture cap(0); if (!cap.isOpened()) { std::cout << "Error opening webcam" << std::endl; return 1; } while (cap.read(frame)) { if (framenum == 0) motiondetector.ResetMotionDetection(&frame); detectandshow(&alpr, frame, "", outputJson); sleep_ms(10); framenum++; } } else if (startsWith(filename, "http://") || startsWith(filename, "https://")) { int framenum = 0; VideoBuffer videoBuffer; videoBuffer.connect(filename, 5); cv::Mat latestFrame; while (program_active) { std::vector regionsOfInterest; int response = videoBuffer.getLatestFrame(&latestFrame, regionsOfInterest); if (response != -1) { if (framenum == 0) motiondetector.ResetMotionDetection(&latestFrame); detectandshow(&alpr, latestFrame, "", outputJson); } // Sleep 10ms sleep_ms(10); framenum++; } videoBuffer.disconnect(); std::cout << "Video processing ended" << std::endl; } else if (hasEndingInsensitive(filename, ".avi") || hasEndingInsensitive(filename, ".mp4") || hasEndingInsensitive(filename, ".webm") || hasEndingInsensitive(filename, ".flv") || hasEndingInsensitive(filename, ".mjpg") || hasEndingInsensitive(filename, ".mjpeg") || hasEndingInsensitive(filename, ".mkv") ) { if (fileExists(filename.c_str())) { int framenum = 0; cv::VideoCapture cap = cv::VideoCapture(); cap.open(filename); cap.set(CV_CAP_PROP_POS_MSEC, seektoms); while (cap.read(frame)) { if (SAVE_LAST_VIDEO_STILL) { cv::imwrite(LAST_VIDEO_STILL_LOCATION, frame); } if (!outputJson) std::cout << "Frame: " << framenum << std::endl; if (framenum == 0) motiondetector.ResetMotionDetection(&frame); detectandshow(&alpr, frame, "", outputJson); //create a 1ms delay sleep_ms(1); framenum++; } } else { std::cerr << "Video file not found: " << filename << std::endl; } } else if (is_supported_image(filename)) { if (fileExists(filename.c_str())) { frame = cv::imread(filename); bool plate_found = detectandshow(&alpr, frame, "", outputJson); if (!plate_found && !outputJson) std::cout << "No license plates found." << std::endl; } else { std::cerr << "Image file not found: " << filename << std::endl; } } else if (DirectoryExists(filename.c_str())) { std::vector files = getFilesInDir(filename.c_str()); std::sort(files.begin(), files.end(), stringCompare); for (int i = 0; i < files.size(); i++) { if (is_supported_image(files[i])) { std::string fullpath = filename + "/" + files[i]; std::cout << fullpath << std::endl; frame = cv::imread(fullpath.c_str()); if (detectandshow(&alpr, frame, "", outputJson)) { //while ((char) cv::waitKey(50) != 'c') { } } else { //cv::waitKey(50); } } } } else { std::cerr << "Unknown file type" << std::endl; return 1; } } return 0; } bool is_supported_image(std::string image_file) { return (hasEndingInsensitive(image_file, ".png") || hasEndingInsensitive(image_file, ".jpg") || hasEndingInsensitive(image_file, ".tif") || hasEndingInsensitive(image_file, ".bmp") || hasEndingInsensitive(image_file, ".jpeg") || hasEndingInsensitive(image_file, ".gif")); } bool detectandshow( Alpr* alpr, cv::Mat frame, std::string region, bool writeJson) { timespec startTime; getTimeMonotonic(&startTime); std::vector regionsOfInterest; if (do_motiondetection) { cv::Rect rectan = motiondetector.MotionDetect(&frame); if (rectan.width>0) regionsOfInterest.push_back(AlprRegionOfInterest(rectan.x, rectan.y, rectan.width, rectan.height)); } else regionsOfInterest.push_back(AlprRegionOfInterest(0, 0, frame.cols, frame.rows)); AlprResults results; if (regionsOfInterest.size()>0) results = alpr->recognize(frame.data, frame.elemSize(), frame.cols, frame.rows, regionsOfInterest); timespec endTime; getTimeMonotonic(&endTime); double totalProcessingTime = diffclock(startTime, endTime); if (measureProcessingTime) std::cout << "Total Time to process image: " << totalProcessingTime << "ms." << std::endl; if (writeJson) { std::cout << alpr->toJson( results ) << std::endl; } else { for (int i = 0; i < results.plates.size(); i++) { std::cout << "plate" << i << ": " << results.plates[i].topNPlates.size() << " results"; if (measureProcessingTime) std::cout << " -- Processing Time = " << results.plates[i].processing_time_ms << "ms."; std::cout << std::endl; if (results.plates[i].regionConfidence > 0) std::cout << "State ID: " << results.plates[i].region << " (" << results.plates[i].regionConfidence << "% confidence)" << std::endl; for (int k = 0; k < results.plates[i].topNPlates.size(); k++) { // Replace the multiline newline character with a dash std::string no_newline = results.plates[i].topNPlates[k].characters; std::replace(no_newline.begin(), no_newline.end(), '\n','-'); std::cout << " - " << no_newline << "\t confidence: " << results.plates[i].topNPlates[k].overall_confidence; if (templatePattern.size() > 0 || results.plates[i].regionConfidence > 0) std::cout << "\t pattern_match: " << results.plates[i].topNPlates[k].matches_template; std::cout << std::endl; } } } return results.plates.size() > 0; } openalpr_2.2.4.orig/src/misc_utilities/000077500000000000000000000000001266464252400202105ustar00rootroot00000000000000openalpr_2.2.4.orig/src/misc_utilities/CMakeLists.txt000066400000000000000000000035121266464252400227510ustar00rootroot00000000000000 ADD_EXECUTABLE( openalpr-utils-sortstate sortstate.cpp ) TARGET_LINK_LIBRARIES(openalpr-utils-sortstate ${OPENALPR_LIB} ${STATE_DETECTION_LIB} support ${OpenCV_LIBS} ${Tesseract_LIBRARIES} ) ADD_EXECUTABLE( openalpr-utils-classifychars classifychars.cpp ) TARGET_LINK_LIBRARIES(openalpr-utils-classifychars ${OPENALPR_LIB} support ${OpenCV_LIBS} ${Tesseract_LIBRARIES} ) if (NOT DEFINED WIN32) ADD_EXECUTABLE(openalpr-utils-benchmark benchmarks/benchmark.cpp benchmarks/benchmark_utils.cpp benchmarks/endtoendtest.cpp ) TARGET_LINK_LIBRARIES(openalpr-utils-benchmark ${OPENALPR_LIB} ${STATE_DETECTION_LIB} support pthread ${OpenCV_LIBS} ${Tesseract_LIBRARIES} ) ENDIF() ADD_EXECUTABLE( openalpr-utils-prepcharsfortraining prepcharsfortraining.cpp ) TARGET_LINK_LIBRARIES(openalpr-utils-prepcharsfortraining support ${OpenCV_LIBS} ) ADD_EXECUTABLE( openalpr-utils-binarizefontsheet binarizefontsheet.cpp ) TARGET_LINK_LIBRARIES(openalpr-utils-binarizefontsheet ${OPENALPR_LIB} support ${OpenCV_LIBS} ) ADD_EXECUTABLE( openalpr-utils-tagplates tagplates.cpp ) TARGET_LINK_LIBRARIES(openalpr-utils-tagplates ${OPENALPR_LIB} support ${OpenCV_LIBS} ) ADD_EXECUTABLE( openalpr-utils-calibrate calibrate.cpp ) TARGET_LINK_LIBRARIES(openalpr-utils-calibrate ${OPENALPR_LIB} support ${OpenCV_LIBS} ${Tesseract_LIBRARIES} ) install (TARGETS openalpr-utils-calibrate DESTINATION bin) install (TARGETS openalpr-utils-classifychars DESTINATION bin) if (NOT DEFINED WIN32) install (TARGETS openalpr-utils-benchmark DESTINATION bin) ENDIF() install (TARGETS openalpr-utils-prepcharsfortraining DESTINATION bin) install (TARGETS openalpr-utils-tagplates DESTINATION bin) install (TARGETS openalpr-utils-calibrate DESTINATION bin) openalpr_2.2.4.orig/src/misc_utilities/benchmarks/000077500000000000000000000000001266464252400223255ustar00rootroot00000000000000openalpr_2.2.4.orig/src/misc_utilities/benchmarks/benchmark.cpp000066400000000000000000000216401266464252400247660ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include #include #include #include #include // std::accumulate #include "alpr_impl.h" #include "endtoendtest.h" #include "detection/detectorfactory.h" #include "support/filesystem.h" using namespace std; using namespace cv; using namespace alpr; // Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters. // These will be used to train the OCR void outputStats(vector datapoints); int main( int argc, const char** argv ) { string country; string benchmarkName; string inDir; string outDir; Mat frame; //Check if user specify image to process if(argc == 5) { country = argv[1]; benchmarkName = argv[2]; inDir = argv[3]; outDir = argv[4]; } else { printf("Use:\n\t%s [country] [benchmark name] [img input dir] [results output dir]\n",argv[0]); printf("\tex: %s us speed ./speed/usimages ./speed\n",argv[0]); printf("\n"); printf("\ttest names are: speed, segocr, detection\n\n" ); return 0; } if (DirectoryExists(inDir.c_str()) == false) { printf("Input dir does not exist\n"); return 0; } if (DirectoryExists(outDir.c_str()) == false) { printf("Output dir does not exist\n"); return 0; } vector files = getFilesInDir(inDir.c_str()); sort( files.begin(), files.end(), stringCompare ); if (benchmarkName.compare("segocr") == 0) { Config* config = new Config(country); config->setDebug(false); config->skipDetection = true; AlprImpl alpr(country); alpr.config = config; for (int i = 0; i< files.size(); i++) { if (hasEnding(files[i], ".png") || hasEnding(files[i], ".jpg")) { string fullpath = inDir + "/" + files[i]; frame = cv::imread(fullpath.c_str()); cv::Rect roi; roi.x = 0; roi.y = 0; roi.width = frame.cols; roi.height = frame.rows; vector rois; rois.push_back(roi); AlprResults results = alpr.recognize(frame, rois); char statecode[3]; statecode[0] = files[i][0]; statecode[1] = files[i][1]; statecode[2] = '\0'; string statecodestr(statecode); if (results.plates.size() == 1) cout << files[i] << "," << statecode << "," << results.plates[0].bestPlate.characters << endl; else if (results.plates.size() == 0) cout << files[i] << "," << statecode << "," << endl; else if (results.plates.size() > 1) cout << files[i] << "," << statecode << ",???+" << endl; imshow("Current LP", frame); waitKey(5); } } delete config; } else if (benchmarkName.compare("detection") == 0) { Config config(country); Detector* plateDetector = createDetector(&config); for (int i = 0; i< files.size(); i++) { if (hasEnding(files[i], ".png") || hasEnding(files[i], ".jpg")) { string fullpath = inDir + "/" + files[i]; frame = imread( fullpath.c_str() ); vector regions = plateDetector->detect(frame); imshow("Current LP", frame); waitKey(5); } } delete plateDetector; } else if (benchmarkName.compare("speed") == 0) { // Benchmarks speed of region detection, plate analysis, and OCR timespec startTime; timespec endTime; Config config(country); config.setDebug(false); AlprImpl alpr(country); alpr.config->setDebug(false); alpr.setDetectRegion(true); Detector* plateDetector = createDetector(&config); OCR ocr(&config); vector endToEndTimes; vector regionDetectionTimes; vector stateIdTimes; vector lpAnalysisPositiveTimes; vector lpAnalysisNegativeTimes; vector ocrTimes; vector postProcessTimes; for (int i = 0; i< files.size(); i++) { if (hasEnding(files[i], ".png") || hasEnding(files[i], ".jpg")) { cout << "Image: " << files[i] << endl; string fullpath = inDir + "/" + files[i]; frame = imread( fullpath.c_str() ); getTimeMonotonic(&startTime); vector regionsOfInterest; regionsOfInterest.push_back(Rect(0, 0, frame.cols, frame.rows)); alpr.recognize(frame, regionsOfInterest); getTimeMonotonic(&endTime); double endToEndTime = diffclock(startTime, endTime); cout << " -- End to End recognition time: " << endToEndTime << "ms." << endl; endToEndTimes.push_back(endToEndTime); getTimeMonotonic(&startTime); vector regions = plateDetector->detect(frame); getTimeMonotonic(&endTime); double regionDetectionTime = diffclock(startTime, endTime); cout << " -- Region detection time: " << regionDetectionTime << "ms." << endl; regionDetectionTimes.push_back(regionDetectionTime); for (int z = 0; z < regions.size(); z++) { PipelineData pipeline_data(frame, regions[z].rect, &config); getTimeMonotonic(&startTime); //stateDetector.detect(&pipeline_data); getTimeMonotonic(&endTime); double stateidTime = diffclock(startTime, endTime); cout << "\tRegion " << z << ": State ID time: " << stateidTime << "ms." << endl; stateIdTimes.push_back(stateidTime); getTimeMonotonic(&startTime); LicensePlateCandidate lp(&pipeline_data); lp.recognize(); getTimeMonotonic(&endTime); double analysisTime = diffclock(startTime, endTime); cout << "\tRegion " << z << ": Analysis time: " << analysisTime << "ms." << endl; if (!pipeline_data.disqualified) { lpAnalysisPositiveTimes.push_back(analysisTime); getTimeMonotonic(&startTime); ocr.performOCR(&pipeline_data); getTimeMonotonic(&endTime); double ocrTime = diffclock(startTime, endTime); cout << "\tRegion " << z << ": OCR time: " << ocrTime << "ms." << endl; ocrTimes.push_back(ocrTime); getTimeMonotonic(&startTime); ocr.postProcessor.analyze("", 25); getTimeMonotonic(&endTime); double postProcessTime = diffclock(startTime, endTime); cout << "\tRegion " << z << ": PostProcess time: " << postProcessTime << "ms." << endl; postProcessTimes.push_back(postProcessTime); } else { lpAnalysisNegativeTimes.push_back(analysisTime); } } waitKey(5); } } cout << endl << "---------------------" << endl; cout << "End to End Time Statistics:" << endl; outputStats(endToEndTimes); cout << endl; cout << "Region Detection Time Statistics:" << endl; outputStats(regionDetectionTimes); cout << endl; cout << "State ID Time Statistics:" << endl; outputStats(stateIdTimes); cout << endl; cout << "Positive Region Analysis Time Statistics:" << endl; outputStats(lpAnalysisPositiveTimes); cout << endl; cout << "Negative Region Analysis Time Statistics:" << endl; outputStats(lpAnalysisNegativeTimes); cout << endl; cout << "OCR Time Statistics:" << endl; outputStats(ocrTimes); cout << endl; cout << "Post Processing Time Statistics:" << endl; outputStats(postProcessTimes); cout << endl; } else if (benchmarkName.compare("endtoend") == 0) { EndToEndTest e2eTest(inDir, outDir); e2eTest.runTest(country, files); } } void outputStats(vector datapoints) { double sum = std::accumulate(datapoints.begin(), datapoints.end(), 0.0); double mean = sum / datapoints.size(); std::vector diff(datapoints.size()); std::transform(datapoints.begin(), datapoints.end(), diff.begin(), std::bind2nd(std::minus(), mean)); double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0); double stdev = std::sqrt(sq_sum / datapoints.size()); cout << "\t" << datapoints.size() << " samples, avg: " << mean << "ms, stdev: " << stdev << endl; } openalpr_2.2.4.orig/src/misc_utilities/benchmarks/benchmark_utils.cpp000066400000000000000000000007701266464252400262070ustar00rootroot00000000000000 #include "support/filesystem.h" #include "benchmark_utils.h" using namespace std; using namespace alpr; vector filterByExtension(vector fileList, string extension) { vector filteredList; if (extension.size() == 0) return filteredList; if (extension[0] != '.') extension = "." + extension; for (int i = 0; i < fileList.size(); i++) { if (hasEnding(fileList[i], extension)) filteredList.push_back(fileList[i]); } return filteredList; }openalpr_2.2.4.orig/src/misc_utilities/benchmarks/benchmark_utils.h000066400000000000000000000003461266464252400256530ustar00rootroot00000000000000#ifndef OPENALPR_BENCHMARKUTILS_H #define OPENALPR_BENCHMARKUTILS_H #include std::vector filterByExtension(std::vector fileList, std::string extension); #endif // OPENALPR_BENCHMARKUTILS_H openalpr_2.2.4.orig/src/misc_utilities/benchmarks/endtoendtest.cpp000066400000000000000000000211361266464252400255340ustar00rootroot00000000000000#include "endtoendtest.h" using namespace std; using namespace cv; using namespace alpr; EndToEndTest::EndToEndTest(string inputDir, string outputDir) { this->inputDir = inputDir; this->outputDir = outputDir; } void EndToEndTest::runTest(string country, vector files) { AlprImpl alpr(country); alpr.config->setDebug(false); alpr.setDetectRegion(false); vector benchmarkResults; vector textFiles = filterByExtension(files, ".txt"); for (int i = 0; i< textFiles.size(); i++) { cout << "Benchmarking file " << (i + 1) << " / " << textFiles.size() << " -- " << textFiles[i] << endl; EndToEndBenchmarkResult benchmarkResult; string fulltextpath = inputDir + "/" + textFiles[i]; ifstream inputFile(fulltextpath.c_str()); string line; getline(inputFile, line); istringstream ss(line); string imgfile, plate_number; int x, y, w, h; ss >> imgfile >> x >> y >> w >> h >> plate_number; string fullimgpath = inputDir + "/" + imgfile; benchmarkResult.imageName = imgfile; Mat frame = imread( fullimgpath.c_str() ); Rect actualPlateRect(x, y, w, h); vector rois; rois.push_back(Rect(0,0,frame.cols,frame.rows)); AlprFullDetails recognitionDetails = alpr.recognizeFullDetails(frame, rois); //cv::circle(frame, centerPoint, 2, Scalar(0, 0, 255), 5); //drawAndWait(&frame); benchmarkResult.detectionFalsePositives = 0; for (int z = 0; z < recognitionDetails.plateRegions.size(); z++) { benchmarkResult.detectionFalsePositives += totalRectCount(recognitionDetails.plateRegions[z]); bool rectmatches = rectMatches(actualPlateRect, recognitionDetails.plateRegions[z]); if (rectmatches) { // This region matches our plate_number benchmarkResult.detectedPlate = true; benchmarkResult.detectionFalsePositives--; break; } } benchmarkResult.resultsFalsePositives = recognitionDetails.results.plates.size(); // Determine if the top result and the top N results match the correct value for (int z = 0; z < recognitionDetails.results.plates.size(); z++) { //cout << "Actual: " << plate_number << endl; //cout << "Candidate: " << recognitionDetails.results[z].bestPlate.characters << endl; if (recognitionDetails.results.plates[z].bestPlate.characters == plate_number) { benchmarkResult.topResultCorrect = true; benchmarkResult.top10ResultCorrect = true; benchmarkResult.resultsFalsePositives--; break; } for (int idx = 0; idx < recognitionDetails.results.plates[z].topNPlates.size(); idx++) { if (recognitionDetails.results.plates[z].topNPlates[idx].characters == plate_number) { benchmarkResult.top10ResultCorrect = true; benchmarkResult.resultsFalsePositives--; break; } } if (benchmarkResult.top10ResultCorrect) break; } benchmarkResults.push_back(benchmarkResult); } // Print results data int image_name_padding = 0; for (int i = 0; i < benchmarkResults.size(); i++) { EndToEndBenchmarkResult br = benchmarkResults[i]; if (br.imageName.length() > image_name_padding) image_name_padding = br.imageName.length(); } image_name_padding += 4; ofstream data; string outputResultsFile = outputDir + "/results.txt"; data.open(outputResultsFile.c_str()); data << setfill(' ') << setw(image_name_padding) << "Image name" << setw(20) << "Detected Plate" << setw(20) << "# False Detections" << setw(20) << "Top Result Correct" << setw(20) << "Top 10 Correct" << setw(20) << "# False Results" << endl; for (int i = 0; i < benchmarkResults.size(); i++) { EndToEndBenchmarkResult br = benchmarkResults[i]; data << setfill(' ') << setw(image_name_padding) << br.imageName << setw(20) << br.detectedPlate << setw(20) << br.detectionFalsePositives << setw(20) << br.topResultCorrect << setw(20) << br.top10ResultCorrect << setw(20) << br.resultsFalsePositives << endl; } data.close(); // Print summary data int totalDetections = 0; int totalTopResultCorrect = 0; int totalTop10Correct = 0; int falseDetectionPositives = 0; int falseResults = 0; for (int i = 0; i < benchmarkResults.size(); i++) { if (benchmarkResults[i].detectedPlate) totalDetections++; if (benchmarkResults[i].topResultCorrect) totalTopResultCorrect++; if (benchmarkResults[i].top10ResultCorrect) totalTop10Correct++; falseDetectionPositives += benchmarkResults[i].detectionFalsePositives; falseResults += benchmarkResults[i].resultsFalsePositives; } // Percentage of how many are correct (higher is better) float detectionScore = 100.0 * ((float) totalDetections) / ((float) benchmarkResults.size()); float topResultScore = 100.0 * ((float) totalTopResultCorrect) / ((float) benchmarkResults.size()); float top10ResultScore = 100.0 * ((float) totalTop10Correct) / ((float) benchmarkResults.size()); // How many false positives per image (higher is worse) float falseDetectionPositivesScore = ((float) falseDetectionPositives) / ((float) benchmarkResults.size()); float falseResultsScore = ((float) falseResults) / ((float) benchmarkResults.size()); string outputSummaryFile = outputDir + "/summary.txt"; data.open(outputSummaryFile.c_str()); data << "-------------------" << endl; data << "| SUMMARY |" << endl; data << "-------------------" << endl; data << endl; data << "Accuracy scores (higher is better)" << endl; data << "Percent of plates DETECTED: " << detectionScore << endl; data << "Percent of correct TOP10: " << top10ResultScore << endl; data << "Percent of correct MATCHES: " << topResultScore << endl; data << endl; data << "False Positives Score (lower is better)" << endl; data << "False DETECTIONS per image: " << falseDetectionPositivesScore << endl; data << "False RESULTS per image: " << falseResultsScore << endl; data.close(); // Print the contents of these files now: string line; ifstream resultsFileIn(outputResultsFile.c_str()); while(getline(resultsFileIn, line)) { cout << line << endl; } ifstream summaryFileIn(outputSummaryFile.c_str()); while(getline(summaryFileIn, line)) { cout << line << endl; } } bool EndToEndTest::rectMatches(cv::Rect actualPlate, PlateRegion candidate) { // Determine if this region matches our plate in the image // Do this simply by verifying that the center point of the plate is within the region // And that the plate region is not x% larger or smaller const float MAX_SIZE_PERCENT_LARGER = 0.65; //int plateCenterX = actualPlate.x + (int) (((float) actualPlate.width) / 2.0); //int plateCenterY = actualPlate.y + (int) (((float) actualPlate.height) / 2.0); //Point centerPoint(plateCenterX, plateCenterY); vector requiredPoints; requiredPoints.push_back(Point( actualPlate.x + (int) (((float) actualPlate.width) * 0.2), actualPlate.y + (int) (((float) actualPlate.height) * 0.15) )); requiredPoints.push_back(Point( actualPlate.x + (int) (((float) actualPlate.width) * 0.8), actualPlate.y + (int) (((float) actualPlate.height) * 0.15) )); requiredPoints.push_back(Point( actualPlate.x + (int) (((float) actualPlate.width) * 0.2), actualPlate.y + (int) (((float) actualPlate.height) * 0.85) )); requiredPoints.push_back(Point( actualPlate.x + (int) (((float) actualPlate.width) * 0.8), actualPlate.y + (int) (((float) actualPlate.height) * 0.85) )); float sizeDiff = 1.0 - ((float) actualPlate.area()) / ((float) candidate.rect.area()); //cout << "Candidate: " << candidate.rect.x << "," << candidate.rect.y << " " << candidate.rect.width << "-" << candidate.rect.height << endl; //cout << "Actual: " << actualPlate.x << "," << actualPlate.y << " " << actualPlate.width << "-" << actualPlate.height << endl; //cout << "size diff: " << sizeDiff << endl; bool hasAllPoints = true; for (int z = 0; z < requiredPoints.size(); z++) { if (candidate.rect.contains(requiredPoints[z]) == false) hasAllPoints = false; break; } if ( hasAllPoints && (sizeDiff < MAX_SIZE_PERCENT_LARGER) ) { return true; } else { for (int i = 0; i < candidate.children.size(); i++) { if (rectMatches(actualPlate, candidate.children[i])) return true; } } return false; } int EndToEndTest::totalRectCount(PlateRegion rootCandidate) { int childCount = 0; for (int i = 0; i < rootCandidate.children.size(); i++) { childCount += totalRectCount(rootCandidate.children[i]); } return childCount + 1; } openalpr_2.2.4.orig/src/misc_utilities/benchmarks/endtoendtest.h000066400000000000000000000020711266464252400251760ustar00rootroot00000000000000#ifndef OPENALPR_ENDTOENDTEST_H #define OPENALPR_ENDTOENDTEST_H #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "alpr_impl.h" #include "benchmark_utils.h" class EndToEndTest { public: EndToEndTest(std::string inputDir, std::string outputDir); void runTest(std::string country, std::vector files); private: bool rectMatches(cv::Rect actualPlate, alpr::PlateRegion candidate); int totalRectCount(alpr::PlateRegion rootCandidate); std::string inputDir; std::string outputDir; }; class EndToEndBenchmarkResult { public: EndToEndBenchmarkResult() { this->imageName = ""; this->detectedPlate = false; this->topResultCorrect = false; this->top10ResultCorrect = false; this->detectionFalsePositives = 0; this->resultsFalsePositives = 0; } std::string imageName; bool detectedPlate; bool topResultCorrect; bool top10ResultCorrect; int detectionFalsePositives; int resultsFalsePositives; }; #endif //OPENALPR_ENDTOENDTEST_Hopenalpr_2.2.4.orig/src/misc_utilities/binarizefontsheet.cpp000066400000000000000000000214621266464252400244440ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include #include #include #include #include "../tclap/CmdLine.h" #include "utility.h" #include "support/utf8.h" using namespace std; using namespace std; using namespace cv; using namespace alpr; // This utility operates on a large image file generated from a TTF font file // The font sheet is used to train OCR. The process is: // Find the exact ttf font used by the number plates. // Generate text with all of the characters that could be on a license plate // Print the pages on a piece of paper // Take pictures with a digital camera under different lighting conditions to add realistic noise // Binarize and process each character with OpenALPR morphology functions to make them look similar to how OpenALPR sees them. Produce a tif file. // Produce a box file based on text from #2 and the image from #5. // Train OCR with this box/tif data // This utility is used before "prepcharsfortraining" // Given a series of images (font sheets) and a text file giving the order, it will pluck out each character, binarize it, // and output it to a single image file for each character. // These characters can later be reassembled into a tif/box file using prepcharsfortraining. bool sort_lined_rectangles(Rect i, Rect j) { // If they're on different lines if (abs(i.y - j.y) > 15) { return i.y < j.y; } // They're on the same line, give the left-most. return (i.x < j.x); } void show_debug_image(vector rectangles, Mat img) { Mat debugImg; cvtColor(img, debugImg, CV_GRAY2BGR); for (unsigned int i = 0; i < rectangles.size(); i++) { Rect mr = rectangles[i]; Mat croppedChar = img(mr); rectangle(debugImg, mr, Scalar(0,0,255), 2); putText(debugImg, toString(i), mr.tl(), FONT_HERSHEY_PLAIN, 1.3, Scalar(0,0,0), 2); } float new_height = 1000; float aspect_ratio = ((float)debugImg.rows) / ((float) new_height); float new_width = ((float) debugImg.cols) / aspect_ratio; resize(debugImg, debugImg, Size(new_width, new_height)); drawAndWait(&debugImg); } int main(int argc, char** argv) { const int MIN_RECTANGLE_AREA_PIXELS = 500; const int MIN_SPECKLE_AREA_PIXELS = 20; const int BLOBBER_EROSION_SIZE=6; vector font_sheet_files; string char_list_file; string out_dir; TCLAP::CmdLine cmd("OpenAlpr OCR Training Font Sheet Prep Utility", ' ', "1.0.0"); TCLAP::UnlabeledMultiArg fontSheetArg( "font_sheet", "List of font sheet images", true, "", "font_sheet" ); TCLAP::ValueArg charListArg("","character_file","Text file with the text/order of the individual characters in the font sheets",true, "" ,"character_file"); TCLAP::ValueArg outDirArg("","out_dir","Output directory to put the character images",true, "" ,"output_dir"); try { cmd.add( fontSheetArg ); cmd.add( charListArg ); cmd.add( outDirArg ); if (cmd.parse( argc, argv ) == false) { // Error occurred while parsing. Exit now. return 1; } font_sheet_files = fontSheetArg.getValue(); char_list_file = charListArg.getValue(); out_dir = outDirArg.getValue(); } catch (TCLAP::ArgException &e) // catch any exceptions { std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; return 1; } if (DirectoryExists(out_dir.c_str()) == false) { cout << "Output dir: " << out_dir << " does not exist" << endl; return 1; } if (fileExists(char_list_file.c_str()) == false) { cout << "Character text file: " << char_list_file << " does not exist" << endl; return 1; } // Verify all the font sheet files exist for (unsigned int i = 0; i < font_sheet_files.size(); i++) { if (fileExists(font_sheet_files[i].c_str()) == false) { cout << "Font sheet image: " << font_sheet_files[i] << " does not exist." << endl; return 1; } } // Read the text content from the character list file std::ifstream fs(char_list_file.c_str()); std::string text_content((std::istreambuf_iterator(fs)), std::istreambuf_iterator()); fs.close(); for (unsigned int font_sheet_index = 0; font_sheet_index < font_sheet_files.size(); font_sheet_index++) { cout << "Processing: " << font_sheet_files[font_sheet_index] << endl; Mat frame = cv::imread( font_sheet_files[font_sheet_index] ); Config config("us"); cvtColor(frame, frame, CV_BGR2GRAY); vector thresholds = produceThresholds(frame, &config); for (unsigned int t = 0; t < thresholds.size(); t++) { // First clean up any tiny speckles Mat speckle_copy(thresholds[t].size(), thresholds[t].type()); thresholds[t].copyTo(speckle_copy); vector > speckle_contours; vector speckle_hierarchy; findContours(speckle_copy, speckle_contours, speckle_hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); Mat testImg = Mat::zeros(thresholds[t].size(), thresholds[t].type()); for (unsigned int i = 0; i < speckle_contours.size(); i++) { Rect speckleRect = boundingRect(speckle_contours[i]); if (speckleRect.area() < MIN_SPECKLE_AREA_PIXELS) { drawContours(thresholds[t], speckle_contours, i, Scalar(0,0,0), CV_FILLED); drawContours(testImg, speckle_contours, i, Scalar(255,255,255), CV_FILLED); } } resize(testImg, testImg, Size(700, 1000)); drawAndWait(&testImg); // Adjust the threshold w/ the morphology operation that OpenALPR uses Mat closureElement = getStructuringElement( 1, Size( 2 + 1, 2+1 ), Point( 1, 1 ) ); morphologyEx(thresholds[t], thresholds[t], MORPH_CLOSE, closureElement); Mat blobby; Mat element = getStructuringElement( MORPH_RECT, Size( 2*BLOBBER_EROSION_SIZE + 1, 2*BLOBBER_EROSION_SIZE+1 ), Point( BLOBBER_EROSION_SIZE, BLOBBER_EROSION_SIZE ) ); dilate(thresholds[t], blobby, element ); erode(blobby, blobby, element); vector > contours; vector hierarchy; findContours(blobby, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); bitwise_not(thresholds[t], thresholds[t]); vector rectangles; for (unsigned int i = 0; i < contours.size(); i++) { Rect mr = boundingRect(contours[i]); if (mr.area() >= MIN_RECTANGLE_AREA_PIXELS) rectangles.push_back(mr); } // sort the rectangles top to bottom left to right std::sort(rectangles.begin(), rectangles.end(), sort_lined_rectangles); //cout << text_content << endl; string::iterator end_it = utf8::find_invalid(text_content.begin(), text_content.end()); if (end_it != text_content.end()) { cout << "Invalid UTF-8 encoding detected " << endl; return 1; } show_debug_image(rectangles, thresholds[t]); int text_content_length = utf8::distance(text_content.begin(), text_content.end()); if (rectangles.size() != text_content_length - 1) { cout << "Number of blobs (" << rectangles.size() << ") != number of characters (" << text_content_length << ")" << endl; cout << "Skipping..." << endl; //return 1; continue; } string::iterator utf_iterator = text_content.begin(); for (unsigned int i = 0; i < rectangles.size(); i++) { Rect mr = rectangles[i]; Mat croppedChar = thresholds[t](mr); int cp = utf8::next(utf_iterator, text_content.end()); stringstream ss; ss << out_dir << "/" << utf8chr(cp) << "-" << font_sheet_index << "-" << t << "-" << i << ".png"; imwrite(ss.str(), croppedChar); } show_debug_image(rectangles, thresholds[t]); } } return 0; } openalpr_2.2.4.orig/src/misc_utilities/calibrate.cpp000066400000000000000000000220171266464252400226440ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "support/filesystem.h" #include "../tclap/CmdLine.h" #include "prewarp.h" #include using namespace std; using namespace cv; const int INSTRUCTIONS_HEIGHT = 32; bool panning; bool left_clicking; Point left_click_start; Point left_click_cur; Point lastPos; float w; float h; float panX = 0; float panY = 0; float rotationx = 0; float rotationy = 0; float rotationz = 0; float stretchX = 1.0; float dist = 1.0; Mat imgOriginal; Mat curWarpedImage; alpr::Config config("us"); const string WINDOW_NAME = "Adjust OpenALPR Perspective"; string get_config() { stringstream output; output << "planar," << std::fixed; output << w << "," << h << ","; output << rotationx << "," << rotationy << "," << rotationz << ","; output << stretchX << "," << dist << ","; output << panX << "," << panY; return output.str(); } void drawImage(Mat img) { config.prewarp = get_config(); alpr::PreWarp prewarp(&config); if (!left_clicking) { curWarpedImage = prewarp.warpImage(img); } Mat imgWithInstructions(curWarpedImage.rows + INSTRUCTIONS_HEIGHT, curWarpedImage.cols, curWarpedImage.type()); curWarpedImage.copyTo(imgWithInstructions(Rect(0, INSTRUCTIONS_HEIGHT, curWarpedImage.cols, curWarpedImage.rows))); if (left_clicking) { Point start = left_click_start; Point end = left_click_cur; start.y += INSTRUCTIONS_HEIGHT; end.y += INSTRUCTIONS_HEIGHT; rectangle(imgWithInstructions, start, end, Scalar(255,0,255), 2); } rectangle(imgWithInstructions, Point(0,0), Point(curWarpedImage.cols, INSTRUCTIONS_HEIGHT), Scalar(255,255,255), -1); putText(imgWithInstructions, "Press 'o' to output config to console.", Point(5,25), FONT_HERSHEY_DUPLEX, 1.0, Scalar(0,0,0)); imshow(WINDOW_NAME, imgWithInstructions); } void mouse_callback(int event, int x, int y, int flags, void* userdata) { y = y - INSTRUCTIONS_HEIGHT; if (y < 0) return; if (event == EVENT_RBUTTONDOWN) { lastPos.x = x; lastPos.y = y; panning = true; } if (event == EVENT_RBUTTONUP) { panning = false; drawImage(imgOriginal); } if (event == EVENT_MOUSEMOVE && panning) { int xdiff = x - lastPos.x; int ydiff = y - lastPos.y; panX -= xdiff; panY -= ydiff; lastPos.x = x; lastPos.y = y; // Reduce the computation by only doing it every 3rd pixel if (x % 3 == 0 && y % 3 == 0) { drawImage(imgOriginal); } } if (event == EVENT_LBUTTONDOWN) { left_click_start.x = x; left_click_start.y = y; left_clicking = true; } if (event == EVENT_LBUTTONUP) { left_clicking = false; drawImage(imgOriginal); } if (event == EVENT_MOUSEMOVE && left_clicking) { left_click_cur.x = x; float IDEAL_PLATE_RATIO = config.plateWidthMM / config.plateHeightMM; float curWidth = left_click_cur.x - left_click_start.x; left_click_cur.y = left_click_start.y + (curWidth / IDEAL_PLATE_RATIO ); // Reduce the computation by only doing it every 3rd pixel if (x % 2 == 0 || y % 2 == 0) { drawImage(imgOriginal); } } } void ZChange(int pos, void* userdata) { rotationz = -((float)pos - 100) / 100.0; drawImage(imgOriginal); } void XChange(int pos, void* userdata) { rotationx = -((float)pos - 100) / 20000.0; drawImage(imgOriginal); } void YChange(int pos, void* userdata) { rotationy = ((float)pos - 100) / 20000.0; drawImage(imgOriginal); } void DistChange(int pos, void* userdata) { dist = 1.0 - ((float)pos - 100) / 200.0; drawImage(imgOriginal); } void StretchChange(int pos, void* userdata) { stretchX = 1.0 + ((float)pos - 100) / -200.0; drawImage(imgOriginal); } int value; void create_window() { namedWindow(WINDOW_NAME, CV_WINDOW_AUTOSIZE | CV_WINDOW_KEEPRATIO | CV_GUI_EXPANDED); value = 100; panX = 0; panY = 0; XChange(100, NULL); YChange(100, NULL); ZChange(100, NULL); DistChange(100, NULL); createTrackbar( "X", WINDOW_NAME, &value, 200, XChange); createTrackbar( "Y", WINDOW_NAME, &value, 200, YChange); createTrackbar( "Z", WINDOW_NAME, &value, 200, ZChange); createTrackbar( "W", WINDOW_NAME, &value, 200, StretchChange); createTrackbar( "D", WINDOW_NAME, &value, 200, DistChange); setMouseCallback(WINDOW_NAME, mouse_callback, NULL); } /* * */ int main(int argc, char** argv) { string filename; string country; string config_path; string translate_config; int max_width; int max_height; TCLAP::CmdLine cmd("OpenAlpr Perspective Utility", ' ', "0.1"); TCLAP::UnlabeledValueArg fileArg( "image_file", "Image containing license plates", true, "", "image_file_path" ); TCLAP::ValueArg countryCodeArg("c","country","Country code to identify (either us for USA or eu for Europe). Default=us",false, "us" ,"country_code"); TCLAP::ValueArg configFileArg("","config","Path to the openalpr.conf file",false, "" ,"config_file"); TCLAP::ValueArg translateTestArg("t","test","Test an image using the provided translation config",false, "" ,"prewarp config"); TCLAP::ValueArg maxWidthArg("w", "maxwidth", "Max Width used for displaying image in this utility. Default=1280",false, 1280 ,"max width"); TCLAP::ValueArg maxHeightArg("", "maxheight", "Max Height used for displaying image in this utility. Default=1024",false, 1024 ,"max height"); try { cmd.add( configFileArg ); cmd.add( fileArg ); cmd.add( countryCodeArg ); cmd.add( translateTestArg ); cmd.add( maxWidthArg ); cmd.add( maxHeightArg ); if (cmd.parse( argc, argv ) == false) { // Error occurred while parsing. Exit now. return 1; } filename = fileArg.getValue(); country = countryCodeArg.getValue(); config_path = configFileArg.getValue(); translate_config = translateTestArg.getValue(); max_width = maxWidthArg.getValue(); max_height = maxHeightArg.getValue(); } catch (TCLAP::ArgException &e) // catch any exceptions { std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; return 1; } if (!alpr::fileExists(filename.c_str())) { cerr << "Could not find image file: " << filename << endl; } config = alpr::Config(country); panning = false; left_clicking = false; imgOriginal = imread(filename); if (imgOriginal.cols > max_width) { float aspect = max_width / ((float)imgOriginal.cols); float y = ((float)imgOriginal.rows) * aspect; resize(imgOriginal, imgOriginal, Size((int) max_width, (int) y)); } if (imgOriginal.rows > max_height) { float aspect = max_height / ((float)imgOriginal.rows); float x = ((float)imgOriginal.cols) * aspect; resize(imgOriginal, imgOriginal, Size((int) x, (int) max_height)); } w = imgOriginal.cols; h = imgOriginal.rows; create_window(); if (translate_config != "") { int first_comma = translate_config.find(","); string name = translate_config.substr(0, first_comma); stringstream ss(translate_config.substr(first_comma + 1, translate_config.length())); ss >> w; ss.ignore(); ss >> h; ss.ignore(); ss >> rotationx; ss.ignore(); // Ignore comma ss >> rotationy; ss.ignore(); // Ignore comma ss >> rotationz; ss.ignore(); // Ignore comma ss >> stretchX; ss.ignore(); // Ignore comma ss >> dist; ss.ignore(); // Ignore comma ss >> panX; ss.ignore(); // Ignore comma ss >> panY; } float width_ratio = w / ((float)imgOriginal.cols); float height_ratio = h / ((float)imgOriginal.rows); w = imgOriginal.cols; h = imgOriginal.rows; rotationx *=width_ratio; rotationy *=width_ratio; panX /= width_ratio; panY /= height_ratio; drawImage(imgOriginal); while (cvGetWindowHandle(WINDOW_NAME.c_str()) != 0) { char c = waitKey(15); if (c == 'o') { cout << "prewarp = " << get_config() << endl; } else if (c == 'q') { cout << "prewarp = " << get_config() << endl; break; } } cvDestroyAllWindows(); return 0; } openalpr_2.2.4.orig/src/misc_utilities/classifychars.cpp000066400000000000000000000302431266464252400235540ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include #include #include #include "postprocess/regexrule.h" #include "licenseplatecandidate.h" #include "utility.h" #include "support/filesystem.h" #include "ocr.h" using namespace std; using namespace cv; using namespace alpr; // Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters. // These will be used to train the OCR #ifdef __APPLE__ const int LEFT_ARROW_KEY = 2; const int RIGHT_ARROW_KEY = 3; const int DOWN_ARROW_KEY = 1; const int UP_ARROW_KEY= 0; #elif WIN32 const int LEFT_ARROW_KEY = 2424832; const int RIGHT_ARROW_KEY = 2555904; const int DOWN_ARROW_KEY = 2621440; const int UP_ARROW_KEY = 2490368; const int ENTER_KEY_ONE = 13; const int ENTER_KEY_TWO = 10; #else const int LEFT_ARROW_KEY = 1113937; const int RIGHT_ARROW_KEY = 1113939; const int DOWN_ARROW_KEY = 1113940; const int UP_ARROW_KEY= 1113938; const int ENTER_KEY_ONE = 1048586; const int ENTER_KEY_TWO = 1048586; #endif const string SPACE = " "; const int SPACE_KEY = 32; const int ESCAPE_KEY = 27; const int DASHBOARD_COLUMNS = 3; void showDashboard(vector images, vector selectedImages, int selectedIndex); vector showCharSelection(Mat image, vector charRegions, string state); int main( int argc, const char** argv ) { string country; string inDir; string outDir; Mat frame; //Check if user specify image to process if(argc == 4) { country = argv[1]; inDir = argv[2]; outDir = argv[3]; } else { printf("Use:\n\t%s country indirectory outdirectory\n",argv[0]); printf("Ex: \n\t%s eu ./pics/ ./out\n",argv[0]); return 0; } if (DirectoryExists(outDir.c_str()) == false) { printf("Output dir does not exist\n"); return 0; } cout << "Usage: " << endl; cout << "\tn -- Next plate" << endl; cout << "\tp -- Previous plate" << endl; cout << "\tW -- Select image and save characters according to OCR results, then go to next image" << endl; cout << "\ts -- Save characters" << endl; cout << "\t<- and -> -- Cycle between images" << endl; cout << "\tEnt/space -- Select plate" << endl; cout << endl; cout << "Within a plate" << endl; cout << "\t<- and -> -- Cycle between characters" << endl; cout << "\t[0-9A-Z] -- Identify a character (saves the image)" << endl; cout << "\tESC/Ent/Space -- Back to plate selection" << endl; Config config(country); config.debugGeneral = false; config.debugCharAnalysis = false; config.debugCharSegmenter = false; OCR ocr(&config); if (DirectoryExists(inDir.c_str())) { vector files = getFilesInDir(inDir.c_str()); sort( files.begin(), files.end(), stringCompare ); for (int i = 0; i< files.size(); i++) { if (hasEnding(files[i], ".png") || hasEnding(files[i], ".jpg")) { string fullpath = inDir + "/" + files[i]; cout << fullpath << endl; frame = imread( fullpath.c_str() ); if(frame.data == NULL) { cout << "Unable to read license plate: " << fullpath << endl; continue; } resize(frame, frame, Size(config.ocrImageWidthPx, config.ocrImageHeightPx)); imshow ("Original", frame); PipelineData pipeline_data(frame, Rect(0, 0, frame.cols, frame.rows), &config); cvtColor(frame, frame, CV_BGR2GRAY); pipeline_data.crop_gray = Mat(frame, Rect(0, 0, frame.cols, frame.rows)); char statecode[3]; statecode[0] = files[i][0]; statecode[1] = files[i][1]; statecode[2] = '\0'; string statecodestr(statecode); CharacterAnalysis regionizer(&pipeline_data); if (pipeline_data.plate_inverted) bitwise_not(pipeline_data.crop_gray, pipeline_data.crop_gray); CharacterSegmenter charSegmenter(&pipeline_data); //ocr.cleanCharRegions(charSegmenter.thresholds, charSegmenter.characters); ocr.performOCR(&pipeline_data); ocr.postProcessor.analyze(statecodestr, 25); cout << "OCR results: " << ocr.postProcessor.bestChars << endl; vector selectedBoxes(pipeline_data.thresholds.size()); for (int z = 0; z < pipeline_data.thresholds.size(); z++) selectedBoxes[z] = false; int curDashboardSelection = 0; vector humanInputs(pipeline_data.charRegionsFlat.size()); for (int z = 0; z < pipeline_data.charRegionsFlat.size(); z++) humanInputs[z] = SPACE; showDashboard(pipeline_data.thresholds, selectedBoxes, 0); int waitkey = waitKey(50); while ((char) waitkey != 'n' && (char) waitkey != 'p') // Next image { if (waitkey == LEFT_ARROW_KEY) // left arrow key { if (curDashboardSelection > 0) curDashboardSelection--; showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection); } else if (waitkey == RIGHT_ARROW_KEY) // right arrow key { if (curDashboardSelection < pipeline_data.thresholds.size() - 1) curDashboardSelection++; showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection); } else if (waitkey == DOWN_ARROW_KEY) { if (curDashboardSelection + DASHBOARD_COLUMNS <= pipeline_data.thresholds.size() - 1) curDashboardSelection += DASHBOARD_COLUMNS; showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection); } else if (waitkey == UP_ARROW_KEY) { if (curDashboardSelection - DASHBOARD_COLUMNS >= 0) curDashboardSelection -= DASHBOARD_COLUMNS; showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection); } else if (waitkey == ENTER_KEY_ONE || waitkey == ENTER_KEY_TWO) { if (pipeline_data.charRegionsFlat.size() > 0) { vector tempdata = showCharSelection(pipeline_data.thresholds[curDashboardSelection], pipeline_data.charRegionsFlat, statecodestr); for (int c = 0; c < pipeline_data.charRegionsFlat.size(); c++) humanInputs[c] = tempdata[c]; } else { cout << "No character regions available in this image" << endl; } } else if ((char) waitkey == SPACE_KEY) { selectedBoxes[curDashboardSelection] = !selectedBoxes[curDashboardSelection]; showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection); } else if ((char) waitkey == 's' || (char) waitkey == 'S' ) { bool somethingSelected = false; bool chardataTagged = false; for (int c = 0; c < pipeline_data.thresholds.size(); c++) { if (selectedBoxes[c]) { somethingSelected = true; break; } } for (int c = 0; c < pipeline_data.charRegionsFlat.size(); c++) { if (humanInputs[c] != SPACE) { chardataTagged = true; break; } } // Save if (somethingSelected && chardataTagged) { for (int c = 0; c < pipeline_data.charRegionsFlat.size(); c++) { if (humanInputs[c] == SPACE) continue; for (int t = 0; t < pipeline_data.thresholds.size(); t++) { if (selectedBoxes[t] == false) continue; stringstream filename; // Ensure that crop rect does not extend beyond extent of image. cv::Rect char_region = expandRect(pipeline_data.charRegionsFlat[c], 0, 0, pipeline_data.thresholds[t].cols, pipeline_data.thresholds[t].rows); Mat cropped = pipeline_data.thresholds[t](char_region); filename << outDir << "/" << humanInputs[c] << "-" << t << "-" << files[i]; imwrite(filename.str(), cropped); cout << "Writing char image: " << filename.str() << endl; } } } else if (somethingSelected == false) cout << "Did not select any boxes" << endl; else if (chardataTagged == false) cout << "You have not tagged any characters" << endl; if ((char) waitkey == 'W') { waitkey = 'n'; continue; } } waitkey = waitKey(50); //std::cout << "key: " << (int) waitkey << std::endl; } if ((char) waitkey == 'p') i = i - 2; if (i < -1) i = -1; } } } } void showDashboard(vector images, vector selectedImages, int selectedIndex) { vector vecCopy; if (selectedIndex < 0) selectedIndex = 0; if (selectedIndex >= images.size()) selectedIndex = images.size() -1; for (int i = 0; i < images.size(); i++) { Mat imgCopy(images[i].size(), images[i].type()); images[i].copyTo(imgCopy); cvtColor(imgCopy, imgCopy, CV_GRAY2BGR); if (i == selectedIndex) { rectangle(imgCopy, Point(1,1), Point(imgCopy.size().width - 1, imgCopy.size().height -1), Scalar(0, 255, 0), 1); } if (selectedImages[i]) { rectangle(imgCopy, Point(2,2), Point(imgCopy.size().width - 2, imgCopy.size().height -2), Scalar(255, 0, 0), 1); } vecCopy.push_back(imgCopy); } Mat dashboard = drawImageDashboard(vecCopy, vecCopy[0].type(), DASHBOARD_COLUMNS); imshow("Selection dashboard", dashboard); } vector showCharSelection(Mat image, vector charRegions, string state) { int curCharIdx = 0; vector humanInputs(charRegions.size()); for (int i = 0; i < charRegions.size(); i++) humanInputs[i] = SPACE; RegexRule regex_rule("", "[\\pL\\pN]", "", ""); int16_t waitkey = waitKey(50); while (waitkey != ENTER_KEY_ONE && waitkey != ENTER_KEY_TWO && waitkey != ESCAPE_KEY) { Mat imgCopy(image.size(), image.type()); image.copyTo(imgCopy); cvtColor(imgCopy, imgCopy, CV_GRAY2BGR); rectangle(imgCopy, charRegions[curCharIdx], Scalar(0, 255, 0), 1); imshow("Character selector", imgCopy); if ((char) waitkey == LEFT_ARROW_KEY) curCharIdx--; else if ((char) waitkey == RIGHT_ARROW_KEY) curCharIdx++; else if (waitkey == SPACE_KEY) { humanInputs[curCharIdx] = " "; curCharIdx++; } else if (waitkey > 0 && regex_rule.match(utf8chr(waitkey))) // Verify that it's an actual character { // Save the character to disk humanInputs[curCharIdx] = utf8chr(waitkey); curCharIdx++; if (curCharIdx >= charRegions.size()) { waitkey = (int16_t) ENTER_KEY_ONE; break; } } if (curCharIdx < 0) curCharIdx = 0; if (curCharIdx >= charRegions.size()) curCharIdx = charRegions.size() -1; waitkey = waitKey(50); } if (waitkey == ENTER_KEY_ONE || waitkey == ENTER_KEY_TWO) { // Save all the inputs for (int i = 0; i < charRegions.size(); i++) { if (humanInputs[i] != SPACE) cout << "Tagged " << state << " char code: '" << humanInputs[i] << "' at char position: " << i << endl; } } destroyWindow("Character selector"); return humanInputs; } openalpr_2.2.4.orig/src/misc_utilities/prepcharsfortraining.cpp000066400000000000000000000161601266464252400251520ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include #include #include #include #include "support/filesystem.h" #include "../tclap/CmdLine.h" #include "support/utf8.h" using namespace std; using namespace cv; using namespace alpr; // Takes a directory full of single char images, and plops them on a big tif files // Also creates a box file so Tesseract can recognize it int main( int argc, const char** argv ) { string inDir; int tile_width; int tile_height; TCLAP::CmdLine cmd("OpenAlpr OCR Training Prep Utility", ' ', "1.0.0"); TCLAP::UnlabeledValueArg inputDirArg( "input_dir", "Folder containing individual character images", true, "", "input_dir_path" ); TCLAP::ValueArg tileWidthArg("","tile_width","Width (in pixels) for each character tile. Default=50",false, 50 ,"tile_width_px"); TCLAP::ValueArg tileHeightArg("","tile_height","Height (in pixels) for each character tile. Default=60",false, 60 ,"tile_height_px"); try { cmd.add( inputDirArg ); cmd.add( tileWidthArg ); cmd.add( tileHeightArg ); if (cmd.parse( argc, argv ) == false) { // Error occurred while parsing. Exit now. return 1; } inDir = inputDirArg.getValue(); tile_width = tileWidthArg.getValue(); tile_height = tileHeightArg.getValue(); } catch (TCLAP::ArgException &e) // catch any exceptions { std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; return 1; } if (DirectoryExists(inDir.c_str()) == false) { printf("Input dir does not exist\n"); return 1; } const int CHAR_PADDING_HORIZONTAL = 0; const int CHAR_PADDING_VERTICAL = 0; const int X_OFFSET = 5; const int Y_OFFSET = 5; const int PAGE_MARGIN_X = 70; const int PAGE_MARGIN_Y = 70; const int HORIZONTAL_RESOLUTION = 3500; const int MAX_VERTICAL_RESOLUTION = 6000; // Maximum vertical size before chopping into additional pages. const int TILE_WIDTH = tile_width; const int TILE_HEIGHT = tile_height; const int FIXED_CHAR_HEIGHT = 40; // RESIZE all characters to this height vector all_files = getFilesInDir(inDir.c_str()); sort( all_files.begin(), all_files.end(), stringCompare ); vector files; for (int i = 0; i< all_files.size(); i++) { if (hasEnding(all_files[i], ".png") || hasEnding(all_files[i], ".jpg")) { files.push_back(all_files[i]); } } int tiles_per_row = ((float) (HORIZONTAL_RESOLUTION - (PAGE_MARGIN_X * 2))) / ((float) TILE_WIDTH) + 1; int lines = files.size() / (tiles_per_row); int vertical_resolution = ((lines + 1) * TILE_HEIGHT) + (PAGE_MARGIN_Y * 2) ; cout << tiles_per_row << " : " << vertical_resolution << endl; Mat bigTif = Mat::zeros(Size(HORIZONTAL_RESOLUTION, vertical_resolution), CV_8U); bitwise_not(bigTif, bigTif); stringstream boxFileOut; for (int i = 0; i< files.size(); i++) { int col = i % tiles_per_row; int line = i / tiles_per_row; int xPos = (col * TILE_WIDTH) + PAGE_MARGIN_X; int yPos = (line * TILE_HEIGHT) + PAGE_MARGIN_Y; if (hasEnding(files[i], ".png") || hasEnding(files[i], ".jpg")) { string fullpath = inDir + "/" + files[i]; cout << "Processing file: " << (i + 1) << " of " << files.size() << " (" << files[i] << ")" << endl; string::iterator utf_iterator = files[i].begin(); int cp = utf8::next(utf_iterator, files[i].end()); string charcode = utf8chr(cp); Mat characterImg = imread(fullpath); Mat charImgCopy = Mat::zeros(Size(150, 150), characterImg.type()); bitwise_not(charImgCopy, charImgCopy); characterImg.copyTo(charImgCopy(Rect(X_OFFSET, Y_OFFSET, characterImg.cols, characterImg.rows))); cvtColor(charImgCopy, charImgCopy, CV_BGR2GRAY); bitwise_not(charImgCopy, charImgCopy); vector > contours; //imshow("copy", charImgCopy); findContours(charImgCopy, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); float minHeightPercent = 0.35; int minHeight = (int) (((float) characterImg.rows) * minHeightPercent); vector tallEnoughRects; for (int c = 0; c < contours.size(); c++) { Rect tmpRect = boundingRect(contours[c]); if (tmpRect.height > minHeight) tallEnoughRects.push_back( tmpRect ); } int xMin = 9999999, xMax = 0, yMin = 9999999, yMax = 0; // Combine all the "tall enough" rectangles into one super rectangle for (int r = 0; r < tallEnoughRects.size(); r++) { if (tallEnoughRects[r].x < xMin) xMin = tallEnoughRects[r].x; if (tallEnoughRects[r].y < yMin) yMin = tallEnoughRects[r].y; if (tallEnoughRects[r].x + tallEnoughRects[r].width > xMax) xMax = tallEnoughRects[r].x + tallEnoughRects[r].width; if (tallEnoughRects[r].y + tallEnoughRects[r].height > yMax) yMax = tallEnoughRects[r].y + tallEnoughRects[r].height; } Rect tallestRect(xMin, yMin, xMax - xMin, yMax - yMin); //cout << tallestRect.x << ":" << tallestRect.y << " -- " << tallestRect.width << ":" << tallestRect.height << endl; Rect cropRect(0, tallestRect.y - Y_OFFSET, tallestRect.width, tallestRect.height); //cout << "Cropped: " << cropRect.x << ":" << cropRect.y << " -- " << cropRect.width << ":" << cropRect.height << endl; Mat cropped(characterImg, cropRect); cvtColor(cropped, cropped, CV_BGR2GRAY); Rect destinationRect(xPos, yPos, tallestRect.width, tallestRect.height); //cout << "1" << endl; cropped.copyTo(bigTif(destinationRect)); int x1 = destinationRect.x - CHAR_PADDING_HORIZONTAL; int y1 = (vertical_resolution - destinationRect.y - destinationRect.height) - CHAR_PADDING_VERTICAL; int x2 = (destinationRect.x + destinationRect.width) + CHAR_PADDING_HORIZONTAL; int y2 = (vertical_resolution - destinationRect.y) + CHAR_PADDING_VERTICAL; //0 70 5602 85 5636 0 boxFileOut << charcode << " " << x1 << " " << y1 << " "; boxFileOut << x2 << " " << y2 ; boxFileOut << " 0" << endl; //rectangle(characterImg, tallestRect, Scalar(0, 255, 0)); //imshow("characterImg", cropped); waitKey(2); } } imwrite("combined.tif", bigTif); ofstream boxFile("combined.box", std::ios::out); boxFile << boxFileOut.str(); } openalpr_2.2.4.orig/src/misc_utilities/sortstate.cpp000066400000000000000000000060271266464252400227510ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include #include #include #include "licenseplatecandidate.h" #include "../statedetection/state_detector.h" #include "utility.h" #include "support/filesystem.h" using namespace std; using namespace cv; using namespace alpr; // Given a directory full of pre-cropped images, identify the state that each image belongs to. // This is used to sort our own positive image database as a first step before grabbing characters to use to train the OCR. bool detectPlate( StateDetector* identifier, Mat frame); int main( int argc, const char** argv ) { #ifndef SKIP_STATE_DETECTION string inDir; string outDir; Mat frame; //Check if user specify image to process if(argc == 3 ) { inDir = argv[1]; outDir = argv[2]; outDir = outDir + "/"; } else { printf("Use:\n\t%s directory \n",argv[0]); printf("Ex: \n\t%s ./pics/ \n",argv[0]); return 0; } Config config("us"); StateDetector identifier(config.country, config.config_file_path, config.runtimeBaseDir); if (DirectoryExists(outDir.c_str()) == false) { printf("Output dir does not exist\n"); return 0; } if (DirectoryExists(inDir.c_str())) { vector files = getFilesInDir(inDir.c_str()); for (int i = 0; i< files.size(); i++) { if (hasEnding(files[i], ".png")) { string fullpath = inDir + "/" + files[i]; cout << fullpath << endl; frame = imread( fullpath.c_str() ); vector candidates = identifier.detect(frame.data, frame.elemSize(), frame.cols, frame.rows); if (candidates.size() > 0) { cout << candidates[0].confidence << " : " << candidates[0].state_code; ostringstream convert; // stream used for the conversion convert << i; // insert the textual representation of 'Number' in the characters in the stream string copyCommand = "cp \"" + fullpath + "\" " + outDir + candidates[0].state_code + convert.str() + ".png"; system( copyCommand.c_str() ); waitKey(50); //while ((char) waitKey(50) != 'c') { } } } } } #endif return 1; } bool detectPlate( StateDetector* identifier, Mat frame); openalpr_2.2.4.orig/src/misc_utilities/tagplates.cpp000066400000000000000000000135701266464252400227060ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include #include #include #include #include "support/filesystem.h" #include "config.h" #ifdef __APPLE__ const int LEFT_ARROW_KEY = 2; const int RIGHT_ARROW_KEY = 3; const int SPACE_KEY = 32; const int ENTER_KEY = 13; const int ESCAPE_KEY = 27; const int BACKSPACE_KEY = 8; const int DOWN_ARROW_KEY = 1; const int UP_ARROW_KEY= 0; #elif WINDOWS // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx const int LEFT_ARROW_KEY = 37; // VK_LEFT const int RIGHT_ARROW_KEY = 39; // VK_RIGHT const int SPACE_KEY = 32; // VK_SPACE const int ENTER_KEY = 13; // VK_RETURN const int ESCAPE_KEY = 27; // VK_ESCAPE const int BACKSPACE_KEY = 8; // VK_BACK const int DOWN_ARROW_KEY = 40; // VK_DOWN const int UP_ARROW_KEY= 38; // VK_UP #else const int LEFT_ARROW_KEY = 81; const int RIGHT_ARROW_KEY = 83; const int SPACE_KEY = 32; const int ENTER_KEY = 10; const int ESCAPE_KEY = 27; const int BACKSPACE_KEY = 8; const int DOWN_ARROW_KEY = 84; const int UP_ARROW_KEY= 82; #endif using namespace std; using namespace cv; using namespace alpr; static bool ldragging = false; static int xPos1 = 0; static int yPos1 = 0; static int xPos2 = 0; static int yPos2 = 0; float ASPECT_RATIO = 1.404; static bool rdragging = false; static int rDragStartX = 0; static int rDragStartY = 0; void mouseCallback(int event, int x, int y, int flags, void* userdata) { if ( event == EVENT_LBUTTONDOWN ) { ldragging = true; xPos1 = x; yPos1 = y; xPos2 = x; yPos2 = y; } else if ( event == EVENT_LBUTTONUP ) { ldragging = false; } else if ( event == EVENT_RBUTTONDOWN ) { rdragging = true; rDragStartX = x; rDragStartY = y; } else if ( event == EVENT_RBUTTONUP ) { rdragging = false; } else if ( event == EVENT_MOUSEMOVE ) { if (ldragging && x > xPos1 && y > yPos1) { int w = x - xPos1; int h = (int) (((float) w) / ASPECT_RATIO); xPos2 = x; yPos2 = yPos1 + h; } else if (rdragging) { int xDiff = x - rDragStartX; int yDiff = y - rDragStartY; xPos1 += xDiff; yPos1 += yDiff; xPos2 += xDiff; yPos2 += yDiff; rDragStartX = x; rDragStartY = y; } } } int main( int argc, const char** argv ) { string country; string inDir; string outDir; //Check if user specify image to process if(argc == 4) { country = argv[1]; inDir = argv[2]; outDir = argv[3]; } else { printf("Use:\n\t%s [country] [img input dir] [data output dir]\n",argv[0]); printf("\tex: %s us ./usimages ./usdata\n",argv[0]); printf("\n\n"); return 0; } if (DirectoryExists(inDir.c_str()) == false) { printf("Input dir does not exist\n"); return 0; } if (DirectoryExists(outDir.c_str()) == false) { printf("Output dir does not exist\n"); return 0; } Config config(country); ASPECT_RATIO = config.plateWidthMM / config.plateHeightMM; vector files = getFilesInDir(inDir.c_str()); vector imgFiles; sort( files.begin(), files.end(), stringCompare ); for (int i = 0; i < files.size(); i++) { if (hasEnding(files[i], ".png") || hasEnding(files[i], ".jpg")) { imgFiles.push_back(files[i]); } } for (int i = 0; i< imgFiles.size(); i++) { cout << "Loading: " << imgFiles[i] << " (" << (i+1) << "/" << imgFiles.size() << ")" << endl; string fullimgpath = inDir + "/" + imgFiles[i]; Mat frame = imread( fullimgpath.c_str() ); if (frame.cols == 0 || frame.rows == 0) continue; string curplatetag = ""; //Create a window namedWindow("Input image", 1); //set the callback function for any mouse event setMouseCallback("Input image", mouseCallback, NULL); char key = (char) cv::waitKey(50); while (key != ENTER_KEY) { if ((key >= '0' && key <= '9') || (key >= 'a' && key <= 'z')) { curplatetag = curplatetag + (char) toupper( key ); } else if (key == BACKSPACE_KEY) { curplatetag = curplatetag.substr(0, curplatetag.size() - 1); } Mat tmpFrame(frame.size(), frame.type()); frame.copyTo(tmpFrame); rectangle(tmpFrame, Point(xPos1, yPos1), Point(xPos2, yPos2), Scalar(0, 0, 255), 2); rectangle(tmpFrame, Point(xPos1, yPos1 - 35), Point(xPos1 + 175, yPos1 - 5), Scalar(255, 255, 255), CV_FILLED); putText(tmpFrame, curplatetag, Point(xPos1 + 2, yPos1 - 10), FONT_HERSHEY_PLAIN, 1.5, Scalar(100,50,0), 2); imshow("Input image", tmpFrame); key = cv::waitKey(50); } if (curplatetag != "") { // Write the data to disk ofstream outputdatafile; std::string outputTextFile = outDir + "/" + filenameWithoutExtension(imgFiles[i]) + ".txt"; outputdatafile.open(outputTextFile.c_str()); outputdatafile << imgFiles[i] << "\t" << xPos1 << "\t" << yPos1 << "\t" << (xPos2 - xPos1) << "\t" << (yPos2 - yPos1) << "\t" << curplatetag << endl; outputdatafile.close(); } } } openalpr_2.2.4.orig/src/openalpr/000077500000000000000000000000001266464252400170025ustar00rootroot00000000000000openalpr_2.2.4.orig/src/openalpr/CMakeLists.txt000066400000000000000000000032431266464252400215440ustar00rootroot00000000000000 set(lpr_source_files alpr.cpp alpr_impl.cpp config.cpp detection/detector.cpp detection/detectorcpu.cpp detection/detectorcuda.cpp detection/detectorocl.cpp detection/detectorfactory.cpp detection/detectormorph.cpp licenseplatecandidate.cpp utility.cpp ocr.cpp postprocess/postprocess.cpp postprocess/regexrule.cpp binarize_wolf.cpp segmentation/charactersegmenter.cpp segmentation/histogram.cpp segmentation/histogramvertical.cpp segmentation/histogramhorizontal.cpp edges/edgefinder.cpp edges/platecorners.cpp edges/platelines.cpp edges/textlinecollection.cpp edges/scorekeeper.cpp colorfilter.cpp prewarp.cpp transformation.cpp textdetection/characteranalysis.cpp textdetection/platemask.cpp textdetection/textcontours.cpp textdetection/textline.cpp textdetection/linefinder.cpp pipeline_data.cpp cjson.c motiondetector.cpp result_aggregator.cpp ) add_subdirectory(simpleini) add_subdirectory(support) add_library(openalpr-static STATIC ${lpr_source_files} ) add_library(openalpr SHARED ${lpr_source_files} ) set_target_properties(openalpr PROPERTIES SOVERSION ${OPENALPR_MAJOR_VERSION}) TARGET_LINK_LIBRARIES(openalpr support ${STATE_DETECTION_LIB} ${OpenCV_LIBS} ${Tesseract_LIBRARIES} ) install (FILES alpr.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include) install (TARGETS openalpr-static DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) install (TARGETS openalpr DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) # Compile GPU detector IF ( WITH_GPU_DETECTOR ) add_definitions(-DCOMPILE_GPU=1) ENDIF() # Add definition for default config file add_definitions(-DDEFAULT_CONFIG_FILE="${CMAKE_INSTALL_SYSCONFDIR}/openalpr/openalpr.conf") openalpr_2.2.4.orig/src/openalpr/TRexpp.h000066400000000000000000000054611266464252400204030ustar00rootroot00000000000000#ifndef _TREXPP_H_ #define _TREXPP_H_ /*************************************************************** T-Rex a tiny regular expression library Copyright (C) 2003-2004 Alberto Demichelis This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ****************************************************************/ extern "C" { #include "trex.h" } struct TRexParseException { TRexParseException(const TRexChar *c):desc(c) {} const TRexChar *desc; }; class TRexpp { public: TRexpp() { _exp = (TRex *)0; } ~TRexpp() { CleanUp(); } // compiles a regular expression void Compile(const TRexChar *pattern) { const TRexChar *error; CleanUp(); if(!(_exp = trex_compile(pattern,&error))) throw TRexParseException(error); } // return true if the given text match the expression bool Match(const TRexChar* text) { return _exp?(trex_match(_exp,text) != 0):false; } // Searches for the first match of the expression in a zero terminated string bool Search(const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end) { return _exp?(trex_search(_exp,text,out_begin,out_end) != 0):false; } // Searches for the first match of the expression in a string sarting at text_begin and ending at text_end bool SearchRange(const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end) { return _exp?(trex_searchrange(_exp,text_begin,text_end,out_begin,out_end) != 0):false; } bool GetSubExp(int n, const TRexChar** out_begin, int *out_len) { TRexMatch match; TRexBool res = _exp?(trex_getsubexp(_exp,n,&match)):TRex_False; if(res) { *out_begin = match.begin; *out_len = match.len; return true; } return false; } int GetSubExpCount() { return _exp?trex_getsubexpcount(_exp):0; } private: void CleanUp() { if(_exp) trex_free(_exp); _exp = (TRex *)0; } TRex *_exp; }; #endif //_TREXPP_H_ openalpr_2.2.4.orig/src/openalpr/alpr.cpp000066400000000000000000000060541266464252400204510ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "alpr.h" #include "alpr_impl.h" namespace alpr { // ALPR code Alpr::Alpr(const std::string country, const std::string configFile, const std::string runtimeDir) { impl = new AlprImpl(country, configFile, runtimeDir); } Alpr::~Alpr() { delete impl; } AlprResults Alpr::recognize(std::string filepath) { std::ifstream ifs(filepath.c_str(), std::ios::binary|std::ios::ate); if (ifs) { std::ifstream::pos_type pos = ifs.tellg(); std::vector buffer(pos); ifs.seekg(0, std::ios::beg); ifs.read(&buffer[0], pos); return this->recognize( buffer ); } else { std::cerr << "file does not exist: " << filepath << std::endl; AlprResults emptyResults; emptyResults.epoch_time = getEpochTimeMs(); emptyResults.img_width = 0; emptyResults.img_height = 0; emptyResults.total_processing_time_ms = 0; return emptyResults; } } AlprResults Alpr::recognize(std::vector imageBytes) { return impl->recognize(imageBytes); } AlprResults Alpr::recognize(std::vector imageBytes, std::vector regionsOfInterest) { return impl->recognize(imageBytes, regionsOfInterest); } AlprResults Alpr::recognize(unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector regionsOfInterest) { return impl->recognize(pixelData, bytesPerPixel, imgWidth, imgHeight, regionsOfInterest); } std::string Alpr::toJson( AlprResults results ) { return AlprImpl::toJson(results); } AlprResults Alpr::fromJson(std::string json) { return AlprImpl::fromJson(json); } void Alpr::setCountry(std::string country) { impl->setCountry(country); } void Alpr::setPrewarp(std::string prewarp_config) { impl->setPrewarp(prewarp_config); } void Alpr::setDetectRegion(bool detectRegion) { impl->setDetectRegion(detectRegion); } void Alpr::setTopN(int topN) { impl->setTopN(topN); } void Alpr::setDefaultRegion(std::string region) { impl->setDefaultRegion(region); } bool Alpr::isLoaded() { return impl->isLoaded(); } std::string Alpr::getVersion() { return AlprImpl::getVersion(); } Config* Alpr::getConfig() { return impl->config; } }openalpr_2.2.4.orig/src/openalpr/alpr.h000066400000000000000000000106301266464252400201110ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_ALPR_H #define OPENALPR_ALPR_H #include #include #include #include #ifdef WIN32 #define OPENALPR_DLL_EXPORT __declspec( dllexport ) #else #define OPENALPR_DLL_EXPORT #endif namespace alpr { struct AlprCoordinate { int x; int y; }; struct AlprChar { AlprCoordinate corners[4]; float confidence; std::string character; }; struct AlprPlate { std::string characters; float overall_confidence; std::vector character_details; bool matches_template; }; class AlprRegionOfInterest { public: AlprRegionOfInterest(); AlprRegionOfInterest(int x, int y, int width, int height) { this->x = x; this->y = y; this->width = width; this->height = height; }; int x; int y; int width; int height; }; class AlprPlateResult { public: AlprPlateResult() {}; virtual ~AlprPlateResult() {}; // The number requested is always >= the topNPlates count int requested_topn; // The country (training data code) that was used to recognize the plate std::string country; // the best plate is the topNPlate with the highest confidence AlprPlate bestPlate; // A list of possible plate number permutations std::vector topNPlates; // The processing time for this plate float processing_time_ms; // the X/Y coordinates of the corners of the plate (clock-wise from top-left) AlprCoordinate plate_points[4]; // The index of the plate if there were multiple plates returned int plate_index; // When region detection is enabled, this returns the region. Region detection is experimental int regionConfidence; std::string region; }; class AlprResults { public: AlprResults() { frame_number = -1; }; virtual ~AlprResults() {}; int64_t epoch_time; int64_t frame_number; int img_width; int img_height; float total_processing_time_ms; std::vector plates; std::vector regionsOfInterest; }; class Config; class AlprImpl; class OPENALPR_DLL_EXPORT Alpr { public: Alpr(const std::string country, const std::string configFile = "", const std::string runtimeDir = ""); virtual ~Alpr(); // Set the country used for plate recognition void setCountry(std::string country); // Update the prewarp setting without reloading the library void setPrewarp(std::string prewarp_config); void setDetectRegion(bool detectRegion); void setTopN(int topN); void setDefaultRegion(std::string region); // Recognize from an image on disk AlprResults recognize(std::string filepath); // Recognize from byte data representing an encoded image (e.g., BMP, PNG, JPG, GIF etc). AlprResults recognize(std::vector imageBytes); // Recognize from byte data representing an encoded image (e.g., BMP, PNG, JPG, GIF etc). AlprResults recognize(std::vector imageBytes, std::vector regionsOfInterest); // Recognize from raw pixel data. AlprResults recognize(unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector regionsOfInterest); static std::string toJson(const AlprResults results); static AlprResults fromJson(std::string json); bool isLoaded(); static std::string getVersion(); Config* getConfig(); private: AlprImpl* impl; }; } #endif // OPENALPR_APLR_H openalpr_2.2.4.orig/src/openalpr/alpr_impl.cpp000066400000000000000000000631031266464252400214700ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "alpr_impl.h" #include "result_aggregator.h" void plateAnalysisThread(void* arg); using namespace std; using namespace cv; namespace alpr { AlprImpl::AlprImpl(const std::string country, const std::string configFile, const std::string runtimeDir) { timespec startTime; getTimeMonotonic(&startTime); config = new Config(country, configFile, runtimeDir); prewarp = ALPR_NULL_PTR; // Config file or runtime dir not found. Don't process any further. if (config->loaded == false) { return; } loadRecognizers(); setNumThreads(0); setDetectRegion(DEFAULT_DETECT_REGION); this->topN = DEFAULT_TOPN; setDefaultRegion(""); prewarp = new PreWarp(config); timespec endTime; getTimeMonotonic(&endTime); if (config->debugTiming) cout << "OpenALPR Initialization Time: " << diffclock(startTime, endTime) << "ms." << endl; } AlprImpl::~AlprImpl() { delete config; typedef std::map::iterator it_type; for(it_type iterator = recognizers.begin(); iterator != recognizers.end(); iterator++) { delete iterator->second.plateDetector; delete iterator->second.stateDetector; delete iterator->second.ocr; } delete prewarp; } bool AlprImpl::isLoaded() { return config->loaded; } AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector regionsOfInterest) { timespec startTime; getTimeMonotonic(&startTime); AlprFullDetails response; int64_t start_time = getEpochTimeMs(); // Fix regions of interest in case they extend beyond the bounds of the image for (unsigned int i = 0; i < regionsOfInterest.size(); i++) regionsOfInterest[i] = expandRect(regionsOfInterest[i], 0, 0, img.cols, img.rows); for (unsigned int i = 0; i < regionsOfInterest.size(); i++) { response.results.regionsOfInterest.push_back(AlprRegionOfInterest(regionsOfInterest[i].x, regionsOfInterest[i].y, regionsOfInterest[i].width, regionsOfInterest[i].height)); } if (!img.data) { // Invalid image if (this->config->debugGeneral) std::cerr << "Invalid image" << std::endl; return response; } // Convert image to grayscale if required Mat grayImg = img; if (img.channels() > 2) cvtColor( img, grayImg, CV_BGR2GRAY ); // Prewarp the image and ROIs if configured] std::vector warpedRegionsOfInterest = regionsOfInterest; // Warp the image if prewarp is provided grayImg = prewarp->warpImage(grayImg); warpedRegionsOfInterest = prewarp->projectRects(regionsOfInterest, grayImg.cols, grayImg.rows, false); // Iterate through each country provided (typically just one) // and aggregate the results if necessary ResultAggregator aggregator; for (unsigned int i = 0; i < config->loaded_countries.size(); i++) { if (config->debugGeneral) cout << "Analyzing: " << config->loaded_countries[i] << endl; config->setCountry(config->loaded_countries[i]); AlprFullDetails sub_results = analyzeSingleCountry(img, grayImg, warpedRegionsOfInterest); sub_results.results.epoch_time = start_time; sub_results.results.img_width = img.cols; sub_results.results.img_height = img.rows; aggregator.addResults(sub_results); } response = aggregator.getAggregateResults(); timespec endTime; getTimeMonotonic(&endTime); if (config->debugTiming) { cout << "Total Time to process image: " << diffclock(startTime, endTime) << "ms." << endl; } if (config->debugGeneral && config->debugShowImages) { for (unsigned int i = 0; i < regionsOfInterest.size(); i++) { rectangle(img, regionsOfInterest[i], Scalar(0,255,0), 2); } for (unsigned int i = 0; i < response.plateRegions.size(); i++) { rectangle(img, response.plateRegions[i].rect, Scalar(0, 0, 255), 2); } for (unsigned int i = 0; i < response.results.plates.size(); i++) { // Draw a box around the license plate for (int z = 0; z < 4; z++) { AlprCoordinate* coords = response.results.plates[i].plate_points; Point p1(coords[z].x, coords[z].y); Point p2(coords[(z + 1) % 4].x, coords[(z + 1) % 4].y); line(img, p1, p2, Scalar(255,0,255), 2); } // Draw the individual character boxes for (int q = 0; q < response.results.plates[i].bestPlate.character_details.size(); q++) { AlprChar details = response.results.plates[i].bestPlate.character_details[q]; line(img, Point(details.corners[0].x, details.corners[0].y), Point(details.corners[1].x, details.corners[1].y), Scalar(0,255,0), 1); line(img, Point(details.corners[1].x, details.corners[1].y), Point(details.corners[2].x, details.corners[2].y), Scalar(0,255,0), 1); line(img, Point(details.corners[2].x, details.corners[2].y), Point(details.corners[3].x, details.corners[3].y), Scalar(0,255,0), 1); line(img, Point(details.corners[3].x, details.corners[3].y), Point(details.corners[0].x, details.corners[0].y), Scalar(0,255,0), 1); } } displayImage(config, "Main Image", img); // Sleep 1ms sleep_ms(1); } if (config->debugPauseOnFrame) { // Pause indefinitely until they press a key while ((char) cv::waitKey(50) == -1) {} } return response; } AlprFullDetails AlprImpl::analyzeSingleCountry(cv::Mat colorImg, cv::Mat grayImg, std::vector warpedRegionsOfInterest) { AlprFullDetails response; AlprRecognizers country_recognizers = recognizers[config->country]; timespec startTime; getTimeMonotonic(&startTime); vector warpedPlateRegions; // Find all the candidate regions if (config->skipDetection == false) { warpedPlateRegions = country_recognizers.plateDetector->detect(grayImg, warpedRegionsOfInterest); } else { // They have elected to skip plate detection. Instead, return a list of plate regions // based on their regions of interest for (unsigned int i = 0; i < warpedRegionsOfInterest.size(); i++) { PlateRegion pr; pr.rect = cv::Rect(warpedRegionsOfInterest[i]); warpedPlateRegions.push_back(pr); } } queue plateQueue; for (unsigned int i = 0; i < warpedPlateRegions.size(); i++) plateQueue.push(warpedPlateRegions[i]); int platecount = 0; while(!plateQueue.empty()) { PlateRegion plateRegion = plateQueue.front(); plateQueue.pop(); PipelineData pipeline_data(colorImg, grayImg, plateRegion.rect, config); pipeline_data.prewarp = prewarp; timespec platestarttime; getTimeMonotonic(&platestarttime); LicensePlateCandidate lp(&pipeline_data); lp.recognize(); bool plateDetected = false; if (pipeline_data.disqualified && config->debugGeneral) { cout << "Disqualify reason: " << pipeline_data.disqualify_reason << endl; } if (!pipeline_data.disqualified) { AlprPlateResult plateResult; plateResult.country = config->country; // If there's only one pattern for a country, use it. Otherwise use the default if (country_recognizers.ocr->postProcessor.getPatterns().size() == 1) plateResult.region = country_recognizers.ocr->postProcessor.getPatterns()[0]; else plateResult.region = defaultRegion; plateResult.regionConfidence = 0; plateResult.plate_index = platecount++; plateResult.requested_topn = topN; // If using prewarp, remap the plate corners to the original image vector cornerPoints = pipeline_data.plate_corners; cornerPoints = prewarp->projectPoints(cornerPoints, true); for (int pointidx = 0; pointidx < 4; pointidx++) { plateResult.plate_points[pointidx].x = (int) cornerPoints[pointidx].x; plateResult.plate_points[pointidx].y = (int) cornerPoints[pointidx].y; } #ifndef SKIP_STATE_DETECTION if (detectRegion && country_recognizers.stateDetector->isLoaded()) { std::vector state_candidates = country_recognizers.stateDetector->detect(pipeline_data.color_deskewed.data, pipeline_data.color_deskewed.elemSize(), pipeline_data.color_deskewed.cols, pipeline_data.color_deskewed.rows); if (state_candidates.size() > 0) { plateResult.region = state_candidates[0].state_code; plateResult.regionConfidence = (int) state_candidates[0].confidence; } } #endif if (plateResult.region.length() > 0 && country_recognizers.ocr->postProcessor.regionIsValid(plateResult.region) == false) { std::cerr << "Invalid pattern provided: " << plateResult.region << std::endl; std::cerr << "Valid patterns are located in the " << config->country << ".patterns file" << std::endl; } country_recognizers.ocr->performOCR(&pipeline_data); country_recognizers.ocr->postProcessor.analyze(plateResult.region, topN); timespec resultsStartTime; getTimeMonotonic(&resultsStartTime); const vector ppResults = country_recognizers.ocr->postProcessor.getResults(); int bestPlateIndex = 0; cv::Mat charTransformMatrix = getCharacterTransformMatrix(&pipeline_data); bool isBestPlateSelected = false; for (unsigned int pp = 0; pp < ppResults.size(); pp++) { // Set our "best plate" match to either the first entry, or the first entry with a postprocessor template match if (isBestPlateSelected == false && ppResults[pp].matchesTemplate){ bestPlateIndex = plateResult.topNPlates.size(); isBestPlateSelected = true; } AlprPlate aplate; aplate.characters = ppResults[pp].letters; aplate.overall_confidence = ppResults[pp].totalscore; aplate.matches_template = ppResults[pp].matchesTemplate; // Grab detailed results for each character for (unsigned int c_idx = 0; c_idx < ppResults[pp].letter_details.size(); c_idx++) { AlprChar character_details; Letter l = ppResults[pp].letter_details[c_idx]; character_details.character = l.letter; character_details.confidence = l.totalscore; cv::Rect char_rect = pipeline_data.charRegionsFlat[l.charposition]; std::vector charpoints = getCharacterPoints(char_rect, charTransformMatrix ); for (int cpt = 0; cpt < 4; cpt++) character_details.corners[cpt] = charpoints[cpt]; aplate.character_details.push_back(character_details); } plateResult.topNPlates.push_back(aplate); } if (plateResult.topNPlates.size() > bestPlateIndex) { AlprPlate bestPlate; bestPlate.characters = plateResult.topNPlates[bestPlateIndex].characters; bestPlate.matches_template = plateResult.topNPlates[bestPlateIndex].matches_template; bestPlate.overall_confidence = plateResult.topNPlates[bestPlateIndex].overall_confidence; bestPlate.character_details = plateResult.topNPlates[bestPlateIndex].character_details; plateResult.bestPlate = bestPlate; } timespec plateEndTime; getTimeMonotonic(&plateEndTime); plateResult.processing_time_ms = diffclock(platestarttime, plateEndTime); if (config->debugTiming) { cout << "Result Generation Time: " << diffclock(resultsStartTime, plateEndTime) << "ms." << endl; } if (plateResult.topNPlates.size() > 0) { plateDetected = true; response.results.plates.push_back(plateResult); } } if (!plateDetected) { // Not a valid plate // Check if this plate has any children, if so, send them back up for processing for (unsigned int childidx = 0; childidx < plateRegion.children.size(); childidx++) { plateQueue.push(plateRegion.children[childidx]); } } } // Unwarp plate regions if necessary prewarp->projectPlateRegions(warpedPlateRegions, grayImg.cols, grayImg.rows, true); response.plateRegions = warpedPlateRegions; timespec endTime; getTimeMonotonic(&endTime); response.results.total_processing_time_ms = diffclock(startTime, endTime); return response; } AlprResults AlprImpl::recognize( std::vector imageBytes) { try { cv::Mat img = cv::imdecode(cv::Mat(imageBytes), 1); return this->recognize(img); } catch (cv::Exception& e) { std::cerr << "Caught exception in OpenALPR recognize: " << e.msg << std::endl; AlprResults emptyresults; return emptyresults; } } AlprResults AlprImpl::recognize(std::vector imageBytes, std::vector regionsOfInterest) { try { cv::Mat img = cv::imdecode(cv::Mat(imageBytes), 1); std::vector rois = convertRects(regionsOfInterest); AlprFullDetails fullDetails = recognizeFullDetails(img, rois); return fullDetails.results; } catch (cv::Exception& e) { std::cerr << "Caught exception in OpenALPR recognize: " << e.msg << std::endl; AlprResults emptyresults; return emptyresults; } } AlprResults AlprImpl::recognize( unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector regionsOfInterest) { try { int arraySize = imgWidth * imgHeight * bytesPerPixel; cv::Mat imgData = cv::Mat(arraySize, 1, CV_8U, pixelData); cv::Mat img = imgData.reshape(bytesPerPixel, imgHeight); if (regionsOfInterest.size() == 0) { AlprRegionOfInterest fullFrame(0,0, img.cols, img.rows); regionsOfInterest.push_back(fullFrame); } return this->recognize(img, this->convertRects(regionsOfInterest)); } catch (cv::Exception& e) { std::cerr << "Caught exception in OpenALPR recognize: " << e.msg << std::endl; AlprResults emptyresults; return emptyresults; } } AlprResults AlprImpl::recognize(cv::Mat img) { std::vector regionsOfInterest; regionsOfInterest.push_back(cv::Rect(0, 0, img.cols, img.rows)); return this->recognize(img, regionsOfInterest); } AlprResults AlprImpl::recognize(cv::Mat img, std::vector regionsOfInterest) { AlprFullDetails fullDetails = recognizeFullDetails(img, regionsOfInterest); return fullDetails.results; } std::vector AlprImpl::convertRects(std::vector regionsOfInterest) { std::vector rectRegions; for (unsigned int i = 0; i < regionsOfInterest.size(); i++) { rectRegions.push_back(cv::Rect(regionsOfInterest[i].x, regionsOfInterest[i].y, regionsOfInterest[i].width, regionsOfInterest[i].height)); } return rectRegions; } string AlprImpl::toJson( const AlprResults results ) { cJSON *root, *jsonResults; root = cJSON_CreateObject(); cJSON_AddNumberToObject(root,"version", 2 ); cJSON_AddStringToObject(root,"data_type", "alpr_results" ); cJSON_AddNumberToObject(root,"epoch_time", results.epoch_time ); cJSON_AddNumberToObject(root,"img_width", results.img_width ); cJSON_AddNumberToObject(root,"img_height", results.img_height ); cJSON_AddNumberToObject(root,"processing_time_ms", results.total_processing_time_ms ); // Add the regions of interest to the JSON cJSON *rois; cJSON_AddItemToObject(root, "regions_of_interest", rois=cJSON_CreateArray()); for (unsigned int i=0;ibestPlate.characters.c_str()); cJSON_AddNumberToObject(root,"confidence", result->bestPlate.overall_confidence); cJSON_AddNumberToObject(root,"matches_template", result->bestPlate.matches_template); cJSON_AddNumberToObject(root,"plate_index", result->plate_index); cJSON_AddStringToObject(root,"region", result->region.c_str()); cJSON_AddNumberToObject(root,"region_confidence", result->regionConfidence); cJSON_AddNumberToObject(root,"processing_time_ms", result->processing_time_ms); cJSON_AddNumberToObject(root,"requested_topn", result->requested_topn); cJSON_AddItemToObject(root, "coordinates", coords=cJSON_CreateArray()); for (int i=0;i<4;i++) { cJSON *coords_object; coords_object = cJSON_CreateObject(); cJSON_AddNumberToObject(coords_object, "x", result->plate_points[i].x); cJSON_AddNumberToObject(coords_object, "y", result->plate_points[i].y); cJSON_AddItemToArray(coords, coords_object); } cJSON_AddItemToObject(root, "candidates", candidates=cJSON_CreateArray()); for (unsigned int i = 0; i < result->topNPlates.size(); i++) { cJSON *candidate_object; candidate_object = cJSON_CreateObject(); cJSON_AddStringToObject(candidate_object, "plate", result->topNPlates[i].characters.c_str()); cJSON_AddNumberToObject(candidate_object, "confidence", result->topNPlates[i].overall_confidence); cJSON_AddNumberToObject(candidate_object, "matches_template", result->topNPlates[i].matches_template); cJSON_AddItemToArray(candidates, candidate_object); } return root; } AlprResults AlprImpl::fromJson(std::string json) { AlprResults allResults; cJSON* root = cJSON_Parse(json.c_str()); int version = cJSON_GetObjectItem(root, "version")->valueint; allResults.epoch_time = (int64_t) cJSON_GetObjectItem(root, "epoch_time")->valuedouble; allResults.img_width = cJSON_GetObjectItem(root, "img_width")->valueint; allResults.img_height = cJSON_GetObjectItem(root, "img_height")->valueint; allResults.total_processing_time_ms = cJSON_GetObjectItem(root, "processing_time_ms")->valueint; cJSON* rois = cJSON_GetObjectItem(root,"regions_of_interest"); int numRois = cJSON_GetArraySize(rois); for (int c = 0; c < numRois; c++) { cJSON* roi = cJSON_GetArrayItem(rois, c); int x = cJSON_GetObjectItem(roi, "x")->valueint; int y = cJSON_GetObjectItem(roi, "y")->valueint; int width = cJSON_GetObjectItem(roi, "width")->valueint; int height = cJSON_GetObjectItem(roi, "height")->valueint; AlprRegionOfInterest alprRegion(x,y,width,height); allResults.regionsOfInterest.push_back(alprRegion); } cJSON* resultsArray = cJSON_GetObjectItem(root,"results"); int resultsSize = cJSON_GetArraySize(resultsArray); for (int i = 0; i < resultsSize; i++) { cJSON* item = cJSON_GetArrayItem(resultsArray, i); AlprPlateResult plate; //plate.bestPlate = cJSON_GetObjectItem(item, "plate")->valuestring; plate.processing_time_ms = cJSON_GetObjectItem(item, "processing_time_ms")->valuedouble; plate.plate_index = cJSON_GetObjectItem(item, "plate_index")->valueint; plate.region = std::string(cJSON_GetObjectItem(item, "region")->valuestring); plate.regionConfidence = cJSON_GetObjectItem(item, "region_confidence")->valueint; plate.requested_topn = cJSON_GetObjectItem(item, "requested_topn")->valueint; cJSON* coordinates = cJSON_GetObjectItem(item,"coordinates"); for (int c = 0; c < 4; c++) { cJSON* coordinate = cJSON_GetArrayItem(coordinates, c); AlprCoordinate alprcoord; alprcoord.x = cJSON_GetObjectItem(coordinate, "x")->valueint; alprcoord.y = cJSON_GetObjectItem(coordinate, "y")->valueint; plate.plate_points[c] = alprcoord; } cJSON* candidates = cJSON_GetObjectItem(item,"candidates"); int numCandidates = cJSON_GetArraySize(candidates); for (int c = 0; c < numCandidates; c++) { cJSON* candidate = cJSON_GetArrayItem(candidates, c); AlprPlate plateCandidate; plateCandidate.characters = std::string(cJSON_GetObjectItem(candidate, "plate")->valuestring); plateCandidate.overall_confidence = cJSON_GetObjectItem(candidate, "confidence")->valuedouble; plateCandidate.matches_template = (cJSON_GetObjectItem(candidate, "matches_template")->valueint) != 0; plate.topNPlates.push_back(plateCandidate); if (c == 0) { plate.bestPlate = plateCandidate; } } allResults.plates.push_back(plate); } cJSON_Delete(root); return allResults; } void AlprImpl::setCountry(std::string country) { config->load_countries(country); loadRecognizers(); } void AlprImpl::setPrewarp(std::string prewarp_config) { if (prewarp_config.length() == 0) prewarp ->clear(); else prewarp->initialize(prewarp_config); } void AlprImpl::setDetectRegion(bool detectRegion) { this->detectRegion = detectRegion; } void AlprImpl::setTopN(int topn) { this->topN = topn; } void AlprImpl::setDefaultRegion(string region) { this->defaultRegion = region; } std::string AlprImpl::getVersion() { std::stringstream ss; ss << OPENALPR_MAJOR_VERSION << "." << OPENALPR_MINOR_VERSION << "." << OPENALPR_PATCH_VERSION; return ss.str(); } void AlprImpl::loadRecognizers() { for (unsigned int i = 0; i < config->loaded_countries.size(); i++) { config->setCountry(config->loaded_countries[i]); if (recognizers.find(config->country) == recognizers.end()) { // Country training data has not already been loaded. Load it. AlprRecognizers recognizer; recognizer.plateDetector = createDetector(config); recognizer.ocr = new OCR(config); #ifndef SKIP_STATE_DETECTION recognizer.stateDetector = new StateDetector(this->config->country, this->config->config_file_path, this->config->runtimeBaseDir); #else recognizer.stateDetector = NULL; #endif recognizers[config->country] = recognizer; } } } cv::Mat AlprImpl::getCharacterTransformMatrix(PipelineData* pipeline_data ) { std::vector crop_corners; crop_corners.push_back(Point2f(0,0)); crop_corners.push_back(Point2f(pipeline_data->crop_gray.cols,0)); crop_corners.push_back(Point2f(pipeline_data->crop_gray.cols,pipeline_data->crop_gray.rows)); crop_corners.push_back(Point2f(0,pipeline_data->crop_gray.rows)); // Transform the points from the cropped region (skew corrected license plate region) back to the original image cv::Mat transmtx = cv::getPerspectiveTransform(crop_corners, pipeline_data->plate_corners); return transmtx; } std::vector AlprImpl::getCharacterPoints(cv::Rect char_rect, cv::Mat transmtx ) { std::vector points; points.push_back(Point2f(char_rect.x, char_rect.y)); points.push_back(Point2f(char_rect.x + char_rect.width, char_rect.y)); points.push_back(Point2f(char_rect.x + char_rect.width, char_rect.y + char_rect.height)); points.push_back(Point2f(char_rect.x, char_rect.y + char_rect.height)); cv::perspectiveTransform(points, points, transmtx); // If using prewarp, remap the points to the original image points = prewarp->projectPoints(points, true); std::vector cornersvector; for (int i = 0; i < 4; i++) { AlprCoordinate coord; coord.x = round(points[i].x); coord.y = round(points[i].y); cornersvector.push_back(coord); } return cornersvector; } } openalpr_2.2.4.orig/src/openalpr/alpr_impl.h000066400000000000000000000070631266464252400211400ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_ALPRIMPL_H #define OPENALPR_ALPRIMPL_H #include #include #include #include #include "alpr.h" #include "config.h" #include "detection/detector.h" #include "detection/detectorfactory.h" #include "prewarp.h" #include "licenseplatecandidate.h" #include "../statedetection/state_detector.h" #include "segmentation/charactersegmenter.h" #include "ocr.h" #include "constants.h" #include "cjson.h" #include "pipeline_data.h" #include "prewarp.h" #include #include "support/platform.h" #include "support/utf8.h" #define DEFAULT_TOPN 25 #define DEFAULT_DETECT_REGION false #define ALPR_NULL_PTR 0 namespace alpr { struct AlprFullDetails { std::vector plateRegions; AlprResults results; }; struct AlprRecognizers { Detector* plateDetector; StateDetector* stateDetector; OCR* ocr; }; class AlprImpl { public: AlprImpl(const std::string country, const std::string configFile = "", const std::string runtimeDir = ""); virtual ~AlprImpl(); AlprFullDetails recognizeFullDetails(cv::Mat img, std::vector regionsOfInterest); AlprResults recognize( std::vector imageBytes ); AlprResults recognize( std::vector imageBytes, std::vector regionsOfInterest ); AlprResults recognize( unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector regionsOfInterest ); AlprResults recognize( cv::Mat img ); AlprResults recognize( cv::Mat img, std::vector regionsOfInterest ); void applyRegionTemplate(AlprPlateResult* result, std::string region); AlprFullDetails analyzeSingleCountry(cv::Mat colorImg, cv::Mat grayImg, std::vector regionsOfInterest); void setCountry(std::string country); void setPrewarp(std::string prewarp_config); void setDetectRegion(bool detectRegion); void setTopN(int topn); void setDefaultRegion(std::string region); static std::string toJson( const AlprResults results ); static AlprResults fromJson(std::string json); static std::string getVersion(); static cJSON* createJsonObj(const AlprPlateResult* result); Config* config; bool isLoaded(); private: std::map recognizers; PreWarp* prewarp; int topN; bool detectRegion; std::string defaultRegion; void loadRecognizers(); cv::Mat getCharacterTransformMatrix(PipelineData* pipeline_data ); std::vector getCharacterPoints(cv::Rect char_rect, cv::Mat transmtx); std::vector convertRects(std::vector regionsOfInterest); }; } #endif // OPENALPR_ALPRIMPL_Hopenalpr_2.2.4.orig/src/openalpr/binarize_wolf.cpp000066400000000000000000000151741266464252400223500ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ /************************************************************** * Binarization with several methods * (0) Niblacks method * (1) Sauvola & Co. * ICDAR 1997, pp 147-152 * (2) by myself - Christian Wolf * Research notebook 19.4.2001, page 129 * (3) by myself - Christian Wolf * 20.4.2007 * * See also: * Research notebook 24.4.2001, page 132 (Calculation of s) **************************************************************/ #include "binarize_wolf.h" using namespace std; using namespace cv; namespace alpr { // ************************************************************* // glide a window across the image and // create two maps: mean and standard deviation. // ************************************************************* double calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy) { Mat im_sum, im_sum_sq; cv::integral(im,im_sum,im_sum_sq,CV_64F); double m,s,max_s,sum,sum_sq; int wxh = winx/2; int wyh = winy/2; int x_firstth= wxh; int y_lastth = im.rows-wyh-1; int y_firstth= wyh; double winarea = winx*winy; max_s = 0; for (int j = y_firstth ; j<=y_lastth; j++){ sum = sum_sq = 0; sum = im_sum.at(j-wyh+winy,winx) - im_sum.at(j-wyh,winx) - im_sum.at(j-wyh+winy,0) + im_sum.at(j-wyh,0); sum_sq = im_sum_sq.at(j-wyh+winy,winx) - im_sum_sq.at(j-wyh,winx) - im_sum_sq.at(j-wyh+winy,0) + im_sum_sq.at(j-wyh,0); m = sum / winarea; s = sqrt ((sum_sq - m*sum)/winarea); if (s > max_s) max_s = s; map_m.fset(x_firstth, j, m); map_s.fset(x_firstth, j, s); // Shift the window, add and remove new/old values to the histogram for (int i=1 ; i <= im.cols-winx; i++) { // Remove the left old column and add the right new column sum -= im_sum.at(j-wyh+winy,i) - im_sum.at(j-wyh,i) - im_sum.at(j-wyh+winy,i-1) + im_sum.at(j-wyh,i-1); sum += im_sum.at(j-wyh+winy,i+winx) - im_sum.at(j-wyh,i+winx) - im_sum.at(j-wyh+winy,i+winx-1) + im_sum.at(j-wyh,i+winx-1); sum_sq -= im_sum_sq.at(j-wyh+winy,i) - im_sum_sq.at(j-wyh,i) - im_sum_sq.at(j-wyh+winy,i-1) + im_sum_sq.at(j-wyh,i-1); sum_sq += im_sum_sq.at(j-wyh+winy,i+winx) - im_sum_sq.at(j-wyh,i+winx) - im_sum_sq.at(j-wyh+winy,i+winx-1) + im_sum_sq.at(j-wyh,i+winx-1); m = sum / winarea; s = sqrt ((sum_sq - m*sum)/winarea); if (s > max_s) max_s = s; map_m.fset(i+wxh, j, m); map_s.fset(i+wxh, j, s); } } return max_s; } /********************************************************** * The binarization routine **********************************************************/ void NiblackSauvolaWolfJolion (Mat im, Mat output, NiblackVersion version, int winx, int winy, double k, double dR) { double m, s, max_s; double th=0; double min_I, max_I; int wxh = winx/2; int wyh = winy/2; int x_firstth= wxh; int x_lastth = im.cols-wxh-1; int y_lastth = im.rows-wyh-1; int y_firstth= wyh; int mx, my; // Create local statistics and store them in a double matrices Mat map_m = Mat::zeros (im.rows, im.cols, CV_32F); Mat map_s = Mat::zeros (im.rows, im.cols, CV_32F); max_s = calcLocalStats (im, map_m, map_s, winx, winy); minMaxLoc(im, &min_I, &max_I); Mat thsurf (im.rows, im.cols, CV_32F); // Create the threshold surface, including border processing // ---------------------------------------------------- for (int j = y_firstth ; j<=y_lastth; j++) { // NORMAL, NON-BORDER AREA IN THE MIDDLE OF THE WINDOW: for (int i=0 ; i <= im.cols-winx; i++) { m = map_m.fget(i+wxh, j); s = map_s.fget(i+wxh, j); // Calculate the threshold switch (version) { case NIBLACK: th = m + k*s; break; case SAUVOLA: th = m * (1 + k*(s/dR-1)); break; case WOLFJOLION: th = m + k * (s/max_s-1) * (m-min_I); break; default: cerr << "Unknown threshold type in ImageThresholder::surfaceNiblackImproved()\n"; exit (1); } thsurf.fset(i+wxh,j,th); if (i==0) { // LEFT BORDER for (int i=0; i<=x_firstth; ++i) thsurf.fset(i,j,th); // LEFT-UPPER CORNER if (j==y_firstth) for (int u=0; u= thsurf.fget(x,y)) { output.uset(x,y,255); } else { output.uset(x,y,0); } } } }openalpr_2.2.4.orig/src/openalpr/binarize_wolf.h000066400000000000000000000027631266464252400220150ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_BINARIZEWOLF_H #define OPENALPR_BINARIZEWOLF_H #include "support/filesystem.h" #include #include #include "opencv2/opencv.hpp" namespace alpr { enum NiblackVersion { NIBLACK=0, SAUVOLA, WOLFJOLION, }; #define BINARIZEWOLF_VERSION "2.3 (February 26th, 2013)" #define BINARIZEWOLF_DEFAULTDR 128 #define uget(x,y) at(y,x) #define uset(x,y,v) at(y,x)=v; #define fget(x,y) at(y,x) #define fset(x,y,v) at(y,x)=v; void NiblackSauvolaWolfJolion (cv::Mat im, cv::Mat output, NiblackVersion version, int winx, int winy, double k, double dR=BINARIZEWOLF_DEFAULTDR); } #endif // OPENALPR_BINARIZEWOLF_H openalpr_2.2.4.orig/src/openalpr/cjson.c000066400000000000000000000551611266464252400202720ustar00rootroot00000000000000/* Copyright (c) 2009 Dave Gamble Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* cJSON */ /* JSON parser in C. */ #include #include #include #include #include #include #include #include "cjson.h" static const char *ep; const char *cJSON_GetErrorPtr(void) {return ep;} static int cJSON_strcasecmp(const char *s1,const char *s2) { if (!s1) return (s1==s2)?0:1;if (!s2) return 1; for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); } static void *(*cJSON_malloc)(size_t sz) = malloc; static void (*cJSON_free)(void *ptr) = free; static char* cJSON_strdup(const char* str) { size_t len; char* copy; len = strlen(str) + 1; if (!(copy = (char*)cJSON_malloc(len))) return 0; memcpy(copy,str,len); return copy; } void cJSON_InitHooks(cJSON_Hooks* hooks) { if (!hooks) { /* Reset hooks */ cJSON_malloc = malloc; cJSON_free = free; return; } cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; cJSON_free = (hooks->free_fn)?hooks->free_fn:free; } /* Internal constructor. */ static cJSON *cJSON_New_Item(void) { cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); if (node) memset(node,0,sizeof(cJSON)); return node; } /* Delete a cJSON structure. */ void cJSON_Delete(cJSON *c) { cJSON *next; while (c) { next=c->next; if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); if (c->string) cJSON_free(c->string); cJSON_free(c); c=next; } } /* Parse the input text to generate a number, and populate the result into item. */ static const char *parse_number(cJSON *item,const char *num) { double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; if (*num=='-') sign=-1,num++; /* Has sign? */ if (*num=='0') num++; /* is zero */ if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ if (*num=='.' || *num==',' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ if (*num=='e' || *num=='E') /* Exponent? */ { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ } n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ item->valuedouble=n; item->valueint=(int)n; item->type=cJSON_Number; return num; } /* Render the number nicely from the given item into a string. */ static char *print_number(cJSON *item) { char *str; double d=item->valuedouble; if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) { str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ if (str) sprintf(str,"%d",item->valueint); } else { str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ if (str) { if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); else sprintf(str,"%f",d); } } return str; } static unsigned parse_hex4(const char *str) { unsigned h=0; if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; h=h<<4;str++; if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; h=h<<4;str++; if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; h=h<<4;str++; if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; return h; } /* Parse the input text into an unescaped cstring, and populate item. */ static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; static const char *parse_string(cJSON *item,const char *str) { const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; if (*str!='\"') {ep=str;return 0;} /* not a string! */ while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ if (!out) return 0; ptr=str+1;ptr2=out; while (*ptr!='\"' && *ptr) { if (*ptr!='\\') *ptr2++=*ptr++; else { ptr++; switch (*ptr) { case 'b': *ptr2++='\b'; break; case 'f': *ptr2++='\f'; break; case 'n': *ptr2++='\n'; break; case 'r': *ptr2++='\r'; break; case 't': *ptr2++='\t'; break; case 'u': /* transcode utf16 to utf8. */ uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ { if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ uc2=parse_hex4(ptr+3);ptr+=6; if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); } len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; switch (len) { case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 1: *--ptr2 =(uc | firstByteMark[len]); } ptr2+=len; break; default: *ptr2++=*ptr; break; } ptr++; } } *ptr2=0; if (*ptr=='\"') ptr++; item->valuestring=out; item->type=cJSON_String; return ptr; } /* Render the cstring provided to an escaped version that can be printed. */ static char *print_string_ptr(const char *str) { const char *ptr;char *ptr2,*out;int len=0;unsigned char token; if (!str) return cJSON_strdup(""); ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} out=(char*)cJSON_malloc(len+3); if (!out) return 0; ptr2=out;ptr=str; *ptr2++='\"'; while (*ptr) { if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; else { *ptr2++='\\'; switch (token=*ptr++) { case '\\': *ptr2++='\\'; break; case '\"': *ptr2++='\"'; break; case '\b': *ptr2++='b'; break; case '\f': *ptr2++='f'; break; case '\n': *ptr2++='n'; break; case '\r': *ptr2++='r'; break; case '\t': *ptr2++='t'; break; default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ } } } *ptr2++='\"';*ptr2++=0; return out; } /* Invote print_string_ptr (which is useful) on an item. */ static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} /* Predeclare these prototypes. */ static const char *parse_value(cJSON *item,const char *value); static char *print_value(cJSON *item,int depth,int fmt); static const char *parse_array(cJSON *item,const char *value); static char *print_array(cJSON *item,int depth,int fmt); static const char *parse_object(cJSON *item,const char *value); static char *print_object(cJSON *item,int depth,int fmt); /* Utility to jump whitespace and cr/lf */ static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} /* Parse an object - create a new root, and populate. */ cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) { const char *end=0; cJSON *c=cJSON_New_Item(); ep=0; if (!c) return 0; /* memory fail */ end=parse_value(c,skip(value)); if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} if (return_parse_end) *return_parse_end=end; return c; } /* Default options for cJSON_Parse */ cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} /* Render a cJSON item/entity/structure to text. */ char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} /* Parser core - when encountering text, process appropriately. */ static const char *parse_value(cJSON *item,const char *value) { if (!value) return 0; /* Fail on null. */ if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } if (*value=='\"') { return parse_string(item,value); } if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } if (*value=='[') { return parse_array(item,value); } if (*value=='{') { return parse_object(item,value); } ep=value;return 0; /* failure. */ } /* Render a value to text. */ static char *print_value(cJSON *item,int depth,int fmt) { char *out=0; if (!item) return 0; switch ((item->type)&255) { case cJSON_NULL: out=cJSON_strdup("null"); break; case cJSON_False: out=cJSON_strdup("false");break; case cJSON_True: out=cJSON_strdup("true"); break; case cJSON_Number: out=print_number(item);break; case cJSON_String: out=print_string(item);break; case cJSON_Array: out=print_array(item,depth,fmt);break; case cJSON_Object: out=print_object(item,depth,fmt);break; } return out; } /* Build an array from input text. */ static const char *parse_array(cJSON *item,const char *value) { cJSON *child; if (*value!='[') {ep=value;return 0;} /* not an array! */ item->type=cJSON_Array; value=skip(value+1); if (*value==']') return value+1; /* empty array. */ item->child=child=cJSON_New_Item(); if (!item->child) return 0; /* memory fail */ value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ if (!value) return 0; while (*value==',') { cJSON *new_item; if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ child->next=new_item;new_item->prev=child;child=new_item; value=skip(parse_value(child,skip(value+1))); if (!value) return 0; /* memory fail */ } if (*value==']') return value+1; /* end of array */ ep=value;return 0; /* malformed. */ } /* Render an array to text */ static char *print_array(cJSON *item,int depth,int fmt) { char **entries; char *out=0,*ptr,*ret;int len=5; cJSON *child=item->child; int numentries=0,i=0,fail=0; /* How many entries in the array? */ while (child) numentries++,child=child->next; /* Explicitly handle numentries==0 */ if (!numentries) { out=(char*)cJSON_malloc(3); if (out) strcpy(out,"[]"); return out; } /* Allocate an array to hold the values for each */ entries=(char**)cJSON_malloc(numentries*sizeof(char*)); if (!entries) return 0; memset(entries,0,numentries*sizeof(char*)); /* Retrieve all the results: */ child=item->child; while (child && !fail) { ret=print_value(child,depth+1,fmt); entries[i++]=ret; if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; child=child->next; } /* If we didn't fail, try to malloc the output string */ if (!fail) out=(char*)cJSON_malloc(len); /* If that fails, we fail. */ if (!out) fail=1; /* Handle failure. */ if (fail) { for (i=0;itype=cJSON_Object; value=skip(value+1); if (*value=='}') return value+1; /* empty array. */ item->child=child=cJSON_New_Item(); if (!item->child) return 0; value=skip(parse_string(child,skip(value))); if (!value) return 0; child->string=child->valuestring;child->valuestring=0; if (*value!=':') {ep=value;return 0;} /* fail! */ value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ if (!value) return 0; while (*value==',') { cJSON *new_item; if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ child->next=new_item;new_item->prev=child;child=new_item; value=skip(parse_string(child,skip(value+1))); if (!value) return 0; child->string=child->valuestring;child->valuestring=0; if (*value!=':') {ep=value;return 0;} /* fail! */ value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ if (!value) return 0; } if (*value=='}') return value+1; /* end of array */ ep=value;return 0; /* malformed. */ } /* Render an object to text. */ static char *print_object(cJSON *item,int depth,int fmt) { char **entries=0,**names=0; char *out=0,*ptr,*ret,*str;int len=7,i=0,j; cJSON *child=item->child; int numentries=0,fail=0; /* Count the number of entries. */ while (child) numentries++,child=child->next; /* Explicitly handle empty object case */ if (!numentries) { out=(char*)cJSON_malloc(fmt?depth+4:3); if (!out) return 0; ptr=out;*ptr++='{'; if (fmt) {*ptr++='\n';for (i=0;ichild;depth++;if (fmt) len+=depth; while (child) { names[i]=str=print_string_ptr(child->string); entries[i++]=ret=print_value(child,depth,fmt); if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; child=child->next; } /* Try to allocate the output string */ if (!fail) out=(char*)cJSON_malloc(len); if (!out) fail=1; /* Handle failure */ if (fail) { for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} /* Utility for array list handling. */ static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} /* Utility for handling references. */ static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} /* Add item to array/object. */ void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} /* Replace array/object items with new ones. */ void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} /* Create basic types: */ cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} /* Create Arrays: */ cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} /* Duplication */ cJSON *cJSON_Duplicate(cJSON *item,int recurse) { cJSON *newitem,*cptr,*nptr=0,*newchild; /* Bail on bad ptr */ if (!item) return 0; /* Create new item */ newitem=cJSON_New_Item(); if (!newitem) return 0; /* Copy over all vars */ newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} /* If non-recursive, then we're done! */ if (!recurse) return newitem; /* Walk the ->next chain for the child. */ cptr=item->child; while (cptr) { newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ if (!newchild) {cJSON_Delete(newitem);return 0;} if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ cptr=cptr->next; } return newitem; } void cJSON_Minify(char *json) { char *into=json; while (*json) { if (*json==' ') json++; else if (*json=='\t') json++; // Whitespace characters. else if (*json=='\r') json++; else if (*json=='\n') json++; else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; // double-slash comments, to end of line. else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} // multiline comments. else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} // string literals, which are \" sensitive. else *into++=*json++; // All other characters. } *into=0; // and null-terminate. } openalpr_2.2.4.orig/src/openalpr/cjson.h000066400000000000000000000152161266464252400202740ustar00rootroot00000000000000/* Copyright (c) 2009 Dave Gamble Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef cJSON__h #define cJSON__h #ifdef __cplusplus extern "C" { #endif /* cJSON Types: */ #define cJSON_False 0 #define cJSON_True 1 #define cJSON_NULL 2 #define cJSON_Number 3 #define cJSON_String 4 #define cJSON_Array 5 #define cJSON_Object 6 #define cJSON_IsReference 256 /* The cJSON structure: */ typedef struct cJSON { struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ int type; /* The type of the item, as above. */ char *valuestring; /* The item's string, if type==cJSON_String */ int valueint; /* The item's number, if type==cJSON_Number */ double valuedouble; /* The item's number, if type==cJSON_Number */ char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ } cJSON; typedef struct cJSON_Hooks { void *(*malloc_fn)(size_t sz); void (*free_fn)(void *ptr); } cJSON_Hooks; /* Supply malloc, realloc and free functions to cJSON */ extern void cJSON_InitHooks(cJSON_Hooks* hooks); /* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ extern cJSON *cJSON_Parse(const char *value); /* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ extern char *cJSON_Print(cJSON *item); /* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ extern char *cJSON_PrintUnformatted(cJSON *item); /* Delete a cJSON entity and all subentities. */ extern void cJSON_Delete(cJSON *c); /* Returns the number of items in an array (or object). */ extern int cJSON_GetArraySize(cJSON *array); /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); /* Get item "string" from object. Case insensitive. */ extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ extern const char *cJSON_GetErrorPtr(void); /* These calls create a cJSON item of the appropriate type. */ extern cJSON *cJSON_CreateNull(void); extern cJSON *cJSON_CreateTrue(void); extern cJSON *cJSON_CreateFalse(void); extern cJSON *cJSON_CreateBool(int b); extern cJSON *cJSON_CreateNumber(double num); extern cJSON *cJSON_CreateString(const char *string); extern cJSON *cJSON_CreateArray(void); extern cJSON *cJSON_CreateObject(void); /* These utilities create an Array of count items. */ extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); extern cJSON *cJSON_CreateStringArray(const char **strings,int count); /* Append item to the specified array/object. */ extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); /* Remove/Detatch items from Arrays/Objects. */ extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); extern void cJSON_DeleteItemFromArray(cJSON *array,int which); extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); /* Update array items. */ extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); /* Duplicate a cJSON item */ extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will need to be released. With recurse!=0, it will duplicate any children connected to the item. The item->next and ->prev pointers are always zero on return from Duplicate. */ /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); extern void cJSON_Minify(char *json); /* Macros for creating things quickly. */ #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) #define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) #define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) #define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) /* When assigning an integer value, it needs to be propagated to valuedouble too. */ #define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) #ifdef __cplusplus } #endif #endif openalpr_2.2.4.orig/src/openalpr/colorfilter.cpp000066400000000000000000000302441266464252400220350ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "colorfilter.h" using namespace cv; using namespace std; namespace alpr { ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config) { timespec startTime; getTimeMonotonic(&startTime); this->config = config; this->debug = config->debugColorFiler; this->grayscale = imageIsGrayscale(image); if (this->debug) cout << "ColorFilter: isGrayscale = " << grayscale << endl; this->hsv = Mat(image.size(), image.type()); cvtColor( image, this->hsv, CV_BGR2HSV ); preprocessImage(); this->charMask = characterMask; this->colorMask = Mat(image.size(), CV_8U); findCharColors(); if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << " -- ColorFilter Time: " << diffclock(startTime, endTime) << "ms." << endl; } } ColorFilter::~ColorFilter() { } bool ColorFilter::imageIsGrayscale(Mat image) { // Check whether the original image is grayscale. If it is, we shouldn't attempt any color filter for (int row = 0; row < image.rows; row++) { for (int col = 0; col < image.cols; col++) { int r = (int) image.at(row, col)[0]; int g = (int) image.at(row, col)[1]; int b = (int) image.at(row, col)[2]; if (r == g == b) { // So far so good } else { // Image is color. return false; } } } return true; } void ColorFilter::preprocessImage() { // Equalize the brightness on the HSV channel "V" vector channels; split(this->hsv,channels); Mat img_equalized = equalizeBrightness(channels[2]); merge(channels,this->hsv); } // Gets the hue/sat/val for areas that we believe are license plate characters // Then uses that to filter the whole image and provide a mask. void ColorFilter::findCharColors() { int MINIMUM_SATURATION = 45; if (this->debug) cout << "ColorFilter::findCharColors" << endl; //charMask.copyTo(this->colorMask); this->colorMask = Mat::zeros(charMask.size(), CV_8U); bitwise_not(this->colorMask, this->colorMask); Mat erodedCharMask(charMask.size(), CV_8U); Mat element = getStructuringElement( 1, Size( 2 + 1, 2+1 ), Point( 1, 1 ) ); erode(charMask, erodedCharMask, element); vector > contours; vector hierarchy; findContours(erodedCharMask, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE); vector hMeans, sMeans, vMeans; vector hStdDevs, sStdDevs, vStdDevs; for (unsigned int i = 0; i < contours.size(); i++) { if (hierarchy[i][3] != -1) continue; Mat singleCharMask = Mat::zeros(hsv.size(), CV_8U); drawContours(singleCharMask, contours, i, // draw this contour cv::Scalar(255,255,255), // in CV_FILLED, 8, hierarchy ); // get rid of the outline by drawing a 1 pixel width black line drawContours(singleCharMask, contours, i, // draw this contour cv::Scalar(0,0,0), // in 1, 8, hierarchy ); //drawAndWait(&singleCharMask); Scalar mean; Scalar stddev; meanStdDev(hsv, mean, stddev, singleCharMask); if (this->debug) { cout << "ColorFilter " << setw(3) << i << ". Mean: h: " << setw(7) << mean[0] << " s: " << setw(7) <getMajorityOpinion(hMeans, .65, 30); int bestSatIndex = this->getMajorityOpinion(sMeans, .65, 35); int bestValIndex = this->getMajorityOpinion(vMeans, .65, 30); if (sMeans[bestSatIndex] < MINIMUM_SATURATION) return; bool doHueFilter = false, doSatFilter = false, doValFilter = false; float hueMin, hueMax; float satMin, satMax; float valMin, valMax; if (this->debug) cout << "ColorFilter Winning indices:" << endl; if (bestHueIndex != -1) { doHueFilter = true; hueMin = hMeans[bestHueIndex] - (2 * hStdDevs[bestHueIndex]); hueMax = hMeans[bestHueIndex] + (2 * hStdDevs[bestHueIndex]); if (abs(hueMin - hueMax) < 20) { hueMin = hMeans[bestHueIndex] - 20; hueMax = hMeans[bestHueIndex] + 20; } if (hueMin < 0) hueMin = 0; if (hueMax > 180) hueMax = 180; if (this->debug) cout << "ColorFilter Hue: " << bestHueIndex << " : " << setw(7) << hMeans[bestHueIndex] << " -- " << hueMin << "-" << hueMax << endl; } if (bestSatIndex != -1) { doSatFilter = true; satMin = sMeans[bestSatIndex] - (2 * sStdDevs[bestSatIndex]); satMax = sMeans[bestSatIndex] + (2 * sStdDevs[bestSatIndex]); if (abs(satMin - satMax) < 20) { satMin = sMeans[bestSatIndex] - 20; satMax = sMeans[bestSatIndex] + 20; } if (satMin < 0) satMin = 0; if (satMax > 255) satMax = 255; if (this->debug) cout << "ColorFilter Sat: " << bestSatIndex << " : " << setw(7) << sMeans[bestSatIndex] << " -- " << satMin << "-" << satMax << endl; } if (bestValIndex != -1) { doValFilter = true; valMin = vMeans[bestValIndex] - (1.5 * vStdDevs[bestValIndex]); valMax = vMeans[bestValIndex] + (1.5 * vStdDevs[bestValIndex]); if (abs(valMin - valMax) < 20) { valMin = vMeans[bestValIndex] - 20; valMax = vMeans[bestValIndex] + 20; } if (valMin < 0) valMin = 0; if (valMax > 255) valMax = 255; if (this->debug) cout << "ColorFilter Val: " << bestValIndex << " : " << setw(7) << vMeans[bestValIndex] << " -- " << valMin << "-" << valMax << endl; } Mat imgDebugHueOnly = Mat::zeros(hsv.size(), hsv.type()); Mat imgDebug = Mat::zeros(hsv.size(), hsv.type()); Mat imgDistanceFromCenter = Mat::zeros(hsv.size(), CV_8U); Mat debugMask = Mat::zeros(hsv.size(), CV_8U); bitwise_not(debugMask, debugMask); for (int row = 0; row < charMask.rows; row++) { for (int col = 0; col < charMask.cols; col++) { int h = (int) hsv.at(row, col)[0]; int s = (int) hsv.at(row, col)[1]; int v = (int) hsv.at(row, col)[2]; bool hPasses = true; bool sPasses = true; bool vPasses = true; int vDistance = abs(v - vMeans[bestValIndex]); imgDebugHueOnly.at(row, col)[0] = h; imgDebugHueOnly.at(row, col)[1] = 255; imgDebugHueOnly.at(row, col)[2] = 255; imgDebug.at(row, col)[0] = 255; imgDebug.at(row, col)[1] = 255; imgDebug.at(row, col)[2] = 255; if (doHueFilter && (h < hueMin || h > hueMax)) { hPasses = false; imgDebug.at(row, col)[0] = 0; debugMask.at(row, col) = 0; } if (doSatFilter && (s < satMin || s > satMax)) { sPasses = false; imgDebug.at(row, col)[1] = 0; } if (doValFilter && (v < valMin || v > valMax)) { vPasses = false; imgDebug.at(row, col)[2] = 0; } //if (pixelPasses) // colorMask.at(row, col) = 255; //else //imgDebug.at(row, col)[0] = hPasses & 255; //imgDebug.at(row, col)[1] = sPasses & 255; //imgDebug.at(row, col)[2] = vPasses & 255; if ((hPasses) || (hPasses && sPasses))//(hPasses && vPasses) || (sPasses && vPasses) || this->colorMask.at(row, col) = 255; else this->colorMask.at(row, col) = 0; if ((hPasses && sPasses) || (hPasses && vPasses) || (sPasses && vPasses)) { vDistance = pow(vDistance, 0.9); } else { vDistance = pow(vDistance, 1.1); } if (vDistance > 255) vDistance = 255; imgDistanceFromCenter.at(row, col) = vDistance; } } vector debugImagesSet; if (this->debug) { debugImagesSet.push_back(addLabel(charMask, "Charecter mask")); //debugImagesSet1.push_back(erodedCharMask); Mat maskCopy(colorMask.size(), colorMask.type()); colorMask.copyTo(maskCopy); debugImagesSet.push_back(addLabel(maskCopy, "color Mask Before")); } Mat bigElement = getStructuringElement( 1, Size( 3 + 1, 3+1 ), Point( 1, 1 ) ); Mat smallElement = getStructuringElement( 1, Size( 1 + 1, 1+1 ), Point( 1, 1 ) ); morphologyEx(this->colorMask, this->colorMask, MORPH_CLOSE, bigElement); //dilate(this->colorMask, this->colorMask, bigElement); Mat combined(charMask.size(), charMask.type()); bitwise_and(charMask, colorMask, combined); if (this->debug) { debugImagesSet.push_back(addLabel(colorMask, "Color Mask After")); debugImagesSet.push_back(addLabel(combined, "Combined")); //displayImage(config, "COLOR filter Mask", colorMask); debugImagesSet.push_back(addLabel(imgDebug, "Color filter Debug")); cvtColor(imgDebugHueOnly, imgDebugHueOnly, CV_HSV2BGR); debugImagesSet.push_back(addLabel(imgDebugHueOnly, "Color Filter Hue")); equalizeHist(imgDistanceFromCenter, imgDistanceFromCenter); debugImagesSet.push_back(addLabel(imgDistanceFromCenter, "COLOR filter Distance")); debugImagesSet.push_back(addLabel(debugMask, "COLOR Hues off")); Mat dashboard = drawImageDashboard(debugImagesSet, imgDebugHueOnly.type(), 3); displayImage(config, "Color Filter Images", dashboard); } } // Goes through an array of values, picks the winner based on the highest percentage of other values that are within the maxValDifference // Return -1 if it fails. int ColorFilter::getMajorityOpinion(vector values, float minPercentAgreement, float maxValDifference) { float bestPercentAgreement = 0; float lowestOverallDiff = 1000000000; int bestPercentAgreementIndex = -1; for (unsigned int i = 0; i < values.size(); i++) { int valuesInRange = 0; float overallDiff = 0; for (unsigned int j = 0; j < values.size(); j++) { float diff = abs(values[i] - values[j]); if (diff < maxValDifference) valuesInRange++; overallDiff += diff; } float percentAgreement = ((float) valuesInRange) / ((float) values.size()); if (overallDiff < lowestOverallDiff && percentAgreement >= bestPercentAgreement && percentAgreement >= minPercentAgreement) { bestPercentAgreement = percentAgreement; bestPercentAgreementIndex = i; lowestOverallDiff = overallDiff; } } return bestPercentAgreementIndex; } }openalpr_2.2.4.orig/src/openalpr/colorfilter.h000066400000000000000000000027551266464252400215100ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_COLORFILTER_H #define OPENALPR_COLORFILTER_H #include #include "opencv2/imgproc/imgproc.hpp" #include "constants.h" #include "utility.h" #include "config.h" namespace alpr { class ColorFilter { public: ColorFilter(cv::Mat image, cv::Mat characterMask, Config* config); virtual ~ColorFilter(); cv::Mat colorMask; private: Config* config; bool debug; cv::Mat hsv; cv::Mat charMask; bool grayscale; void preprocessImage(); void findCharColors(); bool imageIsGrayscale(cv::Mat image); int getMajorityOpinion(std::vector values, float minPercentAgreement, float maxValDifference); }; } #endif // OPENALPR_COLORFILTER_H openalpr_2.2.4.orig/src/openalpr/config.cpp000066400000000000000000000365771266464252400207750ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "config.h" #include "support/filesystem.h" #include "support/platform.h" #include "simpleini/simpleini.h" #include "utility.h" using namespace std; namespace alpr { int getInt(CSimpleIniA* ini, std::string section, std::string key, int defaultValue); float getFloat(CSimpleIniA* ini, std::string section, std::string key, float defaultValue); std::string getString(CSimpleIniA* ini, std::string section, std::string key, std::string defaultValue); bool getBoolean(CSimpleIniA* ini, std::string section, std::string key, bool defaultValue); std::vector getAllFloats(CSimpleIniA* ini, string section, string key); Config::Config(const std::string country, const std::string config_file, const std::string runtime_dir) { string debug_message = ""; this->loaded = false; char* envConfigFile; envConfigFile = getenv (ENV_VARIABLE_CONFIG_FILE); if (config_file.compare("") != 0) { // User has supplied a config file. Use that. config_file_path = config_file; debug_message = "Config file location provided via API"; } else if (envConfigFile != NULL) { // Environment variable is non-empty. Use that. config_file_path = envConfigFile; debug_message = "Config file location provided via environment variable: " + string(ENV_VARIABLE_CONFIG_FILE); } else if (DirectoryExists(getExeDir().c_str()) && fileExists((getExeDir() + CONFIG_FILE).c_str())) { config_file_path = getExeDir() + CONFIG_FILE; debug_message = "Config file location provided via exe location"; } else { // Use the default config_file_path = DEFAULT_CONFIG_FILE; debug_message = "Config file location provided via default location"; } if (fileExists(config_file_path.c_str()) == false) { std::cerr << "--(!) Config file '" << config_file_path << "' does not exist!" << endl; std::cerr << "--(!) You can specify the configuration file location via the command line " << endl; std::cerr << "--(!) or by setting the environment variable '" << ENV_VARIABLE_CONFIG_FILE << "'" << endl; return; } else if (DirectoryExists(config_file_path.c_str())) { std::cerr << "--(!) Config file '" << config_file_path << "' was specified as a directory, rather than a file!" << endl; std::cerr << "--(!) Please specify the full path to the 'openalpr.conf file'" << endl; std::cerr << "--(!) e.g., /etc/openalpr/openalpr.conf" << endl; return; } loadCommonValues(config_file_path); if (runtime_dir.compare("") != 0) { // User provided a runtime directory directly into the library. Use this. this->runtimeBaseDir = runtime_dir; } if ((DirectoryExists(this->runtimeBaseDir.c_str()) == false) && (DirectoryExists((getExeDir() + RUNTIME_DIR).c_str()))) { // Runtime dir in the config is invalid and there is a runtime dir in the same dir as the exe. this->runtimeBaseDir = getExeDir() + RUNTIME_DIR; } if (DirectoryExists(this->runtimeBaseDir.c_str()) == false) { std::cerr << "--(!) Runtime directory '" << this->runtimeBaseDir << "' does not exist!" << endl; std::cerr << "--(!) Please update the OpenALPR config file: '" << config_file_path << "'" << endl; std::cerr << "--(!) to point to the correct location of your runtime_dir" << endl; return; } bool countries_loaded = load_countries(country); if (this->debugGeneral) { std::cout << debug_message << endl; } this->loaded = countries_loaded; } Config::~Config() { } bool Config::load_countries(const std::string countries) { this->loaded_countries = this->parse_country_string(countries); if (this->loaded_countries.size() == 0) { std::cerr << "--(!) Country not specified." << endl; return false; } for (unsigned int i = 0; i < loaded_countries.size(); i++) { bool country_loaded = setCountry(this->loaded_countries[i]); if (!country_loaded) { return false; } } setCountry(this->loaded_countries[0]); return true; } void Config::loadCommonValues(string configFile) { CSimpleIniA iniObj; iniObj.LoadFile(configFile.c_str()); CSimpleIniA* ini = &iniObj; runtimeBaseDir = getString(ini, "", "runtime_dir", "/usr/share/openalpr/runtime_data"); std::string detectorString = getString(ini, "", "detector", "lbpcpu"); std::transform(detectorString.begin(), detectorString.end(), detectorString.begin(), ::tolower); if (detectorString.compare("lbpcpu") == 0) detector = DETECTOR_LBP_CPU; else if (detectorString.compare("lbpgpu") == 0) detector = DETECTOR_LBP_GPU; else if (detectorString.compare("lbpopencl") == 0) detector = DETECTOR_LBP_OPENCL; else if (detectorString.compare("morphcpu") == 0) detector = DETECTOR_MORPH_CPU; else { std::cerr << "Invalid detector specified: " << detectorString << ". Using default" << std::endl; detector = DETECTOR_LBP_CPU; } detection_iteration_increase = getFloat(ini, "", "detection_iteration_increase", 1.1); detectionStrictness = getInt(ini, "", "detection_strictness", 3); maxPlateWidthPercent = getFloat(ini, "", "max_plate_width_percent", 100); maxPlateHeightPercent = getFloat(ini, "", "max_plate_height_percent", 100); maxDetectionInputWidth = getInt(ini, "", "max_detection_input_width", 1280); maxDetectionInputHeight = getInt(ini, "", "max_detection_input_height", 768); mustMatchPattern = getBoolean(ini, "", "must_match_pattern", false); skipDetection = getBoolean(ini, "", "skip_detection", false); prewarp = getString(ini, "", "prewarp", ""); maxPlateAngleDegrees = getInt(ini, "", "max_plate_angle_degrees", 15); ocrImagePercent = getFloat(ini, "", "ocr_img_size_percent", 100); stateIdImagePercent = getFloat(ini, "", "state_id_img_size_percent", 100); ocrMinFontSize = getInt(ini, "", "ocr_min_font_point", 100); postProcessMinConfidence = getFloat(ini, "", "postprocess_min_confidence", 100); postProcessConfidenceSkipLevel = getFloat(ini, "", "postprocess_confidence_skip_level", 100); debugGeneral = getBoolean(ini, "", "debug_general", false); debugTiming = getBoolean(ini, "", "debug_timing", false); debugPrewarp = getBoolean(ini, "", "debug_prewarp", false); debugDetector = getBoolean(ini, "", "debug_detector", false); debugStateId = getBoolean(ini, "", "debug_state_id", false); debugPlateLines = getBoolean(ini, "", "debug_plate_lines", false); debugPlateCorners = getBoolean(ini, "", "debug_plate_corners", false); debugCharSegmenter = getBoolean(ini, "", "debug_char_segment", false); debugCharAnalysis = getBoolean(ini, "", "debug_char_analysis", false); debugColorFiler = getBoolean(ini, "", "debug_color_filter", false); debugOcr = getBoolean(ini, "", "debug_ocr", false); debugPostProcess = getBoolean(ini, "", "debug_postprocess", false); debugShowImages = getBoolean(ini, "", "debug_show_images", false); debugPauseOnFrame = getBoolean(ini, "", "debug_pause_on_frame", false); } void Config::loadCountryValues(string configFile, string country) { CSimpleIniA iniObj; iniObj.SetMultiKey(true); iniObj.LoadFile(configFile.c_str()); CSimpleIniA* ini = &iniObj; minPlateSizeWidthPx = getInt(ini, "", "min_plate_size_width_px", 100); minPlateSizeHeightPx = getInt(ini, "", "min_plate_size_height_px", 100); multiline = getBoolean(ini, "", "multiline", false); string invert_val = getString(ini, "", "invert", "auto"); if (invert_val == "always") { auto_invert = false; always_invert = true; } else if (invert_val == "never") { auto_invert = false; always_invert = false; } else { auto_invert = true; always_invert = false; } plateWidthMM = getFloat(ini, "", "plate_width_mm", 100); plateHeightMM = getFloat(ini, "", "plate_height_mm", 100); charHeightMM = getAllFloats(ini, "", "char_height_mm"); charWidthMM = getAllFloats(ini, "", "char_width_mm"); // Compute the average char height/widths avgCharHeightMM = 0; avgCharWidthMM = 0; for (unsigned int i = 0; i < charHeightMM.size(); i++) { avgCharHeightMM += charHeightMM[i]; avgCharWidthMM += charWidthMM[i]; } avgCharHeightMM /= charHeightMM.size(); avgCharWidthMM /= charHeightMM.size(); charWhitespaceTopMM = getFloat(ini, "", "char_whitespace_top_mm", 100); charWhitespaceBotMM = getFloat(ini, "", "char_whitespace_bot_mm", 100); charWhitespaceBetweenLinesMM = getFloat(ini, "", "char_whitespace_between_lines_mm", 5); templateWidthPx = getInt(ini, "", "template_max_width_px", 100); templateHeightPx = getInt(ini, "", "template_max_height_px", 100); charAnalysisMinPercent = getFloat(ini, "", "char_analysis_min_pct", 0); charAnalysisHeightRange = getFloat(ini, "", "char_analysis_height_range", 0); charAnalysisHeightStepSize = getFloat(ini, "", "char_analysis_height_step_size", 0); charAnalysisNumSteps = getInt(ini, "", "char_analysis_height_num_steps", 0); segmentationMinSpeckleHeightPercent = getFloat(ini, "", "segmentation_min_speckle_height_percent", 0); segmentationMinBoxWidthPx = getInt(ini, "", "segmentation_min_box_width_px", 0); segmentationMinCharHeightPercent = getFloat(ini, "", "segmentation_min_charheight_percent", 0); segmentationMaxCharWidthvsAverage = getFloat(ini, "", "segmentation_max_segment_width_percent_vs_average", 0); plateLinesSensitivityVertical = getFloat(ini, "", "plateline_sensitivity_vertical", 0); plateLinesSensitivityHorizontal = getFloat(ini, "", "plateline_sensitivity_horizontal", 0); detectorFile = getString(ini, "", "detector_file", ""); ocrLanguage = getString(ini, "", "ocr_language", "none"); postProcessRegexLetters = getString(ini, "", "postprocess_regex_letters", "\\pL"); postProcessRegexNumbers = getString(ini, "", "postprocess_regex_numbers", "\\pN"); ocrImageWidthPx = round(((float) templateWidthPx) * ocrImagePercent); ocrImageHeightPx = round(((float)templateHeightPx) * ocrImagePercent); stateIdImageWidthPx = round(((float)templateWidthPx) * stateIdImagePercent); stateIdimageHeightPx = round(((float)templateHeightPx) * stateIdImagePercent); postProcessMinCharacters = getInt(ini, "", "postprocess_min_characters", 4); postProcessMaxCharacters = getInt(ini, "", "postprocess_max_characters", 8); } void Config::setDebug(bool value) { debugGeneral = value; debugTiming = value; debugPrewarp = value; debugDetector = value; debugStateId = value; debugPlateLines = value; debugPlateCorners = value; debugCharSegmenter = value; debugCharAnalysis = value; debugColorFiler = value; debugOcr = value; debugPostProcess = value; debugPauseOnFrame = value; debugShowImages = value; } string Config::getCascadeRuntimeDir() { return this->runtimeBaseDir + CASCADE_DIR; } string Config::getKeypointsRuntimeDir() { return this->runtimeBaseDir + KEYPOINTS_DIR; } string Config::getPostProcessRuntimeDir() { return this->runtimeBaseDir + POSTPROCESS_DIR; } string Config::getTessdataPrefix() { return this->runtimeBaseDir + "/ocr/"; } std::vector Config::parse_country_string(std::string countries) { std::istringstream ss(countries); std::string token; std::vector parsed_countries; while(std::getline(ss, token, ',')) { std::string trimmed_token = trim(token); if (trimmed_token.size() > 0) parsed_countries.push_back(trimmed_token); } return parsed_countries; } bool Config::country_is_loaded(std::string country) { for (uint32_t i = 0; i < loaded_countries.size(); i++) { if (loaded_countries[i] == country) return true; } return false; } bool Config::setCountry(std::string country) { this->country = country; std::string country_config_file = this->runtimeBaseDir + "/config/" + country + ".conf"; if (fileExists(country_config_file.c_str()) == false) { std::cerr << "--(!) Country config file '" << country_config_file << "' does not exist. Missing config for the country: '" << country<< "'!" << endl; return false; } loadCountryValues(country_config_file, country); if (fileExists((this->runtimeBaseDir + "/ocr/tessdata/" + this->ocrLanguage + ".traineddata").c_str()) == false) { std::cerr << "--(!) Runtime directory '" << this->runtimeBaseDir << "' is invalid. Missing OCR data for the country: '" << country<< "'!" << endl; return false; } if (!country_is_loaded(country)) this->loaded_countries.push_back(country); return true; } float getFloat(CSimpleIniA* ini, string section, string key, float defaultValue) { const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/); if (pszValue == NULL) { return defaultValue; } float val = atof(pszValue); return val; } std::vector getAllFloats(CSimpleIniA* ini, string section, string key) { CSimpleIniA::TNamesDepend values; ini->GetAllValues(section.c_str(), key.c_str(), values); // sort the values into the original load order values.sort(CSimpleIniA::Entry::LoadOrder()); std::vector response; // output all of the items CSimpleIniA::TNamesDepend::const_iterator i; for (i = values.begin(); i != values.end(); ++i) { response.push_back(atof(i->pItem)); } return response; } int getInt(CSimpleIniA* ini, string section, string key, int defaultValue) { const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/); if (pszValue == NULL) { return defaultValue; } int val = atoi(pszValue); return val; } bool getBoolean(CSimpleIniA* ini, string section, string key, bool defaultValue) { const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/); if (pszValue == NULL) { return defaultValue; } int val = atoi(pszValue); return val != 0; } string getString(CSimpleIniA* ini, string section, string key, string defaultValue) { const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/); if (pszValue == NULL) { return defaultValue; } string val = string(pszValue); return val; } } openalpr_2.2.4.orig/src/openalpr/config.h000066400000000000000000000103161266464252400204210ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_CONFIG_H #define OPENALPR_CONFIG_H #include "constants.h" #include #include #include #include /* getenv */ #include namespace alpr { class Config { public: Config(const std::string country, const std::string config_file = "", const std::string runtime_dir = ""); virtual ~Config(); bool load_countries(const std::string countries); bool loaded; std::string config_file_path; std::string country; int detector; float detection_iteration_increase; int detectionStrictness; float maxPlateWidthPercent; float maxPlateHeightPercent; int maxDetectionInputWidth; int maxDetectionInputHeight; bool skipDetection; bool auto_invert; bool always_invert; std::string prewarp; int maxPlateAngleDegrees; float minPlateSizeWidthPx; float minPlateSizeHeightPx; bool multiline; float plateWidthMM; float plateHeightMM; std::vector charHeightMM; std::vector charWidthMM; float avgCharHeightMM; float avgCharWidthMM; float charWhitespaceTopMM; float charWhitespaceBotMM; float charWhitespaceBetweenLinesMM; int templateWidthPx; int templateHeightPx; int ocrImageWidthPx; int ocrImageHeightPx; int stateIdImageWidthPx; int stateIdimageHeightPx; float charAnalysisMinPercent; float charAnalysisHeightRange; float charAnalysisHeightStepSize; int charAnalysisNumSteps; float plateLinesSensitivityVertical; float plateLinesSensitivityHorizontal; float segmentationMinSpeckleHeightPercent; int segmentationMinBoxWidthPx; float segmentationMinCharHeightPercent; float segmentationMaxCharWidthvsAverage; std::string detectorFile; std::string ocrLanguage; int ocrMinFontSize; bool mustMatchPattern; float postProcessMinConfidence; float postProcessConfidenceSkipLevel; unsigned int postProcessMinCharacters; unsigned int postProcessMaxCharacters; std::string postProcessRegexLetters; std::string postProcessRegexNumbers; bool debugGeneral; bool debugTiming; bool debugPrewarp; bool debugDetector; bool debugStateId; bool debugPlateLines; bool debugPlateCorners; bool debugCharSegmenter; bool debugCharAnalysis; bool debugColorFiler; bool debugOcr; bool debugPostProcess; bool debugShowImages; bool debugPauseOnFrame; void setDebug(bool value); std::string getKeypointsRuntimeDir(); std::string getCascadeRuntimeDir(); std::string getPostProcessRuntimeDir(); std::string getTessdataPrefix(); std::string runtimeBaseDir; std::vector loaded_countries; bool setCountry(std::string country); private: float ocrImagePercent; float stateIdImagePercent; std::vector parse_country_string(std::string countries); bool country_is_loaded(std::string country); void loadCommonValues(std::string configFile); void loadCountryValues(std::string configFile, std::string country); }; enum DETECTOR_TYPE { DETECTOR_LBP_CPU=0, DETECTOR_LBP_GPU=1, DETECTOR_MORPH_CPU=2, DETECTOR_LBP_OPENCL=3 }; } #endif // OPENALPR_CONFIG_H openalpr_2.2.4.orig/src/openalpr/constants.h000066400000000000000000000022561266464252400211740ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_CONSTANTS_H #define OPENALPR_CONSTANTS_H #define RUNTIME_DIR "/runtime_data" #define CONFIG_FILE "/openalpr.conf" #define KEYPOINTS_DIR "/keypoints" #define CASCADE_DIR "/region/" #define POSTPROCESS_DIR "/postprocess" #ifndef DEFAULT_CONFIG_FILE #define DEFAULT_CONFIG_FILE "/etc/openalpr/openalpr.conf" #endif #define ENV_VARIABLE_CONFIG_FILE "OPENALPR_CONFIG_FILE" #endif // OPENALPR_CONSTANTS_Hopenalpr_2.2.4.orig/src/openalpr/detection/000077500000000000000000000000001266464252400207605ustar00rootroot00000000000000openalpr_2.2.4.orig/src/openalpr/detection/detector.cpp000066400000000000000000000104161266464252400232770ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "detector.h" using namespace cv; using namespace std; namespace alpr { Detector::Detector(Config* config) { this->config = config; } Detector::~Detector() { } bool Detector::isLoaded() { return this->loaded; } vector Detector::detect(cv::Mat frame) { std::vector regionsOfInterest; regionsOfInterest.push_back(Rect(0, 0, frame.cols, frame.rows)); return this->detect(frame, regionsOfInterest); } vector Detector::detect(Mat frame, std::vector regionsOfInterest) { // Must be implemented by subclass std::vector rois; return rois; } std::string Detector::get_detector_file() { if (config->detectorFile.length() == 0) return config->getCascadeRuntimeDir() + config->country + ".xml"; return config->getCascadeRuntimeDir() + config->detectorFile; } float Detector::computeScaleFactor(int width, int height) { float scale_factor = 1.0; if (width > config->maxDetectionInputWidth) { // The frame is too wide scale_factor = ((float) config->maxDetectionInputWidth) / ((float) width); if (config->debugDetector) std::cout << "Input detection image is too wide. Resizing with scale: " << scale_factor << endl; } else if (height > config->maxDetectionInputHeight) { // The frame is too tall scale_factor = ((float) config->maxDetectionInputHeight) / ((float) height); if (config->debugDetector) std::cout << "Input detection image is too tall. Resizing with scale: " << scale_factor << endl; } return scale_factor; } bool rectHasLargerArea(cv::Rect a, cv::Rect b) { return a.area() < b.area(); }; vector Detector::aggregateRegions(vector regions) { // Combines overlapping regions into a parent->child order. // The largest regions will be parents, and they will have children if they are within them. // This way, when processing regions later, we can process the parents first, and only delve into the children // If there was no plate match. Otherwise, we would process everything and that would be wasteful. vector orderedRegions; vector topLevelRegions; // Sort the list of rect regions smallest to largest std::sort(regions.begin(), regions.end(), rectHasLargerArea); // Create new PlateRegions and attach the rectangles to each for (unsigned int i = 0; i < regions.size(); i++) { PlateRegion newRegion; newRegion.rect = regions[i]; orderedRegions.push_back(newRegion); } for (unsigned int i = 0; i < orderedRegions.size(); i++) { bool foundParent = false; for (unsigned int k = i + 1; k < orderedRegions.size(); k++) { Point center( orderedRegions[i].rect.x + (orderedRegions[i].rect.width / 2), orderedRegions[i].rect.y + (orderedRegions[i].rect.height / 2)); // Check if the center of the smaller rectangle is inside the bigger rectangle. // If so, add it to the children and continue on. if (orderedRegions[k].rect.contains(center)) { orderedRegions[k].children.push_back(orderedRegions[i]); foundParent = true; break; } } if (foundParent == false) { // We didn't find any parents for this rectangle. Add it to the top level regions topLevelRegions.push_back(orderedRegions[i]); } } return topLevelRegions; } }openalpr_2.2.4.orig/src/openalpr/detection/detector.h000066400000000000000000000031251266464252400227430ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_REGIONDETECTOR_H #define OPENALPR_REGIONDETECTOR_H #include #include #include "utility.h" #include "support/timing.h" #include "constants.h" namespace alpr { struct PlateRegion { cv::Rect rect; std::vector children; }; class Detector { public: Detector(Config* config); virtual ~Detector(); bool isLoaded(); std::vector detect(cv::Mat frame); virtual std::vector detect(cv::Mat frame, std::vector regionsOfInterest); protected: Config* config; bool loaded; std::string get_detector_file(); float computeScaleFactor(int width, int height); std::vector aggregateRegions(std::vector regions); }; } #endif // OPENALPR_REGIONDETECTOR_H openalpr_2.2.4.orig/src/openalpr/detection/detectorcpu.cpp000066400000000000000000000076501266464252400240150ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "detectorcpu.h" using namespace cv; using namespace std; namespace alpr { DetectorCPU::DetectorCPU(Config* config) : Detector(config) { if( this->plate_cascade.load( get_detector_file() ) ) { this->loaded = true; } else { this->loaded = false; printf("--(!)Error loading CPU classifier %s\n", get_detector_file().c_str()); } } DetectorCPU::~DetectorCPU() { } vector DetectorCPU::detect(Mat frame, std::vector regionsOfInterest) { Mat frame_gray; if (frame.channels() > 2) { cvtColor( frame, frame_gray, CV_BGR2GRAY ); } else { frame.copyTo(frame_gray); } vector detectedRegions; for (int i = 0; i < regionsOfInterest.size(); i++) { // Sanity check. If roi width or height is less than minimum possible plate size, // then skip it if ((regionsOfInterest[i].width < config->minPlateSizeWidthPx) || (regionsOfInterest[i].height < config->minPlateSizeHeightPx)) continue; Mat cropped = frame_gray(regionsOfInterest[i]); vector subRegions = doCascade(cropped, regionsOfInterest[i].x, regionsOfInterest[i].y); for (int j = 0; j < subRegions.size(); j++) detectedRegions.push_back(subRegions[j]); } return detectedRegions; } vector DetectorCPU::doCascade(Mat frame, int offset_x, int offset_y) { int w = frame.size().width; int h = frame.size().height; float scale_factor = computeScaleFactor(w, h); vector plates; equalizeHist( frame, frame ); if (scale_factor != 1.0) resize(frame, frame, Size(w * scale_factor, h * scale_factor)); //-- Detect plates timespec startTime; getTimeMonotonic(&startTime); float maxWidth = ((float) w) * (config->maxPlateWidthPercent / 100.0f) * scale_factor; float maxHeight = ((float) h) * (config->maxPlateHeightPercent / 100.0f) * scale_factor; Size minSize(config->minPlateSizeWidthPx * scale_factor, config->minPlateSizeHeightPx * scale_factor); Size maxSize(maxWidth, maxHeight); plate_cascade.detectMultiScale( frame, plates, config->detection_iteration_increase, config->detectionStrictness, 0, //0|CV_HAAR_SCALE_IMAGE, minSize, maxSize ); if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << "LBP Time: " << diffclock(startTime, endTime) << "ms." << endl; } for( unsigned int i = 0; i < plates.size(); i++ ) { plates[i].x = (plates[i].x / scale_factor); plates[i].y = (plates[i].y / scale_factor); plates[i].width = plates[i].width / scale_factor; plates[i].height = plates[i].height / scale_factor; // Ensure that the rectangle isn't < 0 or > maxWidth/Height plates[i] = expandRect(plates[i], 0, 0, w, h); plates[i].x = plates[i].x + offset_x; plates[i].y = plates[i].y + offset_y; } vector orderedRegions = aggregateRegions(plates); return orderedRegions; } } openalpr_2.2.4.orig/src/openalpr/detection/detectorcpu.h000066400000000000000000000027021266464252400234530ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_DETECTORCPU_H #define OPENALPR_DETECTORCPU_H #include #include #include #include "opencv2/objdetect/objdetect.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/core/core.hpp" #include "opencv2/ml/ml.hpp" #include "detector.h" namespace alpr { class DetectorCPU : public Detector { public: DetectorCPU(Config* config); virtual ~DetectorCPU(); std::vector detect(cv::Mat frame, std::vector regionsOfInterest); private: cv::CascadeClassifier plate_cascade; std::vector doCascade(cv::Mat frame, int offset_x, int offset_y); }; } #endif /* OPENALPR_DETECTORCPU_H */ openalpr_2.2.4.orig/src/openalpr/detection/detectorcuda.cpp000066400000000000000000000076021266464252400241370ustar00rootroot00000000000000/* * Copyright (c) 2013 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "detectorcuda.h" #ifdef COMPILE_GPU using namespace cv; using namespace std; namespace alpr { DetectorCUDA::DetectorCUDA(Config* config) : Detector(config) { if( this->cuda_cascade.load( get_detector_file() ) ) { this->loaded = true; printf("--(!)Loaded CUDA classifier\n"); } else { this->loaded = false; printf("--(!)Error loading CPU classifier %s\n", get_detector_file().c_str()); } } DetectorCUDA::~DetectorCUDA() { } vector DetectorCUDA::detect(Mat frame, std::vector regionsOfInterest) { Mat frame_gray; if (frame.channels() > 2) { cvtColor( frame, frame_gray, CV_BGR2GRAY ); } else { frame.copyTo(frame_gray); } vector detectedRegions; for (int i = 0; i < regionsOfInterest.size(); i++) { Mat cropped = frame_gray(regionsOfInterest[i]); vector subRegions = doCascade(cropped, regionsOfInterest[i].x, regionsOfInterest[i].y); for (int j = 0; j < subRegions.size(); j++) detectedRegions.push_back(subRegions[j]); } return detectedRegions; } vector DetectorCUDA::doCascade(Mat frame, int offset_x, int offset_y) { int w = frame.size().width; int h = frame.size().height; float scale_factor = computeScaleFactor(w, h); vector plates; equalizeHist( frame, frame ); if (scale_factor != 1.0) resize(frame, frame, Size(w * scale_factor, h * scale_factor)); //-- Detect plates timespec startTime; getTimeMonotonic(&startTime); float maxWidth = ((float) w) * (config->maxPlateWidthPercent / 100.0f) * scale_factor; float maxHeight = ((float) h) * (config->maxPlateHeightPercent / 100.0f) * scale_factor; Size minSize(config->minPlateSizeWidthPx * scale_factor, config->minPlateSizeHeightPx * scale_factor); gpu::GpuMat cudaFrame, plateregions_buffer; Mat plateregions_downloaded; cudaFrame.upload(frame); int numdetected = cuda_cascade.detectMultiScale(cudaFrame, plateregions_buffer, (double) config->detection_iteration_increase, config->detectionStrictness, minSize); plateregions_buffer.colRange(0, numdetected).download(plateregions_downloaded); for (int i = 0; i < numdetected; ++i) { plates.push_back(plateregions_downloaded.ptr()[i]); } if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << "LBP Time: " << diffclock(startTime, endTime) << "ms." << endl; } for( unsigned int i = 0; i < plates.size(); i++ ) { plates[i].x = (plates[i].x / scale_factor); plates[i].y = (plates[i].y / scale_factor); plates[i].width = plates[i].width / scale_factor; plates[i].height = plates[i].height / scale_factor; // Ensure that the rectangle isn't < 0 or > maxWidth/Height plates[i] = expandRect(plates[i], 0, 0, w, h); plates[i].x = plates[i].x + offset_x; plates[i].y = plates[i].y + offset_y; } vector orderedRegions = aggregateRegions(plates); return orderedRegions; } } #endifopenalpr_2.2.4.orig/src/openalpr/detection/detectorcuda.h000066400000000000000000000030501266464252400235750ustar00rootroot00000000000000/* * Copyright (c) 2013 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_DETECTORCUDA_H #define OPENALPR_DETECTORCUDA_H #include #include #include #ifdef COMPILE_GPU #include "opencv2/objdetect/objdetect.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/core/core.hpp" #include "opencv2/ml/ml.hpp" #include "opencv2/gpu/gpu.hpp" #include "detector.h" #include "detectorcpu.h" namespace alpr { class DetectorCUDA : public Detector { public: DetectorCUDA(Config* config); virtual ~DetectorCUDA(); std::vector detect(cv::Mat frame, std::vector regionsOfInterest); private: cv::gpu::CascadeClassifier_GPU cuda_cascade; std::vector doCascade(cv::Mat frame, int offset_x, int offset_y); }; } #endif #endif /* OPENALPR_DETECTORCUDA_H */ openalpr_2.2.4.orig/src/openalpr/detection/detectorfactory.cpp000066400000000000000000000022541266464252400246700ustar00rootroot00000000000000#include "detectorfactory.h" #include "detectormorph.h" #include "detectorocl.h" namespace alpr { Detector* createDetector(Config* config) { if (config->detector == DETECTOR_LBP_CPU) { // CPU mode return new DetectorCPU(config); } else if (config->detector == DETECTOR_LBP_GPU) { #ifdef COMPILE_GPU return new DetectorCUDA(config); #else std::cerr << "Error: GPU detector requested, but GPU extensions are not compiled. " << "Add COMPILE_GPU=1 to the compiler definitions to enable GPU compilation." << std::endl; return new DetectorCPU(config); #endif } else if (config->detector == DETECTOR_LBP_OPENCL) { #if OPENCV_MAJOR_VERSION == 3 return new DetectorOCL(config); #else std::cerr << "Error: OpenCL acceleration requires OpenCV 3.0. " << std::endl; return new DetectorCPU(config); #endif } else if (config->detector == DETECTOR_MORPH_CPU) { return new DetectorMorph(config); } else { std::cerr << "Unknown detector requested. Using LBP CPU" << std::endl; return new DetectorCPU(config); } } } openalpr_2.2.4.orig/src/openalpr/detection/detectorfactory.h000066400000000000000000000017711266464252400243400ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_DETECTORFACTORY_H #define OPENALPR_DETECTORFACTORY_H #include "detectorcpu.h" #include "detectorcuda.h" #include "config.h" namespace alpr { Detector* createDetector(Config* config); } #endif /* OPENALPR_DETECTORFACTORY_H */ openalpr_2.2.4.orig/src/openalpr/detection/detectormorph.cpp000066400000000000000000000164321266464252400243510ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "detectormorph.h" using namespace cv; using namespace std; namespace alpr { DetectorMorph::DetectorMorph(Config* config) : Detector(config) { this->loaded = true; } DetectorMorph::~DetectorMorph() { } bool DetectorMorph::ValidateCharAspect(Rect& r0, float idealAspect) { if ((r0.width < 5 || r0.width > 20) || (r0.height < 15 || r0.height > 40)) return false; float aspectChar = ((float)r0.width / (float)r0.height); float deltaChar = fabs(idealAspect - aspectChar); if (deltaChar < 0.25) return true; else return false; } vector DetectorMorph::detect(Mat frame, std::vector regionsOfInterest) { Mat frame_gray,frame_gray_cp; if (frame.channels() > 2) { cvtColor( frame, frame_gray, CV_BGR2GRAY ); } else { frame.copyTo(frame_gray); } frame_gray.copyTo(frame_gray_cp); blur(frame_gray, frame_gray, Size(5, 5)); vector detectedRegions; for (int i = 0; i < regionsOfInterest.size(); i++) { Mat img_open, img_result; Mat element = getStructuringElement(MORPH_RECT, Size(30, 4)); morphologyEx(frame_gray, img_open, CV_MOP_OPEN, element, cv::Point(-1, -1)); img_result = frame_gray - img_open; if (config->debugDetector && config->debugShowImages) { imshow("Opening", img_result); } //threshold image using otsu thresholding Mat img_threshold, img_open2; threshold(img_result, img_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY); if (config->debugDetector && config->debugShowImages) { imshow("Threshold Detector", img_threshold); } Mat diamond(5, 5, CV_8U, cv::Scalar(1)); diamond.at(0, 0) = 0; diamond.at(0, 1) = 0; diamond.at(1, 0) = 0; diamond.at(4, 4) = 0; diamond.at(3, 4) = 0; diamond.at(4, 3) = 0; diamond.at(4, 0) = 0; diamond.at(4, 1) = 0; diamond.at(3, 0) = 0; diamond.at(0, 4) = 0; diamond.at(0, 3) = 0; diamond.at(1, 4) = 0; morphologyEx(img_threshold, img_open2, CV_MOP_OPEN, diamond, cv::Point(-1, -1)); Mat rectElement = getStructuringElement(cv::MORPH_RECT, Size(13, 4)); morphologyEx(img_open2, img_threshold, CV_MOP_CLOSE, rectElement, cv::Point(-1, -1)); if (config->debugDetector && config->debugShowImages) { imshow("Close", img_threshold); waitKey(0); } //Find contours of possibles plates vector< vector< Point> > contours; findContours(img_threshold, contours, // a vector of contours CV_RETR_EXTERNAL, // retrieve the external contours CV_CHAIN_APPROX_NONE); // all pixels of each contours //Start to iterate to each contour founded vector >::iterator itc = contours.begin(); vector rects; //Remove patch that are no inside limits of aspect ratio and area. while (itc != contours.end()) { //Create bounding rect of object RotatedRect mr = minAreaRect(Mat(*itc)); if (mr.angle < -45.) { mr.angle += 90.0; swap(mr.size.width, mr.size.height); } if (!CheckSizes(mr)) itc = contours.erase(itc); else { ++itc; rects.push_back(mr); } } //Now prunning based on checking all candidate plates for a min/max number of blobsc Mat img_crop, img_crop_b, img_crop_th, img_crop_th_inv; vector< vector< Point> > plateBlobs; vector< vector< Point> > plateBlobsInv; double thresholds[] = { 10, 40, 80, 120, 160, 200, 240 }; const int num_thresholds = 7; int numValidChars = 0; Mat rotated; for (int i = 0; i < rects.size(); i++) { numValidChars = 0; RotatedRect PlateRect = rects[i]; Size rect_size = PlateRect.size; // get the rotation matrix Mat M = getRotationMatrix2D(PlateRect.center, PlateRect.angle, 1.0); // perform the affine transformation warpAffine(frame_gray_cp, rotated, M, frame_gray_cp.size(), INTER_CUBIC); //Crop area around candidate plate getRectSubPix(rotated, rect_size, PlateRect.center, img_crop); if (config->debugDetector && config->debugShowImages) { imshow("Tilt Correction", img_crop); waitKey(0); } for (int z = 0; z < num_thresholds; z++) { cv::threshold(img_crop, img_crop_th, thresholds[z], 255, cv::THRESH_BINARY); cv::threshold(img_crop, img_crop_th_inv, thresholds[z], 255, cv::THRESH_BINARY_INV); findContours(img_crop_th, plateBlobs, // a vector of contours CV_RETR_LIST, // retrieve the contour list CV_CHAIN_APPROX_NONE); // all pixels of each contours findContours(img_crop_th_inv, plateBlobsInv, // a vector of contours CV_RETR_LIST, // retrieve the contour list CV_CHAIN_APPROX_NONE); // all pixels of each contours int numBlobs = plateBlobs.size(); int numBlobsInv = plateBlobsInv.size(); float idealAspect = config->avgCharWidthMM / config->avgCharHeightMM; for (int j = 0; j < numBlobs; j++) { cv::Rect r0 = cv::boundingRect(cv::Mat(plateBlobs[j])); if (ValidateCharAspect(r0, idealAspect)) numValidChars++; } for (int j = 0; j < numBlobsInv; j++) { cv::Rect r0 = cv::boundingRect(cv::Mat(plateBlobsInv[j])); if (ValidateCharAspect(r0, idealAspect)) numValidChars++; } } //If too much or too lcittle might not be a true plate //if (numBlobs < 3 || numBlobs > 50) continue; if (numValidChars < 4 || numValidChars > 50) continue; PlateRegion PlateReg; // Ensure that the rectangle isn't < 0 or > maxWidth/Height Rect bounding_rect = PlateRect.boundingRect(); PlateReg.rect = expandRect(bounding_rect, 0, 0, frame.cols, frame.rows); detectedRegions.push_back(PlateReg); } } return detectedRegions; } bool DetectorMorph::CheckSizes(RotatedRect& mr) { float error = 1.2; float aspect = config->plateWidthMM / config->plateHeightMM; //Set a min and max area. All other patchs are discarded int min = 10 * aspect * 10; // minimum area int max = 100 * aspect * 100; // maximum area //Get only patchs that match to a respect ratio. float rmin = 3.0; float rmax = 7.0; int area = mr.size.height * mr.size.width; float r = (float) mr.size.width / (float) mr.size.height; if (r < 1) r = (float) mr.size.height / (float) mr.size.width; if ( (area < min || area > max) || (r < rmin || r > rmax) || (mr.size.width < 70 || mr.size.width > 300) || (mr.size.height < 10 || mr.size.height > 80) ) return false; else return true; } } openalpr_2.2.4.orig/src/openalpr/detection/detectormorph.h000066400000000000000000000026641266464252400240200ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_DETECTORMORPH_H #define OPENALPR_DETECTORMORPH_H #include #include #include #include "opencv2/objdetect/objdetect.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/core/core.hpp" #include "opencv2/ml/ml.hpp" #include "detector.h" namespace alpr { class DetectorMorph : public Detector { public: DetectorMorph(Config* config); virtual ~DetectorMorph(); std::vector detect(cv::Mat frame, std::vector regionsOfInterest); private: bool CheckSizes(cv::RotatedRect& mr); bool ValidateCharAspect(cv::Rect& r0, float idealAspect); }; } #endif /* OPENALPR_DETECTORMORPH_H */ openalpr_2.2.4.orig/src/openalpr/detection/detectorocl.cpp000066400000000000000000000140311266464252400237720ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "detectorocl.h" #include #if OPENCV_MAJOR_VERSION == 3 using namespace cv; using namespace std; tthread::mutex ocl_detector_mutex_m; namespace alpr { DetectorOCL::DetectorOCL(Config* config) : Detector(config) { tthread::lock_guard guard(ocl_detector_mutex_m); cv::ocl::setUseOpenCL(true); if (config->debugDetector) { try{ cout << "\r\nUse of OpenCL LBP detector selected in config file." << endl; cv::ocl::Device ocldevice; std::vector platforms; getPlatfomsInfo(platforms); if (platforms.size()>0) cout << "OpenCL device(s) found:" << endl; int n = 0; for (size_t i = 0; i < platforms.size(); i++) { const cv::ocl::PlatformInfo* platform = &platforms[i]; for (int j = 0; j < platform->deviceNumber(); j++) { platform->getDevice(ocldevice, j); cout << n << " " << ocldevice.name() << " (" << ocldevice.version() << ")" << endl; n++; } } if (n > 1) { ocldevice = cv::ocl::Device::getDefault(); if (ocldevice.available()) { cout << "\r\nCurrent OpenCL device: \r\n " << ocldevice.name() << " (" << ocldevice.version() << ").\r\n" << endl; } else { cout << "\r\nOpenCL error: The selected device is not available.\r\n" << endl; } cout << "Select the OpenCL device by adjusting the environment variable OPENCV_OPENCL_DEVICE, e.g.\r\n-In Windows type at the command prompt:\r\n set OPENCV_OPENCL_DEVICE=::1\r\n" << endl; } } catch (...) { cout << "OpenCL error: No OpenCL device found.\r\n" << endl; } } if (!ocl::haveOpenCL()) { this->loaded = false; cerr << "OpenCL not detected" << endl; } else if( this->plate_cascade.load( get_detector_file() ) ) { this->loaded = true; } else { this->loaded = false; cerr << "--(!)Error loading cascade " << get_detector_file() << "\n" << endl; } } DetectorOCL::~DetectorOCL() { } vector DetectorOCL::detect(Mat frame, std::vector regionsOfInterest) { Mat frame_gray; if (frame.channels() > 2) { cvtColor( frame, frame_gray, CV_BGR2GRAY ); } else { frame.copyTo(frame_gray); } vector detectedRegions; for (int i = 0; i < regionsOfInterest.size(); i++) { // Sanity check. If roi width or height is less than minimum possible plate size, // then skip it if ((regionsOfInterest[i].width < config->minPlateSizeWidthPx) || (regionsOfInterest[i].height < config->minPlateSizeHeightPx)) continue; Mat cropped = frame_gray(regionsOfInterest[i]); vector subRegions = doCascade(cropped, regionsOfInterest[i].x, regionsOfInterest[i].y); for (int j = 0; j < subRegions.size(); j++) detectedRegions.push_back(subRegions[j]); } return detectedRegions; } vector DetectorOCL::doCascade(Mat orig_frame, int offset_x, int offset_y) { int w = orig_frame.size().width; int h = orig_frame.size().height; float scale_factor = computeScaleFactor(w, h); vector plates; //-- Detect plates timespec startTime; getTimeMonotonic(&startTime); float maxWidth = ((float) w) * (config->maxPlateWidthPercent / 100.0f) * scale_factor; float maxHeight = ((float) h) * (config->maxPlateHeightPercent / 100.0f) * scale_factor; Size minSize(config->minPlateSizeWidthPx * scale_factor, config->minPlateSizeHeightPx * scale_factor); Size maxSize(maxWidth, maxHeight); // If we have an OpenCL core available, use it. Otherwise use CPU if (ocl_detector_mutex_m.try_lock()) { UMat openclFrame; orig_frame.copyTo(openclFrame); equalizeHist( openclFrame, openclFrame ); if (scale_factor != 1.0) resize(openclFrame, openclFrame, Size(w * scale_factor, h * scale_factor)); plate_cascade.detectMultiScale( openclFrame, plates, config->detection_iteration_increase, config->detectionStrictness, 0, minSize, maxSize ); ocl_detector_mutex_m.unlock(); } else { equalizeHist( orig_frame, orig_frame ); if (scale_factor != 1.0) resize(orig_frame, orig_frame, Size(w * scale_factor, h * scale_factor)); plate_cascade.detectMultiScale( orig_frame, plates, config->detection_iteration_increase, config->detectionStrictness, 0, minSize, maxSize ); } if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << "LBP Time: " << diffclock(startTime, endTime) << "ms." << endl; } for( unsigned int i = 0; i < plates.size(); i++ ) { plates[i].x = (plates[i].x / scale_factor); plates[i].y = (plates[i].y / scale_factor); plates[i].width = plates[i].width / scale_factor; plates[i].height = plates[i].height / scale_factor; // Ensure that the rectangle isn't < 0 or > maxWidth/Height plates[i] = expandRect(plates[i], 0, 0, w, h); plates[i].x = plates[i].x + offset_x; plates[i].y = plates[i].y + offset_y; } vector orderedRegions = aggregateRegions(plates); return orderedRegions; } } #endifopenalpr_2.2.4.orig/src/openalpr/detection/detectorocl.h000066400000000000000000000030101266464252400234320ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_DETECTOROPENCL_H #define OPENALPR_DETECTOROPENCL_H #include #include #include #if OPENCV_MAJOR_VERSION == 3 #include "opencv2/objdetect/objdetect.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/core/core.hpp" #include "opencv2/ml/ml.hpp" #include "opencv2/core/ocl.hpp" #include "detector.h" namespace alpr { class DetectorOCL : public Detector { public: DetectorOCL(Config* config); virtual ~DetectorOCL(); std::vector detect(cv::Mat frame, std::vector regionsOfInterest); private: cv::CascadeClassifier plate_cascade; std::vector doCascade(cv::Mat frame, int offset_x, int offset_y); }; } #endif #endif /* OPENALPR_DETECTOROPENCL_H */ openalpr_2.2.4.orig/src/openalpr/edges/000077500000000000000000000000001266464252400200715ustar00rootroot00000000000000openalpr_2.2.4.orig/src/openalpr/edges/edgefinder.cpp000066400000000000000000000130371266464252400226750ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "edgefinder.h" #include "textlinecollection.h" using namespace std; using namespace cv; namespace alpr { EdgeFinder::EdgeFinder(PipelineData* pipeline_data) { this->pipeline_data = pipeline_data; // First re-crop the area from the original picture knowing the text position } EdgeFinder::~EdgeFinder() { } std::vector EdgeFinder::findEdgeCorners() { TextLineCollection tlc(pipeline_data->textLines); vector corners; // If the character segment is especially small, just expand the existing box // If it's a nice, long segment, then guess the correct box based on character height/position if (tlc.longerSegment.length > tlc.charHeight * 3) { float charHeightToPlateWidthRatio = pipeline_data->config->plateWidthMM / pipeline_data->config->avgCharHeightMM; float idealPixelWidth = tlc.charHeight * (charHeightToPlateWidthRatio * 1.03); // Add 3% so we don't clip any characters float charHeightToPlateHeightRatio = pipeline_data->config->plateHeightMM / pipeline_data->config->avgCharHeightMM; float idealPixelHeight = tlc.charHeight * charHeightToPlateHeightRatio; float verticalOffset = (idealPixelHeight * 1.5 / 2); float horizontalOffset = (idealPixelWidth * 1.25 / 2); LineSegment topLine = tlc.centerHorizontalLine.getParallelLine(verticalOffset); LineSegment bottomLine = tlc.centerHorizontalLine.getParallelLine(-1 * verticalOffset); LineSegment leftLine = tlc.centerVerticalLine.getParallelLine(-1 * horizontalOffset); LineSegment rightLine = tlc.centerVerticalLine.getParallelLine(horizontalOffset); Point topLeft = topLine.intersection(leftLine); Point topRight = topLine.intersection(rightLine); Point botRight = bottomLine.intersection(rightLine); Point botLeft = bottomLine.intersection(leftLine); corners.push_back(topLeft); corners.push_back(topRight); corners.push_back(botRight); corners.push_back(botLeft); } else { int expandX = (int) ((float) pipeline_data->crop_gray.cols) * 0.15f; int expandY = (int) ((float) pipeline_data->crop_gray.rows) * 0.15f; int w = pipeline_data->crop_gray.cols; int h = pipeline_data->crop_gray.rows; corners.push_back(Point(-1 * expandX, -1 * expandY)); corners.push_back(Point(expandX + w, -1 * expandY)); corners.push_back(Point(expandX + w, expandY + h)); corners.push_back(Point(-1 * expandX, expandY + h)); // for (int i = 0; i < 4; i++) // { // std::cout << "CORNER: " << corners[i].x << " - " << corners[i].y << std::endl; // } } // Re-crop an image (from the original image) using the new coordinates Transformation imgTransform(pipeline_data->grayImg, pipeline_data->crop_gray, pipeline_data->regionOfInterest); vector remappedCorners = imgTransform.transformSmallPointsToBigImage(corners); Size cropSize = imgTransform.getCropSize(remappedCorners, Size(pipeline_data->config->templateWidthPx, pipeline_data->config->templateHeightPx)); Mat transmtx = imgTransform.getTransformationMatrix(remappedCorners, cropSize); Mat newCrop = imgTransform.crop(cropSize, transmtx); // Re-map the textline coordinates to the new crop vector newLines; for (unsigned int i = 0; i < pipeline_data->textLines.size(); i++) { vector textArea = imgTransform.transformSmallPointsToBigImage(pipeline_data->textLines[i].textArea); vector linePolygon = imgTransform.transformSmallPointsToBigImage(pipeline_data->textLines[i].linePolygon); vector textAreaRemapped; vector linePolygonRemapped; textAreaRemapped = imgTransform.remapSmallPointstoCrop(textArea, transmtx); linePolygonRemapped = imgTransform.remapSmallPointstoCrop(linePolygon, transmtx); newLines.push_back(TextLine(textAreaRemapped, linePolygonRemapped, newCrop.size())); } // Find the PlateLines for this crop PlateLines plateLines(pipeline_data); plateLines.processImage(newCrop, newLines, 1.05); // Get the best corners PlateCorners cornerFinder(newCrop, &plateLines, pipeline_data, newLines); vector smallPlateCorners = cornerFinder.findPlateCorners(); // Transform the best corner points back to the original image std::vector imgArea; imgArea.push_back(Point2f(0, 0)); imgArea.push_back(Point2f(newCrop.cols, 0)); imgArea.push_back(Point2f(newCrop.cols, newCrop.rows)); imgArea.push_back(Point2f(0, newCrop.rows)); Mat newCropTransmtx = imgTransform.getTransformationMatrix(imgArea, remappedCorners); vector cornersInOriginalImg = imgTransform.remapSmallPointstoCrop(smallPlateCorners, newCropTransmtx); return cornersInOriginalImg; } }openalpr_2.2.4.orig/src/openalpr/edges/edgefinder.h000066400000000000000000000023251266464252400223400ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_EDGEFINDER_H #define OPENALPR_EDGEFINDER_H #include "opencv2/imgproc/imgproc.hpp" #include "pipeline_data.h" #include "transformation.h" #include "platelines.h" #include "platecorners.h" namespace alpr { class EdgeFinder { public: EdgeFinder(PipelineData* pipeline_data); virtual ~EdgeFinder(); std::vector findEdgeCorners(); private: PipelineData* pipeline_data; }; } #endif /* OPENALPR_EDGEFINDER_H */ openalpr_2.2.4.orig/src/openalpr/edges/platecorners.cpp000066400000000000000000000322741266464252400233060ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "platecorners.h" using namespace cv; using namespace std; namespace alpr { PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData, vector textLines) : tlc(textLines) { this->pipelineData = pipelineData; if (pipelineData->config->debugPlateCorners) cout << "PlateCorners constructor" << endl; this->inputImage = inputImage; this->plateLines = plateLines; this->textLines = textLines; this->bestHorizontalScore = 9999999999999; this->bestVerticalScore = 9999999999999; } PlateCorners::~PlateCorners() { } vector PlateCorners::findPlateCorners() { if (pipelineData->config->debugPlateCorners) cout << "PlateCorners::findPlateCorners" << endl; timespec startTime; getTimeMonotonic(&startTime); int horizontalLines = this->plateLines->horizontalLines.size(); int verticalLines = this->plateLines->verticalLines.size(); // layout horizontal lines for (int h1 = NO_LINE; h1 < horizontalLines; h1++) { for (int h2 = NO_LINE; h2 < horizontalLines; h2++) { if (h1 == h2 && h1 != NO_LINE) continue; this->scoreHorizontals(h1, h2); } } // layout vertical lines for (int v1 = NO_LINE; v1 < verticalLines; v1++) { for (int v2 = NO_LINE; v2 < verticalLines; v2++) { if (v1 == v2 && v1 != NO_LINE) continue; this->scoreVerticals(v1, v2); } } if (pipelineData->config->debugPlateCorners) { cout << "Drawing debug stuff..." << endl; Mat imgCorners = Mat(inputImage.size(), inputImage.type()); inputImage.copyTo(imgCorners); for (unsigned int linenum = 0; linenum < textLines.size(); linenum++) { for (int i = 0; i < 4; i++) circle(imgCorners, textLines[linenum].textArea[i], 2, Scalar(0, 0, 0)); } line(imgCorners, this->bestTop.p1, this->bestTop.p2, Scalar(255, 0, 0), 1, CV_AA); line(imgCorners, this->bestRight.p1, this->bestRight.p2, Scalar(0, 0, 255), 1, CV_AA); line(imgCorners, this->bestBottom.p1, this->bestBottom.p2, Scalar(0, 0, 255), 1, CV_AA); line(imgCorners, this->bestLeft.p1, this->bestLeft.p2, Scalar(255, 0, 0), 1, CV_AA); displayImage(pipelineData->config, "Winning top/bottom Boundaries", imgCorners); } // Check if a left/right edge has been established. if (bestLeft.p1.x == 0 && bestLeft.p1.y == 0 && bestLeft.p2.x == 0 && bestLeft.p2.y == 0) { pipelineData->disqualified = true; pipelineData->disqualify_reason = "platecorners did not find a left/right edge"; } else if (bestTop.p1.x == 0 && bestTop.p1.y == 0 && bestTop.p2.x == 0 && bestTop.p2.y == 0) { pipelineData->disqualified = true; pipelineData->disqualify_reason = "platecorners did not find a top/bottom edge"; } vector corners; corners.push_back(bestTop.intersection(bestLeft)); corners.push_back(bestTop.intersection(bestRight)); corners.push_back(bestBottom.intersection(bestRight)); corners.push_back(bestBottom.intersection(bestLeft)); if (pipelineData->config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << "Plate Corners Time: " << diffclock(startTime, endTime) << "ms." << endl; } return corners; } void PlateCorners::scoreVerticals(int v1, int v2) { ScoreKeeper scoreKeeper; LineSegment left; LineSegment right; float charHeightToPlateWidthRatio = pipelineData->config->plateWidthMM / pipelineData->config->avgCharHeightMM; float idealPixelWidth = tlc.charHeight * (charHeightToPlateWidthRatio * 1.03); // Add 3% so we don't clip any characters float confidenceDiff = 0; float missingSegmentPenalty = 0; if (v1 == NO_LINE && v2 == NO_LINE) { //return; left = tlc.centerVerticalLine.getParallelLine(-1 * idealPixelWidth / 2); right = tlc.centerVerticalLine.getParallelLine(idealPixelWidth / 2 ); missingSegmentPenalty = 2; confidenceDiff += 2; } else if (v1 != NO_LINE && v2 != NO_LINE) { left = this->plateLines->verticalLines[v1].line; right = this->plateLines->verticalLines[v2].line; confidenceDiff += (1.0 - this->plateLines->verticalLines[v1].confidence); confidenceDiff += (1.0 - this->plateLines->verticalLines[v2].confidence); } else if (v1 == NO_LINE && v2 != NO_LINE) { right = this->plateLines->verticalLines[v2].line; left = right.getParallelLine(idealPixelWidth); missingSegmentPenalty++; confidenceDiff += (1.0 - this->plateLines->verticalLines[v2].confidence); } else if (v1 != NO_LINE && v2 == NO_LINE) { left = this->plateLines->verticalLines[v1].line; right = left.getParallelLine(-1 * idealPixelWidth); missingSegmentPenalty++; confidenceDiff += (1.0 - this->plateLines->verticalLines[v1].confidence); } scoreKeeper.setScore("SCORING_LINE_CONFIDENCE_WEIGHT", confidenceDiff, SCORING_LINE_CONFIDENCE_WEIGHT); scoreKeeper.setScore("SCORING_MISSING_SEGMENT_PENALTY_VERTICAL", missingSegmentPenalty, SCORING_MISSING_SEGMENT_PENALTY_VERTICAL); // Make sure that the left and right lines are to the left and right of our text // area if (tlc.isLeftOfText(left) < 1 || tlc.isLeftOfText(right) > -1) return; ///////////////////////////////////////////////////////////////////////// // Score angle difference from detected character box ///////////////////////////////////////////////////////////////////////// float perpendicularCharAngle = tlc.charAngle - 90; float charanglediff = abs(perpendicularCharAngle - left.angle) + abs(perpendicularCharAngle - right.angle); scoreKeeper.setScore("SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT", charanglediff, SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT); ////////////////////////////////////////////////////////////////////////// // SCORE the shape wrt character position and height relative to position ////////////////////////////////////////////////////////////////////////// Point leftMidLinePoint = left.closestPointOnSegmentTo(tlc.centerVerticalLine.midpoint()); Point rightMidLinePoint = right.closestPointOnSegmentTo(tlc.centerVerticalLine.midpoint()); float actual_width = distanceBetweenPoints(leftMidLinePoint, rightMidLinePoint); // Disqualify the pairing if it's less than one quarter of the ideal width if (actual_width < (idealPixelWidth / 4)) return; float plateDistance = abs(idealPixelWidth - actual_width); // normalize for image width plateDistance = plateDistance / ((float)inputImage.cols); scoreKeeper.setScore("SCORING_DISTANCE_WEIGHT_VERTICAL", plateDistance, SCORING_DISTANCE_WEIGHT_VERTICAL); float score = scoreKeeper.getTotal(); if (score < this->bestVerticalScore) { if (pipelineData->config->debugPlateCorners) { cout << "Vertical breakdown Score:" << endl; scoreKeeper.printDebugScores(); } this->bestVerticalScore = score; bestLeft = LineSegment(left.p1.x, left.p1.y, left.p2.x, left.p2.y); bestRight = LineSegment(right.p1.x, right.p1.y, right.p2.x, right.p2.y); } } // Score a collection of lines as a possible license plate region. // If any segments are missing, extrapolate the missing pieces void PlateCorners::scoreHorizontals(int h1, int h2) { ScoreKeeper scoreKeeper; LineSegment top; LineSegment bottom; float charHeightToPlateHeightRatio = pipelineData->config->plateHeightMM / pipelineData->config->avgCharHeightMM; float idealPixelHeight = tlc.charHeight * charHeightToPlateHeightRatio; float confidenceDiff = 0; float missingSegmentPenalty = 0; if (h1 == NO_LINE && h2 == NO_LINE) { // return; top = tlc.centerHorizontalLine.getParallelLine(idealPixelHeight / 2); bottom = tlc.centerHorizontalLine.getParallelLine(-1 * idealPixelHeight / 2 ); missingSegmentPenalty = 2; confidenceDiff += 2; } else if (h1 != NO_LINE && h2 != NO_LINE) { top = this->plateLines->horizontalLines[h1].line; bottom = this->plateLines->horizontalLines[h2].line; confidenceDiff += (1.0 - this->plateLines->horizontalLines[h1].confidence); confidenceDiff += (1.0 - this->plateLines->horizontalLines[h2].confidence); } else if (h1 == NO_LINE && h2 != NO_LINE) { bottom = this->plateLines->horizontalLines[h2].line; top = bottom.getParallelLine(idealPixelHeight); missingSegmentPenalty++; confidenceDiff += (1.0 - this->plateLines->horizontalLines[h2].confidence); } else if (h1 != NO_LINE && h2 == NO_LINE) { top = this->plateLines->horizontalLines[h1].line; bottom = top.getParallelLine(-1 * idealPixelHeight); missingSegmentPenalty++; confidenceDiff += (1.0 - this->plateLines->horizontalLines[h1].confidence); } scoreKeeper.setScore("SCORING_MISSING_SEGMENT_PENALTY_HORIZONTAL", missingSegmentPenalty, SCORING_MISSING_SEGMENT_PENALTY_HORIZONTAL); //scoreKeeper.setScore("SCORING_LINE_CONFIDENCE_WEIGHT", confidenceDiff, SCORING_LINE_CONFIDENCE_WEIGHT); // Make sure that the top and bottom lines are above and below // the text area if (tlc.isAboveText(top) < 1 || tlc.isAboveText(bottom) > -1) return; // We now have 4 possible lines. Let's put them to the test and score them... ////////////////////////////////////////////////////////////////////////// // SCORE the shape wrt character position and height relative to position ////////////////////////////////////////////////////////////////////////// Point topPoint = top.midpoint(); Point botPoint = bottom.closestPointOnSegmentTo(topPoint); float plateHeightPx = distanceBetweenPoints(topPoint, botPoint); // Get the height difference float heightRatio = tlc.charHeight / plateHeightPx; float idealHeightRatio = (pipelineData->config->avgCharHeightMM / pipelineData->config->plateHeightMM); float heightRatioDiff = abs(heightRatio - idealHeightRatio); scoreKeeper.setScore("SCORING_PLATEHEIGHT_WEIGHT", heightRatioDiff, SCORING_PLATEHEIGHT_WEIGHT); ////////////////////////////////////////////////////////////////////////// // SCORE the middliness of the stuff. We want our top and bottom line to have the characters right towards the middle ////////////////////////////////////////////////////////////////////////// Point charAreaMidPoint = tlc.centerVerticalLine.midpoint(); Point topLineSpot = top.closestPointOnSegmentTo(charAreaMidPoint); Point botLineSpot = bottom.closestPointOnSegmentTo(charAreaMidPoint); float topDistanceFromMiddle = distanceBetweenPoints(topLineSpot, charAreaMidPoint); float bottomDistanceFromMiddle = distanceBetweenPoints(botLineSpot, charAreaMidPoint); float idealDistanceFromMiddle = idealPixelHeight / 2; float middleScore = abs(topDistanceFromMiddle - idealDistanceFromMiddle) / idealDistanceFromMiddle; middleScore += abs(bottomDistanceFromMiddle - idealDistanceFromMiddle) / idealDistanceFromMiddle; scoreKeeper.setScore("SCORING_TOP_BOTTOM_SPACE_VS_CHARHEIGHT_WEIGHT", middleScore, SCORING_TOP_BOTTOM_SPACE_VS_CHARHEIGHT_WEIGHT); ////////////////////////////////////////////////////////////// // SCORE: the shape for angles matching the character region ////////////////////////////////////////////////////////////// float charanglediff = abs(tlc.charAngle - top.angle) + abs(tlc.charAngle - bottom.angle); scoreKeeper.setScore("SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT", charanglediff, SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT); if (pipelineData->config->debugPlateCorners) { scoreKeeper.printDebugScores(); Mat debugImg(this->inputImage.size(), this->inputImage.type()); this->inputImage.copyTo(debugImg); cvtColor(debugImg, debugImg, CV_GRAY2BGR); line(debugImg, top.p1, top.p2, Scalar(0,0,255), 2); line(debugImg, bottom.p1, bottom.p2, Scalar(0,0,255), 2); //drawAndWait(&debugImg); } float score = scoreKeeper.getTotal(); if (score < this->bestHorizontalScore) { float scorecomponent; if (pipelineData->config->debugPlateCorners) { cout << "Horizontal breakdown Score:" << endl; scoreKeeper.printDebugScores(); } this->bestHorizontalScore = score; bestTop = LineSegment(top.p1.x, top.p1.y, top.p2.x, top.p2.y); bestBottom = LineSegment(bottom.p1.x, bottom.p1.y, bottom.p2.x, bottom.p2.y); } } } openalpr_2.2.4.orig/src/openalpr/edges/platecorners.h000066400000000000000000000042141266464252400227440ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_PLATECORNERS_H #define OPENALPR_PLATECORNERS_H #include "opencv2/imgproc/imgproc.hpp" #include "platelines.h" #include "utility.h" #include "config.h" #include "textlinecollection.h" #include "scorekeeper.h" #define NO_LINE -1 #define SCORING_MISSING_SEGMENT_PENALTY_VERTICAL 10 #define SCORING_MISSING_SEGMENT_PENALTY_HORIZONTAL 1 #define SCORING_PLATEHEIGHT_WEIGHT 2.2 #define SCORING_TOP_BOTTOM_SPACE_VS_CHARHEIGHT_WEIGHT 2.0 #define SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT 1.1 #define SCORING_DISTANCE_WEIGHT_VERTICAL 4.0 #define SCORING_LINE_CONFIDENCE_WEIGHT 18.0 namespace alpr { class PlateCorners { public: PlateCorners(cv::Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData, std::vector textLines) ; virtual ~PlateCorners(); std::vector findPlateCorners(); private: PipelineData* pipelineData; cv::Mat inputImage; std::vector textLines; TextLineCollection tlc; float bestHorizontalScore; float bestVerticalScore; LineSegment bestTop; LineSegment bestBottom; LineSegment bestLeft; LineSegment bestRight; PlateLines* plateLines; void scoreHorizontals( int h1, int h2 ); void scoreVerticals( int v1, int v2 ); }; } #endif // OPENALPR_PLATELINES_H openalpr_2.2.4.orig/src/openalpr/edges/platelines.cpp000066400000000000000000000173131266464252400227420ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "platelines.h" using namespace cv; using namespace std; const float MIN_CONFIDENCE = 0.3; namespace alpr { PlateLines::PlateLines(PipelineData* pipelineData) { this->pipelineData = pipelineData; this->debug = pipelineData->config->debugPlateLines; if (debug) cout << "PlateLines constructor" << endl; } PlateLines::~PlateLines() { } void PlateLines::processImage(Mat inputImage, vector textLines, float sensitivity) { if (this->debug) cout << "PlateLines findLines" << endl; timespec startTime; getTimeMonotonic(&startTime); // Ignore input images that are pure white or pure black Scalar avgPixelIntensity = mean(inputImage); if (avgPixelIntensity[0] >= 252) return; else if (avgPixelIntensity[0] <= 3) return; // Do a bilateral filter to clean the noise but keep edges sharp Mat smoothed(inputImage.size(), inputImage.type()); bilateralFilter(inputImage, smoothed, 3, 45, 45); int morph_elem = 2; int morph_size = 2; Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); Mat edges(inputImage.size(), inputImage.type()); Canny(smoothed, edges, 66, 133); // Create a mask that is dilated based on the detected characters Mat mask = Mat::zeros(inputImage.size(), CV_8U); for (unsigned int i = 0; i < textLines.size(); i++) { vector > polygons; polygons.push_back(textLines[i].textArea); fillPoly(mask, polygons, Scalar(255,255,255)); } dilate(mask, mask, getStructuringElement( 1, Size( 1 + 1, 2*1+1 ), Point( 1, 1 ) )); bitwise_not(mask, mask); // AND canny edges with the character mask bitwise_and(edges, mask, edges); vector hlines = this->getLines(edges, sensitivity, false); vector vlines = this->getLines(edges, sensitivity, true); for (unsigned int i = 0; i < hlines.size(); i++) this->horizontalLines.push_back(hlines[i]); for (unsigned int i = 0; i < vlines.size(); i++) this->verticalLines.push_back(vlines[i]); // if debug is enabled, draw the image if (this->debug) { Mat debugImgHoriz(edges.size(), edges.type()); Mat debugImgVert(edges.size(), edges.type()); edges.copyTo(debugImgHoriz); edges.copyTo(debugImgVert); cvtColor(debugImgHoriz,debugImgHoriz,CV_GRAY2BGR); cvtColor(debugImgVert,debugImgVert,CV_GRAY2BGR); for( size_t i = 0; i < this->horizontalLines.size(); i++ ) { line( debugImgHoriz, this->horizontalLines[i].line.p1, this->horizontalLines[i].line.p2, Scalar(0,0,255), 1, CV_AA); } for( size_t i = 0; i < this->verticalLines.size(); i++ ) { line( debugImgVert, this->verticalLines[i].line.p1, this->verticalLines[i].line.p2, Scalar(0,0,255), 1, CV_AA); } vector images; images.push_back(debugImgHoriz); images.push_back(debugImgVert); Mat dashboard = drawImageDashboard(images, debugImgVert.type(), 1); displayImage(pipelineData->config, "Hough Lines", dashboard); } if (pipelineData->config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << "Plate Lines Time: " << diffclock(startTime, endTime) << "ms." << endl; } } vector PlateLines::getLines(Mat edges, float sensitivityMultiplier, bool vertical) { if (this->debug) cout << "PlateLines::getLines" << endl; int HORIZONTAL_SENSITIVITY = pipelineData->config->plateLinesSensitivityHorizontal; int VERTICAL_SENSITIVITY = pipelineData->config->plateLinesSensitivityVertical; vector allLines; vector filteredLines; int sensitivity; if (vertical) sensitivity = VERTICAL_SENSITIVITY * (1.0 / sensitivityMultiplier); else sensitivity = HORIZONTAL_SENSITIVITY * (1.0 / sensitivityMultiplier); HoughLines( edges, allLines, 1, CV_PI/180, sensitivity, 0, 0 ); for( size_t i = 0; i < allLines.size(); i++ ) { float rho = allLines[i][0], theta = allLines[i][1]; Point pt1, pt2; double a = cos(theta), b = sin(theta); double x0 = a*rho, y0 = b*rho; double angle = theta * (180 / CV_PI); pt1.x = cvRound(x0 + 1000*(-b)); pt1.y = cvRound(y0 + 1000*(a)); pt2.x = cvRound(x0 - 1000*(-b)); pt2.y = cvRound(y0 - 1000*(a)); if (vertical) { if (angle < 20 || angle > 340 || (angle > 160 && angle < 210)) { // good vertical LineSegment line; if (pt1.y <= pt2.y) line = LineSegment(pt2.x, pt2.y, pt1.x, pt1.y); else line = LineSegment(pt1.x, pt1.y, pt2.x, pt2.y); // Get rid of the -1000, 1000 stuff. Terminate at the edges of the image // Helps with debugging/rounding issues later LineSegment top(0, 0, edges.cols, 0); LineSegment bottom(0, edges.rows, edges.cols, edges.rows); Point p1 = line.intersection(bottom); Point p2 = line.intersection(top); PlateLine plateLine; plateLine.line = LineSegment(p1.x, p1.y, p2.x, p2.y); plateLine.confidence = (1.0 - MIN_CONFIDENCE) * ((float) (allLines.size() - i)) / ((float)allLines.size()) + MIN_CONFIDENCE; filteredLines.push_back(plateLine); } } else { if ( (angle > 70 && angle < 110) || (angle > 250 && angle < 290)) { // good horizontal LineSegment line; if (pt1.x <= pt2.x) line = LineSegment(pt1.x, pt1.y, pt2.x, pt2.y); else line =LineSegment(pt2.x, pt2.y, pt1.x, pt1.y); // Get rid of the -1000, 1000 stuff. Terminate at the edges of the image // Helps with debugging/ rounding issues later int newY1 = line.getPointAt(0); int newY2 = line.getPointAt(edges.cols); PlateLine plateLine; plateLine.line = LineSegment(0, newY1, edges.cols, newY2); plateLine.confidence = (1.0 - MIN_CONFIDENCE) * ((float) (allLines.size() - i)) / ((float)allLines.size()) + MIN_CONFIDENCE; filteredLines.push_back(plateLine); } } } return filteredLines; } Mat PlateLines::customGrayscaleConversion(Mat src) { Mat img_hsv; cvtColor(src,img_hsv,CV_BGR2HSV); Mat grayscale = Mat(img_hsv.size(), CV_8U ); Mat hue(img_hsv.size(), CV_8U ); for (int row = 0; row < img_hsv.rows; row++) { for (int col = 0; col < img_hsv.cols; col++) { int h = (int) img_hsv.at(row, col)[0]; //int s = (int) img_hsv.at(row, col)[1]; int v = (int) img_hsv.at(row, col)[2]; int pixval = pow(v, 1.05); if (pixval > 255) pixval = 255; grayscale.at(row, col) = pixval; hue.at(row, col) = h * (255.0 / 180.0); } } //displayImage(config, "Hue", hue); return grayscale; } }openalpr_2.2.4.orig/src/openalpr/edges/platelines.h000066400000000000000000000033141266464252400224030ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_PLATELINES_H #define OPENALPR_PLATELINES_H #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "utility.h" #include "binarize_wolf.h" #include "config.h" #include "pipeline_data.h" namespace alpr { struct PlateLine { LineSegment line; float confidence; }; class PlateLines { public: PlateLines(PipelineData* pipelineData); virtual ~PlateLines(); void processImage(cv::Mat img, std::vector textLines, float sensitivity=1.0); std::vector horizontalLines; std::vector verticalLines; std::vector winningCorners; private: PipelineData* pipelineData; bool debug; cv::Mat customGrayscaleConversion(cv::Mat src); void findLines(cv::Mat inputImage); std::vector getLines(cv::Mat edges, float sensitivityMultiplier, bool vertical); }; } #endif // OPENALPR_PLATELINES_H openalpr_2.2.4.orig/src/openalpr/edges/scorekeeper.cpp000066400000000000000000000044221266464252400231060ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "scorekeeper.h" namespace alpr { ScoreKeeper::ScoreKeeper() { } ScoreKeeper::~ScoreKeeper() { } void ScoreKeeper::setScore(std::string weight_id, float score, float weight) { // Assume that we never set this value twice weight_ids.push_back(weight_id); scores.push_back(score); weights.push_back(weight); } float ScoreKeeper::getTotal() { float score = 0; for (unsigned int i = 0; i < weights.size(); i++) { score += scores[i] * weights[i]; } return score; } int ScoreKeeper::size() { return weight_ids.size(); } void ScoreKeeper::printDebugScores() { int longest_weight_id = 0; for (unsigned int i = 0; i < weight_ids.size(); i++) { if (weight_ids[i].length() > longest_weight_id) longest_weight_id = weight_ids[i].length(); } float total = getTotal(); std::cout << "--------------------" << std::endl; std::cout << "Total: " << total << std::endl; for (unsigned int i = 0; i < weight_ids.size(); i++) { float percent_of_total = (scores[i] * weights[i]) / total * 100; std::cout << " - " << std::setw(longest_weight_id + 1) << std::left << weight_ids[i] << " Weighted Score: " << std::setw(10) << std::left << (scores[i] * weights[i]) << " Orig Score: " << std::setw(10) << std::left << scores[i] << " (" << percent_of_total << "% of total)" << std::endl; } std::cout << "--------------------" << std::endl; } }openalpr_2.2.4.orig/src/openalpr/edges/scorekeeper.h000066400000000000000000000024121266464252400225500ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_SCOREKEEPER_H #define OPENALPR_SCOREKEEPER_H #include #include #include namespace alpr { class ScoreKeeper { public: ScoreKeeper(); virtual ~ScoreKeeper(); void setScore(std::string weight_id, float score, float weight); float getTotal(); int size(); void printDebugScores(); private: std::vector weight_ids; std::vector weights; std::vector scores; }; } #endif /* OPENALPR_SCOREKEEPER_H */ openalpr_2.2.4.orig/src/openalpr/edges/textlinecollection.cpp000066400000000000000000000111031266464252400245010ustar00rootroot00000000000000/* * File: textlinecollection.cpp * Author: mhill * * Created on October 25, 2014, 4:06 PM */ #include "textlinecollection.h" using namespace cv; using namespace std; namespace alpr { TextLineCollection::TextLineCollection(std::vector textLines) { charHeight = 0; charAngle = 0; for (unsigned int i = 0; i < textLines.size(); i++) { charHeight += textLines[i].lineHeight; charAngle += textLines[i].angle; } charHeight = charHeight / textLines.size(); charAngle = charAngle / textLines.size(); this->topCharArea = textLines[0].charBoxTop; this->bottomCharArea = textLines[0].charBoxBottom; for (unsigned int i = 1; i < textLines.size(); i++) { if (this->topCharArea.isPointBelowLine(textLines[i].charBoxTop.midpoint()) == false) this->topCharArea = textLines[i].charBoxTop; if (this->bottomCharArea.isPointBelowLine(textLines[i].charBoxBottom.midpoint())) this->bottomCharArea = textLines[i].charBoxBottom; } longerSegment = this->bottomCharArea; shorterSegment = this->topCharArea; if (this->topCharArea.length > this->bottomCharArea.length) { longerSegment = this->topCharArea; shorterSegment = this->bottomCharArea; } findCenterHorizontal(); findCenterVertical(); // Center Vertical Line } cv::Mat TextLineCollection::getDebugImage(cv::Size imageSize) { Mat debugImage = Mat::zeros(imageSize, CV_8U); line(debugImage, this->centerHorizontalLine.p1, this->centerHorizontalLine.p2, Scalar(255,255,255), 2); line(debugImage, this->centerVerticalLine.p1, this->centerVerticalLine.p2, Scalar(255,255,255), 2); return debugImage; } // Returns 1 for above, 0 for within, and -1 for below int TextLineCollection::isAboveText(LineSegment line) { // Test four points (left and right corner of top and bottom line) Point topLeft = line.closestPointOnSegmentTo(topCharArea.p1); Point topRight = line.closestPointOnSegmentTo(topCharArea.p2); bool lineIsBelowTop = topCharArea.isPointBelowLine(topLeft) || topCharArea.isPointBelowLine(topRight); if (!lineIsBelowTop) return 1; Point bottomLeft = line.closestPointOnSegmentTo(bottomCharArea.p1); Point bottomRight = line.closestPointOnSegmentTo(bottomCharArea.p2); bool lineIsBelowBottom = bottomCharArea.isPointBelowLine(bottomLeft) && bottomCharArea.isPointBelowLine(bottomRight); if (lineIsBelowBottom) return -1; return 0; } // Returns 1 for left, 0 for within, and -1 for to the right int TextLineCollection::isLeftOfText(LineSegment line) { LineSegment leftSide = LineSegment(bottomCharArea.p1, topCharArea.p1); Point topLeft = line.closestPointOnSegmentTo(leftSide.p2); Point bottomLeft = line.closestPointOnSegmentTo(leftSide.p1); bool lineIsAboveLeft = (!leftSide.isPointBelowLine(topLeft)) && (!leftSide.isPointBelowLine(bottomLeft)); if (lineIsAboveLeft) return 1; LineSegment rightSide = LineSegment(bottomCharArea.p2, topCharArea.p2); Point topRight = line.closestPointOnSegmentTo(rightSide.p2); Point bottomRight = line.closestPointOnSegmentTo(rightSide.p1); bool lineIsBelowRight = rightSide.isPointBelowLine(topRight) && rightSide.isPointBelowLine(bottomRight); if (lineIsBelowRight) return -1; return 0; } void TextLineCollection::findCenterHorizontal() { // To find the center horizontal line: // Find the longer of the lines (if multiline) // Get the nearest point on the bottom-most line for the // left and right Point leftP1 = shorterSegment.closestPointOnSegmentTo(longerSegment.p1); Point leftP2 = longerSegment.p1; LineSegment left = LineSegment(leftP1, leftP2); Point leftMidpoint = left.midpoint(); Point rightP1 = shorterSegment.closestPointOnSegmentTo(longerSegment.p2); Point rightP2 = longerSegment.p2; LineSegment right = LineSegment(rightP1, rightP2); Point rightMidpoint = right.midpoint(); this->centerHorizontalLine = LineSegment(leftMidpoint, rightMidpoint); } void TextLineCollection::findCenterVertical() { // To find the center vertical line: // Choose the longest line (if multiline) // Get the midpoint // Draw a line up/down using the closest point on the bottom line Point p1 = longerSegment.midpoint(); Point p2 = shorterSegment.closestPointOnSegmentTo(p1); // Draw bottom to top if (p1.y < p2.y) this->centerVerticalLine = LineSegment(p1, p2); else this->centerVerticalLine = LineSegment(p2, p1); } }openalpr_2.2.4.orig/src/openalpr/edges/textlinecollection.h000066400000000000000000000016271266464252400241600ustar00rootroot00000000000000/* * File: textlinecollection.h * Author: mhill * * Created on October 25, 2014, 4:06 PM */ #ifndef OPENALPR_TEXTLINECOLLECTION_H #define OPENALPR_TEXTLINECOLLECTION_H #include "utility.h" #include "opencv2/imgproc/imgproc.hpp" #include "textdetection/textline.h" namespace alpr { class TextLineCollection { public: TextLineCollection(std::vector textLines); int isLeftOfText(LineSegment line); int isAboveText(LineSegment line); LineSegment centerHorizontalLine; LineSegment centerVerticalLine; LineSegment longerSegment; LineSegment shorterSegment; float charHeight; float charAngle; cv::Mat getDebugImage(cv::Size imageSize); private: LineSegment topCharArea; LineSegment bottomCharArea; cv::Mat textMask; void findCenterHorizontal(); void findCenterVertical(); }; } #endif /* OPENALPR_TEXTLINECOLLECTION_H */ openalpr_2.2.4.orig/src/openalpr/licenseplatecandidate.cpp000066400000000000000000000117331266464252400240200ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "licenseplatecandidate.h" #include "edges/edgefinder.h" #include "transformation.h" using namespace std; using namespace cv; namespace alpr { LicensePlateCandidate::LicensePlateCandidate(PipelineData* pipeline_data) { this->pipeline_data = pipeline_data; this->config = pipeline_data->config; } LicensePlateCandidate::~LicensePlateCandidate() { delete charSegmenter; } // Must delete this pointer in parent class void LicensePlateCandidate::recognize() { charSegmenter = NULL; pipeline_data->isMultiline = config->multiline; Rect expandedRegion = this->pipeline_data->regionOfInterest; pipeline_data->crop_gray = Mat(this->pipeline_data->grayImg, expandedRegion); resize(pipeline_data->crop_gray, pipeline_data->crop_gray, Size(config->templateWidthPx, config->templateHeightPx)); CharacterAnalysis textAnalysis(pipeline_data); if (pipeline_data->disqualified) return; EdgeFinder edgeFinder(pipeline_data); pipeline_data->plate_corners = edgeFinder.findEdgeCorners(); if (pipeline_data->disqualified) return; timespec startTime; getTimeMonotonic(&startTime); // Compute the transformation matrix to go from the current image to the new plate corners Transformation imgTransform(this->pipeline_data->grayImg, pipeline_data->crop_gray, expandedRegion); Size cropSize = imgTransform.getCropSize(pipeline_data->plate_corners, Size(pipeline_data->config->ocrImageWidthPx, pipeline_data->config->ocrImageHeightPx)); Mat transmtx = imgTransform.getTransformationMatrix(pipeline_data->plate_corners, cropSize); // Crop the plate corners from the original color image (after un-applying prewarp) vector projectedPoints = pipeline_data->prewarp->projectPoints(pipeline_data->plate_corners, true); pipeline_data->color_deskewed = Mat::zeros(cropSize, pipeline_data->colorImg.type()); std::vector deskewed_points; deskewed_points.push_back(cv::Point2f(0,0)); deskewed_points.push_back(cv::Point2f(pipeline_data->color_deskewed.cols,0)); deskewed_points.push_back(cv::Point2f(pipeline_data->color_deskewed.cols,pipeline_data->color_deskewed.rows)); deskewed_points.push_back(cv::Point2f(0,pipeline_data->color_deskewed.rows)); cv::Mat color_transmtx = cv::getPerspectiveTransform(projectedPoints, deskewed_points); cv::warpPerspective(pipeline_data->colorImg, pipeline_data->color_deskewed, color_transmtx, pipeline_data->color_deskewed.size()); if (pipeline_data->color_deskewed.channels() > 2) { // Make a grayscale copy as well for faster processing downstream cv::cvtColor(pipeline_data->color_deskewed, pipeline_data->crop_gray, CV_BGR2GRAY); } else { // Copy the already grayscale image to the crop_gray img pipeline_data->color_deskewed.copyTo(pipeline_data->crop_gray); } if (this->config->debugGeneral) displayImage(config, "quadrilateral", pipeline_data->color_deskewed); // Apply a perspective transformation to the TextLine objects // to match the newly deskewed license plate crop vector newLines; for (unsigned int i = 0; i < pipeline_data->textLines.size(); i++) { vector textArea = imgTransform.transformSmallPointsToBigImage(pipeline_data->textLines[i].textArea); vector linePolygon = imgTransform.transformSmallPointsToBigImage(pipeline_data->textLines[i].linePolygon); vector textAreaRemapped; vector linePolygonRemapped; textAreaRemapped = imgTransform.remapSmallPointstoCrop(textArea, transmtx); linePolygonRemapped = imgTransform.remapSmallPointstoCrop(linePolygon, transmtx); newLines.push_back(TextLine(textAreaRemapped, linePolygonRemapped, pipeline_data->crop_gray.size())); } pipeline_data->textLines.clear(); for (unsigned int i = 0; i < newLines.size(); i++) pipeline_data->textLines.push_back(newLines[i]); if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << "deskew Time: " << diffclock(startTime, endTime) << "ms." << endl; } charSegmenter = new CharacterSegmenter(pipeline_data); } } openalpr_2.2.4.orig/src/openalpr/licenseplatecandidate.h000066400000000000000000000036301266464252400234620ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_LICENSEPLATECANDIDATE_H #define OPENALPR_LICENSEPLATECANDIDATE_H #include #include #include #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/core/core.hpp" #include "utility.h" #include "constants.h" #include "edges/platelines.h" #include "transformation.h" #include "textdetection/characteranalysis.h" #include "segmentation/charactersegmenter.h" #include "edges/platecorners.h" #include "config.h" #include "pipeline_data.h" namespace alpr { class LicensePlateCandidate { public: LicensePlateCandidate(PipelineData* pipeline_data); virtual ~LicensePlateCandidate(); void recognize(); private: PipelineData* pipeline_data; Config* config; CharacterSegmenter* charSegmenter; cv::Mat filterByCharacterHue(std::vector > charRegionContours); std::vector findPlateCorners(cv::Mat inputImage, PlateLines plateLines, CharacterAnalysis textAnalysis); // top-left, top-right, bottom-right, bottom-left cv::Size getCropSize(std::vector areaCorners); }; } #endif // OPENALPR_LICENSEPLATECANDIDATE_H openalpr_2.2.4.orig/src/openalpr/motiondetector.cpp000066400000000000000000000042441266464252400225510ustar00rootroot00000000000000#include "motiondetector.h" using namespace cv; namespace alpr { MotionDetector::MotionDetector() { #if OPENCV_MAJOR_VERSION == 2 pMOG2 = new BackgroundSubtractorMOG2(); #else // OpenCV 3 pMOG2 = createBackgroundSubtractorMOG2(); #endif } MotionDetector::~MotionDetector() { } void MotionDetector::ResetMotionDetection(cv::Mat* frame) { #if OPENCV_MAJOR_VERSION == 2 pMOG2->operator()(*frame, fgMaskMOG2, 1); #else // OpenCV 3 pMOG2->apply(*frame, fgMaskMOG2, 1); #endif } cv::Rect MotionDetector::MotionDetect(cv::Mat* frame) //Detect motion and create ONE recangle that contains all the detected motion { std::vector > contours; std::vector hierarchy; cv::Rect bounding_rect; std::vector rects; cv::Rect largest_rect, rect_temp; // Detect motion #if OPENCV_MAJOR_VERSION == 2 pMOG2->operator()(*frame, fgMaskMOG2, -1); #else // OpenCV 3 pMOG2->apply(*frame, fgMaskMOG2); #endif //Remove noise cv::erode(fgMaskMOG2, fgMaskMOG2, getStructuringElement(cv::MORPH_RECT, cv::Size(6, 6))); // Find the contours of motion areas in the image findContours(fgMaskMOG2, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); // Find the bounding rectangles of the areas of motion if (contours.size() > 0) { for (int i = 0; i < contours.size(); i++) { bounding_rect = boundingRect(contours[i]); rects.push_back(bounding_rect); } // Determine the overall area with motion. largest_rect = rects[0]; for (int i = 1; i < rects.size(); i++) { rect_temp.x = min(largest_rect.x,rects[i].x); rect_temp.y = min(largest_rect.y,rects[i].y); rect_temp.width = max(largest_rect.x + largest_rect.width, rects[i].x + rects[i].width)-rect_temp.x; rect_temp.height = max(largest_rect.y + largest_rect.height, rects[i].y + rects[i].height) - rect_temp.y; largest_rect = rect_temp; } rectangle(*frame, rect_temp, cv::Scalar(0, 255, 0), 1, 8, 0); } else { largest_rect.x = 0; largest_rect.y = 0; largest_rect.width = 0; largest_rect.height = 0; } // imshow("Motion detect", fgMaskMOG2); return expandRect(largest_rect, 0, 0, frame->cols, frame->rows); } }openalpr_2.2.4.orig/src/openalpr/motiondetector.h000066400000000000000000000007771266464252400222250ustar00rootroot00000000000000 #ifndef OPENALPR_MOTIONDETECTOR_H #define OPENALPR_MOTIONDETECTOR_H #include "opencv2/opencv.hpp" #include "utility.h" namespace alpr { class MotionDetector { private: cv::Ptr pMOG2; //MOG2 Background subtractor private: cv::Mat fgMaskMOG2; public: MotionDetector(); virtual ~MotionDetector(); void ResetMotionDetection(cv::Mat* frame); cv::Rect MotionDetect(cv::Mat* frame); }; } #endif // OPENALPR_MOTIONDETECTOR_Hopenalpr_2.2.4.orig/src/openalpr/ocr.cpp000066400000000000000000000126321266464252400202750ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "ocr.h" using namespace std; using namespace cv; using namespace tesseract; namespace alpr { OCR::OCR(Config* config) : postProcessor(config) { const string MINIMUM_TESSERACT_VERSION = "3.03"; this->config = config; if (cmpVersion(tesseract.Version(), MINIMUM_TESSERACT_VERSION.c_str()) < 0) { std::cerr << "Warning: You are running an unsupported version of Tesseract." << endl; std::cerr << "Expecting at least " << MINIMUM_TESSERACT_VERSION << ", your version is: " << tesseract.Version() << endl; } // Tesseract requires the prefix directory to be set as an env variable tesseract.Init(config->getTessdataPrefix().c_str(), config->ocrLanguage.c_str() ); tesseract.SetVariable("save_blob_choices", "T"); tesseract.SetVariable("debug_file", "/dev/null"); tesseract.SetPageSegMode(PSM_SINGLE_CHAR); } OCR::~OCR() { tesseract.End(); } void OCR::performOCR(PipelineData* pipeline_data) { const int SPACE_CHAR_CODE = 32; timespec startTime; getTimeMonotonic(&startTime); postProcessor.clear(); // Don't waste time on OCR processing if it is impossible to get sufficient characters int total_char_spaces = 0; for (unsigned int i = 0; i < pipeline_data->charRegions.size(); i++) total_char_spaces += pipeline_data->charRegions[i].size(); if (total_char_spaces < config->postProcessMinCharacters) { pipeline_data->disqualify_reason = "Insufficient character boxes detected. No OCR performed."; pipeline_data->disqualified = true; return; } for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { // Make it black text on white background bitwise_not(pipeline_data->thresholds[i], pipeline_data->thresholds[i]); tesseract.SetImage((uchar*) pipeline_data->thresholds[i].data, pipeline_data->thresholds[i].size().width, pipeline_data->thresholds[i].size().height, pipeline_data->thresholds[i].channels(), pipeline_data->thresholds[i].step1()); int absolute_charpos = 0; for (unsigned int line_idx = 0; line_idx < pipeline_data->charRegions.size(); line_idx++) { for (unsigned int j = 0; j < pipeline_data->charRegions[line_idx].size(); j++) { Rect expandedRegion = expandRect( pipeline_data->charRegions[line_idx][j], 2, 2, pipeline_data->thresholds[i].cols, pipeline_data->thresholds[i].rows) ; tesseract.SetRectangle(expandedRegion.x, expandedRegion.y, expandedRegion.width, expandedRegion.height); tesseract.Recognize(NULL); tesseract::ResultIterator* ri = tesseract.GetIterator(); tesseract::PageIteratorLevel level = tesseract::RIL_SYMBOL; do { const char* symbol = ri->GetUTF8Text(level); float conf = ri->Confidence(level); bool dontcare; int fontindex = 0; int pointsize = 0; const char* fontName = ri->WordFontAttributes(&dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &pointsize, &fontindex); // Ignore NULL pointers, spaces, and characters that are way too small to be valid if(symbol != 0 && symbol[0] != SPACE_CHAR_CODE && pointsize >= config->ocrMinFontSize) { postProcessor.addLetter(string(symbol), line_idx, absolute_charpos, conf); if (this->config->debugOcr) printf("charpos%d line%d: threshold %d: symbol %s, conf: %f font: %s (index %d) size %dpx", absolute_charpos, line_idx, i, symbol, conf, fontName, fontindex, pointsize); bool indent = false; tesseract::ChoiceIterator ci(*ri); do { const char* choice = ci.GetUTF8Text(); postProcessor.addLetter(string(choice), line_idx, absolute_charpos, ci.Confidence()); if (this->config->debugOcr) { if (indent) printf("\t\t "); printf("\t- "); printf("%s conf: %f\n", choice, ci.Confidence()); } indent = true; } while(ci.Next()); } if (this->config->debugOcr) printf("---------------------------------------------\n"); delete[] symbol; } while((ri->Next(level))); delete ri; absolute_charpos++; } } } if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << "OCR Time: " << diffclock(startTime, endTime) << "ms." << endl; } } }openalpr_2.2.4.orig/src/openalpr/ocr.h000066400000000000000000000026041266464252400177400ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_OCR_H #define OPENALPR_OCR_H #include #include #include "utility.h" #include "postprocess/postprocess.h" #include "config.h" #include "pipeline_data.h" #include "constants.h" #include "opencv2/imgproc/imgproc.hpp" #include "support/filesystem.h" #include "support/version.h" #include "tesseract/baseapi.h" namespace alpr { class OCR { public: OCR(Config* config); virtual ~OCR(); void performOCR(PipelineData* pipeline_data); PostProcess postProcessor; private: Config* config; tesseract::TessBaseAPI tesseract; }; } #endif // OPENALPR_OCR_H openalpr_2.2.4.orig/src/openalpr/pipeline_data.cpp000066400000000000000000000022731266464252400223100ustar00rootroot00000000000000#include "pipeline_data.h" using namespace cv; using namespace std; namespace alpr { PipelineData::PipelineData(Mat colorImage, Rect regionOfInterest, Config* config) { Mat grayImage; if (colorImage.channels() > 2) { cvtColor(colorImage, grayImage, CV_BGR2GRAY); } else { grayImage = colorImage; } this->init(colorImage, grayImage, regionOfInterest, config); } PipelineData::PipelineData(Mat colorImage, Mat grayImg, Rect regionOfInterest, Config* config) { this->init(colorImage, grayImg, regionOfInterest, config); } PipelineData::~PipelineData() { clearThresholds(); } void PipelineData::clearThresholds() { for (unsigned int i = 0; i < thresholds.size(); i++) { thresholds[i].release(); } thresholds.clear(); } void PipelineData::init(cv::Mat colorImage, cv::Mat grayImage, cv::Rect regionOfInterest, Config *config) { this->colorImg = colorImage; this->grayImg = grayImage; this->regionOfInterest = regionOfInterest; this->config = config; this->region_confidence = 0; this->plate_inverted = false; this->disqualified = false; this->disqualify_reason = ""; } } openalpr_2.2.4.orig/src/openalpr/pipeline_data.h000066400000000000000000000031021266464252400217450ustar00rootroot00000000000000 #ifndef OPENALPR_PIPELINEDATA_H #define OPENALPR_PIPELINEDATA_H #include "opencv2/imgproc/imgproc.hpp" #include "utility.h" #include "config.h" #include "textdetection/textline.h" #include "edges/scorekeeper.h" #include "prewarp.h" namespace alpr { class PipelineData { public: PipelineData(cv::Mat colorImage, cv::Rect regionOfInterest, Config* config); PipelineData(cv::Mat colorImage, cv::Mat grayImage, cv::Rect regionOfInterest, Config* config); virtual ~PipelineData(); void init(cv::Mat colorImage, cv::Mat grayImage, cv::Rect regionOfInterest, Config* config); void clearThresholds(); // Inputs Config* config; PreWarp* prewarp; cv::Mat colorImg; cv::Mat grayImg; cv::Rect regionOfInterest; bool isMultiline; cv::Mat crop_gray; cv::Mat color_deskewed; bool hasPlateBorder; cv::Mat plateBorderMask; std::vector textLines; std::vector thresholds; std::vector plate_corners; // Outputs bool plate_inverted; std::string region_code; float region_confidence; bool disqualified; std::string disqualify_reason; ScoreKeeper confidence_weights; // Boxes around characters in cropped image // Each row in a multiline plate is an entry in the vector std::vector > charRegions; // Same data, just not broken down by line std::vector charRegionsFlat; // OCR }; } #endif // OPENALPR_PIPELINEDATA_Hopenalpr_2.2.4.orig/src/openalpr/postprocess/000077500000000000000000000000001266464252400213665ustar00rootroot00000000000000openalpr_2.2.4.orig/src/openalpr/postprocess/postprocess.cpp000066400000000000000000000303701266464252400244610ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "postprocess.h" using namespace std; namespace alpr { PostProcess::PostProcess(Config* config) { this->config = config; stringstream filename; filename << config->getPostProcessRuntimeDir() << "/" << config->country << ".patterns"; std::ifstream infile(filename.str().c_str()); string region, pattern; while (infile >> region >> pattern) { RegexRule* rule = new RegexRule(region, pattern, config->postProcessRegexLetters, config->postProcessRegexNumbers); //cout << "REGION: " << region << " PATTERN: " << pattern << endl; if (rules.find(region) == rules.end()) { vector newRule; newRule.push_back(rule); rules[region] = newRule; } else { vector oldRule = rules[region]; oldRule.push_back(rule); rules[region] = oldRule; } } //vector test = rules["base"]; //for (int i = 0; i < test.size(); i++) // cout << "Rule: " << test[i].regex << endl; } PostProcess::~PostProcess() { // TODO: Delete all entries in rules vector map >::iterator iter; for (iter = rules.begin(); iter != rules.end(); ++iter) { for (int i = 0; i < iter->second.size(); i++) { delete iter->second[i]; } } } void PostProcess::addLetter(string letter, int line_index, int charposition, float score) { if (score < config->postProcessMinConfidence) return; insertLetter(letter, line_index, charposition, score); if (score < config->postProcessConfidenceSkipLevel) { float adjustedScore = abs(config->postProcessConfidenceSkipLevel - score) + config->postProcessMinConfidence; insertLetter(SKIP_CHAR, line_index, charposition, adjustedScore ); } //if (letter == '0') //{ // insertLetter('O', charposition, score - 0.5); //} } void PostProcess::insertLetter(string letter, int line_index, int charposition, float score) { score = score - config->postProcessMinConfidence; int existingIndex = -1; if (letters.size() < charposition + 1) { for (int i = letters.size(); i < charposition + 1; i++) { vector tmp; letters.push_back(tmp); } } for (int i = 0; i < letters[charposition].size(); i++) { if (letters[charposition][i].letter == letter && letters[charposition][i].line_index == line_index && letters[charposition][i].charposition == charposition) { existingIndex = i; break; } } if (existingIndex == -1) { Letter newLetter; newLetter.line_index = line_index; newLetter.charposition = charposition; newLetter.letter = letter; newLetter.occurrences = 1; newLetter.totalscore = score; letters[charposition].push_back(newLetter); } else { letters[charposition][existingIndex].occurrences = letters[charposition][existingIndex].occurrences + 1; letters[charposition][existingIndex].totalscore = letters[charposition][existingIndex].totalscore + score; } } void PostProcess::clear() { for (int i = 0; i < letters.size(); i++) { letters[i].clear(); } letters.resize(0); unknownCharPositions.clear(); unknownCharPositions.resize(0); allPossibilities.clear(); allPossibilitiesLetters.clear(); //allPossibilities.resize(0); bestChars = ""; matchesTemplate = false; } void PostProcess::analyze(string templateregion, int topn) { timespec startTime; getTimeMonotonic(&startTime); // Get a list of missing positions for (int i = letters.size() -1; i >= 0; i--) { if (letters[i].size() == 0) { unknownCharPositions.push_back(i); } } if (letters.size() == 0) return; // Sort the letters as they are for (int i = 0; i < letters.size(); i++) { if (letters[i].size() > 0) std::stable_sort(letters[i].begin(), letters[i].end(), letterCompare); } if (this->config->debugPostProcess) { // Print all letters for (int i = 0; i < letters.size(); i++) { for (int j = 0; j < letters[i].size(); j++) cout << "PostProcess Line " << letters[i][j].line_index << " Letter: " << letters[i][j].charposition << " " << letters[i][j].letter << " -- score: " << letters[i][j].totalscore << " -- occurrences: " << letters[i][j].occurrences << endl; } } timespec permutationStartTime; getTimeMonotonic(&permutationStartTime); findAllPermutations(templateregion, topn); if (config->debugTiming) { timespec permutationEndTime; getTimeMonotonic(&permutationEndTime); cout << " -- PostProcess Permutation Time: " << diffclock(permutationStartTime, permutationEndTime) << "ms." << endl; } if (allPossibilities.size() > 0) { bestChars = allPossibilities[0].letters; for (int z = 0; z < allPossibilities.size(); z++) { if (allPossibilities[z].matchesTemplate) { bestChars = allPossibilities[z].letters; break; } } // Now adjust the confidence scores to a percentage value float maxPercentScore = calculateMaxConfidenceScore(); float highestRelativeScore = (float) allPossibilities[0].totalscore; for (int i = 0; i < allPossibilities.size(); i++) { allPossibilities[i].totalscore = maxPercentScore * (allPossibilities[i].totalscore / highestRelativeScore); } } if (this->config->debugPostProcess) { // Print top words for (int i = 0; i < allPossibilities.size(); i++) { cout << "Top " << topn << " Possibilities: " << allPossibilities[i].letters << " :\t" << allPossibilities[i].totalscore; if (allPossibilities[i].letters == bestChars) cout << " <--- "; cout << endl; } cout << allPossibilities.size() << " total permutations" << endl; } if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << "PostProcess Time: " << diffclock(startTime, endTime) << "ms." << endl; } if (this->config->debugPostProcess) cout << "PostProcess Analysis Complete: " << bestChars << " -- MATCH: " << matchesTemplate << endl; } bool PostProcess::regionIsValid(std::string templateregion) { return rules.find(templateregion) != rules.end(); } float PostProcess::calculateMaxConfidenceScore() { // Take the best score for each char position and average it. float totalScore = 0; int numScores = 0; // Get a list of missing positions for (int i = 0; i < letters.size(); i++) { if (letters[i].size() > 0) { totalScore += (letters[i][0].totalscore / letters[i][0].occurrences) + config->postProcessMinConfidence; numScores++; } } if (numScores == 0) return 0; return totalScore / ((float) numScores); } const vector PostProcess::getResults() { return this->allPossibilities; } struct PermutationCompare { bool operator() (pair > &a, pair > &b) { return (a.first < b.first); } }; void PostProcess::findAllPermutations(string templateregion, int topn) { // use a priority queue to process permutations in highest scoring order priority_queue >, vector > >, PermutationCompare> permutations; set permutationHashes; // push the first word onto the queue float totalscore = 0; for (int i=0; i 0) totalscore += letters[i][0].totalscore; } vector v(letters.size()); permutations.push(make_pair(totalscore, v)); int consecutiveNonMatches = 0; while (permutations.size() > 0) { // get the top permutation and analyze pair > topPermutation = permutations.top(); if (analyzePermutation(topPermutation.second, templateregion, topn) == true) consecutiveNonMatches = 0; else consecutiveNonMatches += 1; permutations.pop(); if (allPossibilities.size() >= topn || consecutiveNonMatches >= (topn*2)) break; // add child permutations to queue for (int i=0; i= letters[i].size()) continue; pair > childPermutation = topPermutation; childPermutation.first -= letters[i][topPermutation.second[i]].totalscore - letters[i][topPermutation.second[i] + 1].totalscore; childPermutation.second[i] += 1; // ignore permutations that have already been visited (assume that score is a good hash for permutation) if (permutationHashes.end() != permutationHashes.find(childPermutation.first)) continue; permutations.push(childPermutation); permutationHashes.insert(childPermutation.first); } } } bool PostProcess::analyzePermutation(vector letterIndices, string templateregion, int topn) { PPResult possibility; possibility.letters = ""; possibility.totalscore = 0; possibility.matchesTemplate = false; int plate_char_length = 0; int last_line = 0; for (int i = 0; i < letters.size(); i++) { if (letters[i].size() == 0) continue; Letter letter = letters[i][letterIndices[i]]; // Add a "\n" on new lines if (letter.line_index != last_line) { possibility.letters = possibility.letters + "\n"; } last_line = letter.line_index; if (letter.letter != SKIP_CHAR) { possibility.letters = possibility.letters + letter.letter; possibility.letter_details.push_back(letter); plate_char_length += 1; } possibility.totalscore = possibility.totalscore + letter.totalscore; } // ignore plates that don't fit the length requirements if (plate_char_length < config->postProcessMinCharacters || plate_char_length > config->postProcessMaxCharacters) return false; // Apply templates if (templateregion != "") { vector regionRules = rules[templateregion]; for (int i = 0; i < regionRules.size(); i++) { possibility.matchesTemplate = regionRules[i]->match(possibility.letters); if (possibility.matchesTemplate) { possibility.letters = regionRules[i]->filterSkips(possibility.letters); break; } } } // ignore duplicate words if (allPossibilitiesLetters.end() != allPossibilitiesLetters.find(possibility.letters)) return false; // If mustMatchPattern is toggled in the config and a template is provided, // only include this result if there is a pattern match if (!config->mustMatchPattern || templateregion.size() == 0 || (config->mustMatchPattern && possibility.matchesTemplate)) { allPossibilities.push_back(possibility); allPossibilitiesLetters.insert(possibility.letters); return true; } return false; } std::vector PostProcess::getPatterns() { vector v; for(map >::iterator it = rules.begin(); it != rules.end(); ++it) { v.push_back(it->first); } return v; } bool letterCompare( const Letter &left, const Letter &right ) { if (left.totalscore < right.totalscore) return false; return true; } }openalpr_2.2.4.orig/src/openalpr/postprocess/postprocess.h000066400000000000000000000047541266464252400241350ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_POSTPROCESS_H #define OPENALPR_POSTPROCESS_H #include "regexrule.h" #include "constants.h" #include "utility.h" #include #include #include #include #include #include #include "config.h" #define SKIP_CHAR "~" namespace alpr { struct Letter { std::string letter; int line_index; int charposition; float totalscore; int occurrences; }; struct PPResult { std::string letters; float totalscore; bool matchesTemplate; std::vector letter_details; }; bool letterCompare( const Letter &left, const Letter &right ); class PostProcess { public: PostProcess(Config* config); ~PostProcess(); void addLetter(std::string letter, int line_index, int charposition, float score); void clear(); void analyze(std::string templateregion, int topn); std::string bestChars; bool matchesTemplate; const std::vector getResults(); bool regionIsValid(std::string templateregion); std::vector getPatterns(); private: Config* config; void findAllPermutations(std::string templateregion, int topn); bool analyzePermutation(std::vector letterIndices, std::string templateregion, int topn); void insertLetter(std::string letter, int line_index, int charPosition, float score); std::map > rules; float calculateMaxConfidenceScore(); std::vector > letters; std::vector unknownCharPositions; std::vector allPossibilities; std::set allPossibilitiesLetters; }; } #endif // OPENALPR_POSTPROCESS_H openalpr_2.2.4.orig/src/openalpr/postprocess/regexrule.cpp000066400000000000000000000074761266464252400241120ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "regexrule.h" using namespace std; tthread::mutex regexrule_mutex_m; namespace alpr { RegexRule::RegexRule(string region, string pattern, std::string letters_regex, std::string numbers_regex) //: re2_regex("") { this->original = pattern; this->region = region; this->regex = ""; this->valid = false; string::iterator end_it = utf8::find_invalid(pattern.begin(), pattern.end()); if (end_it != pattern.end()) { cerr << "Invalid UTF-8 encoding detected " << endl; return; } std::stringstream regexval; string::iterator utf_iterator = pattern.begin(); numchars = 0; while (utf_iterator < pattern.end()) { int cp = utf8::next(utf_iterator, pattern.end()); string utf_character = utf8chr(cp); if (utf_character == "[") { regexval << "["; while (utf_character != "]" ) { if (utf_iterator >= pattern.end()) break; // Invalid regex, don't bother processing int cp = utf8::next(utf_iterator, pattern.end()); utf_character = utf8chr(cp); regexval << utf_character; } } else if (utf_character == "\\") { // Don't add "\" characters to our character count regexval << utf_character; continue; } else if (utf_character == "?") { regexval << "."; this->skipPositions.push_back(numchars); } else if (utf_character == "@") { regexval << letters_regex; } else if (utf_character == "#") { regexval << numbers_regex; } else if ((utf_character == "*") || (utf_character == "+")) { cerr << "Regex with wildcards (* or +) not supported" << endl; } else { regexval << utf_character; } numchars++; } this->regex = regexval.str(); re2_regex = new re2::RE2(this->regex); if (!re2_regex->ok()) { cerr << "Unable to load regex: " << pattern << endl; } else { this->valid = true; } } RegexRule::~RegexRule() { delete re2_regex; } bool RegexRule::match(string text) { if (!this->valid) return false; string::iterator end_it = utf8::find_invalid(text.begin(), text.end()); if (end_it != text.end()) { cerr << "Invalid UTF-8 encoding detected " << endl; return false; } int text_char_length = utf8::distance(text.begin(), text.end()); if (text_char_length != numchars) return false; bool match = re2::RE2::FullMatch(text, *re2_regex); return match; } string RegexRule::filterSkips(string text) { string response = ""; for (int i = 0; i < text.size(); i++) { bool skip = false; for (int j = 0; j < skipPositions.size(); j++) { if (skipPositions[j] == i) { skip = true; break; } } if (skip == false) response = response + text[i]; } return response; } } openalpr_2.2.4.orig/src/openalpr/postprocess/regexrule.h000066400000000000000000000027451266464252400235510ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_REGEXRULE_H #define OPENALPR_REGEXRULE_H #include #include #include #include #include "support/re2.h" #include "support/utf8.h" #include "support/tinythread.h" namespace alpr { class RegexRule { public: RegexRule(std::string region, std::string pattern, std::string letters_regex, std::string numbers_regex); virtual ~RegexRule(); bool match(std::string text); std::string filterSkips(std::string text); private: bool valid; int numchars; re2::RE2* re2_regex; std::string original; std::string regex; std::string region; std::vector skipPositions; }; } #endif /* OPENALPR_REGEXRULE_H */ openalpr_2.2.4.orig/src/openalpr/prewarp.cpp000066400000000000000000000161401266464252400211700ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "prewarp.h" using namespace std; using namespace cv; namespace alpr { PreWarp::PreWarp(Config* config) { this->config = config; initialize(config->prewarp); } void PreWarp::initialize(std::string prewarp_config) { timespec startTime; getTimeMonotonic(&startTime); // Do a cursory verification based on number of commas int commacount = count(prewarp_config.begin(), prewarp_config.end(), ','); if (prewarp_config.length() < 4) { // No config specified. ignore if (this->config->debugPrewarp) cout << "No prewarp configuration specified" << endl; this->valid = false; } else if (commacount != 9) { if (this->config->debugPrewarp) cout << "Invalid prewarp configuration" << endl; this->valid = false; } else { // Parse the warp_config int first_comma = prewarp_config.find(","); string name = prewarp_config.substr(0, first_comma); if (name != "planar") { this->valid = false; } else { stringstream ss(prewarp_config.substr(first_comma + 1, prewarp_config.length())); ss >> w; ss.ignore(); ss >> h; ss.ignore(); ss >> rotationx; ss.ignore(); // Ignore comma ss >> rotationy; ss.ignore(); // Ignore comma ss >> rotationz; ss.ignore(); // Ignore comma ss >> stretchX; ss.ignore(); // Ignore comma ss >> dist; ss.ignore(); // Ignore comma ss >> panX; ss.ignore(); // Ignore comma ss >> panY; this->valid = true; } } timespec endTime; getTimeMonotonic(&endTime); if (config->debugTiming) cout << "Prewarp Initialization Time: " << diffclock(startTime, endTime) << "ms." << endl; } void PreWarp::clear() { this->valid = false; } PreWarp::~PreWarp() { } cv::Mat PreWarp::warpImage(Mat image) { if (!this->valid) { if (this->config->debugPrewarp) cout << "prewarp skipped due to missing prewarp config" << endl; return image; } float width_ratio = w / ((float)image.cols); float height_ratio = h / ((float)image.rows); float rx = rotationx * width_ratio; float ry = rotationy * width_ratio; float px = panX / width_ratio; float py = panY / height_ratio; transform = findTransform(image.cols, image.rows, rx, ry, rotationz, px, py, stretchX, dist); Mat warped_image; warpPerspective(image, warped_image, transform, image.size(), INTER_CUBIC | WARP_INVERSE_MAP); if (this->config->debugPrewarp && this->config->debugShowImages) { imshow("Prewarp", warped_image); } return warped_image; } // Projects a "region of interest" into the new space // The rect needs to be converted to points, warped, then converted back into a // bounding rectangle vector PreWarp::projectRects(vector rects, int maxWidth, int maxHeight, bool inverse) { if (!this->valid) return rects; vector projected_rects; for (unsigned int i = 0; i < rects.size(); i++) { vector points; points.push_back(Point(rects[i].x, rects[i].y)); points.push_back(Point(rects[i].x + rects[i].width, rects[i].y)); points.push_back(Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height)); points.push_back(Point(rects[i].x, rects[i].y + rects[i].height)); vector projectedPoints = projectPoints(points, inverse); Rect projectedRect = boundingRect(projectedPoints); projectedRect = expandRect(projectedRect, 0, 0, maxWidth, maxHeight); projected_rects.push_back(projectedRect); } return projected_rects; } vector PreWarp::projectPoints(vector points, bool inverse) { if (!this->valid) return points; vector output; if (!inverse) perspectiveTransform(points, output, transform.inv()); else perspectiveTransform(points, output, transform); return output; } void PreWarp::projectPlateRegions(vector& plateRegions, int maxWidth, int maxHeight, bool inverse){ if (!this->valid) return; for (unsigned int i = 0; i < plateRegions.size(); i++) { vector singleRect; singleRect.push_back(plateRegions[i].rect); vector transformedRect = projectRects(singleRect, maxWidth, maxHeight, inverse); plateRegions[i].rect.x = transformedRect[0].x; plateRegions[i].rect.y = transformedRect[0].y; plateRegions[i].rect.width = transformedRect[0].width; plateRegions[i].rect.height = transformedRect[0].height; projectPlateRegions(plateRegions[i].children, maxWidth, maxHeight, inverse); } } cv::Mat PreWarp::findTransform(float w, float h, float rotationx, float rotationy, float rotationz, float panX, float panY, float stretchX, float dist) { float alpha = rotationx; float beta = rotationy; float gamma = rotationz; float f = 1.0; // Projection 2D -> 3D matrix Mat A1 = (Mat_(4,3) << 1, 0, -w/2, 0, 1, -h/2, 0, 0, 0, 0, 0, 1); // Camera Intrisecs matrix 3D -> 2D Mat A2 = (Mat_(3,4) << f, 0, w/2, 0, 0, f, h/2, 0, 0, 0, 1, 0); // Rotation matrices around the X axis Mat Rx = (Mat_(4, 4) << 1, 0, 0, 0, 0, cos(alpha), -sin(alpha), 0, 0, sin(alpha), cos(alpha), 0, 0, 0, 0, 1); // Rotation matrices around the Y axis Mat Ry = (Mat_(4, 4) << cos(beta), 0, sin(beta), 0, 0, 1, 0, 0, -sin(beta), 0, cos(beta), 0, 0, 0, 0, 1); // Rotation matrices around the Z axis Mat Rz = (Mat_(4, 4) << cos(gamma), -sin(gamma), 0, 0, sin(gamma), cos(gamma), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); Mat R = Rx*Ry*Rz; // Translation matrix on the Z axis Mat T = (Mat_(4, 4) << stretchX, 0, 0, panX, 0, 1, 0, panY, 0, 0, 1, dist, 0, 0, 0, 1); return A2 * (T * (R * A1)); } } openalpr_2.2.4.orig/src/openalpr/prewarp.h000066400000000000000000000034301266464252400206330ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_PREWARP_H #define OPENALPR_PREWARP_H #include "config.h" #include "utility.h" #include "opencv2/imgproc/imgproc.hpp" #include "detection/detector.h" namespace alpr { class PreWarp { public: PreWarp(Config* config); virtual ~PreWarp(); void initialize(std::string prewarp_config); void clear(); cv::Mat warpImage(cv::Mat image); std::vector projectPoints(std::vector points, bool inverse); std::vector projectRects(std::vector rects, int maxWidth, int maxHeight, bool inverse); void projectPlateRegions(std::vector& plateRegions, int maxWidth, int maxHeight, bool inverse); bool valid; private: Config* config; cv::Mat transform; float w, h, rotationx, rotationy, rotationz, stretchX, dist, panX, panY; cv::Mat findTransform(float w, float h, float rotationx, float rotationy, float rotationz, float panX, float panY, float stretchX, float dist); }; } #endif /* OPENALPR_PREWARP_H */ openalpr_2.2.4.orig/src/openalpr/result_aggregator.cpp000066400000000000000000000130741266464252400232330ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "result_aggregator.h" using namespace std; using namespace cv; namespace alpr { ResultAggregator::ResultAggregator() { } ResultAggregator::~ResultAggregator() { } void ResultAggregator::addResults(AlprFullDetails full_results) { all_results.push_back(full_results); } AlprFullDetails ResultAggregator::getAggregateResults() { assert(all_results.size() > 0); if (all_results.size() == 1) return all_results[0]; AlprFullDetails response; // Plate regions are needed for benchmarking // Copy all detected boxes across all results for (unsigned int i = 0; i < all_results.size(); i++) { for (unsigned int k = 0; k < all_results[i].plateRegions.size(); k++) response.plateRegions.push_back(all_results[i].plateRegions[k]); } response.results.epoch_time = all_results[0].results.epoch_time; response.results.img_height = all_results[0].results.img_height; response.results.img_width = all_results[0].results.img_width; response.results.total_processing_time_ms = all_results[0].results.total_processing_time_ms; response.results.regionsOfInterest = all_results[0].results.regionsOfInterest; vector > clusters = findClusters(); // Assume we have multiple results, one cluster for each unique train data (e.g., eu, eu2) // Now for each cluster of plates, pick the best one for (unsigned int i = 0; i < clusters.size(); i++) { float best_confidence = 0; int best_index = 0; for (unsigned int k = 0; k < clusters[i].size(); k++) { if (clusters[i][k].bestPlate.overall_confidence > best_confidence) { best_confidence = clusters[i][k].bestPlate.overall_confidence; best_index = k; } } response.results.plates.push_back(clusters[i][best_index]); } return response; } // Searches all_plates to find overlapping plates // Returns an array containing "clusters" (overlapping plates) std::vector > ResultAggregator::findClusters() { std::vector > clusters; for (unsigned int i = 0; i < all_results.size(); i++) { for (unsigned int plate_id = 0; plate_id < all_results[i].results.plates.size(); plate_id++) { AlprPlateResult plate = all_results[i].results.plates[plate_id]; int cluster_index = overlaps(plate, clusters); if (cluster_index < 0) { vector new_cluster; new_cluster.push_back(plate); clusters.push_back(new_cluster); } else { clusters[cluster_index].push_back(plate); } } } return clusters; } PlateShapeInfo ResultAggregator::getShapeInfo(AlprPlateResult plate) { int NUM_POINTS = 4; Moments mu; PlateShapeInfo response; vector points; for (int i = 0; i < NUM_POINTS; i++ ) { cv::Point p(plate.plate_points[i].x, plate.plate_points[i].y); points.push_back(p); } mu = moments( points, false ); response.center = cv::Point2f( mu.m10/mu.m00 , mu.m01/mu.m00 ); response.area = mu.m00; Rect r = cv::boundingRect(points); response.max_width = r.width; response.max_height = r.height; return response; } // Returns the cluster ID if the plate overlaps. Otherwise returns -1 int ResultAggregator::overlaps(AlprPlateResult plate, std::vector > clusters) { // Check the center positions to see how close they are to each other // Also compare the size. If it's much much larger/smaller, treat it as a separate cluster PlateShapeInfo psi = getShapeInfo(plate); for (unsigned int i = 0; i < clusters.size(); i++) { for (unsigned int k = 0; k < clusters[i].size(); k++) { PlateShapeInfo cluster_shapeinfo = getShapeInfo(clusters[i][k]); int diffx = abs(psi.center.x - cluster_shapeinfo.center.x); int diffy = abs(psi.center.y - cluster_shapeinfo.center.y); // divide the larger plate area by the smaller plate area to determine a match float area_diff; if (psi.area > cluster_shapeinfo.area) area_diff = psi.area / cluster_shapeinfo.area; else area_diff = cluster_shapeinfo.area / psi.area; int max_x_diff = (psi.max_width + cluster_shapeinfo.max_width) / 2; int max_y_diff = (psi.max_height + cluster_shapeinfo.max_height) / 2; float max_area_diff = 4.0; // Consider it a match if center diffx/diffy are less than the average height // the area is not more than 4x different if (diffx <= max_x_diff && diffy <= max_y_diff && area_diff <= max_area_diff) { return i; } } } return -1; } }openalpr_2.2.4.orig/src/openalpr/result_aggregator.h000066400000000000000000000030731266464252400226760ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_RESULTAGGREGATOR_H #define OPENALPR_RESULTAGGREGATOR_H #include "alpr_impl.h" // Runs the analysis for multiple training sets, and aggregates the results into the best matches struct PlateShapeInfo { cv::Point2f center; float area; int max_width; int max_height; }; namespace alpr { class ResultAggregator { public: ResultAggregator(); virtual ~ResultAggregator(); void addResults(AlprFullDetails full_results); AlprFullDetails getAggregateResults(); private: std::vector all_results; PlateShapeInfo getShapeInfo(AlprPlateResult plate); std::vector > findClusters(); int overlaps(AlprPlateResult plate, std::vector > clusters); }; } #endif //OPENALPR_RESULTAGGREGATOR_H openalpr_2.2.4.orig/src/openalpr/segmentation/000077500000000000000000000000001266464252400214775ustar00rootroot00000000000000openalpr_2.2.4.orig/src/openalpr/segmentation/charactersegmenter.cpp000066400000000000000000001174521266464252400260630ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "charactersegmenter.h" using namespace cv; using namespace std; namespace alpr { CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data) { this->pipeline_data = pipeline_data; this->config = pipeline_data->config; this->confidence = 0; if (this->config->debugCharSegmenter) cout << "Starting CharacterSegmenter" << endl; //CharacterRegion charRegion(img, debug); timespec startTime; getTimeMonotonic(&startTime); if (pipeline_data->plate_inverted) bitwise_not(pipeline_data->crop_gray, pipeline_data->crop_gray); pipeline_data->clearThresholds(); pipeline_data->thresholds = produceThresholds(pipeline_data->crop_gray, config); // TODO: Perhaps a bilateral filter would be better here. medianBlur(pipeline_data->crop_gray, pipeline_data->crop_gray, 3); if (this->config->debugCharSegmenter) cout << "Segmenter: inverted: " << pipeline_data->plate_inverted << endl; if (this->config->debugCharSegmenter) { displayImage(config, "CharacterSegmenter Thresholds", drawImageDashboard(pipeline_data->thresholds, CV_8U, 3)); } Mat edge_filter_mask = Mat::zeros(pipeline_data->thresholds[0].size(), CV_8U); bitwise_not(edge_filter_mask, edge_filter_mask); for (unsigned int lineidx = 0; lineidx < pipeline_data->textLines.size(); lineidx++) { this->top = pipeline_data->textLines[lineidx].topLine; this->bottom = pipeline_data->textLines[lineidx].bottomLine; float avgCharHeight = pipeline_data->textLines[lineidx].lineHeight; float height_to_width_ratio = pipeline_data->config->charHeightMM[lineidx] / pipeline_data->config->charWidthMM[lineidx]; float avgCharWidth = avgCharHeight / height_to_width_ratio; if (config->debugCharSegmenter) { cout << "LINE " << lineidx << ": avgCharHeight: " << avgCharHeight << " - height_to_width_ratio: " << height_to_width_ratio << endl; cout << "LINE " << lineidx << ": avgCharWidth: " << avgCharWidth << endl; } removeSmallContours(pipeline_data->thresholds, avgCharHeight, pipeline_data->textLines[lineidx]); // Do the histogram analysis to figure out char regions timespec startTime; getTimeMonotonic(&startTime); vector allHistograms; vector lineBoxes; for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { Mat histogramMask = Mat::zeros(pipeline_data->thresholds[i].size(), CV_8U); fillConvexPoly(histogramMask, pipeline_data->textLines[lineidx].linePolygon.data(), pipeline_data->textLines[lineidx].linePolygon.size(), Scalar(255,255,255)); HistogramVertical vertHistogram(pipeline_data->thresholds[i], histogramMask); // if (this->config->debugCharSegmenter) // { // Mat histoCopy(vertHistogram.histoImg.size(), vertHistogram.histoImg.type()); // //vertHistogram.copyTo(histoCopy); // cvtColor(vertHistogram.histoImg, histoCopy, CV_GRAY2RGB); // // string label = "threshold: " + toString(i); // allHistograms.push_back(addLabel(histoCopy, label)); // // std::cout << histoCopy.cols << " x " << histoCopy.rows << std::endl; // } float score = 0; vector charBoxes = getHistogramBoxes(vertHistogram, avgCharWidth, avgCharHeight, &score); // if (this->config->debugCharSegmenter) // { // for (unsigned int cboxIdx = 0; cboxIdx < charBoxes.size(); cboxIdx++) // { // rectangle(allHistograms[i], charBoxes[cboxIdx], Scalar(0, 255, 0)); // } // // Mat histDashboard = drawImageDashboard(allHistograms, allHistograms[0].type(), 1); // displayImage(config, "Char seg histograms", histDashboard); // } for (unsigned int z = 0; z < charBoxes.size(); z++) lineBoxes.push_back(charBoxes[z]); //drawAndWait(&histogramMask); } if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << " -- Character Segmentation Create and Score Histograms Time: " << diffclock(startTime, endTime) << "ms." << endl; } vector candidateBoxes = getBestCharBoxes(pipeline_data->thresholds[0], lineBoxes, avgCharWidth); if (this->config->debugCharSegmenter) { // Setup the dashboard images to show the cleaning filters for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { Mat cleanImg = Mat::zeros(pipeline_data->thresholds[i].size(), pipeline_data->thresholds[i].type()); Mat boxMask = getCharBoxMask(pipeline_data->thresholds[i], candidateBoxes); pipeline_data->thresholds[i].copyTo(cleanImg); bitwise_and(cleanImg, boxMask, cleanImg); cvtColor(cleanImg, cleanImg, CV_GRAY2BGR); for (unsigned int c = 0; c < candidateBoxes.size(); c++) rectangle(cleanImg, candidateBoxes[c], Scalar(0, 255, 0), 1); imgDbgCleanStages.push_back(cleanImg); } } getTimeMonotonic(&startTime); Mat edge_mask = filterEdgeBoxes(pipeline_data->thresholds, candidateBoxes, avgCharWidth, avgCharHeight); bitwise_and(edge_filter_mask, edge_mask, edge_filter_mask); candidateBoxes = combineCloseBoxes(candidateBoxes); candidateBoxes = filterMostlyEmptyBoxes(pipeline_data->thresholds, candidateBoxes); pipeline_data->charRegions.push_back(candidateBoxes); for (unsigned int cboxidx = 0; cboxidx < candidateBoxes.size(); cboxidx++) pipeline_data->charRegionsFlat.push_back(candidateBoxes[cboxidx]); if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << " -- Character Segmentation Box cleaning/filtering Time: " << diffclock(startTime, endTime) << "ms." << endl; } if (this->config->debugCharSegmenter) { Mat imgDash = drawImageDashboard(pipeline_data->thresholds, CV_8U, 3); displayImage(config, "Segmentation after cleaning", imgDash); Mat generalDash = drawImageDashboard(this->imgDbgGeneral, this->imgDbgGeneral[0].type(), 2); displayImage(config, "Segmentation General", generalDash); Mat cleanImgDash = drawImageDashboard(this->imgDbgCleanStages, this->imgDbgCleanStages[0].type(), 3); displayImage(config, "Segmentation Clean Filters", cleanImgDash); } } // Apply the edge mask (left and right ends) after all lines have been processed. for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { bitwise_and(pipeline_data->thresholds[i], edge_filter_mask, pipeline_data->thresholds[i]); } vector all_regions_combined; for (unsigned int lidx = 0; lidx < pipeline_data->charRegions.size(); lidx++) { for (unsigned int boxidx = 0; boxidx < pipeline_data->charRegions[lidx].size(); boxidx++) all_regions_combined.push_back(pipeline_data->charRegions[lidx][boxidx]); } cleanCharRegions(pipeline_data->thresholds, all_regions_combined); if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << "Character Segmenter Time: " << diffclock(startTime, endTime) << "ms." << endl; } } CharacterSegmenter::~CharacterSegmenter() { } // Given a histogram and the horizontal line boundaries, respond with an array of boxes where the characters are // Scores the histogram quality as well based on num chars, char volume, and even separation vector CharacterSegmenter::getHistogramBoxes(HistogramVertical histogram, float avgCharWidth, float avgCharHeight, float* score) { float MIN_HISTOGRAM_HEIGHT = avgCharHeight * config->segmentationMinCharHeightPercent; float MAX_SEGMENT_WIDTH = avgCharWidth * config->segmentationMaxCharWidthvsAverage; //float MIN_BOX_AREA = (avgCharWidth * avgCharHeight) * 0.25; int pxLeniency = 2; vector charBoxes; vector allBoxes = convert1DHitsToRect(histogram.get1DHits(pxLeniency), top, bottom); for (unsigned int i = 0; i < allBoxes.size(); i++) { if (allBoxes[i].width >= config->segmentationMinBoxWidthPx && allBoxes[i].width <= MAX_SEGMENT_WIDTH && allBoxes[i].height > MIN_HISTOGRAM_HEIGHT ) { charBoxes.push_back(allBoxes[i]); } else if (allBoxes[i].width > avgCharWidth * 2 && allBoxes[i].width < MAX_SEGMENT_WIDTH * 2 && allBoxes[i].height > MIN_HISTOGRAM_HEIGHT) { // rectangle(histogram.histoImg, allBoxes[i], Scalar(255, 0, 0) ); // drawAndWait(&histogram.histoImg); // Try to split up doubles into two good char regions, check for a break between 40% and 60% int leftEdge = allBoxes[i].x + (int) (((float) allBoxes[i].width) * 0.4f); int rightEdge = allBoxes[i].x + (int) (((float) allBoxes[i].width) * 0.6f); int minX = histogram.getLocalMinimum(leftEdge, rightEdge); int maxXChar1 = histogram.getLocalMaximum(allBoxes[i].x, minX); int maxXChar2 = histogram.getLocalMaximum(minX, allBoxes[i].x + allBoxes[i].width); int minHeight = histogram.getHeightAt(minX); int maxHeightChar1 = histogram.getHeightAt(maxXChar1); int maxHeightChar2 = histogram.getHeightAt(maxXChar2); if (maxHeightChar1 > MIN_HISTOGRAM_HEIGHT && minHeight < (0.25 * ((float) maxHeightChar1))) { // Add a box for Char1 Point botRight = Point(minX - 1, allBoxes[i].y + allBoxes[i].height); charBoxes.push_back(Rect(allBoxes[i].tl(), botRight) ); } if (maxHeightChar2 > MIN_HISTOGRAM_HEIGHT && minHeight < (0.25 * ((float) maxHeightChar2))) { // Add a box for Char2 Point topLeft = Point(minX + 1, allBoxes[i].y); charBoxes.push_back(Rect(topLeft, allBoxes[i].br()) ); } } } return charBoxes; } vector CharacterSegmenter::getBestCharBoxes(Mat img, vector charBoxes, float avgCharWidth) { float MAX_SEGMENT_WIDTH = avgCharWidth * config->segmentationMaxCharWidthvsAverage; // This histogram is based on how many char boxes (from ALL of the many thresholded images) are covering each column // Makes a sort of histogram from all the previous char boxes. Figures out the best fit from that. Mat histoImg = Mat::zeros(Size(img.cols, img.rows), CV_8U); int columnCount; for (int col = 0; col < img.cols; col++) { columnCount = 0; for (unsigned int i = 0; i < charBoxes.size(); i++) { if (col >= charBoxes[i].x && col < (charBoxes[i].x + charBoxes[i].width)) columnCount++; } // Fill the line of the histogram for (; columnCount > 0; columnCount--) histoImg.at(histoImg.rows - columnCount, col) = 255; } HistogramVertical histogram(histoImg, Mat::ones(histoImg.size(), CV_8U)); // Go through each row in the histoImg and score it. Try to find the single line that gives me the most right-sized character regions (based on avgCharWidth) int bestRowIndex = 0; float bestRowScore = 0; vector bestBoxes; for (int row = 0; row < histoImg.rows; row++) { vector validBoxes; int pxLeniency = 0; vector allBoxes = convert1DHitsToRect(histogram.get1DHits(pxLeniency), top, bottom); if (this->config->debugCharSegmenter) cout << "All Boxes size " << allBoxes.size() << endl; if (allBoxes.size() == 0) break; float rowScore = 0; for (unsigned int boxidx = 0; boxidx < allBoxes.size(); boxidx++) { int w = allBoxes[boxidx].width; if (w >= config->segmentationMinBoxWidthPx && w <= MAX_SEGMENT_WIDTH) { float widthDiffPixels = abs(w - avgCharWidth); float widthDiffPercent = widthDiffPixels / avgCharWidth; rowScore += 10 * (1 - widthDiffPercent); if (widthDiffPercent < 0.25) // Bonus points when it's close to the average character width rowScore += 8; validBoxes.push_back(allBoxes[boxidx]); } else if (w > avgCharWidth * 2 && w <= MAX_SEGMENT_WIDTH * 2 ) { // Try to split up doubles into two good char regions, check for a break between 40% and 60% int leftEdge = allBoxes[boxidx].x + (int) (((float) allBoxes[boxidx].width) * 0.4f); int rightEdge = allBoxes[boxidx].x + (int) (((float) allBoxes[boxidx].width) * 0.6f); int minX = histogram.getLocalMinimum(leftEdge, rightEdge); int maxXChar1 = histogram.getLocalMaximum(allBoxes[boxidx].x, minX); int maxXChar2 = histogram.getLocalMaximum(minX, allBoxes[boxidx].x + allBoxes[boxidx].width); int minHeight = histogram.getHeightAt(minX); int maxHeightChar1 = histogram.getHeightAt(maxXChar1); int maxHeightChar2 = histogram.getHeightAt(maxXChar2); if ( minHeight < (0.25 * ((float) maxHeightChar1))) { // Add a box for Char1 Point botRight = Point(minX - 1, allBoxes[boxidx].y + allBoxes[boxidx].height); validBoxes.push_back(Rect(allBoxes[boxidx].tl(), botRight) ); } if ( minHeight < (0.25 * ((float) maxHeightChar2))) { // Add a box for Char2 Point topLeft = Point(minX + 1, allBoxes[boxidx].y); validBoxes.push_back(Rect(topLeft, allBoxes[boxidx].br()) ); } } } if (rowScore > bestRowScore) { bestRowScore = rowScore; bestRowIndex = row; bestBoxes = validBoxes; } } if (this->config->debugCharSegmenter) { cvtColor(histoImg, histoImg, CV_GRAY2BGR); line(histoImg, Point(0, histoImg.rows - 1 - bestRowIndex), Point(histoImg.cols, histoImg.rows - 1 - bestRowIndex), Scalar(0, 255, 0)); Mat imgBestBoxes(img.size(), img.type()); img.copyTo(imgBestBoxes); cvtColor(imgBestBoxes, imgBestBoxes, CV_GRAY2BGR); for (unsigned int i = 0; i < bestBoxes.size(); i++) rectangle(imgBestBoxes, bestBoxes[i], Scalar(0, 255, 0)); this->imgDbgGeneral.push_back(addLabel(histoImg, "All Histograms")); this->imgDbgGeneral.push_back(addLabel(imgBestBoxes, "Best Boxes")); } return bestBoxes; } void CharacterSegmenter::removeSmallContours(vector thresholds, float avgCharHeight, TextLine textLine) { //const float MIN_CHAR_AREA = 0.02 * avgCharWidth * avgCharHeight; // To clear out the tiny specks const float MIN_CONTOUR_HEIGHT = config->segmentationMinSpeckleHeightPercent * avgCharHeight; Mat textLineMask = Mat::zeros(thresholds[0].size(), CV_8U); fillConvexPoly(textLineMask, textLine.linePolygon.data(), textLine.linePolygon.size(), Scalar(255,255,255)); for (unsigned int i = 0; i < thresholds.size(); i++) { vector > contours; vector hierarchy; Mat thresholdsCopy = Mat::zeros(thresholds[i].size(), thresholds[i].type()); thresholds[i].copyTo(thresholdsCopy, textLineMask); findContours(thresholdsCopy, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE); for (unsigned int c = 0; c < contours.size(); c++) { if (contours[c].size() == 0) continue; Rect mr = boundingRect(contours[c]); if (mr.height < MIN_CONTOUR_HEIGHT) { // Erase it drawContours(thresholds[i], contours, c, Scalar(0, 0, 0), -1); continue; } } } } int CharacterSegmenter::getCharGap(cv::Rect leftBox, cv::Rect rightBox) { int right_midpoint = (rightBox.x + (rightBox.width / 2)); int left_midpoint = (leftBox.x + (leftBox.width / 2)); return right_midpoint - left_midpoint; } vector CharacterSegmenter::combineCloseBoxes( vector charBoxes) { // Don't bother combining if there are 2 or fewer characters if (charBoxes.size() <= 2) return charBoxes; // First find the median char gap (the space from midpoint to midpoint of chars) vector char_gaps; for (unsigned int i = 0; i < charBoxes.size(); i++) { if (i == charBoxes.size() - 1) break; // the space between charbox i and i+1 char_gaps.push_back(getCharGap(charBoxes[i], charBoxes[i+1])); } int median_char_gap = median(char_gaps.data(), char_gaps.size()); // Find the second largest char box. Should help ignore a big outlier vector char_sizes; float biggestCharWidth; for (unsigned int i = 0; i < charBoxes.size(); i++) { char_sizes.push_back(charBoxes[i].width); } std::sort(char_sizes.begin(), char_sizes.end()); biggestCharWidth = char_sizes[char_sizes.size() - 2]; vector newCharBoxes; for (unsigned int i = 0; i < charBoxes.size(); i++) { if (i == charBoxes.size() - 1) { newCharBoxes.push_back(charBoxes[i]); break; } float bigWidth = (charBoxes[i + 1].x + charBoxes[i + 1].width - charBoxes[i].x); float left_gap; float right_gap; if (i == 0) left_gap = 999999999999; else left_gap = getCharGap(charBoxes[i-1], charBoxes[i]); right_gap = getCharGap(charBoxes[i], charBoxes[i+1]); int min_gap = (int) ((float)median_char_gap) * 0.75; int max_gap = (int) ((float)median_char_gap) * 1.25; int max_width = (int) ((float)biggestCharWidth) * 1.2; bool has_good_gap = (left_gap >= min_gap && left_gap <= max_gap) || (right_gap >= min_gap && right_gap <= max_gap); if (has_good_gap && bigWidth <= max_width) { Rect bigRect(charBoxes[i].x, charBoxes[i].y, bigWidth, charBoxes[i].height); newCharBoxes.push_back(bigRect); if (this->config->debugCharSegmenter) { for (unsigned int z = 0; z < pipeline_data->thresholds.size(); z++) { Point center(bigRect.x + bigRect.width / 2, bigRect.y + bigRect.height / 2); RotatedRect rrect(center, Size2f(bigRect.width, bigRect.height + (bigRect.height / 2)), 0); ellipse(imgDbgCleanStages[z], rrect, Scalar(0,255,0), 1); } cout << "Merging 2 boxes -- " << i << " and " << i + 1 << endl; } i++; } else { newCharBoxes.push_back(charBoxes[i]); if (this->config->debugCharSegmenter) { cout << "Not Merging 2 boxes -- " << i << " and " << i + 1 << " -- has_good_gap: " << has_good_gap << " bigWidth (" << bigWidth << ") > max_width (" << max_width << ")" << endl; } } } return newCharBoxes; } void CharacterSegmenter::cleanCharRegions(vector thresholds, vector charRegions) { const float MIN_SPECKLE_HEIGHT_PERCENT = 0.13; const float MIN_SPECKLE_WIDTH_PX = 3; const float MIN_CONTOUR_AREA_PERCENT = 0.1; const float MIN_CONTOUR_HEIGHT_PERCENT = config->segmentationMinCharHeightPercent; Mat mask = getCharBoxMask(thresholds[0], charRegions); for (unsigned int i = 0; i < thresholds.size(); i++) { bitwise_and(thresholds[i], mask, thresholds[i]); vector > contours; Mat tempImg(thresholds[i].size(), thresholds[i].type()); thresholds[i].copyTo(tempImg); //Mat element = getStructuringElement( 1, // Size( 2 + 1, 2+1 ), // Point( 1, 1 ) ); //dilate(thresholds[i], tempImg, element); //morphologyEx(thresholds[i], tempImg, MORPH_CLOSE, element); //drawAndWait(&tempImg); findContours(tempImg, contours, RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); for (unsigned int j = 0; j < charRegions.size(); j++) { const float MIN_SPECKLE_HEIGHT = ((float)charRegions[j].height) * MIN_SPECKLE_HEIGHT_PERCENT; const float MIN_CONTOUR_AREA = ((float)charRegions[j].area()) * MIN_CONTOUR_AREA_PERCENT; int tallestContourHeight = 0; float totalArea = 0; for (unsigned int c = 0; c < contours.size(); c++) { if (contours[c].size() == 0) continue; if (charRegions[j].contains(contours[c][0]) == false) continue; Rect r = boundingRect(contours[c]); if (r.height <= MIN_SPECKLE_HEIGHT || r.width <= MIN_SPECKLE_WIDTH_PX) { // Erase this speckle drawContours(thresholds[i], contours, c, Scalar(0,0,0), CV_FILLED); if (this->config->debugCharSegmenter) { drawContours(imgDbgCleanStages[i], contours, c, COLOR_DEBUG_SPECKLES, CV_FILLED); } } else { if (r.height > tallestContourHeight) tallestContourHeight = r.height; totalArea += contourArea(contours[c]); } //else if (r.height > tallestContourHeight) //{ // tallestContourIndex = c; // tallestContourHeight = h; //} } if (totalArea < MIN_CONTOUR_AREA) { // Character is not voluminous enough. Erase it. if (this->config->debugCharSegmenter) { cout << "Character CLEAN: (area) removing box " << j << " in threshold " << i << " -- Area " << totalArea << " < " << MIN_CONTOUR_AREA << endl; Rect boxTop(charRegions[j].x, charRegions[j].y - 10, charRegions[j].width, 10); rectangle(imgDbgCleanStages[i], boxTop, COLOR_DEBUG_MIN_AREA, -1); } rectangle(thresholds[i], charRegions[j], Scalar(0, 0, 0), -1); } else if (tallestContourHeight < ((float) charRegions[j].height * MIN_CONTOUR_HEIGHT_PERCENT)) { // This character is too short. Black the whole thing out if (this->config->debugCharSegmenter) { cout << "Character CLEAN: (height) removing box " << j << " in threshold " << i << " -- Height " << tallestContourHeight << " < " << ((float) charRegions[j].height * MIN_CONTOUR_HEIGHT_PERCENT) << endl; Rect boxBottom(charRegions[j].x, charRegions[j].y + charRegions[j].height, charRegions[j].width, 10); rectangle(imgDbgCleanStages[i], boxBottom, COLOR_DEBUG_MIN_HEIGHT, -1); } rectangle(thresholds[i], charRegions[j], Scalar(0, 0, 0), -1); } } int morph_size = 1; Mat closureElement = getStructuringElement( 2, // 0 Rect, 1 cross, 2 ellipse Size( 2 * morph_size + 1, 2* morph_size + 1 ), Point( morph_size, morph_size ) ); //morphologyEx(thresholds[i], thresholds[i], MORPH_OPEN, element); //dilate(thresholds[i], thresholds[i], element); //erode(thresholds[i], thresholds[i], element); morphologyEx(thresholds[i], thresholds[i], MORPH_CLOSE, closureElement); // Lastly, draw a clipping line between each character boxes for (unsigned int j = 0; j < charRegions.size(); j++) { line(thresholds[i], Point(charRegions[j].x - 1, charRegions[j].y), Point(charRegions[j].x - 1, charRegions[j].y + charRegions[j].height), Scalar(0, 0, 0)); line(thresholds[i], Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y), Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y + charRegions[j].height), Scalar(0, 0, 0)); } } } void CharacterSegmenter::cleanBasedOnColor(vector thresholds, Mat colorMask, vector charRegions) { // If I knock out x% of the contour area from this thing (after applying the color filter) // Consider it a bad news bear. REmove the whole area. const float MIN_PERCENT_CHUNK_REMOVED = 0.6; for (unsigned int i = 0; i < thresholds.size(); i++) { for (unsigned int j = 0; j < charRegions.size(); j++) { Mat boxChar = Mat::zeros(thresholds[i].size(), CV_8U); rectangle(boxChar, charRegions[j], Scalar(255,255,255), CV_FILLED); bitwise_and(thresholds[i], boxChar, boxChar); float meanBefore = mean(boxChar, boxChar)[0]; Mat thresholdCopy(thresholds[i].size(), CV_8U); bitwise_and(colorMask, boxChar, thresholdCopy); float meanAfter = mean(thresholdCopy, boxChar)[0]; if (meanAfter < meanBefore * (1-MIN_PERCENT_CHUNK_REMOVED)) { rectangle(thresholds[i], charRegions[j], Scalar(0,0,0), CV_FILLED); if (this->config->debugCharSegmenter) { //vector tmpDebug; //Mat thresholdCopy2 = Mat::zeros(thresholds[i].size(), CV_8U); //rectangle(thresholdCopy2, charRegions[j], Scalar(255,255,255), CV_FILLED); //tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask")); //bitwise_and(thresholds[i], thresholdCopy2, thresholdCopy2); //tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh")); //bitwise_and(colorMask, thresholdCopy2, thresholdCopy2); //tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh + Color")); // // Mat tmpytmp = addLabel(thresholdCopy2, "box Mask + Thresh + Color"); // Mat tmpx = drawImageDashboard(tmpDebug, tmpytmp.type(), 1); //drawAndWait( &tmpx ); cout << "Segmentation Filter Clean by Color: Removed Threshold " << i << " charregion " << j << endl; cout << "Segmentation Filter Clean by Color: before=" << meanBefore << " after=" << meanAfter << endl; Point topLeft(charRegions[j].x, charRegions[j].y); circle(imgDbgCleanStages[i], topLeft, 5, COLOR_DEBUG_COLORFILTER, CV_FILLED); } } } } } vector CharacterSegmenter::filterMostlyEmptyBoxes(vector thresholds, const vector charRegions) { // Of the n thresholded images, if box 3 (for example) is empty in half (for example) of the thresholded images, // clear all data for every box #3. //const float MIN_AREA_PERCENT = 0.1; const float MIN_CONTOUR_HEIGHT_PERCENT = config->segmentationMinCharHeightPercent; Mat mask = getCharBoxMask(thresholds[0], charRegions); vector boxScores(charRegions.size()); for (unsigned int i = 0; i < charRegions.size(); i++) boxScores[i] = 0; for (unsigned int i = 0; i < thresholds.size(); i++) { for (unsigned int j = 0; j < charRegions.size(); j++) { //float minArea = charRegions[j].area() * MIN_AREA_PERCENT; Mat tempImg = Mat::zeros(thresholds[i].size(), thresholds[i].type()); rectangle(tempImg, charRegions[j], Scalar(255,255,255), CV_FILLED); bitwise_and(thresholds[i], tempImg, tempImg); vector > contours; findContours(tempImg, contours, RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); vector allPointsInBox; for (unsigned int c = 0; c < contours.size(); c++) { if (contours[c].size() == 0) continue; for (unsigned int z = 0; z < contours[c].size(); z++) allPointsInBox.push_back(contours[c][z]); } float height = 0; if (allPointsInBox.size() > 0) { height = boundingRect(allPointsInBox).height; } if (height >= ((float) charRegions[j].height * MIN_CONTOUR_HEIGHT_PERCENT)) { boxScores[j] = boxScores[j] + 1; } else if (this->config->debugCharSegmenter) { drawX(imgDbgCleanStages[i], charRegions[j], COLOR_DEBUG_EMPTYFILTER, 3); } } } vector newCharRegions; int maxBoxScore = 0; for (unsigned int i = 0; i < charRegions.size(); i++) { if (boxScores[i] > maxBoxScore) maxBoxScore = boxScores[i]; } // Need a good char sample in at least 50% of the boxes for it to be valid. int MIN_FULL_BOXES = maxBoxScore * 0.49; // Now check each score. If it's below the minimum, remove the charRegion for (unsigned int i = 0; i < charRegions.size(); i++) { if (boxScores[i] > MIN_FULL_BOXES) newCharRegions.push_back(charRegions[i]); else { // Erase the box from the Mat... mainly for debug purposes if (this->config->debugCharSegmenter) { cout << "Mostly Empty Filter: box index: " << i; cout << " this box had a score of : " << boxScores[i];; cout << " MIN_FULL_BOXES: " << MIN_FULL_BOXES << endl;; for (unsigned int z = 0; z < thresholds.size(); z++) { rectangle(thresholds[z], charRegions[i], Scalar(0,0,0), -1); drawX(imgDbgCleanStages[z], charRegions[i], COLOR_DEBUG_EMPTYFILTER, 1); } } } if (this->config->debugCharSegmenter) cout << " Box Score: " << boxScores[i] << endl; } return newCharRegions; } Mat CharacterSegmenter::filterEdgeBoxes(vector thresholds, const vector charRegions, float avgCharWidth, float avgCharHeight) { const float MIN_ANGLE_FOR_ROTATION = 0.4; int MIN_CONNECTED_EDGE_PIXELS = (avgCharHeight * 1.5); // Sometimes the rectangle won't be very tall, making it impossible to detect an edge // Adjust for this here. int alternate = thresholds[0].rows * 0.92; if (alternate < MIN_CONNECTED_EDGE_PIXELS && alternate > avgCharHeight) MIN_CONNECTED_EDGE_PIXELS = alternate; Mat empty_mask = Mat::zeros(thresholds[0].size(), CV_8U); bitwise_not(empty_mask, empty_mask); // // Pay special attention to the edge boxes. If it's a skinny box, and the vertical height extends above our bounds... remove it. //while (charBoxes.size() > 0 && charBoxes[charBoxes.size() - 1].width < MIN_SEGMENT_WIDTH_EDGES) // charBoxes.erase(charBoxes.begin() + charBoxes.size() - 1); // Now filter the "edge" boxes. We don't want to include skinny boxes on the edges, since these could be plate boundaries //while (charBoxes.size() > 0 && charBoxes[0].width < MIN_SEGMENT_WIDTH_EDGES) // charBoxes.erase(charBoxes.begin() + 0); // TECHNIQUE #1 // Check for long vertical lines. Once the line is too long, mask the whole region if (charRegions.size() <= 1) return empty_mask; // Check both sides to see where the edges are // The first starts at the right edge of the leftmost char region and works its way left // The second starts at the left edge of the rightmost char region and works its way right. // We start by rotating the threshold image to the correct angle // then check each column 1 by 1. vector leftEdges; vector rightEdges; for (unsigned int i = 0; i < thresholds.size(); i++) { Mat rotated; if (abs(top.angle) > MIN_ANGLE_FOR_ROTATION) { // Rotate image: rotated = Mat(thresholds[i].size(), thresholds[i].type()); Mat rot_mat( 2, 3, CV_32FC1 ); Point center = Point( thresholds[i].cols/2, thresholds[i].rows/2 ); rot_mat = getRotationMatrix2D( center, top.angle, 1.0 ); warpAffine( thresholds[i], rotated, rot_mat, thresholds[i].size() ); } else { rotated = thresholds[i]; } int leftEdgeX = 0; int rightEdgeX = rotated.cols; // Do the left side int col = charRegions[0].x + charRegions[0].width; while (col >= 0) { int rowLength = getLongestBlobLengthBetweenLines(rotated, col); if (rowLength > MIN_CONNECTED_EDGE_PIXELS) { leftEdgeX = col; break; } col--; } col = charRegions[charRegions.size() - 1].x; while (col < rotated.cols) { int rowLength = getLongestBlobLengthBetweenLines(rotated, col); if (rowLength > MIN_CONNECTED_EDGE_PIXELS) { rightEdgeX = col; break; } col++; } if (leftEdgeX != 0) leftEdges.push_back(leftEdgeX); if (rightEdgeX != thresholds[i].cols) rightEdges.push_back(rightEdgeX); } int leftEdge = 0; int rightEdge = thresholds[0].cols; // Assign the edge values to the SECOND closest value if (leftEdges.size() > 1) { sort (leftEdges.begin(), leftEdges.begin()+leftEdges.size()); leftEdge = leftEdges[leftEdges.size() - 2] + 1; } if (rightEdges.size() > 1) { sort (rightEdges.begin(), rightEdges.begin()+rightEdges.size()); rightEdge = rightEdges[1] - 1; } if (leftEdge != 0 || rightEdge != thresholds[0].cols) { Mat mask = Mat::zeros(thresholds[0].size(), CV_8U); bitwise_not(mask, mask); rectangle(mask, Point(0, charRegions[0].y), Point(leftEdge, charRegions[0].y+charRegions[0].height), Scalar(0,0,0), -1); rectangle(mask, Point(rightEdge, charRegions[0].y), Point(mask.cols, charRegions[0].y+charRegions[0].height), Scalar(0,0,0), -1); if (abs(top.angle) > MIN_ANGLE_FOR_ROTATION) { // Rotate mask: Mat rot_mat( 2, 3, CV_32FC1 ); Point center = Point( mask.cols/2, mask.rows/2 ); rot_mat = getRotationMatrix2D( center, top.angle * -1, 1.0 ); warpAffine( mask, mask, rot_mat, mask.size() ); } // If our edge mask covers more than x% of the char region, mask the whole thing... const float MAX_COVERAGE_PERCENT = 0.6; int leftCoveragePx = leftEdge - charRegions[0].x; float leftCoveragePercent = ((float) leftCoveragePx) / ((float) charRegions[0].width); float rightCoveragePx = (charRegions[charRegions.size() -1].x + charRegions[charRegions.size() -1].width) - rightEdge; float rightCoveragePercent = ((float) rightCoveragePx) / ((float) charRegions[charRegions.size() -1].width); if ((leftCoveragePercent > MAX_COVERAGE_PERCENT) || (charRegions[0].width - leftCoveragePx < config->segmentationMinBoxWidthPx)) { rectangle(mask, charRegions[0], Scalar(0,0,0), -1); // Mask the whole region if (this->config->debugCharSegmenter) cout << "Edge Filter: Entire left region is erased" << endl; } if ((rightCoveragePercent > MAX_COVERAGE_PERCENT) || (charRegions[charRegions.size() -1].width - rightCoveragePx < config->segmentationMinBoxWidthPx)) { rectangle(mask, charRegions[charRegions.size() -1], Scalar(0,0,0), -1); if (this->config->debugCharSegmenter) cout << "Edge Filter: Entire right region is erased" << endl; } if (this->config->debugCharSegmenter) { cout << "Edge Filter: left=" << leftEdge << " right=" << rightEdge << endl; Mat bordered = addLabel(mask, "Edge Filter #1"); imgDbgGeneral.push_back(bordered); Mat invertedMask(mask.size(), mask.type()); bitwise_not(mask, invertedMask); for (unsigned int z = 0; z < imgDbgCleanStages.size(); z++) fillMask(imgDbgCleanStages[z], invertedMask, Scalar(0,0,255)); } return mask; } return empty_mask; } int CharacterSegmenter::getLongestBlobLengthBetweenLines(Mat img, int col) { int longestBlobLength = 0; bool onSegment = false; bool wasbetweenLines = false; float curSegmentLength = 0; for (int row = 0; row < img.rows; row++) { bool isbetweenLines = false; bool isOn = img.at(row, col); // check two rows at a time. if (!isOn && col < img.cols) isOn = img.at(row, col); if (isOn) { // We're on a segment. Increment the length isbetweenLines = top.isPointBelowLine(Point(col, row)) && !bottom.isPointBelowLine(Point(col, row)); float incrementBy = 1; // Add a little extra to the score if this is outside of the lines if (!isbetweenLines) incrementBy = 1.1; onSegment = true; curSegmentLength += incrementBy; } if (isOn && isbetweenLines) { wasbetweenLines = true; } if (onSegment && (isOn == false || (row == img.rows - 1))) { if (wasbetweenLines && curSegmentLength > longestBlobLength) longestBlobLength = curSegmentLength; onSegment = false; isbetweenLines = false; curSegmentLength = 0; } } return longestBlobLength; } // Checks to see if a skinny, tall line (extending above or below the char Height) is inside the given box. // Returns the contour index if true. -1 otherwise int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector > contours, vector hierarchy, float avgCharWidth, float avgCharHeight) { float MIN_EDGE_CONTOUR_HEIGHT = avgCharHeight * 1.25; // Sometimes the threshold is smaller than the MIN_EDGE_CONTOUR_HEIGHT. // In that case, adjust to be smaller int alternate = threshold.rows * 0.92; if (alternate < MIN_EDGE_CONTOUR_HEIGHT && alternate > avgCharHeight) MIN_EDGE_CONTOUR_HEIGHT = alternate; Rect slightlySmallerBox(box.x, box.y, box.width, box.height); Mat boxMask = Mat::zeros(threshold.size(), CV_8U); rectangle(boxMask, slightlySmallerBox, Scalar(255, 255, 255), -1); for (unsigned int i = 0; i < contours.size(); i++) { // Only bother with the big boxes if (boundingRect(contours[i]).height < MIN_EDGE_CONTOUR_HEIGHT) continue; Mat tempImg = Mat::zeros(threshold.size(), CV_8U); drawContours(tempImg, contours, i, Scalar(255,255,255), -1, 8, hierarchy, 1); bitwise_and(tempImg, boxMask, tempImg); vector > subContours; findContours(tempImg, subContours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); int tallestContourIdx = -1; int tallestContourHeight = 0; int tallestContourWidth = 0; float tallestContourArea = 0; for (unsigned int s = 0; s < subContours.size(); s++) { Rect r = boundingRect(subContours[s]); if (r.height > tallestContourHeight) { tallestContourIdx = s; tallestContourHeight = r.height; tallestContourWidth = r.width; tallestContourArea = contourArea(subContours[s]); } } if (tallestContourIdx != -1) { //cout << "Edge Filter: " << tallestContourHeight << " -- " << avgCharHeight << endl; if (tallestContourHeight >= avgCharHeight * 0.9 && ((tallestContourWidth < config->segmentationMinBoxWidthPx) || (tallestContourArea < avgCharWidth * avgCharHeight * 0.1))) { cout << "Edge Filter: Avg contour width: " << avgCharWidth << " This guy is: " << tallestContourWidth << endl; cout << "Edge Filter: tallestContourArea: " << tallestContourArea << " Minimum: " << avgCharWidth * avgCharHeight * 0.1 << endl; return i; } } } return -1; } Mat CharacterSegmenter::getCharBoxMask(Mat img_threshold, vector charBoxes) { Mat mask = Mat::zeros(img_threshold.size(), CV_8U); for (unsigned int i = 0; i < charBoxes.size(); i++) rectangle(mask, charBoxes[i], Scalar(255, 255, 255), -1); return mask; } std::vector CharacterSegmenter::convert1DHitsToRect(vector > hits, LineSegment top, LineSegment bottom) { vector boxes; for (unsigned int i = 0; i < hits.size(); i++) { Point topLeft = Point(hits[i].first, top.getPointAt(hits[i].first) - 1); Point botRight = Point(hits[i].second, bottom.getPointAt(hits[i].second) + 1); boxes.push_back(Rect(topLeft, botRight)); } return boxes; } } openalpr_2.2.4.orig/src/openalpr/segmentation/charactersegmenter.h000066400000000000000000000066041266464252400255240ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_CHARACTERSEGMENTER_H #define OPENALPR_CHARACTERSEGMENTER_H #include "opencv2/imgproc/imgproc.hpp" #include "constants.h" #include "binarize_wolf.h" #include "utility.h" #include "histogramvertical.h" #include "config.h" #include "textdetection/textcontours.h" #include "pipeline_data.h" namespace alpr { const cv::Scalar COLOR_DEBUG_EDGE(0,0,255); // Red const cv::Scalar COLOR_DEBUG_SPECKLES(0,0,255); // Red const cv::Scalar COLOR_DEBUG_MIN_HEIGHT(255,0,0); // Blue const cv::Scalar COLOR_DEBUG_MIN_AREA(255,0,0); // Blue const cv::Scalar COLOR_DEBUG_FULLBOX(255,255,0); // Blue-green const cv::Scalar COLOR_DEBUG_COLORFILTER(255,0,255); // Magenta const cv::Scalar COLOR_DEBUG_EMPTYFILTER(0,255,255); // Yellow class CharacterSegmenter { public: CharacterSegmenter(PipelineData* pipeline_data); virtual ~CharacterSegmenter(); int confidence; private: Config* config; PipelineData* pipeline_data; LineSegment top; LineSegment bottom; std::vector imgDbgGeneral; std::vector imgDbgCleanStages; cv::Mat getCharBoxMask(cv::Mat img_threshold, std::vector charBoxes); void removeSmallContours(std::vector thresholds, float avgCharHeight, TextLine textLine); std::vector getHistogramBoxes(HistogramVertical histogram, float avgCharWidth, float avgCharHeight, float* score); std::vector getBestCharBoxes(cv::Mat img, std::vector charBoxes, float avgCharWidth); int getCharGap(cv::Rect leftBox, cv::Rect rightBox); std::vector combineCloseBoxes( std::vector charBoxes); std::vector get1DHits(cv::Mat img, int yOffset); void cleanCharRegions(std::vector thresholds, std::vector charRegions); void cleanBasedOnColor(std::vector thresholds, cv::Mat colorMask, std::vector charRegions); std::vector filterMostlyEmptyBoxes(std::vector thresholds, const std::vector charRegions); cv::Mat filterEdgeBoxes(std::vector thresholds, const std::vector charRegions, float avgCharWidth, float avgCharHeight); int getLongestBlobLengthBetweenLines(cv::Mat img, int col); int isSkinnyLineInsideBox(cv::Mat threshold, cv::Rect box, std::vector > contours, std::vector hierarchy, float avgCharWidth, float avgCharHeight); std::vector convert1DHitsToRect(std::vector > hits, LineSegment top, LineSegment bottom); }; } #endif // OPENALPR_CHARACTERSEGMENTER_H openalpr_2.2.4.orig/src/openalpr/segmentation/histogram.cpp000066400000000000000000000137361266464252400242120ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "histogram.h" using namespace cv; using namespace std; namespace alpr { Histogram::Histogram() { } Histogram::~Histogram() { histoImg.release(); colHeights.clear(); } void Histogram::analyzeImage(cv::Mat inputImage, cv::Mat mask, bool use_y_axis) { int max_col_size = 0; int columnCount; if (use_y_axis) { // Calculate the histogram for vertical stripes for (int col = 0; col < inputImage.cols; col++) { columnCount = 0; for (int row = 0; row < inputImage.rows; row++) { if (inputImage.at(row, col) > 0 && mask.at(row, col) > 0) columnCount++; } this->colHeights.push_back(columnCount); if (columnCount > max_col_size) max_col_size = columnCount; } } else { // Calculate the histogram for horizontal stripes for (int row = 0; row < inputImage.rows; row++) { columnCount = 0; for (int col = 0; col < inputImage.cols; col++) { if (inputImage.at(row, col) > 0 && mask.at(row, col) > 0) columnCount++; } this->colHeights.push_back(columnCount); if (columnCount > max_col_size) max_col_size = columnCount; } } int histo_width = this->colHeights.size(); int histo_height = max_col_size + 10; histoImg = Mat::zeros(Size(histo_width, histo_height), CV_8U); // Draw the columns onto an Mat image for (unsigned int col = 0; col < histoImg.cols; col++) { if (col >= this->colHeights.size()) break; int columnCount = this->colHeights[col]; for (; columnCount > 0; columnCount--) histoImg.at(histo_height - columnCount, col) = 255; } } int Histogram::getLocalMinimum(int leftX, int rightX) { int minimum = histoImg.rows + 1; int lowestX = leftX; for (int i = leftX; i <= rightX; i++) { if (colHeights[i] < minimum) { lowestX = i; minimum = colHeights[i]; } } return lowestX; } int Histogram::getLocalMaximum(int leftX, int rightX) { int maximum = -1; int highestX = leftX; for (int i = leftX; i <= rightX; i++) { if (colHeights[i] > maximum) { highestX = i; maximum = colHeights[i]; } } return highestX; } int Histogram::getHeightAt(int x) { return colHeights[x]; } int Histogram::detect_peak( const double* data, /* the data */ int data_count, /* row count of data */ int* emi_peaks, /* emission peaks will be put here */ int* num_emi_peaks, /* number of emission peaks found */ int max_emi_peaks, /* maximum number of emission peaks */ int* absop_peaks, /* absorption peaks will be put here */ int* num_absop_peaks, /* number of absorption peaks found */ int max_absop_peaks, /* maximum number of absorption peaks */ double delta, /* delta used for distinguishing peaks */ int emi_first /* should we search emission peak first of absorption peak first? */ ) { int i; double mx; double mn; int mx_pos = 0; int mn_pos = 0; int is_detecting_emi = emi_first; mx = data[0]; mn = data[0]; *num_emi_peaks = 0; *num_absop_peaks = 0; for(i = 1; i < data_count; ++i) { if(data[i] > mx) { mx_pos = i; mx = data[i]; } if(data[i] < mn) { mn_pos = i; mn = data[i]; } if(is_detecting_emi && data[i] < mx - delta) { if(*num_emi_peaks >= max_emi_peaks) /* not enough spaces */ return 1; emi_peaks[*num_emi_peaks] = mx_pos; ++ (*num_emi_peaks); is_detecting_emi = 0; i = mx_pos - 1; mn = data[mx_pos]; mn_pos = mx_pos; } else if((!is_detecting_emi) && data[i] > mn + delta) { if(*num_absop_peaks >= max_absop_peaks) return 2; absop_peaks[*num_absop_peaks] = mn_pos; ++ (*num_absop_peaks); is_detecting_emi = 1; i = mn_pos - 1; mx = data[mn_pos]; mx_pos = mn_pos; } } return 0; } vector > Histogram::get1DHits(int yOffset) { vector > hits; bool onSegment = false; int curSegmentLength = 0; for (int col = 0; col < histoImg.cols; col++) { bool isOn = histoImg.at(histoImg.rows - 1 - yOffset, col); if (isOn) { // We're on a segment. Increment the length onSegment = true; curSegmentLength++; } if (onSegment && (isOn == false || (col == histoImg.cols - 1))) { // A segment just ended or we're at the very end of the row and we're on a segment pair pair(col - curSegmentLength, col); hits.push_back(pair); onSegment = false; curSegmentLength = 0; } } return hits; } }openalpr_2.2.4.orig/src/openalpr/segmentation/histogram.h000066400000000000000000000033051266464252400236460ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_HISTOGRAM_H #define OPENALPR_HISTOGRAM_H #include "opencv2/imgproc/imgproc.hpp" #include "utility.h" namespace alpr { class Histogram { public: Histogram(); virtual ~Histogram(); cv::Mat histoImg; // Returns the lowest X position between two points. int getLocalMinimum(int leftX, int rightX); // Returns the highest X position between two points. int getLocalMaximum(int leftX, int rightX); int getHeightAt(int x); std::vector > get1DHits(int yOffset); protected: std::vector colHeights; void analyzeImage(cv::Mat inputImage, cv::Mat mask, bool use_y_axis); int detect_peak(const double *data, int data_count, int *emi_peaks, int *num_emi_peaks, int max_emi_peaks, int *absop_peaks, int *num_absop_peaks, int max_absop_peaks, double delta, int emi_first); }; } #endif //OPENALPR_HISTOGRAM_H openalpr_2.2.4.orig/src/openalpr/segmentation/histogramhorizontal.cpp000066400000000000000000000016651266464252400263220ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "histogramhorizontal.h" namespace alpr { HistogramHorizontal::HistogramHorizontal(cv::Mat inputImage, cv::Mat mask) { analyzeImage(inputImage, mask, false); } }openalpr_2.2.4.orig/src/openalpr/segmentation/histogramhorizontal.h000066400000000000000000000021071266464252400257570ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_HISTOGRAMHORIZONTAL_H #define OPENALPR_HISTOGRAMHORIZONTAL_H #include "opencv2/imgproc/imgproc.hpp" #include "histogram.h" namespace alpr { class HistogramHorizontal : public Histogram { public: HistogramHorizontal(cv::Mat inputImage, cv::Mat mask); }; } #endif //OPENALPR_HISTOGRAMHORIZONTAL_H openalpr_2.2.4.orig/src/openalpr/segmentation/histogramvertical.cpp000066400000000000000000000017251266464252400257370ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "histogramvertical.h" using namespace cv; using namespace std; namespace alpr { HistogramVertical::HistogramVertical(Mat inputImage, Mat mask) { analyzeImage(inputImage, mask, true); } }openalpr_2.2.4.orig/src/openalpr/segmentation/histogramvertical.h000066400000000000000000000021341266464252400253770ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_VERTICALHISTOGRAM_H #define OPENALPR_VERTICALHISTOGRAM_H #include "opencv2/imgproc/imgproc.hpp" #include "histogram.h" #include "utility.h" namespace alpr { class HistogramVertical : public Histogram { public: HistogramVertical(cv::Mat inputImage, cv::Mat mask); }; } #endif // OPENALPR_VERTICALHISTOGRAM_H openalpr_2.2.4.orig/src/openalpr/segmentation/segment.cpp000066400000000000000000000032361266464252400236510ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "segment.h" namespace alpr { Segment::Segment(cv::Rect newSegment) { this->segment = newSegment; } Segment::~Segment() { } bool Segment::matches(cv::Rect newSegment) { // Compare the two segments with a given leniency const float WIDTH_LENIENCY_MIN = 0.25; const float WIDTH_LENIENCY_MAX = 0.20; float left_min = segment.x - (((float)segment.width) * WIDTH_LENIENCY_MIN); float left_max = segment.x + (((float)segment.width) * WIDTH_LENIENCY_MAX); float right_min = (segment.x + segment.width) - (((float)segment.width) * WIDTH_LENIENCY_MIN); float right_max = (segment.x + segment.width) + (((float)segment.width) * WIDTH_LENIENCY_MAX); int newSegRight = newSegment.x + newSegment.width; if (newSegment.x >= left_min && newSegment.x <= left_max && newSegRight >= right_min && newSegRight <= right_max) return true; return false; } }openalpr_2.2.4.orig/src/openalpr/segmentation/segment.h000066400000000000000000000021541266464252400233140ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_SEGMENT_H #define OPENALPR_SEGMENT_H #include #include #include "opencv2/imgproc/imgproc.hpp" namespace alpr { class Segment { public: Segment(cv::Rect newSegment); virtual ~Segment(); cv::Rect segment; bool matches(cv::Rect newSegment); }; } #endif // OPENALPR_SEGMENTATIONGROUP_H openalpr_2.2.4.orig/src/openalpr/segmentation/segmentationgroup.cpp000066400000000000000000000024561266464252400257640ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "segmentationgroup.h" namespace alpr { SegmentationGroup::SegmentationGroup() { } SegmentationGroup::~SegmentationGroup() { } void SegmentationGroup::add(int segmentID) { this->segmentIDs.push_back(segmentID); } bool SegmentationGroup::equals(SegmentationGroup otherGroup) { if (segmentIDs.size() != otherGroup.segmentIDs.size()) return false; for (int i = 0; i < segmentIDs.size(); i++) { if (otherGroup.segmentIDs[i] != segmentIDs[i]) return false; } return true; } }openalpr_2.2.4.orig/src/openalpr/segmentation/segmentationgroup.h000066400000000000000000000025111266464252400254210ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_SEGMENTATIONGROUP_H #define OPENALPR_SEGMENTATIONGROUP_H #include #include #include "opencv2/imgproc/imgproc.hpp" #include "segment.h" namespace alpr { class SegmentationGroup { public: SegmentationGroup(); virtual ~SegmentationGroup(); void add(int segmentID); std::vector segmentIDs; bool equals(SegmentationGroup otherGroup); private: float strength; // Debuggin purposes -- how many threshold segmentations match this one perfectly }; } #endif // OPENALPR_SEGMENTATIONGROUP_H openalpr_2.2.4.orig/src/openalpr/simpleini/000077500000000000000000000000001266464252400207735ustar00rootroot00000000000000openalpr_2.2.4.orig/src/openalpr/simpleini/CMakeLists.txt000066400000000000000000000001401266464252400235260ustar00rootroot00000000000000 set(simpleini_source_files ConvertUTF.c ) add_library(simpleini ${simpleini_source_files}) openalpr_2.2.4.orig/src/openalpr/simpleini/ConvertUTF.c000066400000000000000000000451551266464252400231500ustar00rootroot00000000000000/* * Copyright 2001-2004 Unicode, Inc. * * Disclaimer * * This source code is provided as is by Unicode, Inc. No claims are * made as to fitness for any particular purpose. No warranties of any * kind are expressed or implied. The recipient agrees to determine * applicability of information provided. If this file has been * purchased on magnetic or optical media from Unicode, Inc., the * sole remedy for any claim will be exchange of defective media * within 90 days of receipt. * * Limitations on Rights to Redistribute This Code * * Unicode, Inc. hereby grants the right to freely use the information * supplied in this file in the creation of products supporting the * Unicode Standard, and to make copies of this file in any form * for internal or external distribution as long as this notice * remains attached. */ /* --------------------------------------------------------------------- Conversions between UTF32, UTF-16, and UTF-8. Source code file. Author: Mark E. Davis, 1994. Rev History: Rick McGowan, fixes & updates May 2001. Sept 2001: fixed const & error conditions per mods suggested by S. Parent & A. Lillich. June 2002: Tim Dodd added detection and handling of incomplete source sequences, enhanced error detection, added casts to eliminate compiler warnings. July 2003: slight mods to back out aggressive FFFE detection. Jan 2004: updated switches in from-UTF8 conversions. Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. See the header file "ConvertUTF.h" for complete documentation. ------------------------------------------------------------------------ */ #include "ConvertUTF.h" #ifdef CVTUTF_DEBUG #include #endif static const int halfShift = 10; /* used for shifting by 10 bits */ static const UTF32 halfBase = 0x0010000UL; static const UTF32 halfMask = 0x3FFUL; #define UNI_SUR_HIGH_START (UTF32)0xD800 #define UNI_SUR_HIGH_END (UTF32)0xDBFF #define UNI_SUR_LOW_START (UTF32)0xDC00 #define UNI_SUR_LOW_END (UTF32)0xDFFF #define false 0 #define true 1 /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF32toUTF16 ( const UTF32** sourceStart, const UTF32* sourceEnd, UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; const UTF32* source = *sourceStart; UTF16* target = *targetStart; while (source < sourceEnd) { UTF32 ch; if (target >= targetEnd) { result = targetExhausted; break; } ch = *source++; if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { if (flags == strictConversion) { --source; /* return to the illegal value itself */ result = sourceIllegal; break; } else { *target++ = UNI_REPLACEMENT_CHAR; } } else { *target++ = (UTF16)ch; /* normal case */ } } else if (ch > UNI_MAX_LEGAL_UTF32) { if (flags == strictConversion) { result = sourceIllegal; } else { *target++ = UNI_REPLACEMENT_CHAR; } } else { /* target is a character in range 0xFFFF - 0x10FFFF. */ if (target + 1 >= targetEnd) { --source; /* Back up source pointer! */ result = targetExhausted; break; } ch -= halfBase; *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); } } *sourceStart = source; *targetStart = target; return result; } /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF16toUTF32 ( const UTF16** sourceStart, const UTF16* sourceEnd, UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; const UTF16* source = *sourceStart; UTF32* target = *targetStart; UTF32 ch, ch2; while (source < sourceEnd) { const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ ch = *source++; /* If we have a surrogate pair, convert to UTF32 first. */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { /* If the 16 bits following the high surrogate are in the source buffer... */ if (source < sourceEnd) { ch2 = *source; /* If it's a low surrogate, convert to UTF32. */ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) + halfBase; ++source; } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ --source; /* return to the illegal value itself */ result = sourceIllegal; break; } } else { /* We don't have the 16 bits following the high surrogate. */ --source; /* return to the high surrogate */ result = sourceExhausted; break; } } else if (flags == strictConversion) { /* UTF-16 surrogate values are illegal in UTF-32 */ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { --source; /* return to the illegal value itself */ result = sourceIllegal; break; } } if (target >= targetEnd) { source = oldSource; /* Back up source pointer! */ result = targetExhausted; break; } *target++ = ch; } *sourceStart = source; *targetStart = target; #ifdef CVTUTF_DEBUG if (result == sourceIllegal) { fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); fflush(stderr); } #endif return result; } /* --------------------------------------------------------------------- */ /* * Index into the table below with the first byte of a UTF-8 sequence to * get the number of trailing bytes that are supposed to follow it. * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is * left as-is for anyone who may want to do such conversion, which was * allowed in earlier algorithms. */ static const char trailingBytesForUTF8[256] = { 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,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,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,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,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,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,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; /* * Magic values subtracted from a buffer value during UTF8 conversion. * This table contains as many values as there might be trailing bytes * in a UTF-8 sequence. */ static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; /* * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed * into the first byte, depending on how many bytes follow. There are * as many entries in this table as there are UTF-8 sequence types. * (I.e., one byte sequence, two byte... etc.). Remember that sequencs * for *legal* UTF-8 will be 4 or fewer bytes total. */ static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; /* --------------------------------------------------------------------- */ /* The interface converts a whole buffer to avoid function-call overhead. * Constants have been gathered. Loops & conditionals have been removed as * much as possible for efficiency, in favor of drop-through switches. * (See "Note A" at the bottom of the file for equivalent code.) * If your compiler supports it, the "isLegalUTF8" call can be turned * into an inline function. */ /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF16toUTF8 ( const UTF16** sourceStart, const UTF16* sourceEnd, UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; const UTF16* source = *sourceStart; UTF8* target = *targetStart; while (source < sourceEnd) { UTF32 ch; unsigned short bytesToWrite = 0; const UTF32 byteMask = 0xBF; const UTF32 byteMark = 0x80; const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ ch = *source++; /* If we have a surrogate pair, convert to UTF32 first. */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { /* If the 16 bits following the high surrogate are in the source buffer... */ if (source < sourceEnd) { UTF32 ch2 = *source; /* If it's a low surrogate, convert to UTF32. */ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) + halfBase; ++source; } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ --source; /* return to the illegal value itself */ result = sourceIllegal; break; } } else { /* We don't have the 16 bits following the high surrogate. */ --source; /* return to the high surrogate */ result = sourceExhausted; break; } } else if (flags == strictConversion) { /* UTF-16 surrogate values are illegal in UTF-32 */ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { --source; /* return to the illegal value itself */ result = sourceIllegal; break; } } /* Figure out how many bytes the result will require */ if (ch < (UTF32)0x80) { bytesToWrite = 1; } else if (ch < (UTF32)0x800) { bytesToWrite = 2; } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; } else if (ch < (UTF32)0x110000) { bytesToWrite = 4; } else { bytesToWrite = 3; ch = UNI_REPLACEMENT_CHAR; } target += bytesToWrite; if (target > targetEnd) { source = oldSource; /* Back up source pointer! */ target -= bytesToWrite; result = targetExhausted; break; } switch (bytesToWrite) { /* note: everything falls through. */ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); } target += bytesToWrite; } *sourceStart = source; *targetStart = target; return result; } /* --------------------------------------------------------------------- */ /* * Utility routine to tell whether a sequence of bytes is legal UTF-8. * This must be called with the length pre-determined by the first byte. * If not calling this from ConvertUTF8to*, then the length can be set by: * length = trailingBytesForUTF8[*source]+1; * and the sequence is illegal right away if there aren't that many bytes * available. * If presented with a length > 4, this returns false. The Unicode * definition of UTF-8 goes up to 4-byte sequences. */ static Boolean isLegalUTF8(const UTF8 *source, int length) { UTF8 a; const UTF8 *srcptr = source+length; switch (length) { default: return false; /* Everything else falls through when "true"... */ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; case 2: if ((a = (*--srcptr)) > 0xBF) return false; switch (*source) { /* no fall-through in this inner switch */ case 0xE0: if (a < 0xA0) return false; break; case 0xED: if (a > 0x9F) return false; break; case 0xF0: if (a < 0x90) return false; break; case 0xF4: if (a > 0x8F) return false; break; default: if (a < 0x80) return false; } case 1: if (*source >= 0x80 && *source < 0xC2) return false; } if (*source > 0xF4) return false; return true; } /* --------------------------------------------------------------------- */ /* * Exported function to return whether a UTF-8 sequence is legal or not. * This is not used here; it's just exported. */ Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { int length = trailingBytesForUTF8[*source]+1; if (source+length > sourceEnd) { return false; } return isLegalUTF8(source, length); } /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF8toUTF16 ( const UTF8** sourceStart, const UTF8* sourceEnd, UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; const UTF8* source = *sourceStart; UTF16* target = *targetStart; while (source < sourceEnd) { UTF32 ch = 0; unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; if (source + extraBytesToRead >= sourceEnd) { result = sourceExhausted; break; } /* Do this check whether lenient or strict */ if (! isLegalUTF8(source, extraBytesToRead+1)) { result = sourceIllegal; break; } /* * The cases all fall through. See "Note A" below. */ switch (extraBytesToRead) { case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ case 3: ch += *source++; ch <<= 6; case 2: ch += *source++; ch <<= 6; case 1: ch += *source++; ch <<= 6; case 0: ch += *source++; } ch -= offsetsFromUTF8[extraBytesToRead]; if (target >= targetEnd) { source -= (extraBytesToRead+1); /* Back up source pointer! */ result = targetExhausted; break; } if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ /* UTF-16 surrogate values are illegal in UTF-32 */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { if (flags == strictConversion) { source -= (extraBytesToRead+1); /* return to the illegal value itself */ result = sourceIllegal; break; } else { *target++ = UNI_REPLACEMENT_CHAR; } } else { *target++ = (UTF16)ch; /* normal case */ } } else if (ch > UNI_MAX_UTF16) { if (flags == strictConversion) { result = sourceIllegal; source -= (extraBytesToRead+1); /* return to the start */ break; /* Bail out; shouldn't continue */ } else { *target++ = UNI_REPLACEMENT_CHAR; } } else { /* target is a character in range 0xFFFF - 0x10FFFF. */ if (target + 1 >= targetEnd) { source -= (extraBytesToRead+1); /* Back up source pointer! */ result = targetExhausted; break; } ch -= halfBase; *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); } } *sourceStart = source; *targetStart = target; return result; } /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF32toUTF8 ( const UTF32** sourceStart, const UTF32* sourceEnd, UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; const UTF32* source = *sourceStart; UTF8* target = *targetStart; while (source < sourceEnd) { UTF32 ch; unsigned short bytesToWrite = 0; const UTF32 byteMask = 0xBF; const UTF32 byteMark = 0x80; ch = *source++; if (flags == strictConversion ) { /* UTF-16 surrogate values are illegal in UTF-32 */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { --source; /* return to the illegal value itself */ result = sourceIllegal; break; } } /* * Figure out how many bytes the result will require. Turn any * illegally large UTF32 things (> Plane 17) into replacement chars. */ if (ch < (UTF32)0x80) { bytesToWrite = 1; } else if (ch < (UTF32)0x800) { bytesToWrite = 2; } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; } else { bytesToWrite = 3; ch = UNI_REPLACEMENT_CHAR; result = sourceIllegal; } target += bytesToWrite; if (target > targetEnd) { --source; /* Back up source pointer! */ target -= bytesToWrite; result = targetExhausted; break; } switch (bytesToWrite) { /* note: everything falls through. */ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); } target += bytesToWrite; } *sourceStart = source; *targetStart = target; return result; } /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF8toUTF32 ( const UTF8** sourceStart, const UTF8* sourceEnd, UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; const UTF8* source = *sourceStart; UTF32* target = *targetStart; while (source < sourceEnd) { UTF32 ch = 0; unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; if (source + extraBytesToRead >= sourceEnd) { result = sourceExhausted; break; } /* Do this check whether lenient or strict */ if (! isLegalUTF8(source, extraBytesToRead+1)) { result = sourceIllegal; break; } /* * The cases all fall through. See "Note A" below. */ switch (extraBytesToRead) { case 5: ch += *source++; ch <<= 6; case 4: ch += *source++; ch <<= 6; case 3: ch += *source++; ch <<= 6; case 2: ch += *source++; ch <<= 6; case 1: ch += *source++; ch <<= 6; case 0: ch += *source++; } ch -= offsetsFromUTF8[extraBytesToRead]; if (target >= targetEnd) { source -= (extraBytesToRead+1); /* Back up the source pointer! */ result = targetExhausted; break; } if (ch <= UNI_MAX_LEGAL_UTF32) { /* * UTF-16 surrogate values are illegal in UTF-32, and anything * over Plane 17 (> 0x10FFFF) is illegal. */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { if (flags == strictConversion) { source -= (extraBytesToRead+1); /* return to the illegal value itself */ result = sourceIllegal; break; } else { *target++ = UNI_REPLACEMENT_CHAR; } } else { *target++ = ch; } } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ result = sourceIllegal; *target++ = UNI_REPLACEMENT_CHAR; } } *sourceStart = source; *targetStart = target; return result; } /* --------------------------------------------------------------------- Note A. The fall-through switches in UTF-8 reading code save a temp variable, some decrements & conditionals. The switches are equivalent to the following loop: { int tmpBytesToRead = extraBytesToRead+1; do { ch += *source++; --tmpBytesToRead; if (tmpBytesToRead) ch <<= 6; } while (tmpBytesToRead > 0); } In UTF-8 writing code, the switches on "bytesToWrite" are similarly unrolled loops. --------------------------------------------------------------------- */ openalpr_2.2.4.orig/src/openalpr/simpleini/ConvertUTF.h000066400000000000000000000134511266464252400231470ustar00rootroot00000000000000/* * Copyright 2001-2004 Unicode, Inc. * * Disclaimer * * This source code is provided as is by Unicode, Inc. No claims are * made as to fitness for any particular purpose. No warranties of any * kind are expressed or implied. The recipient agrees to determine * applicability of information provided. If this file has been * purchased on magnetic or optical media from Unicode, Inc., the * sole remedy for any claim will be exchange of defective media * within 90 days of receipt. * * Limitations on Rights to Redistribute This Code * * Unicode, Inc. hereby grants the right to freely use the information * supplied in this file in the creation of products supporting the * Unicode Standard, and to make copies of this file in any form * for internal or external distribution as long as this notice * remains attached. */ /* --------------------------------------------------------------------- Conversions between UTF32, UTF-16, and UTF-8. Header file. Several funtions are included here, forming a complete set of conversions between the three formats. UTF-7 is not included here, but is handled in a separate source file. Each of these routines takes pointers to input buffers and output buffers. The input buffers are const. Each routine converts the text between *sourceStart and sourceEnd, putting the result into the buffer between *targetStart and targetEnd. Note: the end pointers are *after* the last item: e.g. *(sourceEnd - 1) is the last item. The return result indicates whether the conversion was successful, and if not, whether the problem was in the source or target buffers. (Only the first encountered problem is indicated.) After the conversion, *sourceStart and *targetStart are both updated to point to the end of last text successfully converted in the respective buffers. Input parameters: sourceStart - pointer to a pointer to the source buffer. The contents of this are modified on return so that it points at the next thing to be converted. targetStart - similarly, pointer to pointer to the target buffer. sourceEnd, targetEnd - respectively pointers to the ends of the two buffers, for overflow checking only. These conversion functions take a ConversionFlags argument. When this flag is set to strict, both irregular sequences and isolated surrogates will cause an error. When the flag is set to lenient, both irregular sequences and isolated surrogates are converted. Whether the flag is strict or lenient, all illegal sequences will cause an error return. This includes sequences such as: , , or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code must check for illegal sequences. When the flag is set to lenient, characters over 0x10FFFF are converted to the replacement character; otherwise (when the flag is set to strict) they constitute an error. Output parameters: The value "sourceIllegal" is returned from some routines if the input sequence is malformed. When "sourceIllegal" is returned, the source value will point to the illegal value that caused the problem. E.g., in UTF-8 when a sequence is malformed, it points to the start of the malformed sequence. Author: Mark E. Davis, 1994. Rev History: Rick McGowan, fixes & updates May 2001. Fixes & updates, Sept 2001. ------------------------------------------------------------------------ */ /* --------------------------------------------------------------------- The following 4 definitions are compiler-specific. The C standard does not guarantee that wchar_t has at least 16 bits, so wchar_t is no less portable than unsigned short! All should be unsigned values to avoid sign extension during bit mask & shift operations. ------------------------------------------------------------------------ */ typedef unsigned int UTF32; /* at least 32 bits */ typedef unsigned short UTF16; /* at least 16 bits */ typedef unsigned char UTF8; /* typically 8 bits */ typedef unsigned char Boolean; /* 0 or 1 */ /* Some fundamental constants */ #define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD #define UNI_MAX_BMP (UTF32)0x0000FFFF #define UNI_MAX_UTF16 (UTF32)0x0010FFFF #define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF #define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF typedef enum { conversionOK, /* conversion successful */ sourceExhausted, /* partial character in source, but hit end */ targetExhausted, /* insuff. room in target for conversion */ sourceIllegal /* source sequence is illegal/malformed */ } ConversionResult; typedef enum { strictConversion = 0, lenientConversion } ConversionFlags; /* This is for C++ and does no harm in C */ #ifdef __cplusplus extern "C" { #endif ConversionResult ConvertUTF8toUTF16 ( const UTF8** sourceStart, const UTF8* sourceEnd, UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); ConversionResult ConvertUTF16toUTF8 ( const UTF16** sourceStart, const UTF16* sourceEnd, UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); ConversionResult ConvertUTF8toUTF32 ( const UTF8** sourceStart, const UTF8* sourceEnd, UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); ConversionResult ConvertUTF32toUTF8 ( const UTF32** sourceStart, const UTF32* sourceEnd, UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); ConversionResult ConvertUTF16toUTF32 ( const UTF16** sourceStart, const UTF16* sourceEnd, UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); ConversionResult ConvertUTF32toUTF16 ( const UTF32** sourceStart, const UTF32* sourceEnd, UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); #ifdef __cplusplus } #endif /* --------------------------------------------------------------------- */ openalpr_2.2.4.orig/src/openalpr/simpleini/LICENCE.txt000066400000000000000000000021351266464252400225770ustar00rootroot00000000000000SimpleIni library license: The MIT License (MIT) Copyright (c) 2006-2013 Brodie Thiesfield Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. openalpr_2.2.4.orig/src/openalpr/simpleini/README.md000066400000000000000000000107171266464252400222600ustar00rootroot00000000000000simpleini ========= A cross-platform library that provides a simple API to read and write INI-style configuration files. It supports data files in ASCII, MBCS and Unicode. It is designed explicitly to be portable to any platform and has been tested on Windows, WinCE and Linux. Released as open-source and free using the MIT licence. # Feature Summary - MIT Licence allows free use in all software (including GPL and commercial) - multi-platform (Windows 95/98/ME/NT/2K/XP/2003, Windows CE, Linux, Unix) - loading and saving of INI-style configuration files - configuration files can have any newline format on all platforms - liberal acceptance of file format * key/values with no section * removal of whitespace around sections, keys and values - support for multi-line values (values with embedded newline characters) - optional support for multiple keys with the same name - optional case-insensitive sections and keys (for ASCII characters only) - saves files with sections and keys in the same order as they were loaded - preserves comments on the file, section and keys where possible. - supports both char or wchar_t programming interfaces - supports both MBCS (system locale) and UTF-8 file encodings - system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file - support for non-ASCII characters in section, keys, values and comments - support for non-standard character types or file encodings via user-written converter classes - support for adding/modifying values programmatically - compiles cleanly in the following compilers: * Windows/VC6 (warning level 3) * Windows/VC.NET 2003 (warning level 4) * Windows/VC 2005 (warning level 4) * Linux/gcc (-Wall) * Windows/MinGW GCC # Documentation Full documentation of the interface is available in doxygen format. # Examples These snippets are included with the distribution in the file snippets.cpp. ### SIMPLE USAGE ```c++ CSimpleIniA ini; ini.SetUnicode(); ini.LoadFile("myfile.ini"); const char * pVal = ini.GetValue("section", "key", "default"); ini.SetValue("section", "key", "newvalue"); ``` ### LOADING DATA ```c++ // load from a data file CSimpleIniA ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine); SI_Error rc = ini.LoadFile(a_pszFile); if (rc < 0) return false; // load from a string std::string strData; rc = ini.LoadData(strData.c_str(), strData.size()); if (rc < 0) return false; ``` ### GETTING SECTIONS AND KEYS ```c++ // get all sections CSimpleIniA::TNamesDepend sections; ini.GetAllSections(sections); // get all keys in a section CSimpleIniA::TNamesDepend keys; ini.GetAllKeys("section-name", keys); ``` ### GETTING VALUES ```c++ // get the value of a key const char * pszValue = ini.GetValue("section-name", "key-name", NULL /*default*/); // get the value of a key which may have multiple // values. If bHasMultipleValues is true, then just // one value has been returned bool bHasMultipleValues; pszValue = ini.GetValue("section-name", "key-name", NULL /*default*/, &bHasMultipleValues); // get all values of a key with multiple values CSimpleIniA::TNamesDepend values; ini.GetAllValues("section-name", "key-name", values); // sort the values into the original load order values.sort(CSimpleIniA::Entry::LoadOrder()); // output all of the items CSimpleIniA::TNamesDepend::const_iterator i; for (i = values.begin(); i != values.end(); ++i) { printf("key-name = '%s'\n", i->pItem); } ``` ### MODIFYING DATA ```c++ // adding a new section rc = ini.SetValue("new-section", NULL, NULL); if (rc < 0) return false; printf("section: %s\n", rc == SI_INSERTED ? "inserted" : "updated"); // adding a new key ("new-section" will be added // automatically if it doesn't already exist) rc = ini.SetValue("new-section", "new-key", "value"); if (rc < 0) return false; printf("key: %s\n", rc == SI_INSERTED ? "inserted" : "updated"); // changing the value of a key rc = ini.SetValue("section", "key", "updated-value"); if (rc < 0) return false; printf("key: %s\n", rc == SI_INSERTED ? "inserted" : "updated"); ``` ### DELETING DATA ```c++ // deleting a key from a section. Optionally the entire // section may be deleted if it is now empty. ini.Delete("section-name", "key-name", true /*delete the section if empty*/); // deleting an entire section and all keys in it ini.Delete("section-name", NULL); ``` ### SAVING DATA ```c++ // save the data to a string rc = ini.Save(strData); if (rc < 0) return false; // save the data back to the file rc = ini.SaveFile(a_pszFile); if (rc < 0) return false; ``` openalpr_2.2.4.orig/src/openalpr/simpleini/ini.syn000066400000000000000000000012301266464252400223010ustar00rootroot00000000000000; Syntax file for ini files - contributed by Brodie Thiesfield ; ; Suggested Colors: ; Comments (;#) Comments, Comments 2 Green ; Sections Characters Red ; Values Strings Blue C=1 [Syntax] Namespace1 = 6 IgnoreCase = Yes KeyWordLength = 1 BracketChars = OperatorChars = PreprocStart = SyntaxStart = SyntaxEnd = HexPrefix = CommentStart = CommentEnd = CommentStartAlt = CommentEndAlt = SingleComment = # SingleCommentCol = SingleCommentAlt = ; SingleCommentColAlt = SingleCommentEsc = StringsSpanLines = No StringStart = StringEnd = StringAlt = = StringEsc = CharStart = [ CharEnd = ] CharEsc = openalpr_2.2.4.orig/src/openalpr/simpleini/simpleini.doxy000066400000000000000000001530131266464252400236740ustar00rootroot00000000000000# Doxyfile 1.5.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file that # follow. The default is UTF-8 which is also the encoding used for all text before # the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into # libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of # possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = SimpleIni # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = D:/src/simpleini-doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, # Italian, Japanese, Japanese-en (Japanese with English messages), Korean, # Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, # Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class " \ "The $name widget " \ "The $name file " \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = "D:/src/simpleini/ " # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to # include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct (or union) is # documented as struct with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code where the coding convention is that all structs are # typedef'ed and only the typedef is referenced never the struct's name. TYPEDEF_HIDES_STRUCT = NO #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be extracted # and appear in the documentation as a namespace called 'anonymous_namespace{file}', # where file will be replaced with the base name of the file that contains the anonymous # namespace. By default anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file($line) : $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = D:/src/simpleini/SimpleIni.h # This tag can be used to specify the character encoding of the source files that # doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default # input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. # See http://www.gnu.org/software/libiconv for the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the output. # The symbol name can be a fully qualified name, a word, or if the wildcard * is used, # a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH # then you must also enable this option. If you don't then doxygen will produce # a warning and turn it on anyway SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentstion. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = SI_HAS_WIDE_FILE \ SI_SUPPORT_IOSTREAMS # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to # produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to # specify the directory where the mscgen tool resides. If left empty the tool is assumed to # be found in the default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a caller dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected # functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the number # of direct children of the root node in a graph is already larger than # MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 1000 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO openalpr_2.2.4.orig/src/openalpr/simpleini/simpleini.h000066400000000000000000003461261266464252400231510ustar00rootroot00000000000000/** @mainpage
Library SimpleIni
File SimpleIni.h
Author Brodie Thiesfield [code at jellycan dot com]
Source https://github.com/brofield/simpleini
Version 4.17
Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation. @section intro INTRODUCTION This component allows an INI-style configuration file to be used on both Windows and Linux/Unix. It is fast, simple and source code using this component will compile unchanged on either OS. @section features FEATURES - MIT Licence allows free use in all software (including GPL and commercial) - multi-platform (Windows 95/98/ME/NT/2K/XP/2003, Windows CE, Linux, Unix) - loading and saving of INI-style configuration files - configuration files can have any newline format on all platforms - liberal acceptance of file format - key/values with no section - removal of whitespace around sections, keys and values - support for multi-line values (values with embedded newline characters) - optional support for multiple keys with the same name - optional case-insensitive sections and keys (for ASCII characters only) - saves files with sections and keys in the same order as they were loaded - preserves comments on the file, section and keys where possible. - supports both char or wchar_t programming interfaces - supports both MBCS (system locale) and UTF-8 file encodings - system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file - support for non-ASCII characters in section, keys, values and comments - support for non-standard character types or file encodings via user-written converter classes - support for adding/modifying values programmatically - compiles cleanly in the following compilers: - Windows/VC6 (warning level 3) - Windows/VC.NET 2003 (warning level 4) - Windows/VC 2005 (warning level 4) - Linux/gcc (-Wall) @section usage USAGE SUMMARY -# Define the appropriate symbol for the converter you wish to use and include the SimpleIni.h header file. If no specific converter is defined then the default converter is used. The default conversion mode uses SI_CONVERT_WIN32 on Windows and SI_CONVERT_GENERIC on all other platforms. If you are using ICU then SI_CONVERT_ICU is supported on all platforms. -# Declare an instance the appropriate class. Note that the following definitions are just shortcuts for commonly used types. Other types (PRUnichar, unsigned short, unsigned char) are also possible.
Interface Case-sensitive Load UTF-8 Load MBCS Typedef
SI_CONVERT_GENERIC
char No Yes Yes #1 CSimpleIniA
char Yes Yes Yes CSimpleIniCaseA
wchar_t No Yes Yes CSimpleIniW
wchar_t Yes Yes Yes CSimpleIniCaseW
SI_CONVERT_WIN32
char No No #2 Yes CSimpleIniA
char Yes Yes Yes CSimpleIniCaseA
wchar_t No Yes Yes CSimpleIniW
wchar_t Yes Yes Yes CSimpleIniCaseW
SI_CONVERT_ICU
char No Yes Yes CSimpleIniA
char Yes Yes Yes CSimpleIniCaseA
UChar No Yes Yes CSimpleIniW
UChar Yes Yes Yes CSimpleIniCaseW
#1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.
#2 Only affects Windows. On Windows this uses MBCS functions and so may fold case incorrectly leading to uncertain results. -# Call LoadData() or LoadFile() to load and parse the INI configuration file -# Access and modify the data of the file using the following functions
GetAllSections Return all section names
GetAllKeys Return all key names within a section
GetAllValues Return all values within a section & key
GetSection Return all key names and values in a section
GetSectionSize Return the number of keys in a section
GetValue Return a value for a section & key
SetValue Add or update a value for a section & key
Delete Remove a section, or a key from a section
-# Call Save() or SaveFile() to save the INI configuration data @section iostreams IO STREAMS SimpleIni supports reading from and writing to STL IO streams. Enable this by defining SI_SUPPORT_IOSTREAMS before including the SimpleIni.h header file. Ensure that if the streams are backed by a file (e.g. ifstream or ofstream) then the flag ios_base::binary has been used when the file was opened. @section multiline MULTI-LINE VALUES Values that span multiple lines are created using the following format.

        key = <<

    Note the following:
    - The text used for ENDTAG can be anything and is used to find
      where the multi-line text ends.
    - The newline after ENDTAG in the start tag, and the newline
      before ENDTAG in the end tag is not included in the data value.
    - The ending tag must be on it's own line with no whitespace before
      or after it.
    - The multi-line value is modified at load so that each line in the value
      is delimited by a single '\\n' character on all platforms. At save time
      it will be converted into the newline format used by the current
      platform.

    @section comments COMMENTS

    Comments are preserved in the file within the following restrictions:
    - Every file may have a single "file comment". It must start with the
      first character in the file, and will end with the first non-comment
      line in the file.
    - Every section may have a single "section comment". It will start
      with the first comment line following the file comment, or the last
      data entry. It ends at the beginning of the section.
    - Every key may have a single "key comment". This comment will start
      with the first comment line following the section start, or the file
      comment if there is no section name.
    - Comments are set at the time that the file, section or key is first
      created. The only way to modify a comment on a section or a key is to
      delete that entry and recreate it with the new comment. There is no
      way to change the file comment.

    @section save SAVE ORDER

    The sections and keys are written out in the same order as they were
    read in from the file. Sections and keys added to the data after the
    file has been loaded will be added to the end of the file when it is
    written. There is no way to specify the location of a section or key
    other than in first-created, first-saved order.

    @section notes NOTES

    - To load UTF-8 data on Windows 95, you need to use Microsoft Layer for
      Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.
    - When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.
    - When using SI_CONVERT_ICU, ICU header files must be on the include
      path and icuuc.lib must be linked in.
    - To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,
      you should use SI_CONVERT_GENERIC.
    - The collation (sorting) order used for sections and keys returned from
      iterators is NOT DEFINED. If collation order of the text is important
      then it should be done yourself by either supplying a replacement
      SI_STRLESS class, or by sorting the strings external to this library.
    - Usage of the  header on Windows can be disabled by defining
      SI_NO_MBCS. This is defined automatically on Windows CE platforms.

    @section contrib CONTRIBUTIONS

    - 2010/05/03: Tobias Gehrig: added GetDoubleValue()

    @section licence MIT LICENCE

    The licence text below is the boilerplate "MIT Licence" used from:
    http://www.opensource.org/licenses/mit-license.php

    Copyright (c) 2006-2012, Brodie Thiesfield

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is furnished
    to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef INCLUDED_SimpleIni_h
#define INCLUDED_SimpleIni_h

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

// Disable these warnings in MSVC:
//  4127 "conditional expression is constant" as the conversion classes trigger
//  it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
//  be optimized away in a release build.
//  4503 'insert' : decorated name length exceeded, name was truncated
//  4702 "unreachable code" as the MS STL header causes it in release mode.
//  Again, the code causing the warning will be cleaned up by the compiler.
//  4786 "identifier truncated to 256 characters" as this is thrown hundreds
//  of times VC6 as soon as STL is used.
#ifdef _MSC_VER
# pragma warning (push)
# pragma warning (disable: 4127 4503 4702 4786)
#endif

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef SI_SUPPORT_IOSTREAMS
# include 
#endif // SI_SUPPORT_IOSTREAMS

#ifdef _DEBUG
# ifndef assert
#  include 
# endif
# define SI_ASSERT(x)   assert(x)
#else
# define SI_ASSERT(x)
#endif

enum SI_Error
{
  SI_OK       =  0,   //!< No error
  SI_UPDATED  =  1,   //!< An existing value was updated
  SI_INSERTED =  2,   //!< A new value was inserted

  // note: test for any error with (retval < 0)
  SI_FAIL     = -1,   //!< Generic failure
  SI_NOMEM    = -2,   //!< Out of memory error
  SI_FILE     = -3    //!< File error (see errno for detail error)
};

#define SI_UTF8_SIGNATURE     "\xEF\xBB\xBF"

#ifdef _WIN32
# define SI_NEWLINE_A   "\r\n"
# define SI_NEWLINE_W   L"\r\n"
#else // !_WIN32
# define SI_NEWLINE_A   "\n"
# define SI_NEWLINE_W   L"\n"
#endif // _WIN32

#if defined(SI_CONVERT_ICU)
# include 
#endif

#if defined(_WIN32)
# define SI_HAS_WIDE_FILE
# define SI_WCHAR_T     wchar_t
#elif defined(SI_CONVERT_ICU)
# define SI_HAS_WIDE_FILE
# define SI_WCHAR_T     UChar
#endif

// ---------------------------------------------------------------------------
//                              MAIN TEMPLATE CLASS
// ---------------------------------------------------------------------------

/** Simple INI file reader.

    This can be instantiated with the choice of unicode or native characterset,
    and case sensitive or insensitive comparisons of section and key names.
    The supported combinations are pre-defined with the following typedefs:

    
Interface Case-sensitive Typedef
char No CSimpleIniA
char Yes CSimpleIniCaseA
wchar_t No CSimpleIniW
wchar_t Yes CSimpleIniCaseW
Note that using other types for the SI_CHAR is supported. For instance, unsigned char, unsigned short, etc. Note that where the alternative type is a different size to char/wchar_t you may need to supply new helper classes for SI_STRLESS and SI_CONVERTER. */ template class CSimpleIniTempl { public: typedef SI_CHAR SI_CHAR_T; /** key entry */ struct Entry { const SI_CHAR * pItem; const SI_CHAR * pComment; int nOrder; Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0) : pItem(a_pszItem) , pComment(NULL) , nOrder(a_nOrder) { } Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder) : pItem(a_pszItem) , pComment(a_pszComment) , nOrder(a_nOrder) { } Entry(const Entry & rhs) { operator=(rhs); } Entry & operator=(const Entry & rhs) { pItem = rhs.pItem; pComment = rhs.pComment; nOrder = rhs.nOrder; return *this; } #if defined(_MSC_VER) && _MSC_VER <= 1200 /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */ bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); } bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); } #endif /** Strict less ordering by name of key only */ struct KeyOrder : std::binary_function { bool operator()(const Entry & lhs, const Entry & rhs) const { const static SI_STRLESS isLess = SI_STRLESS(); return isLess(lhs.pItem, rhs.pItem); } }; /** Strict less ordering by order, and then name of key */ struct LoadOrder : std::binary_function { bool operator()(const Entry & lhs, const Entry & rhs) const { if (lhs.nOrder != rhs.nOrder) { return lhs.nOrder < rhs.nOrder; } return KeyOrder()(lhs.pItem, rhs.pItem); } }; }; /** map keys to values */ typedef std::multimap TKeyVal; /** map sections to key/value map */ typedef std::map TSection; /** set of dependent string pointers. Note that these pointers are dependent on memory owned by CSimpleIni. */ typedef std::list TNamesDepend; /** interface definition for the OutputWriter object to pass to Save() in order to output the INI file data. */ class OutputWriter { public: OutputWriter() { } virtual ~OutputWriter() { } virtual void Write(const char * a_pBuf) = 0; private: OutputWriter(const OutputWriter &); // disable OutputWriter & operator=(const OutputWriter &); // disable }; /** OutputWriter class to write the INI data to a file */ class FileWriter : public OutputWriter { FILE * m_file; public: FileWriter(FILE * a_file) : m_file(a_file) { } void Write(const char * a_pBuf) { fputs(a_pBuf, m_file); } private: FileWriter(const FileWriter &); // disable FileWriter & operator=(const FileWriter &); // disable }; /** OutputWriter class to write the INI data to a string */ class StringWriter : public OutputWriter { std::string & m_string; public: StringWriter(std::string & a_string) : m_string(a_string) { } void Write(const char * a_pBuf) { m_string.append(a_pBuf); } private: StringWriter(const StringWriter &); // disable StringWriter & operator=(const StringWriter &); // disable }; #ifdef SI_SUPPORT_IOSTREAMS /** OutputWriter class to write the INI data to an ostream */ class StreamWriter : public OutputWriter { std::ostream & m_ostream; public: StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { } void Write(const char * a_pBuf) { m_ostream << a_pBuf; } private: StreamWriter(const StreamWriter &); // disable StreamWriter & operator=(const StreamWriter &); // disable }; #endif // SI_SUPPORT_IOSTREAMS /** Characterset conversion utility class to convert strings to the same format as is used for the storage. */ class Converter : private SI_CONVERTER { public: using SI_CONVERTER::SizeToStore; Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) { m_scratch.resize(1024); } Converter(const Converter & rhs) { operator=(rhs); } Converter & operator=(const Converter & rhs) { m_scratch = rhs.m_scratch; return *this; } bool ConvertToStore(const SI_CHAR * a_pszString) { size_t uLen = SizeToStore(a_pszString); if (uLen == (size_t)(-1)) { return false; } while (uLen > m_scratch.size()) { m_scratch.resize(m_scratch.size() * 2); } return SI_CONVERTER::ConvertToStore( a_pszString, const_cast(m_scratch.data()), m_scratch.size()); } const char * Data() { return m_scratch.data(); } private: std::string m_scratch; }; public: /*-----------------------------------------------------------------------*/ /** Default constructor. @param a_bIsUtf8 See the method SetUnicode() for details. @param a_bMultiKey See the method SetMultiKey() for details. @param a_bMultiLine See the method SetMultiLine() for details. */ CSimpleIniTempl( bool a_bIsUtf8 = false, bool a_bMultiKey = false, bool a_bMultiLine = false ); /** Destructor */ ~CSimpleIniTempl(); /** Deallocate all memory stored by this object */ void Reset(); /** Has any data been loaded */ bool IsEmpty() const { return m_data.empty(); } /*-----------------------------------------------------------------------*/ /** @{ @name Settings */ /** Set the storage format of the INI data. This affects both the loading and saving of the INI data using all of the Load/Save API functions. This value cannot be changed after any INI data has been loaded. If the file is not set to Unicode (UTF-8), then the data encoding is assumed to be the OS native encoding. This encoding is the system locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP. If the storage format is set to Unicode then the file will be loaded as UTF-8 encoded data regardless of the native file encoding. If SI_CHAR == char then all of the char* parameters take and return UTF-8 encoded data regardless of the system locale. \param a_bIsUtf8 Assume UTF-8 encoding for the source? */ void SetUnicode(bool a_bIsUtf8 = true) { if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8; } /** Get the storage format of the INI data. */ bool IsUnicode() const { return m_bStoreIsUtf8; } /** Should multiple identical keys be permitted in the file. If set to false then the last value encountered will be used as the value of the key. If set to true, then all values will be available to be queried. For example, with the following input:
        [section]
        test=value1
        test=value2
        
Then with SetMultiKey(true), both of the values "value1" and "value2" will be returned for the key test. If SetMultiKey(false) is used, then the value for "test" will only be "value2". This value may be changed at any time. \param a_bAllowMultiKey Allow multi-keys in the source? */ void SetMultiKey(bool a_bAllowMultiKey = true) { m_bAllowMultiKey = a_bAllowMultiKey; } /** Get the storage format of the INI data. */ bool IsMultiKey() const { return m_bAllowMultiKey; } /** Should data values be permitted to span multiple lines in the file. If set to false then the multi-line construct << SI_CHAR FORMAT char same format as when loaded (MBCS or UTF-8) wchar_t UTF-8 other UTF-8 Note that comments from the original data is preserved as per the documentation on comments. The order of the sections and values from the original file will be preserved. Any data prepended or appended to the output device must use the the same format (MBCS or UTF-8). You may use the GetConverter() method to convert text to the correct format regardless of the output format being used by SimpleIni. To add a BOM to UTF-8 data, write it out manually at the very beginning like is done in SaveFile when a_bUseBOM is true. @param a_oOutput Output writer to write the data to. @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in UTF-8 format. If it is not UTF-8 then this value is ignored. Do not set this to true if anything has already been written to the OutputWriter. @return SI_Error See error definitions */ SI_Error Save( OutputWriter & a_oOutput, bool a_bAddSignature = false ) const; #ifdef SI_SUPPORT_IOSTREAMS /** Save the INI data to an ostream. See Save() for details. @param a_ostream String to have the INI data appended to. @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in UTF-8 format. If it is not UTF-8 then this value is ignored. Do not set this to true if anything has already been written to the stream. @return SI_Error See error definitions */ SI_Error Save( std::ostream & a_ostream, bool a_bAddSignature = false ) const { StreamWriter writer(a_ostream); return Save(writer, a_bAddSignature); } #endif // SI_SUPPORT_IOSTREAMS /** Append the INI data to a string. See Save() for details. @param a_sBuffer String to have the INI data appended to. @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in UTF-8 format. If it is not UTF-8 then this value is ignored. Do not set this to true if anything has already been written to the string. @return SI_Error See error definitions */ SI_Error Save( std::string & a_sBuffer, bool a_bAddSignature = false ) const { StringWriter writer(a_sBuffer); return Save(writer, a_bAddSignature); } /*-----------------------------------------------------------------------*/ /** @} @{ @name Accessing INI Data */ /** Retrieve all section names. The list is returned as an STL vector of names and can be iterated or searched as necessary. Note that the sort order of the returned strings is NOT DEFINED. You can sort the names into the load order if desired. Search this file for ".sort" for an example. NOTE! This structure contains only pointers to strings. The actual string data is stored in memory owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed or Reset() while these pointers are in use! @param a_names Vector that will receive all of the section names. See note above! */ void GetAllSections( TNamesDepend & a_names ) const; /** Retrieve all unique key names in a section. The sort order of the returned strings is NOT DEFINED. You can sort the names into the load order if desired. Search this file for ".sort" for an example. Only unique key names are returned. NOTE! This structure contains only pointers to strings. The actual string data is stored in memory owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed or Reset() while these strings are in use! @param a_pSection Section to request data for @param a_names List that will receive all of the key names. See note above! @return true Section was found. @return false Matching section was not found. */ bool GetAllKeys( const SI_CHAR * a_pSection, TNamesDepend & a_names ) const; /** Retrieve all values for a specific key. This method can be used when multiple keys are both enabled and disabled. Note that the sort order of the returned strings is NOT DEFINED. You can sort the names into the load order if desired. Search this file for ".sort" for an example. NOTE! The returned values are pointers to string data stored in memory owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed or Reset while you are using this pointer! @param a_pSection Section to search @param a_pKey Key to search for @param a_values List to return if the key is not found @return true Key was found. @return false Matching section/key was not found. */ bool GetAllValues( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, TNamesDepend & a_values ) const; /** Query the number of keys in a specific section. Note that if multiple keys are enabled, then this value may be different to the number of keys returned by GetAllKeys. @param a_pSection Section to request data for @return -1 Section does not exist in the file @return >=0 Number of keys in the section */ int GetSectionSize( const SI_CHAR * a_pSection ) const; /** Retrieve all key and value pairs for a section. The data is returned as a pointer to an STL map and can be iterated or searched as desired. Note that multiple entries for the same key may exist when multiple keys have been enabled. NOTE! This structure contains only pointers to strings. The actual string data is stored in memory owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed or Reset() while these strings are in use! @param a_pSection Name of the section to return @return boolean Was a section matching the supplied name found. */ const TKeyVal * GetSection( const SI_CHAR * a_pSection ) const; /** Retrieve the value for a specific key. If multiple keys are enabled (see SetMultiKey) then only the first value associated with that key will be returned, see GetAllValues for getting all values with multikey. NOTE! The returned value is a pointer to string data stored in memory owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed or Reset while you are using this pointer! @param a_pSection Section to search @param a_pKey Key to search for @param a_pDefault Value to return if the key is not found @param a_pHasMultiple Optionally receive notification of if there are multiple entries for this key. @return a_pDefault Key was not found in the section @return other Value of the key */ const SI_CHAR * GetValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, const SI_CHAR * a_pDefault = NULL, bool * a_pHasMultiple = NULL ) const; /** Retrieve a numeric value for a specific key. If multiple keys are enabled (see SetMultiKey) then only the first value associated with that key will be returned, see GetAllValues for getting all values with multikey. @param a_pSection Section to search @param a_pKey Key to search for @param a_nDefault Value to return if the key is not found @param a_pHasMultiple Optionally receive notification of if there are multiple entries for this key. @return a_nDefault Key was not found in the section @return other Value of the key */ long GetLongValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, long a_nDefault = 0, bool * a_pHasMultiple = NULL ) const; /** Retrieve a numeric value for a specific key. If multiple keys are enabled (see SetMultiKey) then only the first value associated with that key will be returned, see GetAllValues for getting all values with multikey. @param a_pSection Section to search @param a_pKey Key to search for @param a_nDefault Value to return if the key is not found @param a_pHasMultiple Optionally receive notification of if there are multiple entries for this key. @return a_nDefault Key was not found in the section @return other Value of the key */ double GetDoubleValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, double a_nDefault = 0, bool * a_pHasMultiple = NULL ) const; /** Retrieve a boolean value for a specific key. If multiple keys are enabled (see SetMultiKey) then only the first value associated with that key will be returned, see GetAllValues for getting all values with multikey. Strings starting with "t", "y", "on" or "1" are returned as logically true. Strings starting with "f", "n", "of" or "0" are returned as logically false. For all other values the default is returned. Character comparisons are case-insensitive. @param a_pSection Section to search @param a_pKey Key to search for @param a_bDefault Value to return if the key is not found @param a_pHasMultiple Optionally receive notification of if there are multiple entries for this key. @return a_nDefault Key was not found in the section @return other Value of the key */ bool GetBoolValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, bool a_bDefault = false, bool * a_pHasMultiple = NULL ) const; /** Add or update a section or value. This will always insert when multiple keys are enabled. @param a_pSection Section to add or update @param a_pKey Key to add or update. Set to NULL to create an empty section. @param a_pValue Value to set. Set to NULL to create an empty section. @param a_pComment Comment to be associated with the section or the key. If a_pKey is NULL then it will be associated with the section, otherwise the key. Note that a comment may be set ONLY when the section or key is first created (i.e. when this function returns the value SI_INSERTED). If you wish to create a section with a comment then you need to create the section separately to the key. The comment string must be in full comment form already (have a comment character starting every line). @param a_bForceReplace Should all existing values in a multi-key INI file be replaced with this entry. This option has no effect if not using multi-key files. The difference between Delete/SetValue and SetValue with a_bForceReplace = true, is that the load order and comment will be preserved this way. @return SI_Error See error definitions @return SI_UPDATED Value was updated @return SI_INSERTED Value was inserted */ SI_Error SetValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, const SI_CHAR * a_pValue, const SI_CHAR * a_pComment = NULL, bool a_bForceReplace = false ) { return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true); } /** Add or update a numeric value. This will always insert when multiple keys are enabled. @param a_pSection Section to add or update @param a_pKey Key to add or update. @param a_nValue Value to set. @param a_pComment Comment to be associated with the key. See the notes on SetValue() for comments. @param a_bUseHex By default the value will be written to the file in decimal format. Set this to true to write it as hexadecimal. @param a_bForceReplace Should all existing values in a multi-key INI file be replaced with this entry. This option has no effect if not using multi-key files. The difference between Delete/SetLongValue and SetLongValue with a_bForceReplace = true, is that the load order and comment will be preserved this way. @return SI_Error See error definitions @return SI_UPDATED Value was updated @return SI_INSERTED Value was inserted */ SI_Error SetLongValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, long a_nValue, const SI_CHAR * a_pComment = NULL, bool a_bUseHex = false, bool a_bForceReplace = false ); /** Add or update a double value. This will always insert when multiple keys are enabled. @param a_pSection Section to add or update @param a_pKey Key to add or update. @param a_nValue Value to set. @param a_pComment Comment to be associated with the key. See the notes on SetValue() for comments. @param a_bForceReplace Should all existing values in a multi-key INI file be replaced with this entry. This option has no effect if not using multi-key files. The difference between Delete/SetDoubleValue and SetDoubleValue with a_bForceReplace = true, is that the load order and comment will be preserved this way. @return SI_Error See error definitions @return SI_UPDATED Value was updated @return SI_INSERTED Value was inserted */ SI_Error SetDoubleValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, double a_nValue, const SI_CHAR * a_pComment = NULL, bool a_bForceReplace = false ); /** Add or update a boolean value. This will always insert when multiple keys are enabled. @param a_pSection Section to add or update @param a_pKey Key to add or update. @param a_bValue Value to set. @param a_pComment Comment to be associated with the key. See the notes on SetValue() for comments. @param a_bForceReplace Should all existing values in a multi-key INI file be replaced with this entry. This option has no effect if not using multi-key files. The difference between Delete/SetBoolValue and SetBoolValue with a_bForceReplace = true, is that the load order and comment will be preserved this way. @return SI_Error See error definitions @return SI_UPDATED Value was updated @return SI_INSERTED Value was inserted */ SI_Error SetBoolValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, bool a_bValue, const SI_CHAR * a_pComment = NULL, bool a_bForceReplace = false ); /** Delete an entire section, or a key from a section. Note that the data returned by GetSection is invalid and must not be used after anything has been deleted from that section using this method. Note when multiple keys is enabled, this will delete all keys with that name; there is no way to selectively delete individual key/values in this situation. @param a_pSection Section to delete key from, or if a_pKey is NULL, the section to remove. @param a_pKey Key to remove from the section. Set to NULL to remove the entire section. @param a_bRemoveEmpty If the section is empty after this key has been deleted, should the empty section be removed? @return true Key or section was deleted. @return false Key or section was not found. */ bool Delete( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, bool a_bRemoveEmpty = false ); /*-----------------------------------------------------------------------*/ /** @} @{ @name Converter */ /** Return a conversion object to convert text to the same encoding as is used by the Save(), SaveFile() and SaveString() functions. Use this to prepare the strings that you wish to append or prepend to the output INI data. */ Converter GetConverter() const { return Converter(m_bStoreIsUtf8); } /*-----------------------------------------------------------------------*/ /** @} */ private: // copying is not permitted CSimpleIniTempl(const CSimpleIniTempl &); // disabled CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled /** Parse the data looking for a file comment and store it if found. */ SI_Error FindFileComment( SI_CHAR *& a_pData, bool a_bCopyStrings ); /** Parse the data looking for the next valid entry. The memory pointed to by a_pData is modified by inserting NULL characters. The pointer is updated to the current location in the block of text. */ bool FindEntry( SI_CHAR *& a_pData, const SI_CHAR *& a_pSection, const SI_CHAR *& a_pKey, const SI_CHAR *& a_pVal, const SI_CHAR *& a_pComment ) const; /** Add the section/key/value to our data. @param a_pSection Section name. Sections will be created if they don't already exist. @param a_pKey Key name. May be NULL to create an empty section. Existing entries will be updated. New entries will be created. @param a_pValue Value for the key. @param a_pComment Comment to be associated with the section or the key. If a_pKey is NULL then it will be associated with the section, otherwise the key. This must be a string in full comment form already (have a comment character starting every line). @param a_bForceReplace Should all existing values in a multi-key INI file be replaced with this entry. This option has no effect if not using multi-key files. The difference between Delete/AddEntry and AddEntry with a_bForceReplace = true, is that the load order and comment will be preserved this way. @param a_bCopyStrings Should copies of the strings be made or not. If false then the pointers will be used as is. */ SI_Error AddEntry( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, const SI_CHAR * a_pValue, const SI_CHAR * a_pComment, bool a_bForceReplace, bool a_bCopyStrings ); /** Is the supplied character a whitespace character? */ inline bool IsSpace(SI_CHAR ch) const { return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); } /** Does the supplied character start a comment line? */ inline bool IsComment(SI_CHAR ch) const { return (ch == ';' || ch == '#'); } /** Skip over a newline character (or characters) for either DOS or UNIX */ inline void SkipNewLine(SI_CHAR *& a_pData) const { a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1; } /** Make a copy of the supplied string, replacing the original pointer */ SI_Error CopyString(const SI_CHAR *& a_pString); /** Delete a string from the copied strings buffer if necessary */ void DeleteString(const SI_CHAR * a_pString); /** Internal use of our string comparison function */ bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const { const static SI_STRLESS isLess = SI_STRLESS(); return isLess(a_pLeft, a_pRight); } bool IsMultiLineTag(const SI_CHAR * a_pData) const; bool IsMultiLineData(const SI_CHAR * a_pData) const; bool LoadMultiLineText( SI_CHAR *& a_pData, const SI_CHAR *& a_pVal, const SI_CHAR * a_pTagName, bool a_bAllowBlankLinesInComment = false ) const; bool IsNewLineChar(SI_CHAR a_c) const; bool OutputMultiLineText( OutputWriter & a_oOutput, Converter & a_oConverter, const SI_CHAR * a_pText ) const; private: /** Copy of the INI file data in our character format. This will be modified when parsed to have NULL characters added after all interesting string entries. All of the string pointers to sections, keys and values point into this block of memory. */ SI_CHAR * m_pData; /** Length of the data that we have stored. Used when deleting strings to determine if the string is stored here or in the allocated string buffer. */ size_t m_uDataLen; /** File comment for this data, if one exists. */ const SI_CHAR * m_pFileComment; /** Parsed INI data. Section -> (Key -> Value). */ TSection m_data; /** This vector stores allocated memory for copies of strings that have been supplied after the file load. It will be empty unless SetValue() has been called. */ TNamesDepend m_strings; /** Is the format of our datafile UTF-8 or MBCS? */ bool m_bStoreIsUtf8; /** Are multiple values permitted for the same key? */ bool m_bAllowMultiKey; /** Are data values permitted to span multiple lines? */ bool m_bAllowMultiLine; /** Should spaces be written out surrounding the equals sign? */ bool m_bSpaces; /** Next order value, used to ensure sections and keys are output in the same order that they are loaded/added. */ int m_nOrder; }; // --------------------------------------------------------------------------- // IMPLEMENTATION // --------------------------------------------------------------------------- template CSimpleIniTempl::CSimpleIniTempl( bool a_bIsUtf8, bool a_bAllowMultiKey, bool a_bAllowMultiLine ) : m_pData(0) , m_uDataLen(0) , m_pFileComment(NULL) , m_bStoreIsUtf8(a_bIsUtf8) , m_bAllowMultiKey(a_bAllowMultiKey) , m_bAllowMultiLine(a_bAllowMultiLine) , m_bSpaces(true) , m_nOrder(0) { } template CSimpleIniTempl::~CSimpleIniTempl() { Reset(); } template void CSimpleIniTempl::Reset() { // remove all data delete[] m_pData; m_pData = NULL; m_uDataLen = 0; m_pFileComment = NULL; if (!m_data.empty()) { m_data.erase(m_data.begin(), m_data.end()); } // remove all strings if (!m_strings.empty()) { typename TNamesDepend::iterator i = m_strings.begin(); for (; i != m_strings.end(); ++i) { delete[] const_cast(i->pItem); } m_strings.erase(m_strings.begin(), m_strings.end()); } } template SI_Error CSimpleIniTempl::LoadFile( const char * a_pszFile ) { FILE * fp = NULL; #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE fopen_s(&fp, a_pszFile, "rb"); #else // !__STDC_WANT_SECURE_LIB__ fp = fopen(a_pszFile, "rb"); #endif // __STDC_WANT_SECURE_LIB__ if (!fp) { return SI_FILE; } SI_Error rc = LoadFile(fp); fclose(fp); return rc; } #ifdef SI_HAS_WIDE_FILE template SI_Error CSimpleIniTempl::LoadFile( const SI_WCHAR_T * a_pwszFile ) { #ifdef _WIN32 FILE * fp = NULL; #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE _wfopen_s(&fp, a_pwszFile, L"rb"); #else // !__STDC_WANT_SECURE_LIB__ fp = _wfopen(a_pwszFile, L"rb"); #endif // __STDC_WANT_SECURE_LIB__ if (!fp) return SI_FILE; SI_Error rc = LoadFile(fp); fclose(fp); return rc; #else // !_WIN32 (therefore SI_CONVERT_ICU) char szFile[256]; u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); return LoadFile(szFile); #endif // _WIN32 } #endif // SI_HAS_WIDE_FILE template SI_Error CSimpleIniTempl::LoadFile( FILE * a_fpFile ) { // load the raw file data int retval = fseek(a_fpFile, 0, SEEK_END); if (retval != 0) { return SI_FILE; } long lSize = ftell(a_fpFile); if (lSize < 0) { return SI_FILE; } if (lSize == 0) { return SI_OK; } // allocate and ensure NULL terminated char * pData = new char[lSize+1]; if (!pData) { return SI_NOMEM; } pData[lSize] = 0; // load data into buffer fseek(a_fpFile, 0, SEEK_SET); size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile); if (uRead != (size_t) lSize) { delete[] pData; return SI_FILE; } // convert the raw data to unicode SI_Error rc = LoadData(pData, uRead); delete[] pData; return rc; } template SI_Error CSimpleIniTempl::LoadData( const char * a_pData, size_t a_uDataLen ) { SI_CONVERTER converter(m_bStoreIsUtf8); if (a_uDataLen == 0) { return SI_OK; } // consume the UTF-8 BOM if it exists if (m_bStoreIsUtf8 && a_uDataLen >= 3) { if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) { a_pData += 3; a_uDataLen -= 3; } } // determine the length of the converted data size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen); if (uLen == (size_t)(-1)) { return SI_FAIL; } // allocate memory for the data, ensure that there is a NULL // terminator wherever the converted data ends SI_CHAR * pData = new SI_CHAR[uLen+1]; if (!pData) { return SI_NOMEM; } memset(pData, 0, sizeof(SI_CHAR)*(uLen+1)); // convert the data if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) { delete[] pData; return SI_FAIL; } // parse it const static SI_CHAR empty = 0; SI_CHAR * pWork = pData; const SI_CHAR * pSection = ∅ const SI_CHAR * pItem = NULL; const SI_CHAR * pVal = NULL; const SI_CHAR * pComment = NULL; // We copy the strings if we are loading data into this class when we // already have stored some. bool bCopyStrings = (m_pData != NULL); // find a file comment if it exists, this is a comment that starts at the // beginning of the file and continues until the first blank line. SI_Error rc = FindFileComment(pWork, bCopyStrings); if (rc < 0) return rc; // add every entry in the file to the data table while (FindEntry(pWork, pSection, pItem, pVal, pComment)) { rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings); if (rc < 0) return rc; } // store these strings if we didn't copy them if (bCopyStrings) { delete[] pData; } else { m_pData = pData; m_uDataLen = uLen+1; } return SI_OK; } #ifdef SI_SUPPORT_IOSTREAMS template SI_Error CSimpleIniTempl::LoadData( std::istream & a_istream ) { std::string strData; char szBuf[512]; do { a_istream.get(szBuf, sizeof(szBuf), '\0'); strData.append(szBuf); } while (a_istream.good()); return LoadData(strData); } #endif // SI_SUPPORT_IOSTREAMS template SI_Error CSimpleIniTempl::FindFileComment( SI_CHAR *& a_pData, bool a_bCopyStrings ) { // there can only be a single file comment if (m_pFileComment) { return SI_OK; } // Load the file comment as multi-line text, this will modify all of // the newline characters to be single \n chars if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) { return SI_OK; } // copy the string if necessary if (a_bCopyStrings) { SI_Error rc = CopyString(m_pFileComment); if (rc < 0) return rc; } return SI_OK; } template bool CSimpleIniTempl::FindEntry( SI_CHAR *& a_pData, const SI_CHAR *& a_pSection, const SI_CHAR *& a_pKey, const SI_CHAR *& a_pVal, const SI_CHAR *& a_pComment ) const { a_pComment = NULL; SI_CHAR * pTrail = NULL; while (*a_pData) { // skip spaces and empty lines while (*a_pData && IsSpace(*a_pData)) { ++a_pData; } if (!*a_pData) { break; } // skip processing of comment lines but keep a pointer to // the start of the comment. if (IsComment(*a_pData)) { LoadMultiLineText(a_pData, a_pComment, NULL, true); continue; } // process section names if (*a_pData == '[') { // skip leading spaces ++a_pData; while (*a_pData && IsSpace(*a_pData)) { ++a_pData; } // find the end of the section name (it may contain spaces) // and convert it to lowercase as necessary a_pSection = a_pData; while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) { ++a_pData; } // if it's an invalid line, just skip it if (*a_pData != ']') { continue; } // remove trailing spaces from the section pTrail = a_pData - 1; while (pTrail >= a_pSection && IsSpace(*pTrail)) { --pTrail; } ++pTrail; *pTrail = 0; // skip to the end of the line ++a_pData; // safe as checked that it == ']' above while (*a_pData && !IsNewLineChar(*a_pData)) { ++a_pData; } a_pKey = NULL; a_pVal = NULL; return true; } // find the end of the key name (it may contain spaces) // and convert it to lowercase as necessary a_pKey = a_pData; while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) { ++a_pData; } // if it's an invalid line, just skip it if (*a_pData != '=') { continue; } // empty keys are invalid if (a_pKey == a_pData) { while (*a_pData && !IsNewLineChar(*a_pData)) { ++a_pData; } continue; } // remove trailing spaces from the key pTrail = a_pData - 1; while (pTrail >= a_pKey && IsSpace(*pTrail)) { --pTrail; } ++pTrail; *pTrail = 0; // skip leading whitespace on the value ++a_pData; // safe as checked that it == '=' above while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) { ++a_pData; } // find the end of the value which is the end of this line a_pVal = a_pData; while (*a_pData && !IsNewLineChar(*a_pData)) { ++a_pData; } // remove trailing spaces from the value pTrail = a_pData - 1; if (*a_pData) // prepare for the next round { SkipNewLine(a_pData); } while (pTrail >= a_pVal && IsSpace(*pTrail)) { --pTrail; } ++pTrail; *pTrail = 0; // check for multi-line entries if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) { // skip the "<<<" to get the tag that will end the multiline const SI_CHAR * pTagName = a_pVal + 3; return LoadMultiLineText(a_pData, a_pVal, pTagName); } // return the standard entry return true; } return false; } template bool CSimpleIniTempl::IsMultiLineTag( const SI_CHAR * a_pVal ) const { // check for the "<<<" prefix for a multi-line entry if (*a_pVal++ != '<') return false; if (*a_pVal++ != '<') return false; if (*a_pVal++ != '<') return false; return true; } template bool CSimpleIniTempl::IsMultiLineData( const SI_CHAR * a_pData ) const { // data is multi-line if it has any of the following features: // * whitespace prefix // * embedded newlines // * whitespace suffix // empty string if (!*a_pData) { return false; } // check for prefix if (IsSpace(*a_pData)) { return true; } // embedded newlines while (*a_pData) { if (IsNewLineChar(*a_pData)) { return true; } ++a_pData; } // check for suffix if (IsSpace(*--a_pData)) { return true; } return false; } template bool CSimpleIniTempl::IsNewLineChar( SI_CHAR a_c ) const { return (a_c == '\n' || a_c == '\r'); } template bool CSimpleIniTempl::LoadMultiLineText( SI_CHAR *& a_pData, const SI_CHAR *& a_pVal, const SI_CHAR * a_pTagName, bool a_bAllowBlankLinesInComment ) const { // we modify this data to strip all newlines down to a single '\n' // character. This means that on Windows we need to strip out some // characters which will make the data shorter. // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become // LINE1-LINE1\nLINE2-LINE2\0 // The pDataLine entry is the pointer to the location in memory that // the current line needs to start to run following the existing one. // This may be the same as pCurrLine in which case no move is needed. SI_CHAR * pDataLine = a_pData; SI_CHAR * pCurrLine; // value starts at the current line a_pVal = a_pData; // find the end tag. This tag must start in column 1 and be // followed by a newline. No whitespace removal is done while // searching for this tag. SI_CHAR cEndOfLineChar = *a_pData; for(;;) { // if we are loading comments then we need a comment character as // the first character on every line if (!a_pTagName && !IsComment(*a_pData)) { // if we aren't allowing blank lines then we're done if (!a_bAllowBlankLinesInComment) { break; } // if we are allowing blank lines then we only include them // in this comment if another comment follows, so read ahead // to find out. SI_CHAR * pCurr = a_pData; int nNewLines = 0; while (IsSpace(*pCurr)) { if (IsNewLineChar(*pCurr)) { ++nNewLines; SkipNewLine(pCurr); } else { ++pCurr; } } // we have a comment, add the blank lines to the output // and continue processing from here if (IsComment(*pCurr)) { for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n'; a_pData = pCurr; continue; } // the comment ends here break; } // find the end of this line pCurrLine = a_pData; while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData; // move this line down to the location that it should be if necessary if (pDataLine < pCurrLine) { size_t nLen = (size_t) (a_pData - pCurrLine); memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR)); pDataLine[nLen] = '\0'; } // end the line with a NULL cEndOfLineChar = *a_pData; *a_pData = 0; // if are looking for a tag then do the check now. This is done before // checking for end of the data, so that if we have the tag at the end // of the data then the tag is removed correctly. if (a_pTagName && (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine))) { break; } // if we are at the end of the data then we just automatically end // this entry and return the current data. if (!cEndOfLineChar) { return true; } // otherwise we need to process this newline to ensure that it consists // of just a single \n character. pDataLine += (a_pData - pCurrLine); *a_pData = cEndOfLineChar; SkipNewLine(a_pData); *pDataLine++ = '\n'; } // if we didn't find a comment at all then return false if (a_pVal == a_pData) { a_pVal = NULL; return false; } // the data (which ends at the end of the last line) needs to be // null-terminated BEFORE before the newline character(s). If the // user wants a new line in the multi-line data then they need to // add an empty line before the tag. *--pDataLine = '\0'; // if looking for a tag and if we aren't at the end of the data, // then move a_pData to the start of the next line. if (a_pTagName && cEndOfLineChar) { SI_ASSERT(IsNewLineChar(cEndOfLineChar)); *a_pData = cEndOfLineChar; SkipNewLine(a_pData); } return true; } template SI_Error CSimpleIniTempl::CopyString( const SI_CHAR *& a_pString ) { size_t uLen = 0; if (sizeof(SI_CHAR) == sizeof(char)) { uLen = strlen((const char *)a_pString); } else if (sizeof(SI_CHAR) == sizeof(wchar_t)) { uLen = wcslen((const wchar_t *)a_pString); } else { for ( ; a_pString[uLen]; ++uLen) /*loop*/ ; } ++uLen; // NULL character SI_CHAR * pCopy = new SI_CHAR[uLen]; if (!pCopy) { return SI_NOMEM; } memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen); m_strings.push_back(pCopy); a_pString = pCopy; return SI_OK; } template SI_Error CSimpleIniTempl::AddEntry( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, const SI_CHAR * a_pValue, const SI_CHAR * a_pComment, bool a_bForceReplace, bool a_bCopyStrings ) { SI_Error rc; bool bInserted = false; SI_ASSERT(!a_pComment || IsComment(*a_pComment)); // if we are copying strings then make a copy of the comment now // because we will need it when we add the entry. if (a_bCopyStrings && a_pComment) { rc = CopyString(a_pComment); if (rc < 0) return rc; } // create the section entry if necessary typename TSection::iterator iSection = m_data.find(a_pSection); if (iSection == m_data.end()) { // if the section doesn't exist then we need a copy as the // string needs to last beyond the end of this function if (a_bCopyStrings) { rc = CopyString(a_pSection); if (rc < 0) return rc; } // only set the comment if this is a section only entry Entry oSection(a_pSection, ++m_nOrder); if (a_pComment && (!a_pKey || !a_pValue)) { oSection.pComment = a_pComment; } typename TSection::value_type oEntry(oSection, TKeyVal()); typedef typename TSection::iterator SectionIterator; std::pair i = m_data.insert(oEntry); iSection = i.first; bInserted = true; } if (!a_pKey || !a_pValue) { // section only entries are specified with pItem and pVal as NULL return bInserted ? SI_INSERTED : SI_UPDATED; } // check for existence of the key TKeyVal & keyval = iSection->second; typename TKeyVal::iterator iKey = keyval.find(a_pKey); // remove all existing entries but save the load order and // comment of the first entry int nLoadOrder = ++m_nOrder; if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) { const SI_CHAR * pComment = NULL; while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) { if (iKey->first.nOrder < nLoadOrder) { nLoadOrder = iKey->first.nOrder; pComment = iKey->first.pComment; } ++iKey; } if (pComment) { DeleteString(a_pComment); a_pComment = pComment; CopyString(a_pComment); } Delete(a_pSection, a_pKey); iKey = keyval.end(); } // make string copies if necessary bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace; if (a_bCopyStrings) { if (bForceCreateNewKey || iKey == keyval.end()) { // if the key doesn't exist then we need a copy as the // string needs to last beyond the end of this function // because we will be inserting the key next rc = CopyString(a_pKey); if (rc < 0) return rc; } // we always need a copy of the value rc = CopyString(a_pValue); if (rc < 0) return rc; } // create the key entry if (iKey == keyval.end() || bForceCreateNewKey) { Entry oKey(a_pKey, nLoadOrder); if (a_pComment) { oKey.pComment = a_pComment; } typename TKeyVal::value_type oEntry(oKey, static_cast(NULL)); iKey = keyval.insert(oEntry); bInserted = true; } iKey->second = a_pValue; return bInserted ? SI_INSERTED : SI_UPDATED; } template const SI_CHAR * CSimpleIniTempl::GetValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, const SI_CHAR * a_pDefault, bool * a_pHasMultiple ) const { if (a_pHasMultiple) { *a_pHasMultiple = false; } if (!a_pSection || !a_pKey) { return a_pDefault; } typename TSection::const_iterator iSection = m_data.find(a_pSection); if (iSection == m_data.end()) { return a_pDefault; } typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey); if (iKeyVal == iSection->second.end()) { return a_pDefault; } // check for multiple entries with the same key if (m_bAllowMultiKey && a_pHasMultiple) { typename TKeyVal::const_iterator iTemp = iKeyVal; if (++iTemp != iSection->second.end()) { if (!IsLess(a_pKey, iTemp->first.pItem)) { *a_pHasMultiple = true; } } } return iKeyVal->second; } template long CSimpleIniTempl::GetLongValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, long a_nDefault, bool * a_pHasMultiple ) const { // return the default if we don't have a value const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); if (!pszValue || !*pszValue) return a_nDefault; // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII char szValue[64] = { 0 }; SI_CONVERTER c(m_bStoreIsUtf8); if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) { return a_nDefault; } // handle the value as hex if prefaced with "0x" long nValue = a_nDefault; char * pszSuffix = szValue; if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) { if (!szValue[2]) return a_nDefault; nValue = strtol(&szValue[2], &pszSuffix, 16); } else { nValue = strtol(szValue, &pszSuffix, 10); } // any invalid strings will return the default value if (*pszSuffix) { return a_nDefault; } return nValue; } template SI_Error CSimpleIniTempl::SetLongValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, long a_nValue, const SI_CHAR * a_pComment, bool a_bUseHex, bool a_bForceReplace ) { // use SetValue to create sections if (!a_pSection || !a_pKey) return SI_FAIL; // convert to an ASCII string char szInput[64]; #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue); #else // !__STDC_WANT_SECURE_LIB__ sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue); #endif // __STDC_WANT_SECURE_LIB__ // convert to output text SI_CHAR szOutput[64]; SI_CONVERTER c(m_bStoreIsUtf8); c.ConvertFromStore(szInput, strlen(szInput) + 1, szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); // actually add it return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); } template double CSimpleIniTempl::GetDoubleValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, double a_nDefault, bool * a_pHasMultiple ) const { // return the default if we don't have a value const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); if (!pszValue || !*pszValue) return a_nDefault; // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII char szValue[64] = { 0 }; SI_CONVERTER c(m_bStoreIsUtf8); if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) { return a_nDefault; } char * pszSuffix = NULL; double nValue = strtod(szValue, &pszSuffix); // any invalid strings will return the default value if (!pszSuffix || *pszSuffix) { return a_nDefault; } return nValue; } template SI_Error CSimpleIniTempl::SetDoubleValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, double a_nValue, const SI_CHAR * a_pComment, bool a_bForceReplace ) { // use SetValue to create sections if (!a_pSection || !a_pKey) return SI_FAIL; // convert to an ASCII string char szInput[64]; #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE sprintf_s(szInput, "%f", a_nValue); #else // !__STDC_WANT_SECURE_LIB__ sprintf(szInput, "%f", a_nValue); #endif // __STDC_WANT_SECURE_LIB__ // convert to output text SI_CHAR szOutput[64]; SI_CONVERTER c(m_bStoreIsUtf8); c.ConvertFromStore(szInput, strlen(szInput) + 1, szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); // actually add it return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); } template bool CSimpleIniTempl::GetBoolValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, bool a_bDefault, bool * a_pHasMultiple ) const { // return the default if we don't have a value const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); if (!pszValue || !*pszValue) return a_bDefault; // we only look at the minimum number of characters switch (pszValue[0]) { case 't': case 'T': // true case 'y': case 'Y': // yes case '1': // 1 (one) return true; case 'f': case 'F': // false case 'n': case 'N': // no case '0': // 0 (zero) return false; case 'o': case 'O': if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off break; } // no recognized value, return the default return a_bDefault; } template SI_Error CSimpleIniTempl::SetBoolValue( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, bool a_bValue, const SI_CHAR * a_pComment, bool a_bForceReplace ) { // use SetValue to create sections if (!a_pSection || !a_pKey) return SI_FAIL; // convert to an ASCII string const char * pszInput = a_bValue ? "true" : "false"; // convert to output text SI_CHAR szOutput[64]; SI_CONVERTER c(m_bStoreIsUtf8); c.ConvertFromStore(pszInput, strlen(pszInput) + 1, szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); // actually add it return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); } template bool CSimpleIniTempl::GetAllValues( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, TNamesDepend & a_values ) const { a_values.clear(); if (!a_pSection || !a_pKey) { return false; } typename TSection::const_iterator iSection = m_data.find(a_pSection); if (iSection == m_data.end()) { return false; } typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey); if (iKeyVal == iSection->second.end()) { return false; } // insert all values for this key a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder)); if (m_bAllowMultiKey) { ++iKeyVal; while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) { a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder)); ++iKeyVal; } } return true; } template int CSimpleIniTempl::GetSectionSize( const SI_CHAR * a_pSection ) const { if (!a_pSection) { return -1; } typename TSection::const_iterator iSection = m_data.find(a_pSection); if (iSection == m_data.end()) { return -1; } const TKeyVal & section = iSection->second; // if multi-key isn't permitted then the section size is // the number of keys that we have. if (!m_bAllowMultiKey || section.empty()) { return (int) section.size(); } // otherwise we need to count them int nCount = 0; const SI_CHAR * pLastKey = NULL; typename TKeyVal::const_iterator iKeyVal = section.begin(); for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) { if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) { ++nCount; pLastKey = iKeyVal->first.pItem; } } return nCount; } template const typename CSimpleIniTempl::TKeyVal * CSimpleIniTempl::GetSection( const SI_CHAR * a_pSection ) const { if (a_pSection) { typename TSection::const_iterator i = m_data.find(a_pSection); if (i != m_data.end()) { return &(i->second); } } return 0; } template void CSimpleIniTempl::GetAllSections( TNamesDepend & a_names ) const { a_names.clear(); typename TSection::const_iterator i = m_data.begin(); for (int n = 0; i != m_data.end(); ++i, ++n ) { a_names.push_back(i->first); } } template bool CSimpleIniTempl::GetAllKeys( const SI_CHAR * a_pSection, TNamesDepend & a_names ) const { a_names.clear(); if (!a_pSection) { return false; } typename TSection::const_iterator iSection = m_data.find(a_pSection); if (iSection == m_data.end()) { return false; } const TKeyVal & section = iSection->second; const SI_CHAR * pLastKey = NULL; typename TKeyVal::const_iterator iKeyVal = section.begin(); for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) { if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) { a_names.push_back(iKeyVal->first); pLastKey = iKeyVal->first.pItem; } } return true; } template SI_Error CSimpleIniTempl::SaveFile( const char * a_pszFile, bool a_bAddSignature ) const { FILE * fp = NULL; #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE fopen_s(&fp, a_pszFile, "wb"); #else // !__STDC_WANT_SECURE_LIB__ fp = fopen(a_pszFile, "wb"); #endif // __STDC_WANT_SECURE_LIB__ if (!fp) return SI_FILE; SI_Error rc = SaveFile(fp, a_bAddSignature); fclose(fp); return rc; } #ifdef SI_HAS_WIDE_FILE template SI_Error CSimpleIniTempl::SaveFile( const SI_WCHAR_T * a_pwszFile, bool a_bAddSignature ) const { #ifdef _WIN32 FILE * fp = NULL; #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE _wfopen_s(&fp, a_pwszFile, L"wb"); #else // !__STDC_WANT_SECURE_LIB__ fp = _wfopen(a_pwszFile, L"wb"); #endif // __STDC_WANT_SECURE_LIB__ if (!fp) return SI_FILE; SI_Error rc = SaveFile(fp, a_bAddSignature); fclose(fp); return rc; #else // !_WIN32 (therefore SI_CONVERT_ICU) char szFile[256]; u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); return SaveFile(szFile, a_bAddSignature); #endif // _WIN32 } #endif // SI_HAS_WIDE_FILE template SI_Error CSimpleIniTempl::SaveFile( FILE * a_pFile, bool a_bAddSignature ) const { FileWriter writer(a_pFile); return Save(writer, a_bAddSignature); } template SI_Error CSimpleIniTempl::Save( OutputWriter & a_oOutput, bool a_bAddSignature ) const { Converter convert(m_bStoreIsUtf8); // add the UTF-8 signature if it is desired if (m_bStoreIsUtf8 && a_bAddSignature) { a_oOutput.Write(SI_UTF8_SIGNATURE); } // get all of the sections sorted in load order TNamesDepend oSections; GetAllSections(oSections); #if defined(_MSC_VER) && _MSC_VER <= 1200 oSections.sort(); #elif defined(__BORLANDC__) oSections.sort(Entry::LoadOrder()); #else oSections.sort(typename Entry::LoadOrder()); #endif // write the file comment if we have one bool bNeedNewLine = false; if (m_pFileComment) { if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) { return SI_FAIL; } bNeedNewLine = true; } // iterate through our sections and output the data typename TNamesDepend::const_iterator iSection = oSections.begin(); for ( ; iSection != oSections.end(); ++iSection ) { // write out the comment if there is one if (iSection->pComment) { if (bNeedNewLine) { a_oOutput.Write(SI_NEWLINE_A); a_oOutput.Write(SI_NEWLINE_A); } if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) { return SI_FAIL; } bNeedNewLine = false; } if (bNeedNewLine) { a_oOutput.Write(SI_NEWLINE_A); a_oOutput.Write(SI_NEWLINE_A); bNeedNewLine = false; } // write the section (unless there is no section name) if (*iSection->pItem) { if (!convert.ConvertToStore(iSection->pItem)) { return SI_FAIL; } a_oOutput.Write("["); a_oOutput.Write(convert.Data()); a_oOutput.Write("]"); a_oOutput.Write(SI_NEWLINE_A); } // get all of the keys sorted in load order TNamesDepend oKeys; GetAllKeys(iSection->pItem, oKeys); #if defined(_MSC_VER) && _MSC_VER <= 1200 oKeys.sort(); #elif defined(__BORLANDC__) oKeys.sort(Entry::LoadOrder()); #else oKeys.sort(typename Entry::LoadOrder()); #endif // write all keys and values typename TNamesDepend::const_iterator iKey = oKeys.begin(); for ( ; iKey != oKeys.end(); ++iKey) { // get all values for this key TNamesDepend oValues; GetAllValues(iSection->pItem, iKey->pItem, oValues); typename TNamesDepend::const_iterator iValue = oValues.begin(); for ( ; iValue != oValues.end(); ++iValue) { // write out the comment if there is one if (iValue->pComment) { a_oOutput.Write(SI_NEWLINE_A); if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) { return SI_FAIL; } } // write the key if (!convert.ConvertToStore(iKey->pItem)) { return SI_FAIL; } a_oOutput.Write(convert.Data()); // write the value if (!convert.ConvertToStore(iValue->pItem)) { return SI_FAIL; } a_oOutput.Write(m_bSpaces ? " = " : "="); if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) { // multi-line data needs to be processed specially to ensure // that we use the correct newline format for the current system a_oOutput.Write("<<pItem)) { return SI_FAIL; } a_oOutput.Write("END_OF_TEXT"); } else { a_oOutput.Write(convert.Data()); } a_oOutput.Write(SI_NEWLINE_A); } } bNeedNewLine = true; } return SI_OK; } template bool CSimpleIniTempl::OutputMultiLineText( OutputWriter & a_oOutput, Converter & a_oConverter, const SI_CHAR * a_pText ) const { const SI_CHAR * pEndOfLine; SI_CHAR cEndOfLineChar = *a_pText; while (cEndOfLineChar) { // find the end of this line pEndOfLine = a_pText; for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ; cEndOfLineChar = *pEndOfLine; // temporarily null terminate, convert and output the line *const_cast(pEndOfLine) = 0; if (!a_oConverter.ConvertToStore(a_pText)) { return false; } *const_cast(pEndOfLine) = cEndOfLineChar; a_pText += (pEndOfLine - a_pText) + 1; a_oOutput.Write(a_oConverter.Data()); a_oOutput.Write(SI_NEWLINE_A); } return true; } template bool CSimpleIniTempl::Delete( const SI_CHAR * a_pSection, const SI_CHAR * a_pKey, bool a_bRemoveEmpty ) { if (!a_pSection) { return false; } typename TSection::iterator iSection = m_data.find(a_pSection); if (iSection == m_data.end()) { return false; } // remove a single key if we have a keyname if (a_pKey) { typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey); if (iKeyVal == iSection->second.end()) { return false; } // remove any copied strings and then the key typename TKeyVal::iterator iDelete; do { iDelete = iKeyVal++; DeleteString(iDelete->first.pItem); DeleteString(iDelete->second); iSection->second.erase(iDelete); } while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)); // done now if the section is not empty or we are not pruning away // the empty sections. Otherwise let it fall through into the section // deletion code if (!a_bRemoveEmpty || !iSection->second.empty()) { return true; } } else { // delete all copied strings from this section. The actual // entries will be removed when the section is removed. typename TKeyVal::iterator iKeyVal = iSection->second.begin(); for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) { DeleteString(iKeyVal->first.pItem); DeleteString(iKeyVal->second); } } // delete the section itself DeleteString(iSection->first.pItem); m_data.erase(iSection); return true; } template void CSimpleIniTempl::DeleteString( const SI_CHAR * a_pString ) { // strings may exist either inside the data block, or they will be // individually allocated and stored in m_strings. We only physically // delete those stored in m_strings. if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) { typename TNamesDepend::iterator i = m_strings.begin(); for (; i != m_strings.end(); ++i) { if (a_pString == i->pItem) { delete[] const_cast(i->pItem); m_strings.erase(i); break; } } } } // --------------------------------------------------------------------------- // CONVERSION FUNCTIONS // --------------------------------------------------------------------------- // Defines the conversion classes for different libraries. Before including // SimpleIni.h, set the converter that you wish you use by defining one of the // following symbols. // // SI_CONVERT_GENERIC Use the Unicode reference conversion library in // the accompanying files ConvertUTF.h/c // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires // ICU headers on include path and icuuc.lib // SI_CONVERT_WIN32 Use the Win32 API functions for conversion. #if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU) # ifdef _WIN32 # define SI_CONVERT_WIN32 # else # define SI_CONVERT_GENERIC # endif #endif /** * Generic case-sensitive less than comparison. This class returns numerically * ordered ASCII case-sensitive text for all possible sizes and types of * SI_CHAR. */ template struct SI_GenericCase { bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { long cmp; for ( ; *pLeft && *pRight; ++pLeft, ++pRight) { cmp = (long) *pLeft - (long) *pRight; if (cmp != 0) { return cmp < 0; } } return *pRight != 0; } }; /** * Generic ASCII case-insensitive less than comparison. This class returns * numerically ordered ASCII case-insensitive text for all possible sizes * and types of SI_CHAR. It is not safe for MBCS text comparison where * ASCII A-Z characters are used in the encoding of multi-byte characters. */ template struct SI_GenericNoCase { inline SI_CHAR locase(SI_CHAR ch) const { return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a'); } bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { long cmp; for ( ; *pLeft && *pRight; ++pLeft, ++pRight) { cmp = (long) locase(*pLeft) - (long) locase(*pRight); if (cmp != 0) { return cmp < 0; } } return *pRight != 0; } }; /** * Null conversion class for MBCS/UTF-8 to char (or equivalent). */ template class SI_ConvertA { bool m_bStoreIsUtf8; protected: SI_ConvertA() { } public: SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { } /* copy and assignment */ SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); } SI_ConvertA & operator=(const SI_ConvertA & rhs) { m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8; return *this; } /** Calculate the number of SI_CHAR required for converting the input * from the storage format. The storage format is always UTF-8 or MBCS. * * @param a_pInputData Data in storage format to be converted to SI_CHAR. * @param a_uInputDataLen Length of storage format data in bytes. This * must be the actual length of the data, including * NULL byte if NULL terminated string is required. * @return Number of SI_CHAR required by the string when * converted. If there are embedded NULL bytes in the * input data, only the string up and not including * the NULL byte will be converted. * @return -1 cast to size_t on a conversion error. */ size_t SizeFromStore( const char * a_pInputData, size_t a_uInputDataLen) { (void)a_pInputData; SI_ASSERT(a_uInputDataLen != (size_t) -1); // ASCII/MBCS/UTF-8 needs no conversion return a_uInputDataLen; } /** Convert the input string from the storage format to SI_CHAR. * The storage format is always UTF-8 or MBCS. * * @param a_pInputData Data in storage format to be converted to SI_CHAR. * @param a_uInputDataLen Length of storage format data in bytes. This * must be the actual length of the data, including * NULL byte if NULL terminated string is required. * @param a_pOutputData Pointer to the output buffer to received the * converted data. * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. * @return true if all of the input data was successfully * converted. */ bool ConvertFromStore( const char * a_pInputData, size_t a_uInputDataLen, SI_CHAR * a_pOutputData, size_t a_uOutputDataSize) { // ASCII/MBCS/UTF-8 needs no conversion if (a_uInputDataLen > a_uOutputDataSize) { return false; } memcpy(a_pOutputData, a_pInputData, a_uInputDataLen); return true; } /** Calculate the number of char required by the storage format of this * data. The storage format is always UTF-8 or MBCS. * * @param a_pInputData NULL terminated string to calculate the number of * bytes required to be converted to storage format. * @return Number of bytes required by the string when * converted to storage format. This size always * includes space for the terminating NULL character. * @return -1 cast to size_t on a conversion error. */ size_t SizeToStore( const SI_CHAR * a_pInputData) { // ASCII/MBCS/UTF-8 needs no conversion return strlen((const char *)a_pInputData) + 1; } /** Convert the input string to the storage format of this data. * The storage format is always UTF-8 or MBCS. * * @param a_pInputData NULL terminated source string to convert. All of * the data will be converted including the * terminating NULL character. * @param a_pOutputData Pointer to the buffer to receive the converted * string. * @param a_uOutputDataSize Size of the output buffer in char. * @return true if all of the input data, including the * terminating NULL character was successfully * converted. */ bool ConvertToStore( const SI_CHAR * a_pInputData, char * a_pOutputData, size_t a_uOutputDataSize) { // calc input string length (SI_CHAR type and size independent) size_t uInputLen = strlen((const char *)a_pInputData) + 1; if (uInputLen > a_uOutputDataSize) { return false; } // ascii/UTF-8 needs no conversion memcpy(a_pOutputData, a_pInputData, uInputLen); return true; } }; // --------------------------------------------------------------------------- // SI_CONVERT_GENERIC // --------------------------------------------------------------------------- #ifdef SI_CONVERT_GENERIC #define SI_Case SI_GenericCase #define SI_NoCase SI_GenericNoCase #include #include "ConvertUTF.h" /** * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference * library functions. This can be used on all platforms. */ template class SI_ConvertW { bool m_bStoreIsUtf8; protected: SI_ConvertW() { } public: SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { } /* copy and assignment */ SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } SI_ConvertW & operator=(const SI_ConvertW & rhs) { m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8; return *this; } /** Calculate the number of SI_CHAR required for converting the input * from the storage format. The storage format is always UTF-8 or MBCS. * * @param a_pInputData Data in storage format to be converted to SI_CHAR. * @param a_uInputDataLen Length of storage format data in bytes. This * must be the actual length of the data, including * NULL byte if NULL terminated string is required. * @return Number of SI_CHAR required by the string when * converted. If there are embedded NULL bytes in the * input data, only the string up and not including * the NULL byte will be converted. * @return -1 cast to size_t on a conversion error. */ size_t SizeFromStore( const char * a_pInputData, size_t a_uInputDataLen) { SI_ASSERT(a_uInputDataLen != (size_t) -1); if (m_bStoreIsUtf8) { // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t // so we just return the same number of characters required as for // the source text. return a_uInputDataLen; } #if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux)) // fall back processing for platforms that don't support a NULL dest to mbstowcs // worst case scenario is 1:1, this will be a sufficient buffer size (void)a_pInputData; return a_uInputDataLen; #else // get the actual required buffer size return mbstowcs(NULL, a_pInputData, a_uInputDataLen); #endif } /** Convert the input string from the storage format to SI_CHAR. * The storage format is always UTF-8 or MBCS. * * @param a_pInputData Data in storage format to be converted to SI_CHAR. * @param a_uInputDataLen Length of storage format data in bytes. This * must be the actual length of the data, including * NULL byte if NULL terminated string is required. * @param a_pOutputData Pointer to the output buffer to received the * converted data. * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. * @return true if all of the input data was successfully * converted. */ bool ConvertFromStore( const char * a_pInputData, size_t a_uInputDataLen, SI_CHAR * a_pOutputData, size_t a_uOutputDataSize) { if (m_bStoreIsUtf8) { // This uses the Unicode reference implementation to do the // conversion from UTF-8 to wchar_t. The required files are // ConvertUTF.h and ConvertUTF.c which should be included in // the distribution but are publicly available from unicode.org // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/ ConversionResult retval; const UTF8 * pUtf8 = (const UTF8 *) a_pInputData; if (sizeof(wchar_t) == sizeof(UTF32)) { UTF32 * pUtf32 = (UTF32 *) a_pOutputData; retval = ConvertUTF8toUTF32( &pUtf8, pUtf8 + a_uInputDataLen, &pUtf32, pUtf32 + a_uOutputDataSize, lenientConversion); } else if (sizeof(wchar_t) == sizeof(UTF16)) { UTF16 * pUtf16 = (UTF16 *) a_pOutputData; retval = ConvertUTF8toUTF16( &pUtf8, pUtf8 + a_uInputDataLen, &pUtf16, pUtf16 + a_uOutputDataSize, lenientConversion); } return retval == conversionOK; } // convert to wchar_t size_t retval = mbstowcs(a_pOutputData, a_pInputData, a_uOutputDataSize); return retval != (size_t)(-1); } /** Calculate the number of char required by the storage format of this * data. The storage format is always UTF-8 or MBCS. * * @param a_pInputData NULL terminated string to calculate the number of * bytes required to be converted to storage format. * @return Number of bytes required by the string when * converted to storage format. This size always * includes space for the terminating NULL character. * @return -1 cast to size_t on a conversion error. */ size_t SizeToStore( const SI_CHAR * a_pInputData) { if (m_bStoreIsUtf8) { // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char size_t uLen = 0; while (a_pInputData[uLen]) { ++uLen; } return (6 * uLen) + 1; } else { size_t uLen = wcstombs(NULL, a_pInputData, 0); if (uLen == (size_t)(-1)) { return uLen; } return uLen + 1; // include NULL terminator } } /** Convert the input string to the storage format of this data. * The storage format is always UTF-8 or MBCS. * * @param a_pInputData NULL terminated source string to convert. All of * the data will be converted including the * terminating NULL character. * @param a_pOutputData Pointer to the buffer to receive the converted * string. * @param a_uOutputDataSize Size of the output buffer in char. * @return true if all of the input data, including the * terminating NULL character was successfully * converted. */ bool ConvertToStore( const SI_CHAR * a_pInputData, char * a_pOutputData, size_t a_uOutputDataSize ) { if (m_bStoreIsUtf8) { // calc input string length (SI_CHAR type and size independent) size_t uInputLen = 0; while (a_pInputData[uInputLen]) { ++uInputLen; } ++uInputLen; // include the NULL char // This uses the Unicode reference implementation to do the // conversion from wchar_t to UTF-8. The required files are // ConvertUTF.h and ConvertUTF.c which should be included in // the distribution but are publicly available from unicode.org // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/ ConversionResult retval; UTF8 * pUtf8 = (UTF8 *) a_pOutputData; if (sizeof(wchar_t) == sizeof(UTF32)) { const UTF32 * pUtf32 = (const UTF32 *) a_pInputData; retval = ConvertUTF32toUTF8( &pUtf32, pUtf32 + uInputLen, &pUtf8, pUtf8 + a_uOutputDataSize, lenientConversion); } else if (sizeof(wchar_t) == sizeof(UTF16)) { const UTF16 * pUtf16 = (const UTF16 *) a_pInputData; retval = ConvertUTF16toUTF8( &pUtf16, pUtf16 + uInputLen, &pUtf8, pUtf8 + a_uOutputDataSize, lenientConversion); } return retval == conversionOK; } else { size_t retval = wcstombs(a_pOutputData, a_pInputData, a_uOutputDataSize); return retval != (size_t) -1; } } }; #endif // SI_CONVERT_GENERIC // --------------------------------------------------------------------------- // SI_CONVERT_ICU // --------------------------------------------------------------------------- #ifdef SI_CONVERT_ICU #define SI_Case SI_GenericCase #define SI_NoCase SI_GenericNoCase #include /** * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms. */ template class SI_ConvertW { const char * m_pEncoding; UConverter * m_pConverter; protected: SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { } public: SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) { m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL; } /* copy and assignment */ SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } SI_ConvertW & operator=(const SI_ConvertW & rhs) { m_pEncoding = rhs.m_pEncoding; m_pConverter = NULL; return *this; } ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); } /** Calculate the number of UChar required for converting the input * from the storage format. The storage format is always UTF-8 or MBCS. * * @param a_pInputData Data in storage format to be converted to UChar. * @param a_uInputDataLen Length of storage format data in bytes. This * must be the actual length of the data, including * NULL byte if NULL terminated string is required. * @return Number of UChar required by the string when * converted. If there are embedded NULL bytes in the * input data, only the string up and not including * the NULL byte will be converted. * @return -1 cast to size_t on a conversion error. */ size_t SizeFromStore( const char * a_pInputData, size_t a_uInputDataLen) { SI_ASSERT(a_uInputDataLen != (size_t) -1); UErrorCode nError; if (!m_pConverter) { nError = U_ZERO_ERROR; m_pConverter = ucnv_open(m_pEncoding, &nError); if (U_FAILURE(nError)) { return (size_t) -1; } } nError = U_ZERO_ERROR; int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0, a_pInputData, (int32_t) a_uInputDataLen, &nError); if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) { return (size_t) -1; } return (size_t) nLen; } /** Convert the input string from the storage format to UChar. * The storage format is always UTF-8 or MBCS. * * @param a_pInputData Data in storage format to be converted to UChar. * @param a_uInputDataLen Length of storage format data in bytes. This * must be the actual length of the data, including * NULL byte if NULL terminated string is required. * @param a_pOutputData Pointer to the output buffer to received the * converted data. * @param a_uOutputDataSize Size of the output buffer in UChar. * @return true if all of the input data was successfully * converted. */ bool ConvertFromStore( const char * a_pInputData, size_t a_uInputDataLen, UChar * a_pOutputData, size_t a_uOutputDataSize) { UErrorCode nError; if (!m_pConverter) { nError = U_ZERO_ERROR; m_pConverter = ucnv_open(m_pEncoding, &nError); if (U_FAILURE(nError)) { return false; } } nError = U_ZERO_ERROR; ucnv_toUChars(m_pConverter, a_pOutputData, (int32_t) a_uOutputDataSize, a_pInputData, (int32_t) a_uInputDataLen, &nError); if (U_FAILURE(nError)) { return false; } return true; } /** Calculate the number of char required by the storage format of this * data. The storage format is always UTF-8 or MBCS. * * @param a_pInputData NULL terminated string to calculate the number of * bytes required to be converted to storage format. * @return Number of bytes required by the string when * converted to storage format. This size always * includes space for the terminating NULL character. * @return -1 cast to size_t on a conversion error. */ size_t SizeToStore( const UChar * a_pInputData) { UErrorCode nError; if (!m_pConverter) { nError = U_ZERO_ERROR; m_pConverter = ucnv_open(m_pEncoding, &nError); if (U_FAILURE(nError)) { return (size_t) -1; } } nError = U_ZERO_ERROR; int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0, a_pInputData, -1, &nError); if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) { return (size_t) -1; } return (size_t) nLen + 1; } /** Convert the input string to the storage format of this data. * The storage format is always UTF-8 or MBCS. * * @param a_pInputData NULL terminated source string to convert. All of * the data will be converted including the * terminating NULL character. * @param a_pOutputData Pointer to the buffer to receive the converted * string. * @param a_pOutputDataSize Size of the output buffer in char. * @return true if all of the input data, including the * terminating NULL character was successfully * converted. */ bool ConvertToStore( const UChar * a_pInputData, char * a_pOutputData, size_t a_uOutputDataSize) { UErrorCode nError; if (!m_pConverter) { nError = U_ZERO_ERROR; m_pConverter = ucnv_open(m_pEncoding, &nError); if (U_FAILURE(nError)) { return false; } } nError = U_ZERO_ERROR; ucnv_fromUChars(m_pConverter, a_pOutputData, (int32_t) a_uOutputDataSize, a_pInputData, -1, &nError); if (U_FAILURE(nError)) { return false; } return true; } }; #endif // SI_CONVERT_ICU // --------------------------------------------------------------------------- // SI_CONVERT_WIN32 // --------------------------------------------------------------------------- #ifdef SI_CONVERT_WIN32 #define SI_Case SI_GenericCase // Windows CE doesn't have errno or MBCS libraries #ifdef _WIN32_WCE # ifndef SI_NO_MBCS # define SI_NO_MBCS # endif #endif #include #ifdef SI_NO_MBCS # define SI_NoCase SI_GenericNoCase #else // !SI_NO_MBCS /** * Case-insensitive comparison class using Win32 MBCS functions. This class * returns a case-insensitive semi-collation order for MBCS text. It may not * be safe for UTF-8 text returned in char format as we don't know what * characters will be folded by the function! Therefore, if you are using * SI_CHAR == char and SetUnicode(true), then you need to use the generic * SI_NoCase class instead. */ #include template struct SI_NoCase { bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { if (sizeof(SI_CHAR) == sizeof(char)) { return _mbsicmp((const unsigned char *)pLeft, (const unsigned char *)pRight) < 0; } if (sizeof(SI_CHAR) == sizeof(wchar_t)) { return _wcsicmp((const wchar_t *)pLeft, (const wchar_t *)pRight) < 0; } return SI_GenericNoCase()(pLeft, pRight); } }; #endif // SI_NO_MBCS /** * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses * only the Win32 functions and doesn't require the external Unicode UTF-8 * conversion library. It will not work on Windows 95 without using Microsoft * Layer for Unicode in your application. */ template class SI_ConvertW { UINT m_uCodePage; protected: SI_ConvertW() { } public: SI_ConvertW(bool a_bStoreIsUtf8) { m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP; } /* copy and assignment */ SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } SI_ConvertW & operator=(const SI_ConvertW & rhs) { m_uCodePage = rhs.m_uCodePage; return *this; } /** Calculate the number of SI_CHAR required for converting the input * from the storage format. The storage format is always UTF-8 or MBCS. * * @param a_pInputData Data in storage format to be converted to SI_CHAR. * @param a_uInputDataLen Length of storage format data in bytes. This * must be the actual length of the data, including * NULL byte if NULL terminated string is required. * @return Number of SI_CHAR required by the string when * converted. If there are embedded NULL bytes in the * input data, only the string up and not including * the NULL byte will be converted. * @return -1 cast to size_t on a conversion error. */ size_t SizeFromStore( const char * a_pInputData, size_t a_uInputDataLen) { SI_ASSERT(a_uInputDataLen != (size_t) -1); int retval = MultiByteToWideChar( m_uCodePage, 0, a_pInputData, (int) a_uInputDataLen, 0, 0); return (size_t)(retval > 0 ? retval : -1); } /** Convert the input string from the storage format to SI_CHAR. * The storage format is always UTF-8 or MBCS. * * @param a_pInputData Data in storage format to be converted to SI_CHAR. * @param a_uInputDataLen Length of storage format data in bytes. This * must be the actual length of the data, including * NULL byte if NULL terminated string is required. * @param a_pOutputData Pointer to the output buffer to received the * converted data. * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. * @return true if all of the input data was successfully * converted. */ bool ConvertFromStore( const char * a_pInputData, size_t a_uInputDataLen, SI_CHAR * a_pOutputData, size_t a_uOutputDataSize) { int nSize = MultiByteToWideChar( m_uCodePage, 0, a_pInputData, (int) a_uInputDataLen, (wchar_t *) a_pOutputData, (int) a_uOutputDataSize); return (nSize > 0); } /** Calculate the number of char required by the storage format of this * data. The storage format is always UTF-8. * * @param a_pInputData NULL terminated string to calculate the number of * bytes required to be converted to storage format. * @return Number of bytes required by the string when * converted to storage format. This size always * includes space for the terminating NULL character. * @return -1 cast to size_t on a conversion error. */ size_t SizeToStore( const SI_CHAR * a_pInputData) { int retval = WideCharToMultiByte( m_uCodePage, 0, (const wchar_t *) a_pInputData, -1, 0, 0, 0, 0); return (size_t) (retval > 0 ? retval : -1); } /** Convert the input string to the storage format of this data. * The storage format is always UTF-8 or MBCS. * * @param a_pInputData NULL terminated source string to convert. All of * the data will be converted including the * terminating NULL character. * @param a_pOutputData Pointer to the buffer to receive the converted * string. * @param a_pOutputDataSize Size of the output buffer in char. * @return true if all of the input data, including the * terminating NULL character was successfully * converted. */ bool ConvertToStore( const SI_CHAR * a_pInputData, char * a_pOutputData, size_t a_uOutputDataSize) { int retval = WideCharToMultiByte( m_uCodePage, 0, (const wchar_t *) a_pInputData, -1, a_pOutputData, (int) a_uOutputDataSize, 0, 0); return retval > 0; } }; #endif // SI_CONVERT_WIN32 // --------------------------------------------------------------------------- // TYPE DEFINITIONS // --------------------------------------------------------------------------- typedef CSimpleIniTempl,SI_ConvertA > CSimpleIniA; typedef CSimpleIniTempl,SI_ConvertA > CSimpleIniCaseA; #if defined(SI_CONVERT_ICU) typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniW; typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniCaseW; #else typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniW; typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniCaseW; #endif #ifdef _UNICODE # define CSimpleIni CSimpleIniW # define CSimpleIniCase CSimpleIniCaseW # define SI_NEWLINE SI_NEWLINE_W #else // !_UNICODE # define CSimpleIni CSimpleIniA # define CSimpleIniCase CSimpleIniCaseA # define SI_NEWLINE SI_NEWLINE_A #endif // _UNICODE #ifdef _MSC_VER # pragma warning (pop) #endif #endif // INCLUDED_SimpleIni_h openalpr_2.2.4.orig/src/openalpr/simpleini/snippets.cpp000066400000000000000000000063031266464252400233460ustar00rootroot00000000000000// File: snippets.cpp // Library: SimpleIni // Author: Brodie Thiesfield // Source: http://code.jellycan.com/simpleini/ // // Snippets that are used on the website #ifdef _WIN32 # pragma warning(disable: 4786) #endif #ifndef _WIN32 # include #endif #include #define SI_SUPPORT_IOSTREAMS #include "SimpleIni.h" bool snippets( const char * a_pszFile, bool a_bIsUtf8, bool a_bUseMultiKey, bool a_bUseMultiLine ) { // LOADING DATA // load from a data file CSimpleIniA ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine); SI_Error rc = ini.LoadFile(a_pszFile); if (rc < 0) return false; // load from a string std::string strData; rc = ini.LoadData(strData.c_str(), strData.size()); if (rc < 0) return false; // GETTING SECTIONS AND KEYS // get all sections CSimpleIniA::TNamesDepend sections; ini.GetAllSections(sections); // get all keys in a section CSimpleIniA::TNamesDepend keys; ini.GetAllKeys("section-name", keys); // GETTING VALUES // get the value of a key const char * pszValue = ini.GetValue("section-name", "key-name", NULL /*default*/); // get the value of a key which may have multiple // values. If bHasMultipleValues is true, then just // one value has been returned bool bHasMultipleValues; pszValue = ini.GetValue("section-name", "key-name", NULL /*default*/, &bHasMultipleValues); // get all values of a key with multiple values CSimpleIniA::TNamesDepend values; ini.GetAllValues("section-name", "key-name", values); // sort the values into the original load order #if defined(_MSC_VER) && _MSC_VER <= 1200 /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */ values.sort(); #else values.sort(CSimpleIniA::Entry::LoadOrder()); #endif // output all of the items CSimpleIniA::TNamesDepend::const_iterator i; for (i = values.begin(); i != values.end(); ++i) { printf("key-name = '%s'\n", i->pItem); } // MODIFYING DATA // adding a new section rc = ini.SetValue("new-section", NULL, NULL); if (rc < 0) return false; printf("section: %s\n", rc == SI_INSERTED ? "inserted" : "updated"); // adding a new key ("new-section" will be added // automatically if it doesn't already exist. rc = ini.SetValue("new-section", "new-key", "value"); if (rc < 0) return false; printf("key: %s\n", rc == SI_INSERTED ? "inserted" : "updated"); // changing the value of a key rc = ini.SetValue("section", "key", "updated-value"); if (rc < 0) return false; printf("key: %s\n", rc == SI_INSERTED ? "inserted" : "updated"); // DELETING DATA // deleting a key from a section. Optionally the entire // section may be deleted if it is now empty. ini.Delete("section-name", "key-name", true /*delete the section if empty*/); // deleting an entire section and all keys in it ini.Delete("section-name", NULL); // SAVING DATA // save the data to a string rc = ini.Save(strData); if (rc < 0) return false; // save the data back to the file rc = ini.SaveFile(a_pszFile); if (rc < 0) return false; return true; } openalpr_2.2.4.orig/src/openalpr/support/000077500000000000000000000000001266464252400205165ustar00rootroot00000000000000openalpr_2.2.4.orig/src/openalpr/support/CMakeLists.txt000066400000000000000000000013341266464252400232570ustar00rootroot00000000000000 set(support_source_files filesystem.cpp timing.cpp tinythread.cpp platform.cpp utf8.cpp version.cpp ) set(regex_source_files re2/bitstate.cc re2/compile.cc re2/dfa.cc re2/filtered_re2.cc re2/mimics_pcre.cc re2/nfa.cc re2/onepass.cc re2/parse.cc re2/perl_groups.cc re2/prefilter.cc re2/prefilter_tree.cc re2/prog.cc re2/re2.cc re2/regexp.cc re2/set.cc re2/simplify.cc re2/stringpiece.cc re2/tostring.cc re2/unicode_casefold.cc re2/unicode_groups.cc re2/util/hash.cc re2/util/stringprintf.cc re2/util/rune.cc re2/util/strutil.cc re2/util/valgrind.cc ) include_directories(.) add_library(support STATIC ${support_source_files} ${regex_source_files} ) SET_TARGET_PROPERTIES( support PROPERTIES COMPILE_FLAGS -fPIC)openalpr_2.2.4.orig/src/openalpr/support/fast_mutex.h000066400000000000000000000154341266464252400230550ustar00rootroot00000000000000/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- Copyright (c) 2010-2012 Marcus Geelnard This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef _FAST_MUTEX_H_ #define _FAST_MUTEX_H_ /// @file // Which platform are we on? #if !defined(_TTHREAD_PLATFORM_DEFINED_) #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) #define _TTHREAD_WIN32_ #else #define _TTHREAD_POSIX_ #endif #define _TTHREAD_PLATFORM_DEFINED_ #endif // Check if we can support the assembly language level implementation (otherwise // revert to the system API) #if (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) || \ (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))) || \ (defined(__GNUC__) && (defined(__ppc__))) #define _FAST_MUTEX_ASM_ #else #define _FAST_MUTEX_SYS_ #endif #if defined(_TTHREAD_WIN32_) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #define __UNDEF_LEAN_AND_MEAN #endif #include #ifdef __UNDEF_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN #undef __UNDEF_LEAN_AND_MEAN #endif #else #ifdef _FAST_MUTEX_ASM_ #include #else #include #endif #endif namespace tthread { /// Fast mutex class. /// This is a mutual exclusion object for synchronizing access to shared /// memory areas for several threads. It is similar to the tthread::mutex class, /// but instead of using system level functions, it is implemented as an atomic /// spin lock with very low CPU overhead. /// /// The \c fast_mutex class is NOT compatible with the \c condition_variable /// class (however, it IS compatible with the \c lock_guard class). It should /// also be noted that the \c fast_mutex class typically does not provide /// as accurate thread scheduling as a the standard \c mutex class does. /// /// Because of the limitations of the class, it should only be used in /// situations where the mutex needs to be locked/unlocked very frequently. /// /// @note The "fast" version of this class relies on inline assembler language, /// which is currently only supported for 32/64-bit Intel x86/AMD64 and /// PowerPC architectures on a limited number of compilers (GNU g++ and MS /// Visual C++). /// For other architectures/compilers, system functions are used instead. class fast_mutex { public: /// Constructor. #if defined(_FAST_MUTEX_ASM_) fast_mutex() : mLock(0) {} #else fast_mutex() { #if defined(_TTHREAD_WIN32_) InitializeCriticalSection(&mHandle); #elif defined(_TTHREAD_POSIX_) pthread_mutex_init(&mHandle, NULL); #endif } #endif #if !defined(_FAST_MUTEX_ASM_) /// Destructor. ~fast_mutex() { #if defined(_TTHREAD_WIN32_) DeleteCriticalSection(&mHandle); #elif defined(_TTHREAD_POSIX_) pthread_mutex_destroy(&mHandle); #endif } #endif /// Lock the mutex. /// The method will block the calling thread until a lock on the mutex can /// be obtained. The mutex remains locked until \c unlock() is called. /// @see lock_guard inline void lock() { #if defined(_FAST_MUTEX_ASM_) bool gotLock; do { gotLock = try_lock(); if(!gotLock) { #if defined(_TTHREAD_WIN32_) Sleep(0); #elif defined(_TTHREAD_POSIX_) sched_yield(); #endif } } while(!gotLock); #else #if defined(_TTHREAD_WIN32_) EnterCriticalSection(&mHandle); #elif defined(_TTHREAD_POSIX_) pthread_mutex_lock(&mHandle); #endif #endif } /// Try to lock the mutex. /// The method will try to lock the mutex. If it fails, the function will /// return immediately (non-blocking). /// @return \c true if the lock was acquired, or \c false if the lock could /// not be acquired. inline bool try_lock() { #if defined(_FAST_MUTEX_ASM_) int oldLock; #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) asm volatile ( "movl $1,%%eax\n\t" "xchg %%eax,%0\n\t" "movl %%eax,%1\n\t" : "=m" (mLock), "=m" (oldLock) : : "%eax", "memory" ); #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) int *ptrLock = &mLock; __asm { mov eax,1 mov ecx,ptrLock xchg eax,[ecx] mov oldLock,eax } #elif defined(__GNUC__) && (defined(__ppc__)) int newLock = 1; asm volatile ( "\n1:\n\t" "lwarx %0,0,%1\n\t" "cmpwi 0,%0,0\n\t" "bne- 2f\n\t" "stwcx. %2,0,%1\n\t" "bne- 1b\n\t" "isync\n" "2:\n\t" : "=&r" (oldLock) : "r" (&mLock), "r" (newLock) : "cr0", "memory" ); #endif return (oldLock == 0); #else #if defined(_TTHREAD_WIN32_) return TryEnterCriticalSection(&mHandle) ? true : false; #elif defined(_TTHREAD_POSIX_) return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; #endif #endif } /// Unlock the mutex. /// If any threads are waiting for the lock on this mutex, one of them will /// be unblocked. inline void unlock() { #if defined(_FAST_MUTEX_ASM_) #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) asm volatile ( "movl $0,%%eax\n\t" "xchg %%eax,%0\n\t" : "=m" (mLock) : : "%eax", "memory" ); #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) int *ptrLock = &mLock; __asm { mov eax,0 mov ecx,ptrLock xchg eax,[ecx] } #elif defined(__GNUC__) && (defined(__ppc__)) asm volatile ( "sync\n\t" // Replace with lwsync where possible? : : : "memory" ); mLock = 0; #endif #else #if defined(_TTHREAD_WIN32_) LeaveCriticalSection(&mHandle); #elif defined(_TTHREAD_POSIX_) pthread_mutex_unlock(&mHandle); #endif #endif } private: #if defined(_FAST_MUTEX_ASM_) int mLock; #else #if defined(_TTHREAD_WIN32_) CRITICAL_SECTION mHandle; #elif defined(_TTHREAD_POSIX_) pthread_mutex_t mHandle; #endif #endif }; } #endif // _FAST_MUTEX_H_ openalpr_2.2.4.orig/src/openalpr/support/filesystem.cpp000066400000000000000000000133631266464252400234140ustar00rootroot00000000000000#include "filesystem.h" #include #include namespace alpr { bool startsWith(std::string const &fullString, std::string const &prefix) { if(fullString.substr(0, prefix.size()).compare(prefix) == 0) { return true; } return false; } bool hasEnding (std::string const &fullString, std::string const &ending) { if (fullString.length() >= ending.length()) { return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); } else { return false; } } bool hasEndingInsensitive(const std::string& fullString, const std::string& ending) { if (fullString.length() < ending.length()) return false; int startidx = fullString.length() - ending.length(); for (unsigned int i = startidx; i < fullString.length(); ++i) if (tolower(fullString[i]) != tolower(ending[i - startidx])) return false; return true; } bool DirectoryExists( const char* pzPath ) { if ( pzPath == NULL) return false; DIR *pDir; bool bExists = false; pDir = opendir (pzPath); if (pDir != NULL) { bExists = true; (void) closedir (pDir); } return bExists; } bool fileExists( const char* pzPath ) { if (pzPath == NULL) return false; bool fExists = false; std::ifstream f(pzPath); fExists = f.is_open(); f.close(); return fExists; } std::vector getFilesInDir(const char* dirPath) { DIR *dir; std::vector files; struct dirent *ent; if ((dir = opendir (dirPath)) != NULL) { /* print all the files and directories within directory */ while ((ent = readdir (dir)) != NULL) { if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0) files.push_back(ent->d_name); } closedir (dir); } else { /* could not open directory */ perror (""); return files; } return files; } bool stringCompare( const std::string &left, const std::string &right ) { for( std::string::const_iterator lit = left.begin(), rit = right.begin(); lit != left.end() && rit != right.end(); ++lit, ++rit ) if( tolower( *lit ) < tolower( *rit ) ) return true; else if( tolower( *lit ) > tolower( *rit ) ) return false; if( left.size() < right.size() ) return true; return false; } std::string filenameWithoutExtension(std::string filename) { int lastindex = filename.find_last_of("."); return filename.substr(0, lastindex); } std::string get_filename_from_path(std::string file_path) { size_t found; found=file_path.find_last_of("/\\"); if (found >= 0) return file_path.substr(found+1); return ""; } std::string get_directory_from_path(std::string file_path) { if (DirectoryExists(file_path.c_str())) return file_path; size_t found; found=file_path.find_last_of("/\\"); if (found >= 0) return file_path.substr(0,found); return ""; } #ifdef WINDOWS // Stub out these functions on Windows. They're used for the daemon anyway, which isn't supported on Windows. static int makeDir(const char *path, mode_t mode) { return 0; } bool makePath(const char* path, mode_t mode) { std::stringstream pathstream; pathstream << "mkdir " << path; std::string candidate_path = pathstream.str(); std::replace(candidate_path.begin(), candidate_path.end(), '/', '\\'); system(candidate_path.c_str()); return true; } FileInfo getFileInfo(std::string filename) { FileInfo response; response.creation_time = 0; response.size = 0; return response; } #else FileInfo getFileInfo(std::string filename) { FileInfo response; struct stat stat_buf; int rc = stat(filename.c_str(), &stat_buf); //return rc == 0 ? stat_buf.st_size : -1; // 512 bytes is the standard block size if (rc == 0) { #if defined(__APPLE__) double milliseconds = (stat_buf.st_ctimespec.tv_sec * 1000) + (((double) stat_buf.st_ctimespec.tv_nsec) / 1000000.0); #else double milliseconds = (stat_buf.st_ctim.tv_sec * 1000) + (((double) stat_buf.st_ctim.tv_nsec) / 1000000.0); #endif response.size = 512 * stat_buf.st_blocks; response.creation_time = (int64_t) milliseconds; } else { response.creation_time = 0; response.size = 0; } return response; } static int makeDir(const char *path, mode_t mode) { struct stat st; int status = 0; if (stat(path, &st) != 0) { /* Directory does not exist. EEXIST for race condition */ if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1; } else if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR; status = -1; } return(status); } /** ** makePath - ensure all directories in path exist ** Algorithm takes the pessimistic view and works top-down to ensure ** each directory in path exists, rather than optimistically creating ** the last element and working backwards. */ bool makePath(const char* path, mode_t mode) { char *pp; char *sp; int status; char *copypath = strdup(path); status = 0; pp = copypath; while (status == 0 && (sp = strchr(pp, '/')) != 0) { if (sp != pp) { /* Neither root nor double slash in path */ *sp = '\0'; status = makeDir(copypath, mode); *sp = '/'; } pp = sp + 1; } if (status == 0) status = makeDir(path, mode); free(copypath); return (status == 0); } #endif } openalpr_2.2.4.orig/src/openalpr/support/filesystem.h000066400000000000000000000023731266464252400230600ustar00rootroot00000000000000 #ifndef FILESYSTEM_H #define FILESYSTEM_H #ifdef WINDOWS #include #include "windows/dirent.h" #include "windows/utils.h" #include "windows/unistd_partial.h" typedef int mode_t; #else #include #include #endif #include #include #include #include #include #include #include #include namespace alpr { struct FileInfo { int64_t size; int64_t creation_time; }; bool startsWith(std::string const &fullString, std::string const &prefix); bool hasEnding (std::string const &fullString, std::string const &ending); bool hasEndingInsensitive(const std::string& fullString, const std::string& ending); std::string filenameWithoutExtension(std::string filename); FileInfo getFileInfo(std::string filename); bool DirectoryExists( const char* pzPath ); bool fileExists( const char* pzPath ); std::vector getFilesInDir(const char* dirPath); bool stringCompare( const std::string &left, const std::string &right ); bool makePath(const char* path, mode_t mode); std::string get_directory_from_path(std::string file_path); std::string get_filename_from_path(std::string file_path); } #endif // FILESYSTEM_H openalpr_2.2.4.orig/src/openalpr/support/platform.cpp000066400000000000000000000032171266464252400230510ustar00rootroot00000000000000#include "platform.h" namespace alpr { void sleep_ms(int sleepMs) { #ifdef WINDOWS Sleep(sleepMs); #else usleep(sleepMs * 1000); // usleep takes sleep time in us (1 millionth of a second) #endif } std::string getExeDir() { #ifdef WINDOWS TCHAR szEXEPath[2048]; std::stringstream ss; GetModuleFileName(NULL, szEXEPath, 2048); ss << szEXEPath; std::string exeFile = ss.str(); std::string directory; const size_t last_slash_idx = exeFile.rfind('\\'); if (std::string::npos != last_slash_idx) { directory = exeFile.substr(0, last_slash_idx); } return directory; #else char buffer[2048]; memset(buffer, 0, sizeof(buffer)); if (readlink("/proc/self/exe", buffer, sizeof(buffer)) > 0) { std::stringstream ss; ss << buffer; std::string exeFile = ss.str(); std::string directory; const size_t last_slash_idx = exeFile.rfind('/'); if (std::string::npos != last_slash_idx) { directory = exeFile.substr(0, last_slash_idx); } return directory; } else { return "/"; } #endif } }openalpr_2.2.4.orig/src/openalpr/support/platform.h000066400000000000000000000004261266464252400225150ustar00rootroot00000000000000#ifndef OPENALPR_PLATFORM_H #define OPENALPR_PLATFORM_H #include #include #ifdef WINDOWS #include #else #include #endif namespace alpr { void sleep_ms(int sleepMs); std::string getExeDir(); } #endif //OPENALPR_PLATFORM_H openalpr_2.2.4.orig/src/openalpr/support/re2.h000066400000000000000000001037161266464252400213670ustar00rootroot00000000000000// Copyright 2003-2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #ifndef RE2_RE2_H #define RE2_RE2_H // C++ interface to the re2 regular-expression library. // RE2 supports Perl-style regular expressions (with extensions like // \d, \w, \s, ...). // // ----------------------------------------------------------------------- // REGEXP SYNTAX: // // This module uses the re2 library and hence supports // its syntax for regular expressions, which is similar to Perl's with // some of the more complicated things thrown away. In particular, // backreferences and generalized assertions are not available, nor is \Z. // // See https://github.com/google/re2/wiki/Syntax for the syntax // supported by RE2, and a comparison with PCRE and PERL regexps. // // For those not familiar with Perl's regular expressions, // here are some examples of the most commonly used extensions: // // "hello (\\w+) world" -- \w matches a "word" character // "version (\\d+)" -- \d matches a digit // "hello\\s+world" -- \s matches any whitespace character // "\\b(\\w+)\\b" -- \b matches non-empty string at word boundary // "(?i)hello" -- (?i) turns on case-insensitive matching // "/\\*(.*?)\\*/" -- .*? matches . minimum no. of times possible // // ----------------------------------------------------------------------- // MATCHING INTERFACE: // // The "FullMatch" operation checks that supplied text matches a // supplied pattern exactly. // // Example: successful match // CHECK(RE2::FullMatch("hello", "h.*o")); // // Example: unsuccessful match (requires full match): // CHECK(!RE2::FullMatch("hello", "e")); // // ----------------------------------------------------------------------- // UTF-8 AND THE MATCHING INTERFACE: // // By default, the pattern and input text are interpreted as UTF-8. // The RE2::Latin1 option causes them to be interpreted as Latin-1. // // Example: // CHECK(RE2::FullMatch(utf8_string, RE2(utf8_pattern))); // CHECK(RE2::FullMatch(latin1_string, RE2(latin1_pattern, RE2::Latin1))); // // ----------------------------------------------------------------------- // MATCHING WITH SUB-STRING EXTRACTION: // // You can supply extra pointer arguments to extract matched subpieces. // // Example: extracts "ruby" into "s" and 1234 into "i" // int i; // string s; // CHECK(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", &s, &i)); // // Example: fails because string cannot be stored in integer // CHECK(!RE2::FullMatch("ruby", "(.*)", &i)); // // Example: fails because there aren't enough sub-patterns: // CHECK(!RE2::FullMatch("ruby:1234", "\\w+:\\d+", &s)); // // Example: does not try to extract any extra sub-patterns // CHECK(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", &s)); // // Example: does not try to extract into NULL // CHECK(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", NULL, &i)); // // Example: integer overflow causes failure // CHECK(!RE2::FullMatch("ruby:1234567891234", "\\w+:(\\d+)", &i)); // // NOTE(rsc): Asking for substrings slows successful matches quite a bit. // This may get a little faster in the future, but right now is slower // than PCRE. On the other hand, failed matches run *very* fast (faster // than PCRE), as do matches without substring extraction. // // ----------------------------------------------------------------------- // PARTIAL MATCHES // // You can use the "PartialMatch" operation when you want the pattern // to match any substring of the text. // // Example: simple search for a string: // CHECK(RE2::PartialMatch("hello", "ell")); // // Example: find first number in a string // int number; // CHECK(RE2::PartialMatch("x*100 + 20", "(\\d+)", &number)); // CHECK_EQ(number, 100); // // ----------------------------------------------------------------------- // PRE-COMPILED REGULAR EXPRESSIONS // // RE2 makes it easy to use any string as a regular expression, without // requiring a separate compilation step. // // If speed is of the essence, you can create a pre-compiled "RE2" // object from the pattern and use it multiple times. If you do so, // you can typically parse text faster than with sscanf. // // Example: precompile pattern for faster matching: // RE2 pattern("h.*o"); // while (ReadLine(&str)) { // if (RE2::FullMatch(str, pattern)) ...; // } // // ----------------------------------------------------------------------- // SCANNING TEXT INCREMENTALLY // // The "Consume" operation may be useful if you want to repeatedly // match regular expressions at the front of a string and skip over // them as they match. This requires use of the "StringPiece" type, // which represents a sub-range of a real string. // // Example: read lines of the form "var = value" from a string. // string contents = ...; // Fill string somehow // StringPiece input(contents); // Wrap a StringPiece around it // // string var; // int value; // while (RE2::Consume(&input, "(\\w+) = (\\d+)\n", &var, &value)) { // ...; // } // // Each successful call to "Consume" will set "var/value", and also // advance "input" so it points past the matched text. Note that if the // regular expression matches an empty string, input will advance // by 0 bytes. If the regular expression being used might match // an empty string, the loop body must check for this case and either // advance the string or break out of the loop. // // The "FindAndConsume" operation is similar to "Consume" but does not // anchor your match at the beginning of the string. For example, you // could extract all words from a string by repeatedly calling // RE2::FindAndConsume(&input, "(\\w+)", &word) // // ----------------------------------------------------------------------- // USING VARIABLE NUMBER OF ARGUMENTS // // The above operations require you to know the number of arguments // when you write the code. This is not always possible or easy (for // example, the regular expression may be calculated at run time). // You can use the "N" version of the operations when the number of // match arguments are determined at run time. // // Example: // const RE2::Arg* args[10]; // int n; // // ... populate args with pointers to RE2::Arg values ... // // ... set n to the number of RE2::Arg objects ... // bool match = RE2::FullMatchN(input, pattern, args, n); // // The last statement is equivalent to // // bool match = RE2::FullMatch(input, pattern, // *args[0], *args[1], ..., *args[n - 1]); // // ----------------------------------------------------------------------- // PARSING HEX/OCTAL/C-RADIX NUMBERS // // By default, if you pass a pointer to a numeric value, the // corresponding text is interpreted as a base-10 number. You can // instead wrap the pointer with a call to one of the operators Hex(), // Octal(), or CRadix() to interpret the text in another base. The // CRadix operator interprets C-style "0" (base-8) and "0x" (base-16) // prefixes, but defaults to base-10. // // Example: // int a, b, c, d; // CHECK(RE2::FullMatch("100 40 0100 0x40", "(.*) (.*) (.*) (.*)", // RE2::Octal(&a), RE2::Hex(&b), RE2::CRadix(&c), RE2::CRadix(&d)); // will leave 64 in a, b, c, and d. #include #include #include #include "re2/stringpiece.h" #include "re2/variadic_function.h" #ifndef RE2_HAVE_LONGLONG #define RE2_HAVE_LONGLONG 1 #endif namespace re2 { using std::string; using std::map; class Mutex; class Prog; class Regexp; // The following enum should be used only as a constructor argument to indicate // that the variable has static storage class, and that the constructor should // do nothing to its state. It indicates to the reader that it is legal to // declare a static instance of the class, provided the constructor is given // the LINKER_INITIALIZED argument. Normally, it is unsafe to declare a // static variable that has a constructor or a destructor because invocation // order is undefined. However, IF the type can be initialized by filling with // zeroes (which the loader does for static variables), AND the type's // destructor does nothing to the storage, then a constructor for static // initialization can be declared as // explicit MyClass(LinkerInitialized x) {} // and invoked as // static MyClass my_variable_name(LINKER_INITIALIZED); enum LinkerInitialized { LINKER_INITIALIZED }; // Interface for regular expression matching. Also corresponds to a // pre-compiled regular expression. An "RE2" object is safe for // concurrent use by multiple threads. class RE2 { public: // We convert user-passed pointers into special Arg objects class Arg; class Options; // Defined in set.h. class Set; enum ErrorCode { NoError = 0, // Unexpected error ErrorInternal, // Parse errors ErrorBadEscape, // bad escape sequence ErrorBadCharClass, // bad character class ErrorBadCharRange, // bad character class range ErrorMissingBracket, // missing closing ] ErrorMissingParen, // missing closing ) ErrorTrailingBackslash, // trailing \ at end of regexp ErrorRepeatArgument, // repeat argument missing, e.g. "*" ErrorRepeatSize, // bad repetition argument ErrorRepeatOp, // bad repetition operator ErrorBadPerlOp, // bad perl operator ErrorBadUTF8, // invalid UTF-8 in regexp ErrorBadNamedCapture, // bad named capture group ErrorPatternTooLarge // pattern too large (compile failed) }; // Predefined common options. // If you need more complicated things, instantiate // an Option class, possibly passing one of these to // the Option constructor, change the settings, and pass that // Option class to the RE2 constructor. enum CannedOptions { DefaultOptions = 0, Latin1, // treat input as Latin-1 (default UTF-8) POSIX, // POSIX syntax, leftmost-longest match Quiet // do not log about regexp parse errors }; // Need to have the const char* and const string& forms for implicit // conversions when passing string literals to FullMatch and PartialMatch. // Otherwise the StringPiece form would be sufficient. #ifndef SWIG RE2(const char* pattern); RE2(const string& pattern); #endif RE2(const StringPiece& pattern); RE2(const StringPiece& pattern, const Options& option); ~RE2(); // Returns whether RE2 was created properly. bool ok() const { return error_code() == NoError; } // The string specification for this RE2. E.g. // RE2 re("ab*c?d+"); // re.pattern(); // "ab*c?d+" const string& pattern() const { return pattern_; } // If RE2 could not be created properly, returns an error string. // Else returns the empty string. const string& error() const { return *error_; } // If RE2 could not be created properly, returns an error code. // Else returns RE2::NoError (== 0). ErrorCode error_code() const { return error_code_; } // If RE2 could not be created properly, returns the offending // portion of the regexp. const string& error_arg() const { return error_arg_; } // Returns the program size, a very approximate measure of a regexp's "cost". // Larger numbers are more expensive than smaller numbers. int ProgramSize() const; // EXPERIMENTAL! SUBJECT TO CHANGE! // Outputs the program fanout as a histogram bucketed by powers of 2. // Returns the number of the largest non-empty bucket. int ProgramFanout(map* histogram) const; // Returns the underlying Regexp; not for general use. // Returns entire_regexp_ so that callers don't need // to know about prefix_ and prefix_foldcase_. re2::Regexp* Regexp() const { return entire_regexp_; } /***** The useful part: the matching interface *****/ // Matches "text" against "pattern". If pointer arguments are // supplied, copies matched sub-patterns into them. // // You can pass in a "const char*" or a "string" for "text". // You can pass in a "const char*" or a "string" or a "RE2" for "pattern". // // The provided pointer arguments can be pointers to any scalar numeric // type, or one of: // string (matched piece is copied to string) // StringPiece (StringPiece is mutated to point to matched piece) // T (where "bool T::ParseFrom(const char*, int)" exists) // (void*)NULL (the corresponding matched sub-pattern is not copied) // // Returns true iff all of the following conditions are satisfied: // a. "text" matches "pattern" exactly // b. The number of matched sub-patterns is >= number of supplied pointers // c. The "i"th argument has a suitable type for holding the // string captured as the "i"th sub-pattern. If you pass in // NULL for the "i"th argument, or pass fewer arguments than // number of sub-patterns, "i"th captured sub-pattern is // ignored. // // CAVEAT: An optional sub-pattern that does not exist in the // matched string is assigned the empty string. Therefore, the // following will return false (because the empty string is not a // valid number): // int number; // RE2::FullMatch("abc", "[a-z]+(\\d+)?", &number); static bool FullMatchN(const StringPiece& text, const RE2& re, const Arg* const args[], int argc); static const VariadicFunction2< bool, const StringPiece&, const RE2&, Arg, RE2::FullMatchN> FullMatch; // Exactly like FullMatch(), except that "pattern" is allowed to match // a substring of "text". static bool PartialMatchN(const StringPiece& text, const RE2& re, // 3..16 args const Arg* const args[], int argc); static const VariadicFunction2< bool, const StringPiece&, const RE2&, Arg, RE2::PartialMatchN> PartialMatch; // Like FullMatch() and PartialMatch(), except that pattern has to // match a prefix of "text", and "input" is advanced past the matched // text. Note: "input" is modified iff this routine returns true. static bool ConsumeN(StringPiece* input, const RE2& pattern, // 3..16 args const Arg* const args[], int argc); static const VariadicFunction2< bool, StringPiece*, const RE2&, Arg, RE2::ConsumeN> Consume; // Like Consume(..), but does not anchor the match at the beginning of the // string. That is, "pattern" need not start its match at the beginning of // "input". For example, "FindAndConsume(s, "(\\w+)", &word)" finds the next // word in "s" and stores it in "word". static bool FindAndConsumeN(StringPiece* input, const RE2& pattern, const Arg* const args[], int argc); static const VariadicFunction2< bool, StringPiece*, const RE2&, Arg, RE2::FindAndConsumeN> FindAndConsume; // Replace the first match of "pattern" in "str" with "rewrite". // Within "rewrite", backslash-escaped digits (\1 to \9) can be // used to insert text matching corresponding parenthesized group // from the pattern. \0 in "rewrite" refers to the entire matching // text. E.g., // // string s = "yabba dabba doo"; // CHECK(RE2::Replace(&s, "b+", "d")); // // will leave "s" containing "yada dabba doo" // // Returns true if the pattern matches and a replacement occurs, // false otherwise. static bool Replace(string *str, const RE2& pattern, const StringPiece& rewrite); // Like Replace(), except replaces successive non-overlapping occurrences // of the pattern in the string with the rewrite. E.g. // // string s = "yabba dabba doo"; // CHECK(RE2::GlobalReplace(&s, "b+", "d")); // // will leave "s" containing "yada dada doo" // Replacements are not subject to re-matching. // // Because GlobalReplace only replaces non-overlapping matches, // replacing "ana" within "banana" makes only one replacement, not two. // // Returns the number of replacements made. static int GlobalReplace(string *str, const RE2& pattern, const StringPiece& rewrite); // Like Replace, except that if the pattern matches, "rewrite" // is copied into "out" with substitutions. The non-matching // portions of "text" are ignored. // // Returns true iff a match occurred and the extraction happened // successfully; if no match occurs, the string is left unaffected. // // REQUIRES: "text" must not alias any part of "*out". static bool Extract(const StringPiece &text, const RE2& pattern, const StringPiece &rewrite, string *out); // Escapes all potentially meaningful regexp characters in // 'unquoted'. The returned string, used as a regular expression, // will exactly match the original string. For example, // 1.5-2.0? // may become: // 1\.5\-2\.0\? static string QuoteMeta(const StringPiece& unquoted); // Computes range for any strings matching regexp. The min and max can in // some cases be arbitrarily precise, so the caller gets to specify the // maximum desired length of string returned. // // Assuming PossibleMatchRange(&min, &max, N) returns successfully, any // string s that is an anchored match for this regexp satisfies // min <= s && s <= max. // // Note that PossibleMatchRange() will only consider the first copy of an // infinitely repeated element (i.e., any regexp element followed by a '*' or // '+' operator). Regexps with "{N}" constructions are not affected, as those // do not compile down to infinite repetitions. // // Returns true on success, false on error. bool PossibleMatchRange(string* min, string* max, int maxlen) const; // Generic matching interface // Type of match. enum Anchor { UNANCHORED, // No anchoring ANCHOR_START, // Anchor at start only ANCHOR_BOTH // Anchor at start and end }; // Return the number of capturing subpatterns, or -1 if the // regexp wasn't valid on construction. The overall match ($0) // does not count: if the regexp is "(a)(b)", returns 2. int NumberOfCapturingGroups() const; // Return a map from names to capturing indices. // The map records the index of the leftmost group // with the given name. // Only valid until the re is deleted. const map& NamedCapturingGroups() const; // Return a map from capturing indices to names. // The map has no entries for unnamed groups. // Only valid until the re is deleted. const map& CapturingGroupNames() const; // General matching routine. // Match against text starting at offset startpos // and stopping the search at offset endpos. // Returns true if match found, false if not. // On a successful match, fills in match[] (up to nmatch entries) // with information about submatches. // I.e. matching RE2("(foo)|(bar)baz") on "barbazbla" will return true, // setting match[0] = "barbaz", match[1] = NULL, match[2] = "bar", // match[3] = NULL, ..., up to match[nmatch-1] = NULL. // // Don't ask for more match information than you will use: // runs much faster with nmatch == 1 than nmatch > 1, and // runs even faster if nmatch == 0. // Doesn't make sense to use nmatch > 1 + NumberOfCapturingGroups(), // but will be handled correctly. // // Passing text == StringPiece(NULL, 0) will be handled like any other // empty string, but note that on return, it will not be possible to tell // whether submatch i matched the empty string or did not match: // either way, match[i] == NULL. bool Match(const StringPiece& text, int startpos, int endpos, Anchor anchor, StringPiece *match, int nmatch) const; // Check that the given rewrite string is suitable for use with this // regular expression. It checks that: // * The regular expression has enough parenthesized subexpressions // to satisfy all of the \N tokens in rewrite // * The rewrite string doesn't have any syntax errors. E.g., // '\' followed by anything other than a digit or '\'. // A true return value guarantees that Replace() and Extract() won't // fail because of a bad rewrite string. bool CheckRewriteString(const StringPiece& rewrite, string* error) const; // Returns the maximum submatch needed for the rewrite to be done by // Replace(). E.g. if rewrite == "foo \\2,\\1", returns 2. static int MaxSubmatch(const StringPiece& rewrite); // Append the "rewrite" string, with backslash subsitutions from "vec", // to string "out". // Returns true on success. This method can fail because of a malformed // rewrite string. CheckRewriteString guarantees that the rewrite will // be successful. bool Rewrite(string *out, const StringPiece &rewrite, const StringPiece* vec, int veclen) const; // Constructor options class Options { public: // The options are (defaults in parentheses): // // utf8 (true) text and pattern are UTF-8; otherwise Latin-1 // posix_syntax (false) restrict regexps to POSIX egrep syntax // longest_match (false) search for longest match, not first match // log_errors (true) log syntax and execution errors to ERROR // max_mem (see below) approx. max memory footprint of RE2 // literal (false) interpret string as literal, not regexp // never_nl (false) never match \n, even if it is in regexp // dot_nl (false) dot matches everything including new line // never_capture (false) parse all parens as non-capturing // case_sensitive (true) match is case-sensitive (regexp can override // with (?i) unless in posix_syntax mode) // // The following options are only consulted when posix_syntax == true. // (When posix_syntax == false these features are always enabled and // cannot be turned off.) // perl_classes (false) allow Perl's \d \s \w \D \S \W // word_boundary (false) allow Perl's \b \B (word boundary and not) // one_line (false) ^ and $ only match beginning and end of text // // The max_mem option controls how much memory can be used // to hold the compiled form of the regexp (the Prog) and // its cached DFA graphs. Code Search placed limits on the number // of Prog instructions and DFA states: 10,000 for both. // In RE2, those limits would translate to about 240 KB per Prog // and perhaps 2.5 MB per DFA (DFA state sizes vary by regexp; RE2 does a // better job of keeping them small than Code Search did). // Each RE2 has two Progs (one forward, one reverse), and each Prog // can have two DFAs (one first match, one longest match). // That makes 4 DFAs: // // forward, first-match - used for UNANCHORED or ANCHOR_LEFT searches // if opt.longest_match() == false // forward, longest-match - used for all ANCHOR_BOTH searches, // and the other two kinds if // opt.longest_match() == true // reverse, first-match - never used // reverse, longest-match - used as second phase for unanchored searches // // The RE2 memory budget is statically divided between the two // Progs and then the DFAs: two thirds to the forward Prog // and one third to the reverse Prog. The forward Prog gives half // of what it has left over to each of its DFAs. The reverse Prog // gives it all to its longest-match DFA. // // Once a DFA fills its budget, it flushes its cache and starts over. // If this happens too often, RE2 falls back on the NFA implementation. // For now, make the default budget something close to Code Search. static const int kDefaultMaxMem = 8<<20; enum Encoding { EncodingUTF8 = 1, EncodingLatin1 }; Options() : encoding_(EncodingUTF8), posix_syntax_(false), longest_match_(false), log_errors_(true), max_mem_(kDefaultMaxMem), literal_(false), never_nl_(false), dot_nl_(false), never_capture_(false), case_sensitive_(true), perl_classes_(false), word_boundary_(false), one_line_(false) { } /*implicit*/ Options(CannedOptions); Encoding encoding() const { return encoding_; } void set_encoding(Encoding encoding) { encoding_ = encoding; } // Legacy interface to encoding. // TODO(rsc): Remove once clients have been converted. bool utf8() const { return encoding_ == EncodingUTF8; } void set_utf8(bool b) { if (b) { encoding_ = EncodingUTF8; } else { encoding_ = EncodingLatin1; } } bool posix_syntax() const { return posix_syntax_; } void set_posix_syntax(bool b) { posix_syntax_ = b; } bool longest_match() const { return longest_match_; } void set_longest_match(bool b) { longest_match_ = b; } bool log_errors() const { return log_errors_; } void set_log_errors(bool b) { log_errors_ = b; } int64_t max_mem() const { return max_mem_; } void set_max_mem(int64_t m) { max_mem_ = m; } bool literal() const { return literal_; } void set_literal(bool b) { literal_ = b; } bool never_nl() const { return never_nl_; } void set_never_nl(bool b) { never_nl_ = b; } bool dot_nl() const { return dot_nl_; } void set_dot_nl(bool b) { dot_nl_ = b; } bool never_capture() const { return never_capture_; } void set_never_capture(bool b) { never_capture_ = b; } bool case_sensitive() const { return case_sensitive_; } void set_case_sensitive(bool b) { case_sensitive_ = b; } bool perl_classes() const { return perl_classes_; } void set_perl_classes(bool b) { perl_classes_ = b; } bool word_boundary() const { return word_boundary_; } void set_word_boundary(bool b) { word_boundary_ = b; } bool one_line() const { return one_line_; } void set_one_line(bool b) { one_line_ = b; } void Copy(const Options& src) { encoding_ = src.encoding_; posix_syntax_ = src.posix_syntax_; longest_match_ = src.longest_match_; log_errors_ = src.log_errors_; max_mem_ = src.max_mem_; literal_ = src.literal_; never_nl_ = src.never_nl_; dot_nl_ = src.dot_nl_; never_capture_ = src.never_capture_; case_sensitive_ = src.case_sensitive_; perl_classes_ = src.perl_classes_; word_boundary_ = src.word_boundary_; one_line_ = src.one_line_; } int ParseFlags() const; private: Encoding encoding_; bool posix_syntax_; bool longest_match_; bool log_errors_; int64_t max_mem_; bool literal_; bool never_nl_; bool dot_nl_; bool never_capture_; bool case_sensitive_; bool perl_classes_; bool word_boundary_; bool one_line_; //DISALLOW_COPY_AND_ASSIGN(Options); Options(const Options&); void operator=(const Options&); }; // Returns the options set in the constructor. const Options& options() const { return options_; }; // Argument converters; see below. static inline Arg CRadix(short* x); static inline Arg CRadix(unsigned short* x); static inline Arg CRadix(int* x); static inline Arg CRadix(unsigned int* x); static inline Arg CRadix(long* x); static inline Arg CRadix(unsigned long* x); #if RE2_HAVE_LONGLONG static inline Arg CRadix(long long* x); static inline Arg CRadix(unsigned long long* x); #endif static inline Arg Hex(short* x); static inline Arg Hex(unsigned short* x); static inline Arg Hex(int* x); static inline Arg Hex(unsigned int* x); static inline Arg Hex(long* x); static inline Arg Hex(unsigned long* x); #if RE2_HAVE_LONGLONG static inline Arg Hex(long long* x); static inline Arg Hex(unsigned long long* x); #endif static inline Arg Octal(short* x); static inline Arg Octal(unsigned short* x); static inline Arg Octal(int* x); static inline Arg Octal(unsigned int* x); static inline Arg Octal(long* x); static inline Arg Octal(unsigned long* x); #if RE2_HAVE_LONGLONG static inline Arg Octal(long long* x); static inline Arg Octal(unsigned long long* x); #endif private: void Init(const StringPiece& pattern, const Options& options); bool DoMatch(const StringPiece& text, Anchor anchor, int* consumed, const Arg* const args[], int n) const; re2::Prog* ReverseProg() const; mutable Mutex* mutex_; string pattern_; // string regular expression Options options_; // option flags string prefix_; // required prefix (before regexp_) bool prefix_foldcase_; // prefix is ASCII case-insensitive re2::Regexp* entire_regexp_; // parsed regular expression re2::Regexp* suffix_regexp_; // parsed regular expression, prefix removed re2::Prog* prog_; // compiled program for regexp mutable re2::Prog* rprog_; // reverse program for regexp bool is_one_pass_; // can use prog_->SearchOnePass? mutable const string* error_; // Error indicator // (or points to empty string) mutable ErrorCode error_code_; // Error code mutable string error_arg_; // Fragment of regexp showing error mutable int num_captures_; // Number of capturing groups // Map from capture names to indices mutable const map* named_groups_; // Map from capture indices to names mutable const map* group_names_; //DISALLOW_COPY_AND_ASSIGN(RE2); RE2(const RE2&); void operator=(const RE2&); }; /***** Implementation details *****/ // Hex/Octal/Binary? // Special class for parsing into objects that define a ParseFrom() method template class _RE2_MatchObject { public: static inline bool Parse(const char* str, int n, void* dest) { if (dest == NULL) return true; T* object = reinterpret_cast(dest); return object->ParseFrom(str, n); } }; class RE2::Arg { public: // Empty constructor so we can declare arrays of RE2::Arg Arg(); // Constructor specially designed for NULL arguments Arg(void*); typedef bool (*Parser)(const char* str, int n, void* dest); // Type-specific parsers #define MAKE_PARSER(type,name) \ Arg(type* p) : arg_(p), parser_(name) { } \ Arg(type* p, Parser parser) : arg_(p), parser_(parser) { } \ MAKE_PARSER(char, parse_char); MAKE_PARSER(signed char, parse_char); MAKE_PARSER(unsigned char, parse_uchar); MAKE_PARSER(short, parse_short); MAKE_PARSER(unsigned short, parse_ushort); MAKE_PARSER(int, parse_int); MAKE_PARSER(unsigned int, parse_uint); MAKE_PARSER(long, parse_long); MAKE_PARSER(unsigned long, parse_ulong); #if RE2_HAVE_LONGLONG MAKE_PARSER(long long, parse_longlong); MAKE_PARSER(unsigned long long, parse_ulonglong); #endif MAKE_PARSER(float, parse_float); MAKE_PARSER(double, parse_double); MAKE_PARSER(string, parse_string); MAKE_PARSER(StringPiece, parse_stringpiece); #undef MAKE_PARSER // Generic constructor templates template Arg(T* p) : arg_(p), parser_(_RE2_MatchObject::Parse) { } template Arg(T* p, Parser parser) : arg_(p), parser_(parser) { } // Parse the data bool Parse(const char* str, int n) const; private: void* arg_; Parser parser_; static bool parse_null (const char* str, int n, void* dest); static bool parse_char (const char* str, int n, void* dest); static bool parse_uchar (const char* str, int n, void* dest); static bool parse_float (const char* str, int n, void* dest); static bool parse_double (const char* str, int n, void* dest); static bool parse_string (const char* str, int n, void* dest); static bool parse_stringpiece (const char* str, int n, void* dest); #define DECLARE_INTEGER_PARSER(name) \ private: \ static bool parse_ ## name(const char* str, int n, void* dest); \ static bool parse_ ## name ## _radix( \ const char* str, int n, void* dest, int radix); \ public: \ static bool parse_ ## name ## _hex(const char* str, int n, void* dest); \ static bool parse_ ## name ## _octal(const char* str, int n, void* dest); \ static bool parse_ ## name ## _cradix(const char* str, int n, void* dest) DECLARE_INTEGER_PARSER(short); DECLARE_INTEGER_PARSER(ushort); DECLARE_INTEGER_PARSER(int); DECLARE_INTEGER_PARSER(uint); DECLARE_INTEGER_PARSER(long); DECLARE_INTEGER_PARSER(ulong); #if RE2_HAVE_LONGLONG DECLARE_INTEGER_PARSER(longlong); DECLARE_INTEGER_PARSER(ulonglong); #endif #undef DECLARE_INTEGER_PARSER }; inline RE2::Arg::Arg() : arg_(NULL), parser_(parse_null) { } inline RE2::Arg::Arg(void* p) : arg_(p), parser_(parse_null) { } inline bool RE2::Arg::Parse(const char* str, int n) const { return (*parser_)(str, n, arg_); } // This part of the parser, appropriate only for ints, deals with bases #define MAKE_INTEGER_PARSER(type, name) \ inline RE2::Arg RE2::Hex(type* ptr) { \ return RE2::Arg(ptr, RE2::Arg::parse_ ## name ## _hex); } \ inline RE2::Arg RE2::Octal(type* ptr) { \ return RE2::Arg(ptr, RE2::Arg::parse_ ## name ## _octal); } \ inline RE2::Arg RE2::CRadix(type* ptr) { \ return RE2::Arg(ptr, RE2::Arg::parse_ ## name ## _cradix); } MAKE_INTEGER_PARSER(short, short) MAKE_INTEGER_PARSER(unsigned short, ushort) MAKE_INTEGER_PARSER(int, int) MAKE_INTEGER_PARSER(unsigned int, uint) MAKE_INTEGER_PARSER(long, long) MAKE_INTEGER_PARSER(unsigned long, ulong) #if RE2_HAVE_LONGLONG MAKE_INTEGER_PARSER(long long, longlong) MAKE_INTEGER_PARSER(unsigned long long, ulonglong) #endif #undef MAKE_INTEGER_PARSER } // namespace re2 using re2::RE2; #endif /* RE2_RE2_H */ openalpr_2.2.4.orig/src/openalpr/support/re2/000077500000000000000000000000001266464252400212065ustar00rootroot00000000000000openalpr_2.2.4.orig/src/openalpr/support/re2/bitstate.cc000066400000000000000000000255121266464252400233410ustar00rootroot00000000000000// Copyright 2008 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Tested by search_test.cc, exhaustive_test.cc, tester.cc // Prog::SearchBitState is a regular expression search with submatch // tracking for small regular expressions and texts. Like // testing/backtrack.cc, it allocates a bit vector with (length of // text) * (length of prog) bits, to make sure it never explores the // same (character position, instruction) state multiple times. This // limits the search to run in time linear in the length of the text. // // Unlike testing/backtrack.cc, SearchBitState is not recursive // on the text. // // SearchBitState is a fast replacement for the NFA code on small // regexps and texts when SearchOnePass cannot be used. #include "re2/prog.h" #include "re2/regexp.h" namespace re2 { struct Job { int id; int arg; const char* p; }; class BitState { public: explicit BitState(Prog* prog); ~BitState(); // The usual Search prototype. // Can only call Search once per BitState. bool Search(const StringPiece& text, const StringPiece& context, bool anchored, bool longest, StringPiece* submatch, int nsubmatch); private: inline bool ShouldVisit(int id, const char* p); void Push(int id, const char* p, int arg); bool GrowStack(); bool TrySearch(int id, const char* p); // Search parameters Prog* prog_; // program being run StringPiece text_; // text being searched StringPiece context_; // greater context of text being searched bool anchored_; // whether search is anchored at text.begin() bool longest_; // whether search wants leftmost-longest match bool endmatch_; // whether match must end at text.end() StringPiece *submatch_; // submatches to fill in int nsubmatch_; // # of submatches to fill in // Search state const char** cap_; // capture registers int ncap_; static const int VisitedBits = 32; uint32 *visited_; // bitmap: (Inst*, char*) pairs already backtracked int nvisited_; // # of words in bitmap Job *job_; // stack of text positions to explore int njob_; int maxjob_; }; BitState::BitState(Prog* prog) : prog_(prog), anchored_(false), longest_(false), endmatch_(false), submatch_(NULL), nsubmatch_(0), cap_(NULL), ncap_(0), visited_(NULL), nvisited_(0), job_(NULL), njob_(0), maxjob_(0) { } BitState::~BitState() { delete[] visited_; delete[] job_; delete[] cap_; } // Should the search visit the pair ip, p? // If so, remember that it was visited so that the next time, // we don't repeat the visit. bool BitState::ShouldVisit(int id, const char* p) { uint n = id * (text_.size() + 1) + (p - text_.begin()); if (visited_[n/VisitedBits] & (1 << (n & (VisitedBits-1)))) return false; visited_[n/VisitedBits] |= 1 << (n & (VisitedBits-1)); return true; } // Grow the stack. bool BitState::GrowStack() { // VLOG(0) << "Reallocate."; maxjob_ *= 2; Job* newjob = new Job[maxjob_]; memmove(newjob, job_, njob_*sizeof job_[0]); delete[] job_; job_ = newjob; if (njob_ >= maxjob_) { LOG(DFATAL) << "Job stack overflow."; return false; } return true; } // Push the triple (id, p, arg) onto the stack, growing it if necessary. void BitState::Push(int id, const char* p, int arg) { if (njob_ >= maxjob_) { if (!GrowStack()) return; } int op = prog_->inst(id)->opcode(); if (op == kInstFail) return; // Only check ShouldVisit when arg == 0. // When arg > 0, we are continuing a previous visit. if (arg == 0 && !ShouldVisit(id, p)) return; Job* j = &job_[njob_++]; j->id = id; j->p = p; j->arg = arg; } // Try a search from instruction id0 in state p0. // Return whether it succeeded. bool BitState::TrySearch(int id0, const char* p0) { bool matched = false; const char* end = text_.end(); njob_ = 0; Push(id0, p0, 0); while (njob_ > 0) { // Pop job off stack. --njob_; int id = job_[njob_].id; const char* p = job_[njob_].p; int arg = job_[njob_].arg; // Optimization: rather than push and pop, // code that is going to Push and continue // the loop simply updates ip, p, and arg // and jumps to CheckAndLoop. We have to // do the ShouldVisit check that Push // would have, but we avoid the stack // manipulation. if (0) { CheckAndLoop: if (!ShouldVisit(id, p)) continue; } // Visit ip, p. // VLOG(0) << "Job: " << ip->id() << " " // << (p - text_.begin()) << " " << arg; Prog::Inst* ip = prog_->inst(id); switch (ip->opcode()) { case kInstFail: return false; default: LOG(DFATAL) << "Unexpected opcode: " << ip->opcode() << " arg " << arg; return false; case kInstAlt: // Cannot just // Push(ip->out1(), p, 0); // Push(ip->out(), p, 0); // If, during the processing of ip->out(), we encounter // ip->out1() via another path, we want to process it then. // Pushing it here will inhibit that. Instead, re-push // ip with arg==1 as a reminder to push ip->out1() later. switch (arg) { case 0: Push(id, p, 1); // come back when we're done id = ip->out(); goto CheckAndLoop; case 1: // Finished ip->out(); try ip->out1(). arg = 0; id = ip->out1(); goto CheckAndLoop; } LOG(DFATAL) << "Bad arg in kInstCapture: " << arg; continue; case kInstAltMatch: // One opcode is byte range; the other leads to match. if (ip->greedy(prog_)) { // out1 is the match Push(ip->out1(), p, 0); id = ip->out1(); p = end; goto CheckAndLoop; } // out is the match - non-greedy Push(ip->out(), end, 0); id = ip->out(); goto CheckAndLoop; case kInstByteRange: { int c = -1; if (p < end) c = *p & 0xFF; if (ip->Matches(c)) { id = ip->out(); p++; goto CheckAndLoop; } continue; } case kInstCapture: switch (arg) { case 0: if (0 <= ip->cap() && ip->cap() < ncap_) { // Capture p to register, but save old value. Push(id, cap_[ip->cap()], 1); // come back when we're done cap_[ip->cap()] = p; } // Continue on. id = ip->out(); goto CheckAndLoop; case 1: // Finished ip->out(); restore the old value. cap_[ip->cap()] = p; continue; } LOG(DFATAL) << "Bad arg in kInstCapture: " << arg; continue; case kInstEmptyWidth: if (ip->empty() & ~Prog::EmptyFlags(context_, p)) continue; id = ip->out(); goto CheckAndLoop; case kInstNop: id = ip->out(); goto CheckAndLoop; case kInstMatch: { if (endmatch_ && p != text_.end()) continue; // VLOG(0) << "Found match."; // We found a match. If the caller doesn't care // where the match is, no point going further. if (nsubmatch_ == 0) return true; // Record best match so far. // Only need to check end point, because this entire // call is only considering one start position. matched = true; cap_[1] = p; if (submatch_[0].data() == NULL || (longest_ && p > submatch_[0].end())) { for (int i = 0; i < nsubmatch_; i++) submatch_[i] = StringPiece(cap_[2*i], cap_[2*i+1] - cap_[2*i]); } // If going for first match, we're done. if (!longest_) return true; // If we used the entire text, no longer match is possible. if (p == text_.end()) return true; // Otherwise, continue on in hope of a longer match. continue; } } } return matched; } // Search text (within context) for prog_. bool BitState::Search(const StringPiece& text, const StringPiece& context, bool anchored, bool longest, StringPiece* submatch, int nsubmatch) { // Search parameters. text_ = text; context_ = context; if (context_.begin() == NULL) context_ = text; if (prog_->anchor_start() && context_.begin() != text.begin()) return false; if (prog_->anchor_end() && context_.end() != text.end()) return false; anchored_ = anchored || prog_->anchor_start(); longest_ = longest || prog_->anchor_end(); endmatch_ = prog_->anchor_end(); submatch_ = submatch; nsubmatch_ = nsubmatch; for (int i = 0; i < nsubmatch_; i++) submatch_[i] = NULL; // Allocate scratch space. nvisited_ = (prog_->size() * (text.size()+1) + VisitedBits-1) / VisitedBits; visited_ = new uint32[nvisited_]; memset(visited_, 0, nvisited_*sizeof visited_[0]); // VLOG(0) << "nvisited_ = " << nvisited_; ncap_ = 2*nsubmatch; if (ncap_ < 2) ncap_ = 2; cap_ = new const char*[ncap_]; memset(cap_, 0, ncap_*sizeof cap_[0]); maxjob_ = 256; job_ = new Job[maxjob_]; // Anchored search must start at text.begin(). if (anchored_) { cap_[0] = text.begin(); return TrySearch(prog_->start(), text.begin()); } // Unanchored search, starting from each possible text position. // Notice that we have to try the empty string at the end of // the text, so the loop condition is p <= text.end(), not p < text.end(). // This looks like it's quadratic in the size of the text, // but we are not clearing visited_ between calls to TrySearch, // so no work is duplicated and it ends up still being linear. for (const char* p = text.begin(); p <= text.end(); p++) { cap_[0] = p; if (TrySearch(prog_->start(), p)) // Match must be leftmost; done. return true; } return false; } // Bit-state search. bool Prog::SearchBitState(const StringPiece& text, const StringPiece& context, Anchor anchor, MatchKind kind, StringPiece* match, int nmatch) { // If full match, we ask for an anchored longest match // and then check that match[0] == text. // So make sure match[0] exists. StringPiece sp0; if (kind == kFullMatch) { anchor = kAnchored; if (nmatch < 1) { match = &sp0; nmatch = 1; } } // Run the search. BitState b(this); bool anchored = anchor == kAnchored; bool longest = kind != kFirstMatch; if (!b.Search(text, context, anchored, longest, match, nmatch)) return false; if (kind == kFullMatch && match[0].end() != text.end()) return false; return true; } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/compile.cc000066400000000000000000001004501266464252400231450ustar00rootroot00000000000000// Copyright 2007 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Compile regular expression to Prog. // // Prog and Inst are defined in prog.h. // This file's external interface is just Regexp::CompileToProg. // The Compiler class defined in this file is private. #include "re2/prog.h" #include "re2.h" #include "re2/regexp.h" #include "re2/walker-inl.h" namespace re2 { // List of pointers to Inst* that need to be filled in (patched). // Because the Inst* haven't been filled in yet, // we can use the Inst* word to hold the list's "next" pointer. // It's kind of sleazy, but it works well in practice. // See http://swtch.com/~rsc/regexp/regexp1.html for inspiration. // // Because the out and out1 fields in Inst are no longer pointers, // we can't use pointers directly here either. Instead, p refers // to inst_[p>>1].out (p&1 == 0) or inst_[p>>1].out1 (p&1 == 1). // p == 0 represents the NULL list. This is okay because instruction #0 // is always the fail instruction, which never appears on a list. struct PatchList { uint32 p; // Returns patch list containing just p. static PatchList Mk(uint32 p); // Patches all the entries on l to have value v. // Caller must not ever use patch list again. static void Patch(Prog::Inst *inst0, PatchList l, uint32 v); // Deref returns the next pointer pointed at by p. static PatchList Deref(Prog::Inst *inst0, PatchList l); // Appends two patch lists and returns result. static PatchList Append(Prog::Inst *inst0, PatchList l1, PatchList l2); }; static PatchList nullPatchList = { 0 }; // Returns patch list containing just p. PatchList PatchList::Mk(uint32 p) { PatchList l; l.p = p; return l; } // Returns the next pointer pointed at by l. PatchList PatchList::Deref(Prog::Inst* inst0, PatchList l) { Prog::Inst* ip = &inst0[l.p>>1]; if (l.p&1) l.p = ip->out1(); else l.p = ip->out(); return l; } // Patches all the entries on l to have value v. void PatchList::Patch(Prog::Inst *inst0, PatchList l, uint32 val) { while (l.p != 0) { Prog::Inst* ip = &inst0[l.p>>1]; if (l.p&1) { l.p = ip->out1(); ip->out1_ = val; } else { l.p = ip->out(); ip->set_out(val); } } } // Appends two patch lists and returns result. PatchList PatchList::Append(Prog::Inst* inst0, PatchList l1, PatchList l2) { if (l1.p == 0) return l2; if (l2.p == 0) return l1; PatchList l = l1; for (;;) { PatchList next = PatchList::Deref(inst0, l); if (next.p == 0) break; l = next; } Prog::Inst* ip = &inst0[l.p>>1]; if (l.p&1) ip->out1_ = l2.p; else ip->set_out(l2.p); return l1; } // Compiled program fragment. struct Frag { uint32 begin; PatchList end; Frag() : begin(0) { end.p = 0; } // needed so Frag can go in vector Frag(uint32 begin, PatchList end) : begin(begin), end(end) {} }; // Input encodings. enum Encoding { kEncodingUTF8 = 1, // UTF-8 (0-10FFFF) kEncodingLatin1, // Latin1 (0-FF) }; class Compiler : public Regexp::Walker { public: explicit Compiler(); ~Compiler(); // Compiles Regexp to a new Prog. // Caller is responsible for deleting Prog when finished with it. // If reversed is true, compiles for walking over the input // string backward (reverses all concatenations). static Prog *Compile(Regexp* re, bool reversed, int64 max_mem); // Compiles alternation of all the re to a new Prog. // Each re has a match with an id equal to its index in the vector. static Prog* CompileSet(const RE2::Options& options, RE2::Anchor anchor, Regexp* re); // Interface for Regexp::Walker, which helps traverse the Regexp. // The walk is purely post-recursive: given the machines for the // children, PostVisit combines them to create the machine for // the current node. The child_args are Frags. // The Compiler traverses the Regexp parse tree, visiting // each node in depth-first order. It invokes PreVisit before // visiting the node's children and PostVisit after visiting // the children. Frag PreVisit(Regexp* re, Frag parent_arg, bool* stop); Frag PostVisit(Regexp* re, Frag parent_arg, Frag pre_arg, Frag* child_args, int nchild_args); Frag ShortVisit(Regexp* re, Frag parent_arg); Frag Copy(Frag arg); // Given fragment a, returns a+ or a+?; a* or a*?; a? or a?? Frag Plus(Frag a, bool nongreedy); Frag Star(Frag a, bool nongreedy); Frag Quest(Frag a, bool nongreedy); // Given fragment a, returns (a) capturing as \n. Frag Capture(Frag a, int n); // Given fragments a and b, returns ab; a|b Frag Cat(Frag a, Frag b); Frag Alt(Frag a, Frag b); // Returns a fragment that can't match anything. Frag NoMatch(); // Returns a fragment that matches the empty string. Frag Match(int32 id); // Returns a no-op fragment. Frag Nop(); // Returns a fragment matching the byte range lo-hi. Frag ByteRange(int lo, int hi, bool foldcase); // Returns a fragment matching an empty-width special op. Frag EmptyWidth(EmptyOp op); // Adds n instructions to the program. // Returns the index of the first one. // Returns -1 if no more instructions are available. int AllocInst(int n); // Deletes unused instructions. void Trim(); // Rune range compiler. // Begins a new alternation. void BeginRange(); // Adds a fragment matching the rune range lo-hi. void AddRuneRange(Rune lo, Rune hi, bool foldcase); void AddRuneRangeLatin1(Rune lo, Rune hi, bool foldcase); void AddRuneRangeUTF8(Rune lo, Rune hi, bool foldcase); void Add_80_10ffff(); // New suffix that matches the byte range lo-hi, then goes to next. int RuneByteSuffix(uint8 lo, uint8 hi, bool foldcase, int next); int UncachedRuneByteSuffix(uint8 lo, uint8 hi, bool foldcase, int next); // Adds a suffix to alternation. void AddSuffix(int id); // Returns the alternation of all the added suffixes. Frag EndRange(); // Single rune. Frag Literal(Rune r, bool foldcase); void Setup(Regexp::ParseFlags, int64, RE2::Anchor); Prog* Finish(); // Returns .* where dot = any byte Frag DotStar(); private: Prog* prog_; // Program being built. bool failed_; // Did we give up compiling? Encoding encoding_; // Input encoding bool reversed_; // Should program run backward over text? int max_inst_; // Maximum number of instructions. Prog::Inst* inst_; // Pointer to first instruction. int inst_len_; // Number of instructions used. int inst_cap_; // Number of instructions allocated. int64 max_mem_; // Total memory budget. map rune_cache_; Frag rune_range_; RE2::Anchor anchor_; // anchor mode for RE2::Set DISALLOW_COPY_AND_ASSIGN(Compiler); }; Compiler::Compiler() { prog_ = new Prog(); failed_ = false; encoding_ = kEncodingUTF8; reversed_ = false; inst_ = NULL; inst_len_ = 0; inst_cap_ = 0; max_inst_ = 1; // make AllocInst for fail instruction okay max_mem_ = 0; int fail = AllocInst(1); inst_[fail].InitFail(); max_inst_ = 0; // Caller must change } Compiler::~Compiler() { delete prog_; delete[] inst_; } int Compiler::AllocInst(int n) { if (failed_ || inst_len_ + n > max_inst_) { failed_ = true; return -1; } if (inst_len_ + n > inst_cap_) { if (inst_cap_ == 0) inst_cap_ = 8; while (inst_len_ + n > inst_cap_) inst_cap_ *= 2; Prog::Inst* ip = new Prog::Inst[inst_cap_]; memmove(ip, inst_, inst_len_ * sizeof ip[0]); memset(ip + inst_len_, 0, (inst_cap_ - inst_len_) * sizeof ip[0]); delete[] inst_; inst_ = ip; } int id = inst_len_; inst_len_ += n; return id; } void Compiler::Trim() { if (inst_len_ < inst_cap_) { Prog::Inst* ip = new Prog::Inst[inst_len_]; memmove(ip, inst_, inst_len_ * sizeof ip[0]); delete[] inst_; inst_ = ip; inst_cap_ = inst_len_; } } // These routines are somewhat hard to visualize in text -- // see http://swtch.com/~rsc/regexp/regexp1.html for // pictures explaining what is going on here. // Returns an unmatchable fragment. Frag Compiler::NoMatch() { return Frag(0, nullPatchList); } // Is a an unmatchable fragment? static bool IsNoMatch(Frag a) { return a.begin == 0; } // Given fragments a and b, returns fragment for ab. Frag Compiler::Cat(Frag a, Frag b) { if (IsNoMatch(a) || IsNoMatch(b)) return NoMatch(); // Elide no-op. Prog::Inst* begin = &inst_[a.begin]; if (begin->opcode() == kInstNop && a.end.p == (a.begin << 1) && begin->out() == 0) { PatchList::Patch(inst_, a.end, b.begin); // in case refs to a somewhere return b; } // To run backward over string, reverse all concatenations. if (reversed_) { PatchList::Patch(inst_, b.end, a.begin); return Frag(b.begin, a.end); } PatchList::Patch(inst_, a.end, b.begin); return Frag(a.begin, b.end); } // Given fragments for a and b, returns fragment for a|b. Frag Compiler::Alt(Frag a, Frag b) { // Special case for convenience in loops. if (IsNoMatch(a)) return b; if (IsNoMatch(b)) return a; int id = AllocInst(1); if (id < 0) return NoMatch(); inst_[id].InitAlt(a.begin, b.begin); return Frag(id, PatchList::Append(inst_, a.end, b.end)); } // When capturing submatches in like-Perl mode, a kOpAlt Inst // treats out_ as the first choice, out1_ as the second. // // For *, +, and ?, if out_ causes another repetition, // then the operator is greedy. If out1_ is the repetition // (and out_ moves forward), then the operator is non-greedy. // Given a fragment a, returns a fragment for a* or a*? (if nongreedy) Frag Compiler::Star(Frag a, bool nongreedy) { int id = AllocInst(1); if (id < 0) return NoMatch(); inst_[id].InitAlt(0, 0); PatchList::Patch(inst_, a.end, id); if (nongreedy) { inst_[id].out1_ = a.begin; return Frag(id, PatchList::Mk(id << 1)); } else { inst_[id].set_out(a.begin); return Frag(id, PatchList::Mk((id << 1) | 1)); } } // Given a fragment for a, returns a fragment for a+ or a+? (if nongreedy) Frag Compiler::Plus(Frag a, bool nongreedy) { // a+ is just a* with a different entry point. Frag f = Star(a, nongreedy); return Frag(a.begin, f.end); } // Given a fragment for a, returns a fragment for a? or a?? (if nongreedy) Frag Compiler::Quest(Frag a, bool nongreedy) { if (IsNoMatch(a)) return Nop(); int id = AllocInst(1); if (id < 0) return NoMatch(); PatchList pl; if (nongreedy) { inst_[id].InitAlt(0, a.begin); pl = PatchList::Mk(id << 1); } else { inst_[id].InitAlt(a.begin, 0); pl = PatchList::Mk((id << 1) | 1); } return Frag(id, PatchList::Append(inst_, pl, a.end)); } // Returns a fragment for the byte range lo-hi. Frag Compiler::ByteRange(int lo, int hi, bool foldcase) { int id = AllocInst(1); if (id < 0) return NoMatch(); inst_[id].InitByteRange(lo, hi, foldcase, 0); prog_->byte_inst_count_++; prog_->MarkByteRange(lo, hi); if (foldcase && lo <= 'z' && hi >= 'a') { if (lo < 'a') lo = 'a'; if (hi > 'z') hi = 'z'; if (lo <= hi) prog_->MarkByteRange(lo + 'A' - 'a', hi + 'A' - 'a'); } return Frag(id, PatchList::Mk(id << 1)); } // Returns a no-op fragment. Sometimes unavoidable. Frag Compiler::Nop() { int id = AllocInst(1); if (id < 0) return NoMatch(); inst_[id].InitNop(0); return Frag(id, PatchList::Mk(id << 1)); } // Returns a fragment that signals a match. Frag Compiler::Match(int32 match_id) { int id = AllocInst(1); if (id < 0) return NoMatch(); inst_[id].InitMatch(match_id); return Frag(id, nullPatchList); } // Returns a fragment matching a particular empty-width op (like ^ or $) Frag Compiler::EmptyWidth(EmptyOp empty) { int id = AllocInst(1); if (id < 0) return NoMatch(); inst_[id].InitEmptyWidth(empty, 0); if (empty & (kEmptyBeginLine|kEmptyEndLine)) prog_->MarkByteRange('\n', '\n'); if (empty & (kEmptyWordBoundary|kEmptyNonWordBoundary)) { int j; for (int i = 0; i < 256; i = j) { for (j = i+1; j < 256 && Prog::IsWordChar(i) == Prog::IsWordChar(j); j++) ; prog_->MarkByteRange(i, j-1); } } return Frag(id, PatchList::Mk(id << 1)); } // Given a fragment a, returns a fragment with capturing parens around a. Frag Compiler::Capture(Frag a, int n) { if (IsNoMatch(a)) return NoMatch(); int id = AllocInst(2); if (id < 0) return NoMatch(); inst_[id].InitCapture(2*n, a.begin); inst_[id+1].InitCapture(2*n+1, 0); PatchList::Patch(inst_, a.end, id+1); return Frag(id, PatchList::Mk((id+1) << 1)); } // A Rune is a name for a Unicode code point. // Returns maximum rune encoded by UTF-8 sequence of length len. static int MaxRune(int len) { int b; // number of Rune bits in len-byte UTF-8 sequence (len < UTFmax) if (len == 1) b = 7; else b = 8-(len+1) + 6*(len-1); return (1<::iterator it = rune_cache_.find(key); if (it != rune_cache_.end()) return it->second; int id = UncachedRuneByteSuffix(lo, hi, foldcase, next); rune_cache_[key] = id; return id; } void Compiler::AddSuffix(int id) { if (rune_range_.begin == 0) { rune_range_.begin = id; return; } int alt = AllocInst(1); if (alt < 0) { rune_range_.begin = 0; return; } inst_[alt].InitAlt(rune_range_.begin, id); rune_range_.begin = alt; } Frag Compiler::EndRange() { return rune_range_; } // Converts rune range lo-hi into a fragment that recognizes // the bytes that would make up those runes in the current // encoding (Latin 1 or UTF-8). // This lets the machine work byte-by-byte even when // using multibyte encodings. void Compiler::AddRuneRange(Rune lo, Rune hi, bool foldcase) { switch (encoding_) { default: case kEncodingUTF8: AddRuneRangeUTF8(lo, hi, foldcase); break; case kEncodingLatin1: AddRuneRangeLatin1(lo, hi, foldcase); break; } } void Compiler::AddRuneRangeLatin1(Rune lo, Rune hi, bool foldcase) { // Latin1 is easy: runes *are* bytes. if (lo > hi || lo > 0xFF) return; if (hi > 0xFF) hi = 0xFF; AddSuffix(RuneByteSuffix(lo, hi, foldcase, 0)); } // Table describing how to make a UTF-8 matching machine // for the rune range 80-10FFFF (Runeself-Runemax). // This range happens frequently enough (for example /./ and /[^a-z]/) // and the rune_cache_ map is slow enough that this is worth // special handling. Makes compilation of a small expression // with a dot in it about 10% faster. // The * in the comments below mark whole sequences. static struct ByteRangeProg { int next; int lo; int hi; } prog_80_10ffff[] = { // Two-byte { -1, 0x80, 0xBF, }, // 0: 80-BF { 0, 0xC2, 0xDF, }, // 1: C2-DF 80-BF* // Three-byte { 0, 0xA0, 0xBF, }, // 2: A0-BF 80-BF { 2, 0xE0, 0xE0, }, // 3: E0 A0-BF 80-BF* { 0, 0x80, 0xBF, }, // 4: 80-BF 80-BF { 4, 0xE1, 0xEF, }, // 5: E1-EF 80-BF 80-BF* // Four-byte { 4, 0x90, 0xBF, }, // 6: 90-BF 80-BF 80-BF { 6, 0xF0, 0xF0, }, // 7: F0 90-BF 80-BF 80-BF* { 4, 0x80, 0xBF, }, // 8: 80-BF 80-BF 80-BF { 8, 0xF1, 0xF3, }, // 9: F1-F3 80-BF 80-BF 80-BF* { 4, 0x80, 0x8F, }, // 10: 80-8F 80-BF 80-BF { 10, 0xF4, 0xF4, }, // 11: F4 80-8F 80-BF 80-BF* }; void Compiler::Add_80_10ffff() { int inst[arraysize(prog_80_10ffff)] = { 0 }; // does not need to be initialized; silences gcc warning for (int i = 0; i < arraysize(prog_80_10ffff); i++) { const ByteRangeProg& p = prog_80_10ffff[i]; int next = 0; if (p.next >= 0) next = inst[p.next]; inst[i] = UncachedRuneByteSuffix(p.lo, p.hi, false, next); if ((p.lo & 0xC0) != 0x80) AddSuffix(inst[i]); } } void Compiler::AddRuneRangeUTF8(Rune lo, Rune hi, bool foldcase) { if (lo > hi) return; // Pick off 80-10FFFF as a common special case // that can bypass the slow rune_cache_. if (lo == 0x80 && hi == 0x10ffff && !reversed_) { Add_80_10ffff(); return; } // Split range into same-length sized ranges. for (int i = 1; i < UTFmax; i++) { Rune max = MaxRune(i); if (lo <= max && max < hi) { AddRuneRangeUTF8(lo, max, foldcase); AddRuneRangeUTF8(max+1, hi, foldcase); return; } } // ASCII range is always a special case. if (hi < Runeself) { AddSuffix(RuneByteSuffix(lo, hi, foldcase, 0)); return; } // Split range into sections that agree on leading bytes. for (int i = 1; i < UTFmax; i++) { uint m = (1<<(6*i)) - 1; // last i bytes of a UTF-8 sequence if ((lo & ~m) != (hi & ~m)) { if ((lo & m) != 0) { AddRuneRangeUTF8(lo, lo|m, foldcase); AddRuneRangeUTF8((lo|m)+1, hi, foldcase); return; } if ((hi & m) != m) { AddRuneRangeUTF8(lo, (hi&~m)-1, foldcase); AddRuneRangeUTF8(hi&~m, hi, foldcase); return; } } } // Finally. Generate byte matching equivalent for lo-hi. uint8 ulo[UTFmax], uhi[UTFmax]; int n = runetochar(reinterpret_cast(ulo), &lo); int m = runetochar(reinterpret_cast(uhi), &hi); (void)m; // USED(m) DCHECK_EQ(n, m); int id = 0; if (reversed_) { for (int i = 0; i < n; i++) id = RuneByteSuffix(ulo[i], uhi[i], false, id); } else { for (int i = n-1; i >= 0; i--) id = RuneByteSuffix(ulo[i], uhi[i], false, id); } AddSuffix(id); } // Should not be called. Frag Compiler::Copy(Frag arg) { // We're using WalkExponential; there should be no copying. LOG(DFATAL) << "Compiler::Copy called!"; failed_ = true; return NoMatch(); } // Visits a node quickly; called once WalkExponential has // decided to cut this walk short. Frag Compiler::ShortVisit(Regexp* re, Frag) { failed_ = true; return NoMatch(); } // Called before traversing a node's children during the walk. Frag Compiler::PreVisit(Regexp* re, Frag, bool* stop) { // Cut off walk if we've already failed. if (failed_) *stop = true; return Frag(); // not used by caller } Frag Compiler::Literal(Rune r, bool foldcase) { switch (encoding_) { default: return Frag(); case kEncodingLatin1: return ByteRange(r, r, foldcase); case kEncodingUTF8: { if (r < Runeself) // Make common case fast. return ByteRange(r, r, foldcase); uint8 buf[UTFmax]; int n = runetochar(reinterpret_cast(buf), &r); Frag f = ByteRange((uint8)buf[0], buf[0], false); for (int i = 1; i < n; i++) f = Cat(f, ByteRange((uint8)buf[i], buf[i], false)); return f; } } } // Called after traversing the node's children during the walk. // Given their frags, build and return the frag for this re. Frag Compiler::PostVisit(Regexp* re, Frag, Frag, Frag* child_frags, int nchild_frags) { // If a child failed, don't bother going forward, especially // since the child_frags might contain Frags with NULLs in them. if (failed_) return NoMatch(); // Given the child fragments, return the fragment for this node. switch (re->op()) { case kRegexpRepeat: // Should not see; code at bottom of function will print error break; case kRegexpNoMatch: return NoMatch(); case kRegexpEmptyMatch: return Nop(); case kRegexpHaveMatch: { Frag f = Match(re->match_id()); // Remember unanchored match to end of string. if (anchor_ != RE2::ANCHOR_BOTH) f = Cat(DotStar(), Cat(EmptyWidth(kEmptyEndText), f)); return f; } case kRegexpConcat: { Frag f = child_frags[0]; for (int i = 1; i < nchild_frags; i++) f = Cat(f, child_frags[i]); return f; } case kRegexpAlternate: { Frag f = child_frags[0]; for (int i = 1; i < nchild_frags; i++) f = Alt(f, child_frags[i]); return f; } case kRegexpStar: return Star(child_frags[0], re->parse_flags()&Regexp::NonGreedy); case kRegexpPlus: return Plus(child_frags[0], re->parse_flags()&Regexp::NonGreedy); case kRegexpQuest: return Quest(child_frags[0], re->parse_flags()&Regexp::NonGreedy); case kRegexpLiteral: return Literal(re->rune(), re->parse_flags()&Regexp::FoldCase); case kRegexpLiteralString: { // Concatenation of literals. if (re->nrunes() == 0) return Nop(); Frag f; for (int i = 0; i < re->nrunes(); i++) { Frag f1 = Literal(re->runes()[i], re->parse_flags()&Regexp::FoldCase); if (i == 0) f = f1; else f = Cat(f, f1); } return f; } case kRegexpAnyChar: BeginRange(); AddRuneRange(0, Runemax, false); return EndRange(); case kRegexpAnyByte: return ByteRange(0x00, 0xFF, false); case kRegexpCharClass: { CharClass* cc = re->cc(); if (cc->empty()) { // This can't happen. LOG(DFATAL) << "No ranges in char class"; failed_ = true; return NoMatch(); } // ASCII case-folding optimization: if the char class // behaves the same on A-Z as it does on a-z, // discard any ranges wholly contained in A-Z // and mark the other ranges as foldascii. // This reduces the size of a program for // (?i)abc from 3 insts per letter to 1 per letter. bool foldascii = cc->FoldsASCII(); // Character class is just a big OR of the different // character ranges in the class. BeginRange(); for (CharClass::iterator i = cc->begin(); i != cc->end(); ++i) { // ASCII case-folding optimization (see above). if (foldascii && 'A' <= i->lo && i->hi <= 'Z') continue; // If this range contains all of A-Za-z or none of it, // the fold flag is unnecessary; don't bother. bool fold = foldascii; if ((i->lo <= 'A' && 'z' <= i->hi) || i->hi < 'A' || 'z' < i->lo || ('Z' < i->lo && i->hi < 'a')) fold = false; AddRuneRange(i->lo, i->hi, fold); } return EndRange(); } case kRegexpCapture: // If this is a non-capturing parenthesis -- (?:foo) -- // just use the inner expression. if (re->cap() < 0) return child_frags[0]; return Capture(child_frags[0], re->cap()); case kRegexpBeginLine: return EmptyWidth(reversed_ ? kEmptyEndLine : kEmptyBeginLine); case kRegexpEndLine: return EmptyWidth(reversed_ ? kEmptyBeginLine : kEmptyEndLine); case kRegexpBeginText: return EmptyWidth(reversed_ ? kEmptyEndText : kEmptyBeginText); case kRegexpEndText: return EmptyWidth(reversed_ ? kEmptyBeginText : kEmptyEndText); case kRegexpWordBoundary: return EmptyWidth(kEmptyWordBoundary); case kRegexpNoWordBoundary: return EmptyWidth(kEmptyNonWordBoundary); } LOG(DFATAL) << "Missing case in Compiler: " << re->op(); failed_ = true; return NoMatch(); } // Is this regexp required to start at the beginning of the text? // Only approximate; can return false for complicated regexps like (\Aa|\Ab), // but handles (\A(a|b)). Could use the Walker to write a more exact one. static bool IsAnchorStart(Regexp** pre, int depth) { Regexp* re = *pre; Regexp* sub; // The depth limit makes sure that we don't overflow // the stack on a deeply nested regexp. As the comment // above says, IsAnchorStart is conservative, so returning // a false negative is okay. The exact limit is somewhat arbitrary. if (re == NULL || depth >= 4) return false; switch (re->op()) { default: break; case kRegexpConcat: if (re->nsub() > 0) { sub = re->sub()[0]->Incref(); if (IsAnchorStart(&sub, depth+1)) { Regexp** subcopy = new Regexp*[re->nsub()]; subcopy[0] = sub; // already have reference for (int i = 1; i < re->nsub(); i++) subcopy[i] = re->sub()[i]->Incref(); *pre = Regexp::Concat(subcopy, re->nsub(), re->parse_flags()); delete[] subcopy; re->Decref(); return true; } sub->Decref(); } break; case kRegexpCapture: sub = re->sub()[0]->Incref(); if (IsAnchorStart(&sub, depth+1)) { *pre = Regexp::Capture(sub, re->parse_flags(), re->cap()); re->Decref(); return true; } sub->Decref(); break; case kRegexpBeginText: *pre = Regexp::LiteralString(NULL, 0, re->parse_flags()); re->Decref(); return true; } return false; } // Is this regexp required to start at the end of the text? // Only approximate; can return false for complicated regexps like (a\z|b\z), // but handles ((a|b)\z). Could use the Walker to write a more exact one. static bool IsAnchorEnd(Regexp** pre, int depth) { Regexp* re = *pre; Regexp* sub; // The depth limit makes sure that we don't overflow // the stack on a deeply nested regexp. As the comment // above says, IsAnchorEnd is conservative, so returning // a false negative is okay. The exact limit is somewhat arbitrary. if (re == NULL || depth >= 4) return false; switch (re->op()) { default: break; case kRegexpConcat: if (re->nsub() > 0) { sub = re->sub()[re->nsub() - 1]->Incref(); if (IsAnchorEnd(&sub, depth+1)) { Regexp** subcopy = new Regexp*[re->nsub()]; subcopy[re->nsub() - 1] = sub; // already have reference for (int i = 0; i < re->nsub() - 1; i++) subcopy[i] = re->sub()[i]->Incref(); *pre = Regexp::Concat(subcopy, re->nsub(), re->parse_flags()); delete[] subcopy; re->Decref(); return true; } sub->Decref(); } break; case kRegexpCapture: sub = re->sub()[0]->Incref(); if (IsAnchorEnd(&sub, depth+1)) { *pre = Regexp::Capture(sub, re->parse_flags(), re->cap()); re->Decref(); return true; } sub->Decref(); break; case kRegexpEndText: *pre = Regexp::LiteralString(NULL, 0, re->parse_flags()); re->Decref(); return true; } return false; } void Compiler::Setup(Regexp::ParseFlags flags, int64 max_mem, RE2::Anchor anchor) { prog_->set_flags(flags); if (flags & Regexp::Latin1) encoding_ = kEncodingLatin1; max_mem_ = max_mem; if (max_mem <= 0) { max_inst_ = 100000; // more than enough } else if (max_mem <= static_cast(sizeof(Prog))) { // No room for anything. max_inst_ = 0; } else { int64 m = (max_mem - sizeof(Prog)) / sizeof(Prog::Inst); // Limit instruction count so that inst->id() fits nicely in an int. // SparseArray also assumes that the indices (inst->id()) are ints. // The call to WalkExponential uses 2*max_inst_ below, // and other places in the code use 2 or 3 * prog->size(). // Limiting to 2^24 should avoid overflow in those places. // (The point of allowing more than 32 bits of memory is to // have plenty of room for the DFA states, not to use it up // on the program.) if (m >= 1<<24) m = 1<<24; // Inst imposes its own limit (currently bigger than 2^24 but be safe). if (m > Prog::Inst::kMaxInst) m = Prog::Inst::kMaxInst; max_inst_ = m; } anchor_ = anchor; } // Compiles re, returning program. // Caller is responsible for deleting prog_. // If reversed is true, compiles a program that expects // to run over the input string backward (reverses all concatenations). // The reversed flag is also recorded in the returned program. Prog* Compiler::Compile(Regexp* re, bool reversed, int64 max_mem) { Compiler c; c.Setup(re->parse_flags(), max_mem, RE2::ANCHOR_BOTH /* unused */); c.reversed_ = reversed; // Simplify to remove things like counted repetitions // and character classes like \d. Regexp* sre = re->Simplify(); if (sre == NULL) return NULL; // Record whether prog is anchored, removing the anchors. // (They get in the way of other optimizations.) bool is_anchor_start = IsAnchorStart(&sre, 0); bool is_anchor_end = IsAnchorEnd(&sre, 0); // Generate fragment for entire regexp. Frag f = c.WalkExponential(sre, Frag(), 2*c.max_inst_); sre->Decref(); if (c.failed_) return NULL; // Success! Finish by putting Match node at end, and record start. // Turn off c.reversed_ (if it is set) to force the remaining concatenations // to behave normally. c.reversed_ = false; Frag all = c.Cat(f, c.Match(0)); c.prog_->set_start(all.begin); if (reversed) { c.prog_->set_anchor_start(is_anchor_end); c.prog_->set_anchor_end(is_anchor_start); } else { c.prog_->set_anchor_start(is_anchor_start); c.prog_->set_anchor_end(is_anchor_end); } // Also create unanchored version, which starts with a .*? loop. if (c.prog_->anchor_start()) { c.prog_->set_start_unanchored(c.prog_->start()); } else { Frag unanchored = c.Cat(c.DotStar(), all); c.prog_->set_start_unanchored(unanchored.begin); } c.prog_->set_reversed(reversed); // Hand ownership of prog_ to caller. return c.Finish(); } Prog* Compiler::Finish() { if (failed_) return NULL; if (prog_->start() == 0 && prog_->start_unanchored() == 0) { // No possible matches; keep Fail instruction only. inst_len_ = 1; } // Trim instruction to minimum array and transfer to Prog. Trim(); prog_->inst_ = inst_; prog_->size_ = inst_len_; inst_ = NULL; // Compute byte map. prog_->ComputeByteMap(); prog_->Optimize(); // Record remaining memory for DFA. if (max_mem_ <= 0) { prog_->set_dfa_mem(1<<20); } else { int64 m = max_mem_ - sizeof(Prog) - inst_len_*sizeof(Prog::Inst); if (m < 0) m = 0; prog_->set_dfa_mem(m); } Prog* p = prog_; prog_ = NULL; return p; } // Converts Regexp to Prog. Prog* Regexp::CompileToProg(int64 max_mem) { return Compiler::Compile(this, false, max_mem); } Prog* Regexp::CompileToReverseProg(int64 max_mem) { return Compiler::Compile(this, true, max_mem); } Frag Compiler::DotStar() { return Star(ByteRange(0x00, 0xff, false), true); } // Compiles RE set to Prog. Prog* Compiler::CompileSet(const RE2::Options& options, RE2::Anchor anchor, Regexp* re) { Compiler c; Regexp::ParseFlags pf = static_cast(options.ParseFlags()); c.Setup(pf, options.max_mem(), anchor); // Compile alternation of fragments. Frag all = c.WalkExponential(re, Frag(), 2*c.max_inst_); re->Decref(); if (c.failed_) return NULL; if (anchor == RE2::UNANCHORED) { // The trailing .* was added while handling kRegexpHaveMatch. // We just have to add the leading one. all = c.Cat(c.DotStar(), all); } c.prog_->set_start(all.begin); c.prog_->set_start_unanchored(all.begin); c.prog_->set_anchor_start(true); c.prog_->set_anchor_end(true); Prog* prog = c.Finish(); if (prog == NULL) return NULL; // Make sure DFA has enough memory to operate, // since we're not going to fall back to the NFA. bool failed; StringPiece sp = "hello, world"; prog->SearchDFA(sp, sp, Prog::kAnchored, Prog::kManyMatch, NULL, &failed, NULL); if (failed) { delete prog; return NULL; } return prog; } Prog* Prog::CompileSet(const RE2::Options& options, RE2::Anchor anchor, Regexp* re) { return Compiler::CompileSet(options, anchor, re); } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/dfa.cc000066400000000000000000002171521266464252400222570ustar00rootroot00000000000000// Copyright 2008 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // A DFA (deterministic finite automaton)-based regular expression search. // // The DFA search has two main parts: the construction of the automaton, // which is represented by a graph of State structures, and the execution // of the automaton over a given input string. // // The basic idea is that the State graph is constructed so that the // execution can simply start with a state s, and then for each byte c in // the input string, execute "s = s->next[c]", checking at each point whether // the current s represents a matching state. // // The simple explanation just given does convey the essence of this code, // but it omits the details of how the State graph gets constructed as well // as some performance-driven optimizations to the execution of the automaton. // All these details are explained in the comments for the code following // the definition of class DFA. // // See http://swtch.com/~rsc/regexp/ for a very bare-bones equivalent. #include "re2/prog.h" #include "re2/stringpiece.h" #include "util/atomicops.h" #include "util/flags.h" #include "util/sparse_set.h" DEFINE_bool(re2_dfa_bail_when_slow, true, "Whether the RE2 DFA should bail out early " "if the NFA would be faster (for testing)."); namespace re2 { #if !defined(__linux__) /* only Linux seems to have memrchr */ static void* memrchr(const void* s, int c, size_t n) { const unsigned char* p = (const unsigned char*)s; for (p += n; n > 0; n--) if (*--p == c) return (void*)p; return NULL; } #endif // Changing this to true compiles in prints that trace execution of the DFA. // Generates a lot of output -- only useful for debugging. static const bool DebugDFA = false; // A DFA implementation of a regular expression program. // Since this is entirely a forward declaration mandated by C++, // some of the comments here are better understood after reading // the comments in the sections that follow the DFA definition. class DFA { public: DFA(Prog* prog, Prog::MatchKind kind, int64 max_mem); ~DFA(); bool ok() const { return !init_failed_; } Prog::MatchKind kind() { return kind_; } // Searches for the regular expression in text, which is considered // as a subsection of context for the purposes of interpreting flags // like ^ and $ and \A and \z. // Returns whether a match was found. // If a match is found, sets *ep to the end point of the best match in text. // If "anchored", the match must begin at the start of text. // If "want_earliest_match", the match that ends first is used, not // necessarily the best one. // If "run_forward" is true, the DFA runs from text.begin() to text.end(). // If it is false, the DFA runs from text.end() to text.begin(), // returning the leftmost end of the match instead of the rightmost one. // If the DFA cannot complete the search (for example, if it is out of // memory), it sets *failed and returns false. bool Search(const StringPiece& text, const StringPiece& context, bool anchored, bool want_earliest_match, bool run_forward, bool* failed, const char** ep, vector* matches); // Builds out all states for the entire DFA. FOR TESTING ONLY // Returns number of states. int BuildAllStates(); // Computes min and max for matching strings. Won't return strings // bigger than maxlen. bool PossibleMatchRange(string* min, string* max, int maxlen); // These data structures are logically private, but C++ makes it too // difficult to mark them as such. class Workq; class RWLocker; class StateSaver; // A single DFA state. The DFA is represented as a graph of these // States, linked by the next_ pointers. If in state s and reading // byte c, the next state should be s->next_[c]. struct State { inline bool IsMatch() const { return flag_ & kFlagMatch; } void SaveMatch(vector* v); int* inst_; // Instruction pointers in the state. int ninst_; // # of inst_ pointers. uint flag_; // Empty string bitfield flags in effect on the way // into this state, along with kFlagMatch if this // is a matching state. State** next_; // Outgoing arrows from State, // one per input byte class }; enum { kByteEndText = 256, // imaginary byte at end of text kFlagEmptyMask = 0xFFF, // State.flag_: bits holding kEmptyXXX flags kFlagMatch = 0x1000, // State.flag_: this is a matching state kFlagLastWord = 0x2000, // State.flag_: last byte was a word char kFlagNeedShift = 16, // needed kEmpty bits are or'ed in shifted left }; #ifndef STL_MSVC // STL function structures for use with unordered_set. struct StateEqual { bool operator()(const State* a, const State* b) const { if (a == b) return true; if (a == NULL || b == NULL) return false; if (a->ninst_ != b->ninst_) return false; if (a->flag_ != b->flag_) return false; for (int i = 0; i < a->ninst_; i++) if (a->inst_[i] != b->inst_[i]) return false; return true; // they're equal } }; #endif // STL_MSVC struct StateHash { size_t operator()(const State* a) const { if (a == NULL) return 0; const char* s = reinterpret_cast(a->inst_); int len = a->ninst_ * sizeof a->inst_[0]; if (sizeof(size_t) == sizeof(uint32)) return Hash32StringWithSeed(s, len, a->flag_); else return Hash64StringWithSeed(s, len, a->flag_); } #ifdef STL_MSVC // Less than operator. bool operator()(const State* a, const State* b) const { if (a == b) return false; if (a == NULL || b == NULL) return a == NULL; if (a->ninst_ != b->ninst_) return a->ninst_ < b->ninst_; if (a->flag_ != b->flag_) return a->flag_ < b->flag_; for (int i = 0; i < a->ninst_; ++i) if (a->inst_[i] != b->inst_[i]) return a->inst_[i] < b->inst_[i]; return false; // they're equal } // The two public members are required by msvc. 4 and 8 are default values. // Reference: http://msdn.microsoft.com/en-us/library/1s1byw77.aspx static const size_t bucket_size = 4; static const size_t min_buckets = 8; #endif // STL_MSVC }; #ifdef STL_MSVC typedef unordered_set StateSet; #else // !STL_MSVC typedef unordered_set StateSet; #endif // STL_MSVC private: // Special "firstbyte" values for a state. (Values >= 0 denote actual bytes.) enum { kFbUnknown = -1, // No analysis has been performed. kFbMany = -2, // Many bytes will lead out of this state. kFbNone = -3, // No bytes lead out of this state. }; enum { // Indices into start_ for unanchored searches. // Add kStartAnchored for anchored searches. kStartBeginText = 0, // text at beginning of context kStartBeginLine = 2, // text at beginning of line kStartAfterWordChar = 4, // text follows a word character kStartAfterNonWordChar = 6, // text follows non-word character kMaxStart = 8, kStartAnchored = 1, }; // Resets the DFA State cache, flushing all saved State* information. // Releases and reacquires cache_mutex_ via cache_lock, so any // State* existing before the call are not valid after the call. // Use a StateSaver to preserve important states across the call. // cache_mutex_.r <= L < mutex_ // After: cache_mutex_.w <= L < mutex_ void ResetCache(RWLocker* cache_lock); // Looks up and returns the State corresponding to a Workq. // L >= mutex_ State* WorkqToCachedState(Workq* q, uint flag); // Looks up and returns a State matching the inst, ninst, and flag. // L >= mutex_ State* CachedState(int* inst, int ninst, uint flag); // Clear the cache entirely. // Must hold cache_mutex_.w or be in destructor. void ClearCache(); // Converts a State into a Workq: the opposite of WorkqToCachedState. // L >= mutex_ static void StateToWorkq(State* s, Workq* q); // Runs a State on a given byte, returning the next state. State* RunStateOnByteUnlocked(State*, int); // cache_mutex_.r <= L < mutex_ State* RunStateOnByte(State*, int); // L >= mutex_ // Runs a Workq on a given byte followed by a set of empty-string flags, // producing a new Workq in nq. If a match instruction is encountered, // sets *ismatch to true. // L >= mutex_ void RunWorkqOnByte(Workq* q, Workq* nq, int c, uint flag, bool* ismatch, Prog::MatchKind kind); // Runs a Workq on a set of empty-string flags, producing a new Workq in nq. // L >= mutex_ void RunWorkqOnEmptyString(Workq* q, Workq* nq, uint flag); // Adds the instruction id to the Workq, following empty arrows // according to flag. // L >= mutex_ void AddToQueue(Workq* q, int id, uint flag); // For debugging, returns a text representation of State. static string DumpState(State* state); // For debugging, returns a text representation of a Workq. static string DumpWorkq(Workq* q); // Search parameters struct SearchParams { SearchParams(const StringPiece& text, const StringPiece& context, RWLocker* cache_lock) : text(text), context(context), anchored(false), want_earliest_match(false), run_forward(false), start(NULL), firstbyte(kFbUnknown), cache_lock(cache_lock), failed(false), ep(NULL), matches(NULL) { } StringPiece text; StringPiece context; bool anchored; bool want_earliest_match; bool run_forward; State* start; int firstbyte; RWLocker *cache_lock; bool failed; // "out" parameter: whether search gave up const char* ep; // "out" parameter: end pointer for match vector* matches; private: DISALLOW_COPY_AND_ASSIGN(SearchParams); }; // Before each search, the parameters to Search are analyzed by // AnalyzeSearch to determine the state in which to start and the // "firstbyte" for that state, if any. struct StartInfo { StartInfo() : start(NULL), firstbyte(kFbUnknown) { } State* start; volatile int firstbyte; }; // Fills in params->start and params->firstbyte using // the other search parameters. Returns true on success, // false on failure. // cache_mutex_.r <= L < mutex_ bool AnalyzeSearch(SearchParams* params); bool AnalyzeSearchHelper(SearchParams* params, StartInfo* info, uint flags); // The generic search loop, inlined to create specialized versions. // cache_mutex_.r <= L < mutex_ // Might unlock and relock cache_mutex_ via params->cache_lock. inline bool InlinedSearchLoop(SearchParams* params, bool have_firstbyte, bool want_earliest_match, bool run_forward); // The specialized versions of InlinedSearchLoop. The three letters // at the ends of the name denote the true/false values used as the // last three parameters of InlinedSearchLoop. // cache_mutex_.r <= L < mutex_ // Might unlock and relock cache_mutex_ via params->cache_lock. bool SearchFFF(SearchParams* params); bool SearchFFT(SearchParams* params); bool SearchFTF(SearchParams* params); bool SearchFTT(SearchParams* params); bool SearchTFF(SearchParams* params); bool SearchTFT(SearchParams* params); bool SearchTTF(SearchParams* params); bool SearchTTT(SearchParams* params); // The main search loop: calls an appropriate specialized version of // InlinedSearchLoop. // cache_mutex_.r <= L < mutex_ // Might unlock and relock cache_mutex_ via params->cache_lock. bool FastSearchLoop(SearchParams* params); // For debugging, a slow search loop that calls InlinedSearchLoop // directly -- because the booleans passed are not constants, the // loop is not specialized like the SearchFFF etc. versions, so it // runs much more slowly. Useful only for debugging. // cache_mutex_.r <= L < mutex_ // Might unlock and relock cache_mutex_ via params->cache_lock. bool SlowSearchLoop(SearchParams* params); // Looks up bytes in bytemap_ but handles case c == kByteEndText too. int ByteMap(int c) { if (c == kByteEndText) return prog_->bytemap_range(); return prog_->bytemap()[c]; } // Constant after initialization. Prog* prog_; // The regular expression program to run. Prog::MatchKind kind_; // The kind of DFA. bool init_failed_; // initialization failed (out of memory) Mutex mutex_; // mutex_ >= cache_mutex_.r // Scratch areas, protected by mutex_. Workq* q0_; // Two pre-allocated work queues. Workq* q1_; int* astack_; // Pre-allocated stack for AddToQueue int nastack_; // State* cache. Many threads use and add to the cache simultaneously, // holding cache_mutex_ for reading and mutex_ (above) when adding. // If the cache fills and needs to be discarded, the discarding is done // while holding cache_mutex_ for writing, to avoid interrupting other // readers. Any State* pointers are only valid while cache_mutex_ // is held. Mutex cache_mutex_; int64 mem_budget_; // Total memory budget for all States. int64 state_budget_; // Amount of memory remaining for new States. StateSet state_cache_; // All States computed so far. StartInfo start_[kMaxStart]; bool cache_warned_; // have printed to LOG(INFO) about the cache }; // Shorthand for casting to uint8*. static inline const uint8* BytePtr(const void* v) { return reinterpret_cast(v); } // Work queues // Marks separate thread groups of different priority // in the work queue when in leftmost-longest matching mode. #define Mark (-1) // Internally, the DFA uses a sparse array of // program instruction pointers as a work queue. // In leftmost longest mode, marks separate sections // of workq that started executing at different // locations in the string (earlier locations first). class DFA::Workq : public SparseSet { public: // Constructor: n is number of normal slots, maxmark number of mark slots. Workq(int n, int maxmark) : SparseSet(n+maxmark), n_(n), maxmark_(maxmark), nextmark_(n), last_was_mark_(true) { } bool is_mark(int i) { return i >= n_; } int maxmark() { return maxmark_; } void clear() { SparseSet::clear(); nextmark_ = n_; } void mark() { if (last_was_mark_) return; last_was_mark_ = false; SparseSet::insert_new(nextmark_++); } int size() { return n_ + maxmark_; } void insert(int id) { if (contains(id)) return; insert_new(id); } void insert_new(int id) { last_was_mark_ = false; SparseSet::insert_new(id); } private: int n_; // size excluding marks int maxmark_; // maximum number of marks int nextmark_; // id of next mark bool last_was_mark_; // last inserted was mark DISALLOW_COPY_AND_ASSIGN(Workq); }; DFA::DFA(Prog* prog, Prog::MatchKind kind, int64 max_mem) : prog_(prog), kind_(kind), init_failed_(false), q0_(NULL), q1_(NULL), astack_(NULL), mem_budget_(max_mem), cache_warned_(false) { if (DebugDFA) fprintf(stderr, "\nkind %d\n%s\n", (int)kind_, prog_->DumpUnanchored().c_str()); int nmark = 0; if (kind_ == Prog::kLongestMatch) nmark = prog->size(); nastack_ = 2 * prog->size() + nmark; // Account for space needed for DFA, q0, q1, astack. mem_budget_ -= sizeof(DFA); mem_budget_ -= (prog_->size() + nmark) * (sizeof(int)+sizeof(int)) * 2; // q0, q1 mem_budget_ -= nastack_ * sizeof(int); // astack if (mem_budget_ < 0) { LOG(INFO) << StringPrintf("DFA out of memory: prog size %lld mem %lld", prog_->size(), max_mem); init_failed_ = true; return; } state_budget_ = mem_budget_; // Make sure there is a reasonable amount of working room left. // At minimum, the search requires room for two states in order // to limp along, restarting frequently. We'll get better performance // if there is room for a larger number of states, say 20. int64 one_state = sizeof(State) + (prog_->size()+nmark)*sizeof(int) + (prog_->bytemap_range()+1)*sizeof(State*); if (state_budget_ < 20*one_state) { LOG(INFO) << StringPrintf("DFA out of memory: prog size %lld mem %lld", prog_->size(), max_mem); init_failed_ = true; return; } q0_ = new Workq(prog->size(), nmark); q1_ = new Workq(prog->size(), nmark); astack_ = new int[nastack_]; } DFA::~DFA() { delete q0_; delete q1_; delete[] astack_; ClearCache(); } // In the DFA state graph, s->next[c] == NULL means that the // state has not yet been computed and needs to be. We need // a different special value to signal that s->next[c] is a // state that can never lead to a match (and thus the search // can be called off). Hence DeadState. #define DeadState reinterpret_cast(1) // Signals that the rest of the string matches no matter what it is. #define FullMatchState reinterpret_cast(2) #define SpecialStateMax FullMatchState // Debugging printouts // For debugging, returns a string representation of the work queue. string DFA::DumpWorkq(Workq* q) { string s; const char* sep = ""; for (DFA::Workq::iterator it = q->begin(); it != q->end(); ++it) { if (q->is_mark(*it)) { StringAppendF(&s, "|"); sep = ""; } else { StringAppendF(&s, "%s%d", sep, *it); sep = ","; } } return s; } // For debugging, returns a string representation of the state. string DFA::DumpState(State* state) { if (state == NULL) return "_"; if (state == DeadState) return "X"; if (state == FullMatchState) return "*"; string s; const char* sep = ""; StringAppendF(&s, "(%p)", state); for (int i = 0; i < state->ninst_; i++) { if (state->inst_[i] == Mark) { StringAppendF(&s, "|"); sep = ""; } else { StringAppendF(&s, "%s%d", sep, state->inst_[i]); sep = ","; } } StringAppendF(&s, " flag=%#x", state->flag_); return s; } ////////////////////////////////////////////////////////////////////// // // DFA state graph construction. // // The DFA state graph is a heavily-linked collection of State* structures. // The state_cache_ is a set of all the State structures ever allocated, // so that if the same state is reached by two different paths, // the same State structure can be used. This reduces allocation // requirements and also avoids duplication of effort across the two // identical states. // // A State is defined by an ordered list of instruction ids and a flag word. // // The choice of an ordered list of instructions differs from a typical // textbook DFA implementation, which would use an unordered set. // Textbook descriptions, however, only care about whether // the DFA matches, not where it matches in the text. To decide where the // DFA matches, we need to mimic the behavior of the dominant backtracking // implementations like PCRE, which try one possible regular expression // execution, then another, then another, stopping when one of them succeeds. // The DFA execution tries these many executions in parallel, representing // each by an instruction id. These pointers are ordered in the State.inst_ // list in the same order that the executions would happen in a backtracking // search: if a match is found during execution of inst_[2], inst_[i] for i>=3 // can be discarded. // // Textbooks also typically do not consider context-aware empty string operators // like ^ or $. These are handled by the flag word, which specifies the set // of empty-string operators that should be matched when executing at the // current text position. These flag bits are defined in prog.h. // The flag word also contains two DFA-specific bits: kFlagMatch if the state // is a matching state (one that reached a kInstMatch in the program) // and kFlagLastWord if the last processed byte was a word character, for the // implementation of \B and \b. // // The flag word also contains, shifted up 16 bits, the bits looked for by // any kInstEmptyWidth instructions in the state. These provide a useful // summary indicating when new flags might be useful. // // The permanent representation of a State's instruction ids is just an array, // but while a state is being analyzed, these instruction ids are represented // as a Workq, which is an array that allows iteration in insertion order. // NOTE(rsc): The choice of State construction determines whether the DFA // mimics backtracking implementations (so-called leftmost first matching) or // traditional DFA implementations (so-called leftmost longest matching as // prescribed by POSIX). This implementation chooses to mimic the // backtracking implementations, because we want to replace PCRE. To get // POSIX behavior, the states would need to be considered not as a simple // ordered list of instruction ids, but as a list of unordered sets of instruction // ids. A match by a state in one set would inhibit the running of sets // farther down the list but not other instruction ids in the same set. Each // set would correspond to matches beginning at a given point in the string. // This is implemented by separating different sets with Mark pointers. // Looks in the State cache for a State matching q, flag. // If one is found, returns it. If one is not found, allocates one, // inserts it in the cache, and returns it. DFA::State* DFA::WorkqToCachedState(Workq* q, uint flag) { if (DEBUG_MODE) mutex_.AssertHeld(); // Construct array of instruction ids for the new state. // Only ByteRange, EmptyWidth, and Match instructions are useful to keep: // those are the only operators with any effect in // RunWorkqOnEmptyString or RunWorkqOnByte. int* inst = new int[q->size()]; int n = 0; uint needflags = 0; // flags needed by kInstEmptyWidth instructions bool sawmatch = false; // whether queue contains guaranteed kInstMatch bool sawmark = false; // whether queue contains a Mark if (DebugDFA) fprintf(stderr, "WorkqToCachedState %s [%#x]", DumpWorkq(q).c_str(), flag); for (Workq::iterator it = q->begin(); it != q->end(); ++it) { int id = *it; if (sawmatch && (kind_ == Prog::kFirstMatch || q->is_mark(id))) break; if (q->is_mark(id)) { if (n > 0 && inst[n-1] != Mark) { sawmark = true; inst[n++] = Mark; } continue; } Prog::Inst* ip = prog_->inst(id); switch (ip->opcode()) { case kInstAltMatch: // This state will continue to a match no matter what // the rest of the input is. If it is the highest priority match // being considered, return the special FullMatchState // to indicate that it's all matches from here out. if (kind_ != Prog::kManyMatch && (kind_ != Prog::kFirstMatch || (it == q->begin() && ip->greedy(prog_))) && (kind_ != Prog::kLongestMatch || !sawmark) && (flag & kFlagMatch)) { delete[] inst; if (DebugDFA) fprintf(stderr, " -> FullMatchState\n"); return FullMatchState; } // Fall through. case kInstByteRange: // These are useful. case kInstEmptyWidth: case kInstMatch: case kInstAlt: // Not useful, but necessary [*] inst[n++] = *it; if (ip->opcode() == kInstEmptyWidth) needflags |= ip->empty(); if (ip->opcode() == kInstMatch && !prog_->anchor_end()) sawmatch = true; break; default: // The rest are not. break; } // [*] kInstAlt would seem useless to record in a state, since // we've already followed both its arrows and saved all the // interesting states we can reach from there. The problem // is that one of the empty-width instructions might lead // back to the same kInstAlt (if an empty-width operator is starred), // producing a different evaluation order depending on whether // we keep the kInstAlt to begin with. Sigh. // A specific case that this affects is /(^|a)+/ matching "a". // If we don't save the kInstAlt, we will match the whole "a" (0,1) // but in fact the correct leftmost-first match is the leading "" (0,0). } DCHECK_LE(n, q->size()); if (n > 0 && inst[n-1] == Mark) n--; // If there are no empty-width instructions waiting to execute, // then the extra flag bits will not be used, so there is no // point in saving them. (Discarding them reduces the number // of distinct states.) if (needflags == 0) flag &= kFlagMatch; // NOTE(rsc): The code above cannot do flag &= needflags, // because if the right flags were present to pass the current // kInstEmptyWidth instructions, new kInstEmptyWidth instructions // might be reached that in turn need different flags. // The only sure thing is that if there are no kInstEmptyWidth // instructions at all, no flags will be needed. // We could do the extra work to figure out the full set of // possibly needed flags by exploring past the kInstEmptyWidth // instructions, but the check above -- are any flags needed // at all? -- handles the most common case. More fine-grained // analysis can only be justified by measurements showing that // too many redundant states are being allocated. // If there are no Insts in the list, it's a dead state, // which is useful to signal with a special pointer so that // the execution loop can stop early. This is only okay // if the state is *not* a matching state. if (n == 0 && flag == 0) { delete[] inst; if (DebugDFA) fprintf(stderr, " -> DeadState\n"); return DeadState; } // If we're in longest match mode, the state is a sequence of // unordered state sets separated by Marks. Sort each set // to canonicalize, to reduce the number of distinct sets stored. if (kind_ == Prog::kLongestMatch) { int* ip = inst; int* ep = ip + n; while (ip < ep) { int* markp = ip; while (markp < ep && *markp != Mark) markp++; sort(ip, markp); if (markp < ep) markp++; ip = markp; } } // Save the needed empty-width flags in the top bits for use later. flag |= needflags << kFlagNeedShift; State* state = CachedState(inst, n, flag); delete[] inst; return state; } // Looks in the State cache for a State matching inst, ninst, flag. // If one is found, returns it. If one is not found, allocates one, // inserts it in the cache, and returns it. DFA::State* DFA::CachedState(int* inst, int ninst, uint flag) { if (DEBUG_MODE) mutex_.AssertHeld(); // Look in the cache for a pre-existing state. State state = { inst, ninst, flag, NULL }; StateSet::iterator it = state_cache_.find(&state); if (it != state_cache_.end()) { if (DebugDFA) fprintf(stderr, " -cached-> %s\n", DumpState(*it).c_str()); return *it; } // Must have enough memory for new state. // In addition to what we're going to allocate, // the state cache hash table seems to incur about 32 bytes per // State*, empirically. const int kStateCacheOverhead = 32; int nnext = prog_->bytemap_range() + 1; // + 1 for kByteEndText slot int mem = sizeof(State) + nnext*sizeof(State*) + ninst*sizeof(int); if (mem_budget_ < mem + kStateCacheOverhead) { mem_budget_ = -1; return NULL; } mem_budget_ -= mem + kStateCacheOverhead; // Allocate new state, along with room for next and inst. char* space = new char[mem]; State* s = reinterpret_cast(space); s->next_ = reinterpret_cast(s + 1); s->inst_ = reinterpret_cast(s->next_ + nnext); memset(s->next_, 0, nnext*sizeof s->next_[0]); memmove(s->inst_, inst, ninst*sizeof s->inst_[0]); s->ninst_ = ninst; s->flag_ = flag; if (DebugDFA) fprintf(stderr, " -> %s\n", DumpState(s).c_str()); // Put state in cache and return it. state_cache_.insert(s); return s; } // Clear the cache. Must hold cache_mutex_.w or be in destructor. void DFA::ClearCache() { // In case state_cache_ doesn't support deleting entries // during iteration, copy into a vector and then delete. vector v; v.reserve(state_cache_.size()); for (StateSet::iterator it = state_cache_.begin(); it != state_cache_.end(); ++it) v.push_back(*it); state_cache_.clear(); for (size_t i = 0; i < v.size(); i++) delete[] reinterpret_cast(v[i]); } // Copies insts in state s to the work queue q. void DFA::StateToWorkq(State* s, Workq* q) { q->clear(); for (int i = 0; i < s->ninst_; i++) { if (s->inst_[i] == Mark) q->mark(); else q->insert_new(s->inst_[i]); } } // Adds ip to the work queue, following empty arrows according to flag // and expanding kInstAlt instructions (two-target gotos). void DFA::AddToQueue(Workq* q, int id, uint flag) { // Use astack_ to hold our stack of states yet to process. // It is sized to have room for nastack_ == 2*prog->size() + nmark // instructions, which is enough: each instruction can be // processed by the switch below only once, and the processing // pushes at most two instructions plus maybe a mark. // (If we're using marks, nmark == prog->size(); otherwise nmark == 0.) int* stk = astack_; int nstk = 0; stk[nstk++] = id; while (nstk > 0) { DCHECK_LE(nstk, nastack_); id = stk[--nstk]; if (id == Mark) { q->mark(); continue; } if (id == 0) continue; // If ip is already on the queue, nothing to do. // Otherwise add it. We don't actually keep all the ones // that get added -- for example, kInstAlt is ignored // when on a work queue -- but adding all ip's here // increases the likelihood of q->contains(id), // reducing the amount of duplicated work. if (q->contains(id)) continue; q->insert_new(id); // Process instruction. Prog::Inst* ip = prog_->inst(id); switch (ip->opcode()) { case kInstFail: // can't happen: discarded above break; case kInstByteRange: // just save these on the queue case kInstMatch: break; case kInstCapture: // DFA treats captures as no-ops. case kInstNop: stk[nstk++] = ip->out(); break; case kInstAlt: // two choices: expand both, in order case kInstAltMatch: // Want to visit out then out1, so push on stack in reverse order. // This instruction is the [00-FF]* loop at the beginning of // a leftmost-longest unanchored search, separate out from out1 // with a Mark, so that out1's threads (which will start farther // to the right in the string being searched) are lower priority // than the current ones. stk[nstk++] = ip->out1(); if (q->maxmark() > 0 && id == prog_->start_unanchored() && id != prog_->start()) stk[nstk++] = Mark; stk[nstk++] = ip->out(); break; case kInstEmptyWidth: // Continue on if we have all the right flag bits. if (ip->empty() & ~flag) break; stk[nstk++] = ip->out(); break; } } } // Running of work queues. In the work queue, order matters: // the queue is sorted in priority order. If instruction i comes before j, // then the instructions that i produces during the run must come before // the ones that j produces. In order to keep this invariant, all the // work queue runners have to take an old queue to process and then // also a new queue to fill in. It's not acceptable to add to the end of // an existing queue, because new instructions will not end up in the // correct position. // Runs the work queue, processing the empty strings indicated by flag. // For example, flag == kEmptyBeginLine|kEmptyEndLine means to match // both ^ and $. It is important that callers pass all flags at once: // processing both ^ and $ is not the same as first processing only ^ // and then processing only $. Doing the two-step sequence won't match // ^$^$^$ but processing ^ and $ simultaneously will (and is the behavior // exhibited by existing implementations). void DFA::RunWorkqOnEmptyString(Workq* oldq, Workq* newq, uint flag) { newq->clear(); for (Workq::iterator i = oldq->begin(); i != oldq->end(); ++i) { if (oldq->is_mark(*i)) AddToQueue(newq, Mark, flag); else AddToQueue(newq, *i, flag); } } // Runs the work queue, processing the single byte c followed by any empty // strings indicated by flag. For example, c == 'a' and flag == kEmptyEndLine, // means to match c$. Sets the bool *ismatch to true if the end of the // regular expression program has been reached (the regexp has matched). void DFA::RunWorkqOnByte(Workq* oldq, Workq* newq, int c, uint flag, bool* ismatch, Prog::MatchKind kind) { if (DEBUG_MODE) mutex_.AssertHeld(); newq->clear(); for (Workq::iterator i = oldq->begin(); i != oldq->end(); ++i) { if (oldq->is_mark(*i)) { if (*ismatch) return; newq->mark(); continue; } int id = *i; Prog::Inst* ip = prog_->inst(id); switch (ip->opcode()) { case kInstFail: // never succeeds case kInstCapture: // already followed case kInstNop: // already followed case kInstAlt: // already followed case kInstAltMatch: // already followed case kInstEmptyWidth: // already followed break; case kInstByteRange: // can follow if c is in range if (ip->Matches(c)) AddToQueue(newq, ip->out(), flag); break; case kInstMatch: if (prog_->anchor_end() && c != kByteEndText) break; *ismatch = true; if (kind == Prog::kFirstMatch) { // Can stop processing work queue since we found a match. return; } break; } } if (DebugDFA) fprintf(stderr, "%s on %d[%#x] -> %s [%d]\n", DumpWorkq(oldq).c_str(), c, flag, DumpWorkq(newq).c_str(), *ismatch); } // Processes input byte c in state, returning new state. // Caller does not hold mutex. DFA::State* DFA::RunStateOnByteUnlocked(State* state, int c) { // Keep only one RunStateOnByte going // even if the DFA is being run by multiple threads. MutexLock l(&mutex_); return RunStateOnByte(state, c); } // Processes input byte c in state, returning new state. DFA::State* DFA::RunStateOnByte(State* state, int c) { if (DEBUG_MODE) mutex_.AssertHeld(); if (state <= SpecialStateMax) { if (state == FullMatchState) { // It is convenient for routines like PossibleMatchRange // if we implement RunStateOnByte for FullMatchState: // once you get into this state you never get out, // so it's pretty easy. return FullMatchState; } if (state == DeadState) { LOG(DFATAL) << "DeadState in RunStateOnByte"; return NULL; } if (state == NULL) { LOG(DFATAL) << "NULL state in RunStateOnByte"; return NULL; } LOG(DFATAL) << "Unexpected special state in RunStateOnByte"; return NULL; } // If someone else already computed this, return it. State* ns; ATOMIC_LOAD_CONSUME(ns, &state->next_[ByteMap(c)]); if (ns != NULL) return ns; // Convert state into Workq. StateToWorkq(state, q0_); // Flags marking the kinds of empty-width things (^ $ etc) // around this byte. Before the byte we have the flags recorded // in the State structure itself. After the byte we have // nothing yet (but that will change: read on). uint needflag = state->flag_ >> kFlagNeedShift; uint beforeflag = state->flag_ & kFlagEmptyMask; uint oldbeforeflag = beforeflag; uint afterflag = 0; if (c == '\n') { // Insert implicit $ and ^ around \n beforeflag |= kEmptyEndLine; afterflag |= kEmptyBeginLine; } if (c == kByteEndText) { // Insert implicit $ and \z before the fake "end text" byte. beforeflag |= kEmptyEndLine | kEmptyEndText; } // The state flag kFlagLastWord says whether the last // byte processed was a word character. Use that info to // insert empty-width (non-)word boundaries. bool islastword = state->flag_ & kFlagLastWord; bool isword = (c != kByteEndText && Prog::IsWordChar(c)); if (isword == islastword) beforeflag |= kEmptyNonWordBoundary; else beforeflag |= kEmptyWordBoundary; // Okay, finally ready to run. // Only useful to rerun on empty string if there are new, useful flags. if (beforeflag & ~oldbeforeflag & needflag) { RunWorkqOnEmptyString(q0_, q1_, beforeflag); swap(q0_, q1_); } bool ismatch = false; RunWorkqOnByte(q0_, q1_, c, afterflag, &ismatch, kind_); // Most of the time, we build the state from the output of // RunWorkqOnByte, so swap q0_ and q1_ here. However, so that // RE2::Set can tell exactly which match instructions // contributed to the match, don't swap if c is kByteEndText. // The resulting state wouldn't be correct for further processing // of the string, but we're at the end of the text so that's okay. // Leaving q0_ alone preseves the match instructions that led to // the current setting of ismatch. if (c != kByteEndText || kind_ != Prog::kManyMatch) swap(q0_, q1_); // Save afterflag along with ismatch and isword in new state. uint flag = afterflag; if (ismatch) flag |= kFlagMatch; if (isword) flag |= kFlagLastWord; ns = WorkqToCachedState(q0_, flag); // Flush ns before linking to it. // Write barrier before updating state->next_ so that the // main search loop can proceed without any locking, for speed. // (Otherwise it would need one mutex operation per input byte.) ATOMIC_STORE_RELEASE(&state->next_[ByteMap(c)], ns); return ns; } ////////////////////////////////////////////////////////////////////// // DFA cache reset. // Reader-writer lock helper. // // The DFA uses a reader-writer mutex to protect the state graph itself. // Traversing the state graph requires holding the mutex for reading, // and discarding the state graph and starting over requires holding the // lock for writing. If a search needs to expand the graph but is out // of memory, it will need to drop its read lock and then acquire the // write lock. Since it cannot then atomically downgrade from write lock // to read lock, it runs the rest of the search holding the write lock. // (This probably helps avoid repeated contention, but really the decision // is forced by the Mutex interface.) It's a bit complicated to keep // track of whether the lock is held for reading or writing and thread // that through the search, so instead we encapsulate it in the RWLocker // and pass that around. class DFA::RWLocker { public: explicit RWLocker(Mutex* mu); ~RWLocker(); // If the lock is only held for reading right now, // drop the read lock and re-acquire for writing. // Subsequent calls to LockForWriting are no-ops. // Notice that the lock is *released* temporarily. void LockForWriting(); // Returns whether the lock is already held for writing. bool IsLockedForWriting() { return writing_; } private: Mutex* mu_; bool writing_; DISALLOW_COPY_AND_ASSIGN(RWLocker); }; DFA::RWLocker::RWLocker(Mutex* mu) : mu_(mu), writing_(false) { mu_->ReaderLock(); } // This function is marked as NO_THREAD_SAFETY_ANALYSIS because the annotations // does not support lock upgrade. void DFA::RWLocker::LockForWriting() NO_THREAD_SAFETY_ANALYSIS { if (!writing_) { mu_->ReaderUnlock(); mu_->Lock(); writing_ = true; } } DFA::RWLocker::~RWLocker() { if (writing_) mu_->WriterUnlock(); else mu_->ReaderUnlock(); } // When the DFA's State cache fills, we discard all the states in the // cache and start over. Many threads can be using and adding to the // cache at the same time, so we synchronize using the cache_mutex_ // to keep from stepping on other threads. Specifically, all the // threads using the current cache hold cache_mutex_ for reading. // When a thread decides to flush the cache, it drops cache_mutex_ // and then re-acquires it for writing. That ensures there are no // other threads accessing the cache anymore. The rest of the search // runs holding cache_mutex_ for writing, avoiding any contention // with or cache pollution caused by other threads. void DFA::ResetCache(RWLocker* cache_lock) { // Re-acquire the cache_mutex_ for writing (exclusive use). bool was_writing = cache_lock->IsLockedForWriting(); cache_lock->LockForWriting(); // If we already held cache_mutex_ for writing, it means // this invocation of Search() has already reset the // cache once already. That's a pretty clear indication // that the cache is too small. Warn about that, once. // TODO(rsc): Only warn if state_cache_.size() < some threshold. if (was_writing && !cache_warned_) { LOG(INFO) << "DFA memory cache could be too small: " << "only room for " << state_cache_.size() << " states."; cache_warned_ = true; } // Clear the cache, reset the memory budget. for (int i = 0; i < kMaxStart; i++) { start_[i].start = NULL; start_[i].firstbyte = kFbUnknown; } ClearCache(); mem_budget_ = state_budget_; } // Typically, a couple States do need to be preserved across a cache // reset, like the State at the current point in the search. // The StateSaver class helps keep States across cache resets. // It makes a copy of the state's guts outside the cache (before the reset) // and then can be asked, after the reset, to recreate the State // in the new cache. For example, in a DFA method ("this" is a DFA): // // StateSaver saver(this, s); // ResetCache(cache_lock); // s = saver.Restore(); // // The saver should always have room in the cache to re-create the state, // because resetting the cache locks out all other threads, and the cache // is known to have room for at least a couple states (otherwise the DFA // constructor fails). class DFA::StateSaver { public: explicit StateSaver(DFA* dfa, State* state); ~StateSaver(); // Recreates and returns a state equivalent to the // original state passed to the constructor. // Returns NULL if the cache has filled, but // since the DFA guarantees to have room in the cache // for a couple states, should never return NULL // if used right after ResetCache. State* Restore(); private: DFA* dfa_; // the DFA to use int* inst_; // saved info from State int ninst_; uint flag_; bool is_special_; // whether original state was special State* special_; // if is_special_, the original state DISALLOW_COPY_AND_ASSIGN(StateSaver); }; DFA::StateSaver::StateSaver(DFA* dfa, State* state) { dfa_ = dfa; if (state <= SpecialStateMax) { inst_ = NULL; ninst_ = 0; flag_ = 0; is_special_ = true; special_ = state; return; } is_special_ = false; special_ = NULL; flag_ = state->flag_; ninst_ = state->ninst_; inst_ = new int[ninst_]; memmove(inst_, state->inst_, ninst_*sizeof inst_[0]); } DFA::StateSaver::~StateSaver() { if (!is_special_) delete[] inst_; } DFA::State* DFA::StateSaver::Restore() { if (is_special_) return special_; MutexLock l(&dfa_->mutex_); State* s = dfa_->CachedState(inst_, ninst_, flag_); if (s == NULL) LOG(DFATAL) << "StateSaver failed to restore state."; return s; } ////////////////////////////////////////////////////////////////////// // // DFA execution. // // The basic search loop is easy: start in a state s and then for each // byte c in the input, s = s->next[c]. // // This simple description omits a few efficiency-driven complications. // // First, the State graph is constructed incrementally: it is possible // that s->next[c] is null, indicating that that state has not been // fully explored. In this case, RunStateOnByte must be invoked to // determine the next state, which is cached in s->next[c] to save // future effort. An alternative reason for s->next[c] to be null is // that the DFA has reached a so-called "dead state", in which any match // is no longer possible. In this case RunStateOnByte will return NULL // and the processing of the string can stop early. // // Second, a 256-element pointer array for s->next_ makes each State // quite large (2kB on 64-bit machines). Instead, dfa->bytemap_[] // maps from bytes to "byte classes" and then next_ only needs to have // as many pointers as there are byte classes. A byte class is simply a // range of bytes that the regexp never distinguishes between. // A regexp looking for a[abc] would have four byte ranges -- 0 to 'a'-1, // 'a', 'b' to 'c', and 'c' to 0xFF. The bytemap slows us a little bit // but in exchange we typically cut the size of a State (and thus our // memory footprint) by about 5-10x. The comments still refer to // s->next[c] for simplicity, but code should refer to s->next_[bytemap_[c]]. // // Third, it is common for a DFA for an unanchored match to begin in a // state in which only one particular byte value can take the DFA to a // different state. That is, s->next[c] != s for only one c. In this // situation, the DFA can do better than executing the simple loop. // Instead, it can call memchr to search very quickly for the byte c. // Whether the start state has this property is determined during a // pre-compilation pass, and if so, the byte b is passed to the search // loop as the "firstbyte" argument, along with a boolean "have_firstbyte". // // Fourth, the desired behavior is to search for the leftmost-best match // (approximately, the same one that Perl would find), which is not // necessarily the match ending earliest in the string. Each time a // match is found, it must be noted, but the DFA must continue on in // hope of finding a higher-priority match. In some cases, the caller only // cares whether there is any match at all, not which one is found. // The "want_earliest_match" flag causes the search to stop at the first // match found. // // Fifth, one algorithm that uses the DFA needs it to run over the // input string backward, beginning at the end and ending at the beginning. // Passing false for the "run_forward" flag causes the DFA to run backward. // // The checks for these last three cases, which in a naive implementation // would be performed once per input byte, slow the general loop enough // to merit specialized versions of the search loop for each of the // eight possible settings of the three booleans. Rather than write // eight different functions, we write one general implementation and then // inline it to create the specialized ones. // // Note that matches are delayed by one byte, to make it easier to // accommodate match conditions depending on the next input byte (like $ and \b). // When s->next[c]->IsMatch(), it means that there is a match ending just // *before* byte c. // The generic search loop. Searches text for a match, returning // the pointer to the end of the chosen match, or NULL if no match. // The bools are equal to the same-named variables in params, but // making them function arguments lets the inliner specialize // this function to each combination (see two paragraphs above). inline bool DFA::InlinedSearchLoop(SearchParams* params, bool have_firstbyte, bool want_earliest_match, bool run_forward) { State* start = params->start; const uint8* bp = BytePtr(params->text.begin()); // start of text const uint8* p = bp; // text scanning point const uint8* ep = BytePtr(params->text.end()); // end of text const uint8* resetp = NULL; // p at last cache reset if (!run_forward) swap(p, ep); const uint8* bytemap = prog_->bytemap(); const uint8* lastmatch = NULL; // most recent matching position in text bool matched = false; State* s = start; if (s->IsMatch()) { matched = true; lastmatch = p; if (want_earliest_match) { params->ep = reinterpret_cast(lastmatch); return true; } } while (p != ep) { if (DebugDFA) fprintf(stderr, "@%d: %s\n", static_cast(p - bp), DumpState(s).c_str()); if (have_firstbyte && s == start) { // In start state, only way out is to find firstbyte, // so use optimized assembly in memchr to skip ahead. // If firstbyte isn't found, we can skip to the end // of the string. if (run_forward) { if ((p = BytePtr(memchr(p, params->firstbyte, ep - p))) == NULL) { p = ep; break; } } else { if ((p = BytePtr(memrchr(ep, params->firstbyte, p - ep))) == NULL) { p = ep; break; } p++; } } int c; if (run_forward) c = *p++; else c = *--p; // Note that multiple threads might be consulting // s->next_[bytemap[c]] simultaneously. // RunStateOnByte takes care of the appropriate locking, // including a memory barrier so that the unlocked access // (sometimes known as "double-checked locking") is safe. // The alternative would be either one DFA per thread // or one mutex operation per input byte. // // ns == DeadState means the state is known to be dead // (no more matches are possible). // ns == NULL means the state has not yet been computed // (need to call RunStateOnByteUnlocked). // RunStateOnByte returns ns == NULL if it is out of memory. // ns == FullMatchState means the rest of the string matches. // // Okay to use bytemap[] not ByteMap() here, because // c is known to be an actual byte and not kByteEndText. State* ns; ATOMIC_LOAD_CONSUME(ns, &s->next_[bytemap[c]]); if (ns == NULL) { ns = RunStateOnByteUnlocked(s, c); if (ns == NULL) { // After we reset the cache, we hold cache_mutex exclusively, // so if resetp != NULL, it means we filled the DFA state // cache with this search alone (without any other threads). // Benchmarks show that doing a state computation on every // byte runs at about 0.2 MB/s, while the NFA (nfa.cc) can do the // same at about 2 MB/s. Unless we're processing an average // of 10 bytes per state computation, fail so that RE2 can // fall back to the NFA. if (FLAGS_re2_dfa_bail_when_slow && resetp != NULL && static_cast(p - resetp) < 10*state_cache_.size()) { params->failed = true; return false; } resetp = p; // Prepare to save start and s across the reset. StateSaver save_start(this, start); StateSaver save_s(this, s); // Discard all the States in the cache. ResetCache(params->cache_lock); // Restore start and s so we can continue. if ((start = save_start.Restore()) == NULL || (s = save_s.Restore()) == NULL) { // Restore already did LOG(DFATAL). params->failed = true; return false; } ns = RunStateOnByteUnlocked(s, c); if (ns == NULL) { LOG(DFATAL) << "RunStateOnByteUnlocked failed after ResetCache"; params->failed = true; return false; } } } if (ns <= SpecialStateMax) { if (ns == DeadState) { params->ep = reinterpret_cast(lastmatch); return matched; } // FullMatchState params->ep = reinterpret_cast(ep); return true; } s = ns; if (s->IsMatch()) { matched = true; // The DFA notices the match one byte late, // so adjust p before using it in the match. if (run_forward) lastmatch = p - 1; else lastmatch = p + 1; if (DebugDFA) fprintf(stderr, "match @%d! [%s]\n", static_cast(lastmatch - bp), DumpState(s).c_str()); if (want_earliest_match) { params->ep = reinterpret_cast(lastmatch); return true; } } } // Process one more byte to see if it triggers a match. // (Remember, matches are delayed one byte.) int lastbyte; if (run_forward) { if (params->text.end() == params->context.end()) lastbyte = kByteEndText; else lastbyte = params->text.end()[0] & 0xFF; } else { if (params->text.begin() == params->context.begin()) lastbyte = kByteEndText; else lastbyte = params->text.begin()[-1] & 0xFF; } State* ns; ATOMIC_LOAD_CONSUME(ns, &s->next_[ByteMap(lastbyte)]); if (ns == NULL) { ns = RunStateOnByteUnlocked(s, lastbyte); if (ns == NULL) { StateSaver save_s(this, s); ResetCache(params->cache_lock); if ((s = save_s.Restore()) == NULL) { params->failed = true; return false; } ns = RunStateOnByteUnlocked(s, lastbyte); if (ns == NULL) { LOG(DFATAL) << "RunStateOnByteUnlocked failed after Reset"; params->failed = true; return false; } } } s = ns; if (DebugDFA) fprintf(stderr, "@_: %s\n", DumpState(s).c_str()); if (s == FullMatchState) { params->ep = reinterpret_cast(ep); return true; } if (s > SpecialStateMax && s->IsMatch()) { matched = true; lastmatch = p; if (params->matches && kind_ == Prog::kManyMatch) { vector* v = params->matches; v->clear(); for (int i = 0; i < s->ninst_; i++) { Prog::Inst* ip = prog_->inst(s->inst_[i]); if (ip->opcode() == kInstMatch) v->push_back(ip->match_id()); } } if (DebugDFA) fprintf(stderr, "match @%d! [%s]\n", static_cast(lastmatch - bp), DumpState(s).c_str()); } params->ep = reinterpret_cast(lastmatch); return matched; } // Inline specializations of the general loop. bool DFA::SearchFFF(SearchParams* params) { return InlinedSearchLoop(params, 0, 0, 0); } bool DFA::SearchFFT(SearchParams* params) { return InlinedSearchLoop(params, 0, 0, 1); } bool DFA::SearchFTF(SearchParams* params) { return InlinedSearchLoop(params, 0, 1, 0); } bool DFA::SearchFTT(SearchParams* params) { return InlinedSearchLoop(params, 0, 1, 1); } bool DFA::SearchTFF(SearchParams* params) { return InlinedSearchLoop(params, 1, 0, 0); } bool DFA::SearchTFT(SearchParams* params) { return InlinedSearchLoop(params, 1, 0, 1); } bool DFA::SearchTTF(SearchParams* params) { return InlinedSearchLoop(params, 1, 1, 0); } bool DFA::SearchTTT(SearchParams* params) { return InlinedSearchLoop(params, 1, 1, 1); } // For debugging, calls the general code directly. bool DFA::SlowSearchLoop(SearchParams* params) { return InlinedSearchLoop(params, params->firstbyte >= 0, params->want_earliest_match, params->run_forward); } // For performance, calls the appropriate specialized version // of InlinedSearchLoop. bool DFA::FastSearchLoop(SearchParams* params) { // Because the methods are private, the Searches array // cannot be declared at top level. static bool (DFA::*Searches[])(SearchParams*) = { &DFA::SearchFFF, &DFA::SearchFFT, &DFA::SearchFTF, &DFA::SearchFTT, &DFA::SearchTFF, &DFA::SearchTFT, &DFA::SearchTTF, &DFA::SearchTTT, }; bool have_firstbyte = (params->firstbyte >= 0); int index = 4 * have_firstbyte + 2 * params->want_earliest_match + 1 * params->run_forward; return (this->*Searches[index])(params); } // The discussion of DFA execution above ignored the question of how // to determine the initial state for the search loop. There are two // factors that influence the choice of start state. // // The first factor is whether the search is anchored or not. // The regexp program (Prog*) itself has // two different entry points: one for anchored searches and one for // unanchored searches. (The unanchored version starts with a leading ".*?" // and then jumps to the anchored one.) // // The second factor is where text appears in the larger context, which // determines which empty-string operators can be matched at the beginning // of execution. If text is at the very beginning of context, \A and ^ match. // Otherwise if text is at the beginning of a line, then ^ matches. // Otherwise it matters whether the character before text is a word character // or a non-word character. // // The two cases (unanchored vs not) and four cases (empty-string flags) // combine to make the eight cases recorded in the DFA's begin_text_[2], // begin_line_[2], after_wordchar_[2], and after_nonwordchar_[2] cached // StartInfos. The start state for each is filled in the first time it // is used for an actual search. // Examines text, context, and anchored to determine the right start // state for the DFA search loop. Fills in params and returns true on success. // Returns false on failure. bool DFA::AnalyzeSearch(SearchParams* params) { const StringPiece& text = params->text; const StringPiece& context = params->context; // Sanity check: make sure that text lies within context. if (text.begin() < context.begin() || text.end() > context.end()) { LOG(DFATAL) << "Text is not inside context."; params->start = DeadState; return true; } // Determine correct search type. int start; uint flags; if (params->run_forward) { if (text.begin() == context.begin()) { start = kStartBeginText; flags = kEmptyBeginText|kEmptyBeginLine; } else if (text.begin()[-1] == '\n') { start = kStartBeginLine; flags = kEmptyBeginLine; } else if (Prog::IsWordChar(text.begin()[-1] & 0xFF)) { start = kStartAfterWordChar; flags = kFlagLastWord; } else { start = kStartAfterNonWordChar; flags = 0; } } else { if (text.end() == context.end()) { start = kStartBeginText; flags = kEmptyBeginText|kEmptyBeginLine; } else if (text.end()[0] == '\n') { start = kStartBeginLine; flags = kEmptyBeginLine; } else if (Prog::IsWordChar(text.end()[0] & 0xFF)) { start = kStartAfterWordChar; flags = kFlagLastWord; } else { start = kStartAfterNonWordChar; flags = 0; } } if (params->anchored || prog_->anchor_start()) start |= kStartAnchored; StartInfo* info = &start_[start]; // Try once without cache_lock for writing. // Try again after resetting the cache // (ResetCache will relock cache_lock for writing). if (!AnalyzeSearchHelper(params, info, flags)) { ResetCache(params->cache_lock); if (!AnalyzeSearchHelper(params, info, flags)) { LOG(DFATAL) << "Failed to analyze start state."; params->failed = true; return false; } } if (DebugDFA) { int fb; ATOMIC_LOAD_RELAXED(fb, &info->firstbyte); fprintf(stderr, "anchored=%d fwd=%d flags=%#x state=%s firstbyte=%d\n", params->anchored, params->run_forward, flags, DumpState(info->start).c_str(), fb); } params->start = info->start; ATOMIC_LOAD_ACQUIRE(params->firstbyte, &info->firstbyte); return true; } // Fills in info if needed. Returns true on success, false on failure. bool DFA::AnalyzeSearchHelper(SearchParams* params, StartInfo* info, uint flags) { // Quick check. int fb; ATOMIC_LOAD_ACQUIRE(fb, &info->firstbyte); if (fb != kFbUnknown) return true; MutexLock l(&mutex_); if (info->firstbyte != kFbUnknown) return true; q0_->clear(); AddToQueue(q0_, params->anchored ? prog_->start() : prog_->start_unanchored(), flags); info->start = WorkqToCachedState(q0_, flags); if (info->start == NULL) return false; if (info->start == DeadState) { // Synchronize with "quick check" above. ATOMIC_STORE_RELEASE(&info->firstbyte, kFbNone); return true; } if (info->start == FullMatchState) { // Synchronize with "quick check" above. ATOMIC_STORE_RELEASE(&info->firstbyte, kFbNone); // will be ignored return true; } // Compute info->firstbyte by running state on all // possible byte values, looking for a single one that // leads to a different state. int firstbyte = kFbNone; for (int i = 0; i < 256; i++) { State* s = RunStateOnByte(info->start, i); if (s == NULL) { // Synchronize with "quick check" above. ATOMIC_STORE_RELEASE(&info->firstbyte, firstbyte); return false; } if (s == info->start) continue; // Goes to new state... if (firstbyte == kFbNone) { firstbyte = i; // ... first one } else { firstbyte = kFbMany; // ... too many break; } } // Synchronize with "quick check" above. ATOMIC_STORE_RELEASE(&info->firstbyte, firstbyte); return true; } // The actual DFA search: calls AnalyzeSearch and then FastSearchLoop. bool DFA::Search(const StringPiece& text, const StringPiece& context, bool anchored, bool want_earliest_match, bool run_forward, bool* failed, const char** epp, vector* matches) { *epp = NULL; if (!ok()) { *failed = true; return false; } *failed = false; if (DebugDFA) { fprintf(stderr, "\nprogram:\n%s\n", prog_->DumpUnanchored().c_str()); fprintf(stderr, "text %s anchored=%d earliest=%d fwd=%d kind %d\n", text.as_string().c_str(), anchored, want_earliest_match, run_forward, kind_); } RWLocker l(&cache_mutex_); SearchParams params(text, context, &l); params.anchored = anchored; params.want_earliest_match = want_earliest_match; params.run_forward = run_forward; params.matches = matches; if (!AnalyzeSearch(¶ms)) { *failed = true; return false; } if (params.start == DeadState) return false; if (params.start == FullMatchState) { if (run_forward == want_earliest_match) *epp = text.begin(); else *epp = text.end(); return true; } if (DebugDFA) fprintf(stderr, "start %s\n", DumpState(params.start).c_str()); bool ret = FastSearchLoop(¶ms); if (params.failed) { *failed = true; return false; } *epp = params.ep; return ret; } // Deletes dfa. // // This is a separate function so that // prog.h can be used without moving the definition of // class DFA out of this file. If you set // prog->dfa_ = dfa; // then you also have to set // prog->delete_dfa_ = DeleteDFA; // so that ~Prog can delete the dfa. static void DeleteDFA(DFA* dfa) { delete dfa; } DFA* Prog::GetDFA(MatchKind kind) { DFA*volatile* pdfa; if (kind == kFirstMatch || kind == kManyMatch) { pdfa = &dfa_first_; } else { kind = kLongestMatch; pdfa = &dfa_longest_; } // Quick check. DFA *dfa; ATOMIC_LOAD_ACQUIRE(dfa, pdfa); if (dfa != NULL) return dfa; MutexLock l(&dfa_mutex_); dfa = *pdfa; if (dfa != NULL) return dfa; // For a forward DFA, half the memory goes to each DFA. // For a reverse DFA, all the memory goes to the // "longest match" DFA, because RE2 never does reverse // "first match" searches. int64 m = dfa_mem_/2; if (reversed_) { if (kind == kLongestMatch || kind == kManyMatch) m = dfa_mem_; else m = 0; } dfa = new DFA(this, kind, m); delete_dfa_ = DeleteDFA; // Synchronize with "quick check" above. ATOMIC_STORE_RELEASE(pdfa, dfa); return dfa; } // Executes the regexp program to search in text, // which itself is inside the larger context. (As a convenience, // passing a NULL context is equivalent to passing text.) // Returns true if a match is found, false if not. // If a match is found, fills in match0->end() to point at the end of the match // and sets match0->begin() to text.begin(), since the DFA can't track // where the match actually began. // // This is the only external interface (class DFA only exists in this file). // bool Prog::SearchDFA(const StringPiece& text, const StringPiece& const_context, Anchor anchor, MatchKind kind, StringPiece* match0, bool* failed, vector* matches) { *failed = false; StringPiece context = const_context; if (context.begin() == NULL) context = text; bool carat = anchor_start(); bool dollar = anchor_end(); if (reversed_) { bool t = carat; carat = dollar; dollar = t; } if (carat && context.begin() != text.begin()) return false; if (dollar && context.end() != text.end()) return false; // Handle full match by running an anchored longest match // and then checking if it covers all of text. bool anchored = anchor == kAnchored || anchor_start() || kind == kFullMatch; bool endmatch = false; if (kind == kManyMatch) { endmatch = true; } else if (kind == kFullMatch || anchor_end()) { endmatch = true; kind = kLongestMatch; } // If the caller doesn't care where the match is (just whether one exists), // then we can stop at the very first match we find, the so-called // "shortest match". bool want_shortest_match = false; if (match0 == NULL && !endmatch) { want_shortest_match = true; kind = kLongestMatch; } DFA* dfa = GetDFA(kind); const char* ep; bool matched = dfa->Search(text, context, anchored, want_shortest_match, !reversed_, failed, &ep, matches); if (*failed) return false; if (!matched) return false; if (endmatch && ep != (reversed_ ? text.begin() : text.end())) return false; // If caller cares, record the boundary of the match. // We only know where it ends, so use the boundary of text // as the beginning. if (match0) { if (reversed_) *match0 = StringPiece(ep, text.end() - ep); else *match0 = StringPiece(text.begin(), ep - text.begin()); } return true; } // Build out all states in DFA. Returns number of states. int DFA::BuildAllStates() { if (!ok()) return 0; // Pick out start state for unanchored search // at beginning of text. RWLocker l(&cache_mutex_); SearchParams params(NULL, NULL, &l); params.anchored = false; if (!AnalyzeSearch(¶ms) || params.start <= SpecialStateMax) return 0; // Add start state to work queue. StateSet queued; vector q; queued.insert(params.start); q.push_back(params.start); // Flood to expand every state. for (size_t i = 0; i < q.size(); i++) { State* s = q[i]; for (int c = 0; c < 257; c++) { State* ns = RunStateOnByteUnlocked(s, c); if (ns > SpecialStateMax && queued.find(ns) == queued.end()) { queued.insert(ns); q.push_back(ns); } } } return q.size(); } // Build out all states in DFA for kind. Returns number of states. int Prog::BuildEntireDFA(MatchKind kind) { //LOG(ERROR) << "BuildEntireDFA is only for testing."; return GetDFA(kind)->BuildAllStates(); } // Computes min and max for matching string. // Won't return strings bigger than maxlen. bool DFA::PossibleMatchRange(string* min, string* max, int maxlen) { if (!ok()) return false; // NOTE: if future users of PossibleMatchRange want more precision when // presented with infinitely repeated elements, consider making this a // parameter to PossibleMatchRange. static int kMaxEltRepetitions = 0; // Keep track of the number of times we've visited states previously. We only // revisit a given state if it's part of a repeated group, so if the value // portion of the map tuple exceeds kMaxEltRepetitions we bail out and set // |*max| to |PrefixSuccessor(*max)|. // // Also note that previously_visited_states[UnseenStatePtr] will, in the STL // tradition, implicitly insert a '0' value at first use. We take advantage // of that property below. map previously_visited_states; // Pick out start state for anchored search at beginning of text. RWLocker l(&cache_mutex_); SearchParams params(NULL, NULL, &l); params.anchored = true; if (!AnalyzeSearch(¶ms)) return false; if (params.start == DeadState) { // No matching strings *min = ""; *max = ""; return true; } if (params.start == FullMatchState) // Every string matches: no max return false; // The DFA is essentially a big graph rooted at params.start, // and paths in the graph correspond to accepted strings. // Each node in the graph has potentially 256+1 arrows // coming out, one for each byte plus the magic end of // text character kByteEndText. // To find the smallest possible prefix of an accepted // string, we just walk the graph preferring to follow // arrows with the lowest bytes possible. To find the // largest possible prefix, we follow the largest bytes // possible. // The test for whether there is an arrow from s on byte j is // ns = RunStateOnByteUnlocked(s, j); // if (ns == NULL) // return false; // if (ns != DeadState && ns->ninst > 0) // The RunStateOnByteUnlocked call asks the DFA to build out the graph. // It returns NULL only if the DFA has run out of memory, // in which case we can't be sure of anything. // The second check sees whether there was graph built // and whether it is interesting graph. Nodes might have // ns->ninst == 0 if they exist only to represent the fact // that a match was found on the previous byte. // Build minimum prefix. State* s = params.start; min->clear(); MutexLock lock(&mutex_); for (int i = 0; i < maxlen; i++) { if (previously_visited_states[s] > kMaxEltRepetitions) { VLOG(2) << "Hit kMaxEltRepetitions=" << kMaxEltRepetitions << " for state s=" << s << " and min=" << CEscape(*min); break; } previously_visited_states[s]++; // Stop if min is a match. State* ns = RunStateOnByte(s, kByteEndText); if (ns == NULL) // DFA out of memory return false; if (ns != DeadState && (ns == FullMatchState || ns->IsMatch())) break; // Try to extend the string with low bytes. bool extended = false; for (int j = 0; j < 256; j++) { ns = RunStateOnByte(s, j); if (ns == NULL) // DFA out of memory return false; if (ns == FullMatchState || (ns > SpecialStateMax && ns->ninst_ > 0)) { extended = true; min->append(1, j); s = ns; break; } } if (!extended) break; } // Build maximum prefix. previously_visited_states.clear(); s = params.start; max->clear(); for (int i = 0; i < maxlen; i++) { if (previously_visited_states[s] > kMaxEltRepetitions) { VLOG(2) << "Hit kMaxEltRepetitions=" << kMaxEltRepetitions << " for state s=" << s << " and max=" << CEscape(*max); break; } previously_visited_states[s] += 1; // Try to extend the string with high bytes. bool extended = false; for (int j = 255; j >= 0; j--) { State* ns = RunStateOnByte(s, j); if (ns == NULL) return false; if (ns == FullMatchState || (ns > SpecialStateMax && ns->ninst_ > 0)) { extended = true; max->append(1, j); s = ns; break; } } if (!extended) { // Done, no need for PrefixSuccessor. return true; } } // Stopped while still adding to *max - round aaaaaaaaaa... to aaaa...b *max = PrefixSuccessor(*max); // If there are no bytes left, we have no way to say "there is no maximum // string". We could make the interface more complicated and be able to // return "there is no maximum but here is a minimum", but that seems like // overkill -- the most common no-max case is all possible strings, so not // telling the caller that the empty string is the minimum match isn't a // great loss. if (max->empty()) return false; return true; } // PossibleMatchRange for a Prog. bool Prog::PossibleMatchRange(string* min, string* max, int maxlen) { DFA* dfa = NULL; { MutexLock l(&dfa_mutex_); // Have to use dfa_longest_ to get all strings for full matches. // For example, (a|aa) never matches aa in first-match mode. dfa = dfa_longest_; if (dfa == NULL) { dfa = new DFA(this, Prog::kLongestMatch, dfa_mem_/2); ATOMIC_STORE_RELEASE(&dfa_longest_, dfa); delete_dfa_ = DeleteDFA; } } return dfa->PossibleMatchRange(min, max, maxlen); } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/filtered_re2.cc000066400000000000000000000056761266464252400241010ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include #include "util/util.h" #include "re2/filtered_re2.h" #include "re2/prefilter.h" #include "re2/prefilter_tree.h" namespace re2 { FilteredRE2::FilteredRE2() : compiled_(false), prefilter_tree_(new PrefilterTree()) { } FilteredRE2::~FilteredRE2() { for (size_t i = 0; i < re2_vec_.size(); i++) delete re2_vec_[i]; delete prefilter_tree_; } RE2::ErrorCode FilteredRE2::Add(const StringPiece& pattern, const RE2::Options& options, int* id) { RE2* re = new RE2(pattern, options); RE2::ErrorCode code = re->error_code(); if (!re->ok()) { if (options.log_errors()) { LOG(ERROR) << "Couldn't compile regular expression, skipping: " << re << " due to error " << re->error(); } delete re; } else { *id = re2_vec_.size(); re2_vec_.push_back(re); } return code; } void FilteredRE2::Compile(vector* atoms) { if (compiled_ || re2_vec_.size() == 0) { LOG(INFO) << "C: " << compiled_ << " S:" << re2_vec_.size(); return; } for (size_t i = 0; i < re2_vec_.size(); i++) { Prefilter* prefilter = Prefilter::FromRE2(re2_vec_[i]); prefilter_tree_->Add(prefilter); } atoms->clear(); prefilter_tree_->Compile(atoms); compiled_ = true; } int FilteredRE2::SlowFirstMatch(const StringPiece& text) const { for (size_t i = 0; i < re2_vec_.size(); i++) if (RE2::PartialMatch(text, *re2_vec_[i])) return i; return -1; } int FilteredRE2::FirstMatch(const StringPiece& text, const vector& atoms) const { if (!compiled_) { LOG(DFATAL) << "FirstMatch called before Compile"; return -1; } vector regexps; prefilter_tree_->RegexpsGivenStrings(atoms, ®exps); for (size_t i = 0; i < regexps.size(); i++) if (RE2::PartialMatch(text, *re2_vec_[regexps[i]])) return regexps[i]; return -1; } bool FilteredRE2::AllMatches( const StringPiece& text, const vector& atoms, vector* matching_regexps) const { matching_regexps->clear(); vector regexps; prefilter_tree_->RegexpsGivenStrings(atoms, ®exps); for (size_t i = 0; i < regexps.size(); i++) if (RE2::PartialMatch(text, *re2_vec_[regexps[i]])) matching_regexps->push_back(regexps[i]); return !matching_regexps->empty(); } void FilteredRE2::AllPotentials( const vector& atoms, vector* potential_regexps) const { prefilter_tree_->RegexpsGivenStrings(atoms, potential_regexps); } void FilteredRE2::RegexpsGivenStrings(const vector& matched_atoms, vector* passed_regexps) { prefilter_tree_->RegexpsGivenStrings(matched_atoms, passed_regexps); } void FilteredRE2::PrintPrefilter(int regexpid) { prefilter_tree_->PrintPrefilter(regexpid); } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/filtered_re2.h000066400000000000000000000076071266464252400237370ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // The class FilteredRE2 is used as a wrapper to multiple RE2 regexps. // It provides a prefilter mechanism that helps in cutting down the // number of regexps that need to be actually searched. // // By design, it does not include a string matching engine. This is to // allow the user of the class to use their favorite string match // engine. The overall flow is: Add all the regexps using Add, then // Compile the FilteredRE2. The compile returns strings that need to // be matched. Note that all returned strings are lowercase. For // applying regexps to a search text, the caller does the string // matching using the strings returned. When doing the string match, // note that the caller has to do that on lower cased version of the // search text. Then call FirstMatch or AllMatches with a vector of // indices of strings that were found in the text to get the actual // regexp matches. #ifndef RE2_FILTERED_RE2_H_ #define RE2_FILTERED_RE2_H_ #include #include "re2.h" namespace re2 { using std::vector; class PrefilterTree; class FilteredRE2 { public: FilteredRE2(); ~FilteredRE2(); // Uses RE2 constructor to create a RE2 object (re). Returns // re->error_code(). If error_code is other than NoError, then re is // deleted and not added to re2_vec_. RE2::ErrorCode Add(const StringPiece& pattern, const RE2::Options& options, int *id); // Prepares the regexps added by Add for filtering. Returns a set // of strings that the caller should check for in candidate texts. // The returned strings are lowercased. When doing string matching, // the search text should be lowercased first to find matching // strings from the set of strings returned by Compile. Call after // all Add calls are done. void Compile(vector* strings_to_match); // Returns the index of the first matching regexp. // Returns -1 on no match. Can be called prior to Compile. // Does not do any filtering: simply tries to Match the // regexps in a loop. int SlowFirstMatch(const StringPiece& text) const; // Returns the index of the first matching regexp. // Returns -1 on no match. Compile has to be called before // calling this. int FirstMatch(const StringPiece& text, const vector& atoms) const; // Returns the indices of all matching regexps, after first clearing // matched_regexps. bool AllMatches(const StringPiece& text, const vector& atoms, vector* matching_regexps) const; // Returns the indices of all potentially matching regexps after first // clearing potential_regexps. // A regexp is potentially matching if it passes the filter. // If a regexp passes the filter it may still not match. // A regexp that does not pass the filter is guaranteed to not match. void AllPotentials(const vector& atoms, vector* potential_regexps) const; // The number of regexps added. int NumRegexps() const { return re2_vec_.size(); } private: // Get the individual RE2 objects. Useful for testing. RE2* GetRE2(int regexpid) const { return re2_vec_[regexpid]; } // Print prefilter. void PrintPrefilter(int regexpid); // Useful for testing and debugging. void RegexpsGivenStrings(const vector& matched_atoms, vector* passed_regexps); // All the regexps in the FilteredRE2. vector re2_vec_; // Has the FilteredRE2 been compiled using Compile() bool compiled_; // An AND-OR tree of string atoms used for filtering regexps. PrefilterTree* prefilter_tree_; //DISALLOW_COPY_AND_ASSIGN(FilteredRE2); FilteredRE2(const FilteredRE2&); void operator=(const FilteredRE2&); }; } // namespace re2 #endif // RE2_FILTERED_RE2_H_ openalpr_2.2.4.orig/src/openalpr/support/re2/mimics_pcre.cc000066400000000000000000000135031266464252400240110ustar00rootroot00000000000000// Copyright 2008 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Determine whether this library should match PCRE exactly // for a particular Regexp. (If so, the testing framework can // check that it does.) // // This library matches PCRE except in these cases: // * the regexp contains a repetition of an empty string, // like (a*)* or (a*)+. In this case, PCRE will treat // the repetition sequence as ending with an empty string, // while this library does not. // * Perl and PCRE differ on whether \v matches \n. // For historical reasons, this library implements the Perl behavior. // * Perl and PCRE allow $ in one-line mode to match either the very // end of the text or just before a \n at the end of the text. // This library requires it to match only the end of the text. // * Similarly, Perl and PCRE do not allow ^ in multi-line mode to // match the end of the text if the last character is a \n. // This library does allow it. // // Regexp::MimicsPCRE checks for any of these conditions. #include "util/util.h" #include "re2/regexp.h" #include "re2/walker-inl.h" namespace re2 { // Returns whether re might match an empty string. static bool CanBeEmptyString(Regexp *re); // Walker class to compute whether library handles a regexp // exactly as PCRE would. See comment at top for conditions. class PCREWalker : public Regexp::Walker { public: PCREWalker() {} bool PostVisit(Regexp* re, bool parent_arg, bool pre_arg, bool* child_args, int nchild_args); bool ShortVisit(Regexp* re, bool a) { // Should never be called: we use Walk not WalkExponential. LOG(DFATAL) << "EmptyStringWalker::ShortVisit called"; return a; } }; // Called after visiting each of re's children and accumulating // the return values in child_args. So child_args contains whether // this library mimics PCRE for those subexpressions. bool PCREWalker::PostVisit(Regexp* re, bool parent_arg, bool pre_arg, bool* child_args, int nchild_args) { // If children failed, so do we. for (int i = 0; i < nchild_args; i++) if (!child_args[i]) return false; // Otherwise look for other reasons to fail. switch (re->op()) { // Look for repeated empty string. case kRegexpStar: case kRegexpPlus: case kRegexpQuest: if (CanBeEmptyString(re->sub()[0])) return false; break; case kRegexpRepeat: if (re->max() == -1 && CanBeEmptyString(re->sub()[0])) return false; break; // Look for \v case kRegexpLiteral: if (re->rune() == '\v') return false; break; // Look for $ in single-line mode. case kRegexpEndText: case kRegexpEmptyMatch: if (re->parse_flags() & Regexp::WasDollar) return false; break; // Look for ^ in multi-line mode. case kRegexpBeginLine: // No condition: in single-line mode ^ becomes kRegexpBeginText. return false; default: break; } // Not proven guilty. return true; } // Returns whether this regexp's behavior will mimic PCRE's exactly. bool Regexp::MimicsPCRE() { PCREWalker w; return w.Walk(this, true); } // Walker class to compute whether a Regexp can match an empty string. // It is okay to overestimate. For example, \b\B cannot match an empty // string, because \b and \B are mutually exclusive, but this isn't // that smart and will say it can. Spurious empty strings // will reduce the number of regexps we sanity check against PCRE, // but they won't break anything. class EmptyStringWalker : public Regexp::Walker { public: EmptyStringWalker() { } bool PostVisit(Regexp* re, bool parent_arg, bool pre_arg, bool* child_args, int nchild_args); bool ShortVisit(Regexp* re, bool a) { // Should never be called: we use Walk not WalkExponential. LOG(DFATAL) << "EmptyStringWalker::ShortVisit called"; return a; } private: DISALLOW_COPY_AND_ASSIGN(EmptyStringWalker); }; // Called after visiting re's children. child_args contains the return // value from each of the children's PostVisits (i.e., whether each child // can match an empty string). Returns whether this clause can match an // empty string. bool EmptyStringWalker::PostVisit(Regexp* re, bool parent_arg, bool pre_arg, bool* child_args, int nchild_args) { switch (re->op()) { case kRegexpNoMatch: // never empty case kRegexpLiteral: case kRegexpAnyChar: case kRegexpAnyByte: case kRegexpCharClass: case kRegexpLiteralString: return false; case kRegexpEmptyMatch: // always empty case kRegexpBeginLine: // always empty, when they match case kRegexpEndLine: case kRegexpNoWordBoundary: case kRegexpWordBoundary: case kRegexpBeginText: case kRegexpEndText: case kRegexpStar: // can always be empty case kRegexpQuest: case kRegexpHaveMatch: return true; case kRegexpConcat: // can be empty if all children can for (int i = 0; i < nchild_args; i++) if (!child_args[i]) return false; return true; case kRegexpAlternate: // can be empty if any child can for (int i = 0; i < nchild_args; i++) if (child_args[i]) return true; return false; case kRegexpPlus: // can be empty if the child can case kRegexpCapture: return child_args[0]; case kRegexpRepeat: // can be empty if child can or is x{0} return child_args[0] || re->min() == 0; } return false; } // Returns whether re can match an empty string. static bool CanBeEmptyString(Regexp* re) { EmptyStringWalker w; return w.Walk(re, true); } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/nfa.cc000066400000000000000000000535701266464252400222730ustar00rootroot00000000000000// Copyright 2006-2007 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Tested by search_test.cc. // // Prog::SearchNFA, an NFA search. // This is an actual NFA like the theorists talk about, // not the pseudo-NFA found in backtracking regexp implementations. // // IMPLEMENTATION // // This algorithm is a variant of one that appeared in Rob Pike's sam editor, // which is a variant of the one described in Thompson's 1968 CACM paper. // See http://swtch.com/~rsc/regexp/ for various history. The main feature // over the DFA implementation is that it tracks submatch boundaries. // // When the choice of submatch boundaries is ambiguous, this particular // implementation makes the same choices that traditional backtracking // implementations (in particular, Perl and PCRE) do. // Note that unlike in Perl and PCRE, this algorithm *cannot* take exponential // time in the length of the input. // // Like Thompson's original machine and like the DFA implementation, this // implementation notices a match only once it is one byte past it. #include "re2/prog.h" #include "re2/regexp.h" #include "util/sparse_array.h" #include "util/sparse_set.h" namespace re2 { class NFA { public: NFA(Prog* prog); ~NFA(); // Searches for a matching string. // * If anchored is true, only considers matches starting at offset. // Otherwise finds lefmost match at or after offset. // * If longest is true, returns the longest match starting // at the chosen start point. Otherwise returns the so-called // left-biased match, the one traditional backtracking engines // (like Perl and PCRE) find. // Records submatch boundaries in submatch[1..nsubmatch-1]. // Submatch[0] is the entire match. When there is a choice in // which text matches each subexpression, the submatch boundaries // are chosen to match what a backtracking implementation would choose. bool Search(const StringPiece& text, const StringPiece& context, bool anchored, bool longest, StringPiece* submatch, int nsubmatch); static const int Debug = 0; private: struct Thread { union { int id; Thread* next; // when on free list }; const char** capture; }; // State for explicit stack in AddToThreadq. struct AddState { int id; // Inst to process int j; const char* cap_j; // if j>=0, set capture[j] = cap_j before processing ip AddState() : id(0), j(-1), cap_j(NULL) {} explicit AddState(int id) : id(id), j(-1), cap_j(NULL) {} AddState(int id, const char* cap_j, int j) : id(id), j(j), cap_j(cap_j) {} }; // Threadq is a list of threads. The list is sorted by the order // in which Perl would explore that particular state -- the earlier // choices appear earlier in the list. typedef SparseArray Threadq; inline Thread* AllocThread(); inline void FreeThread(Thread*); // Add id (or its children, following unlabeled arrows) // to the workqueue q with associated capture info. void AddToThreadq(Threadq* q, int id, int flag, const char* p, const char** capture); // Run runq on byte c, appending new states to nextq. // Updates matched_ and match_ as new, better matches are found. // p is position of the next byte (the one after c) // in the input string, used when processing capturing parens. // flag is the bitwise or of Bol, Eol, etc., specifying whether // ^, $ and \b match the current input point (after c). inline int Step(Threadq* runq, Threadq* nextq, int c, int flag, const char* p); // Returns text version of capture information, for debugging. string FormatCapture(const char** capture); inline void CopyCapture(const char** dst, const char** src); // Computes whether all matches must begin with the same first // byte, and if so, returns that byte. If not, returns -1. int ComputeFirstByte(); Prog* prog_; // underlying program int start_; // start instruction in program int ncapture_; // number of submatches to track bool longest_; // whether searching for longest match bool endmatch_; // whether match must end at text.end() const char* btext_; // beginning of text being matched (for FormatSubmatch) const char* etext_; // end of text being matched (for endmatch_) Threadq q0_, q1_; // pre-allocated for Search. const char** match_; // best match so far bool matched_; // any match so far? AddState* astack_; // pre-allocated for AddToThreadq int nastack_; int first_byte_; // required first byte for match, or -1 if none Thread* free_threads_; // free list DISALLOW_COPY_AND_ASSIGN(NFA); }; NFA::NFA(Prog* prog) { prog_ = prog; start_ = prog->start(); ncapture_ = 0; longest_ = false; endmatch_ = false; btext_ = NULL; etext_ = NULL; q0_.resize(prog_->size()); q1_.resize(prog_->size()); nastack_ = 2*prog_->size(); astack_ = new AddState[nastack_]; match_ = NULL; matched_ = false; free_threads_ = NULL; first_byte_ = ComputeFirstByte(); } NFA::~NFA() { delete[] match_; delete[] astack_; Thread* next; for (Thread* t = free_threads_; t; t = next) { next = t->next; delete[] t->capture; delete t; } } void NFA::FreeThread(Thread *t) { if (t == NULL) return; t->next = free_threads_; free_threads_ = t; } NFA::Thread* NFA::AllocThread() { Thread* t = free_threads_; if (t == NULL) { t = new Thread; t->capture = new const char*[ncapture_]; return t; } free_threads_ = t->next; return t; } void NFA::CopyCapture(const char** dst, const char** src) { for (int i = 0; i < ncapture_; i+=2) { dst[i] = src[i]; dst[i+1] = src[i+1]; } } // Follows all empty arrows from id0 and enqueues all the states reached. // The bits in flag (Bol, Eol, etc.) specify whether ^, $ and \b match. // The pointer p is the current input position, and m is the // current set of match boundaries. void NFA::AddToThreadq(Threadq* q, int id0, int flag, const char* p, const char** capture) { if (id0 == 0) return; // Astack_ is pre-allocated to avoid resize operations. // It has room for 2*prog_->size() entries, which is enough: // Each inst in prog can be processed at most once, // pushing at most two entries on stk. int nstk = 0; AddState* stk = astack_; stk[nstk++] = AddState(id0); while (nstk > 0) { DCHECK_LE(nstk, nastack_); const AddState& a = stk[--nstk]; if (a.j >= 0) capture[a.j] = a.cap_j; int id = a.id; if (id == 0) continue; if (q->has_index(id)) { if (Debug) fprintf(stderr, " [%d%s]\n", id, FormatCapture(capture).c_str()); continue; } // Create entry in q no matter what. We might fill it in below, // or we might not. Even if not, it is necessary to have it, // so that we don't revisit id0 during the recursion. q->set_new(id, NULL); Thread** tp = &q->find(id)->second; int j; Thread* t; Prog::Inst* ip = prog_->inst(id); switch (ip->opcode()) { default: LOG(DFATAL) << "unhandled " << ip->opcode() << " in AddToThreadq"; break; case kInstFail: break; case kInstAltMatch: // Save state; will pick up at next byte. t = AllocThread(); t->id = id; CopyCapture(t->capture, capture); *tp = t; // fall through case kInstAlt: // Explore alternatives. stk[nstk++] = AddState(ip->out1()); stk[nstk++] = AddState(ip->out()); break; case kInstNop: // Continue on. stk[nstk++] = AddState(ip->out()); break; case kInstCapture: if ((j=ip->cap()) < ncapture_) { // Push a dummy whose only job is to restore capture[j] // once we finish exploring this possibility. stk[nstk++] = AddState(0, capture[j], j); // Record capture. capture[j] = p; } stk[nstk++] = AddState(ip->out()); break; case kInstMatch: case kInstByteRange: // Save state; will pick up at next byte. t = AllocThread(); t->id = id; CopyCapture(t->capture, capture); *tp = t; if (Debug) fprintf(stderr, " + %d%s [%p]\n", id, FormatCapture(t->capture).c_str(), t); break; case kInstEmptyWidth: // Continue on if we have all the right flag bits. if (ip->empty() & ~flag) break; stk[nstk++] = AddState(ip->out()); break; } } } // Run runq on byte c, appending new states to nextq. // Updates match as new, better matches are found. // p is position of the byte c in the input string, // used when processing capturing parens. // flag is the bitwise or of Bol, Eol, etc., specifying whether // ^, $ and \b match the current input point (after c). // Frees all the threads on runq. // If there is a shortcut to the end, returns that shortcut. int NFA::Step(Threadq* runq, Threadq* nextq, int c, int flag, const char* p) { nextq->clear(); for (Threadq::iterator i = runq->begin(); i != runq->end(); ++i) { Thread* t = i->second; if (t == NULL) continue; if (longest_) { // Can skip any threads started after our current best match. if (matched_ && match_[0] < t->capture[0]) { FreeThread(t); continue; } } int id = t->id; Prog::Inst* ip = prog_->inst(id); switch (ip->opcode()) { default: // Should only see the values handled below. LOG(DFATAL) << "Unhandled " << ip->opcode() << " in step"; break; case kInstByteRange: if (ip->Matches(c)) AddToThreadq(nextq, ip->out(), flag, p+1, t->capture); break; case kInstAltMatch: if (i != runq->begin()) break; // The match is ours if we want it. if (ip->greedy(prog_) || longest_) { CopyCapture((const char**)match_, t->capture); FreeThread(t); for (++i; i != runq->end(); ++i) FreeThread(i->second); runq->clear(); matched_ = true; if (ip->greedy(prog_)) return ip->out1(); return ip->out(); } break; case kInstMatch: if (endmatch_ && p != etext_) break; const char* old = t->capture[1]; // previous end pointer t->capture[1] = p; if (longest_) { // Leftmost-longest mode: save this match only if // it is either farther to the left or at the same // point but longer than an existing match. if (!matched_ || t->capture[0] < match_[0] || (t->capture[0] == match_[0] && t->capture[1] > match_[1])) CopyCapture((const char**)match_, t->capture); } else { // Leftmost-biased mode: this match is by definition // better than what we've already found (see next line). CopyCapture((const char**)match_, t->capture); // Cut off the threads that can only find matches // worse than the one we just found: don't run the // rest of the current Threadq. t->capture[0] = old; FreeThread(t); for (++i; i != runq->end(); ++i) FreeThread(i->second); runq->clear(); matched_ = true; return 0; } t->capture[0] = old; matched_ = true; break; } FreeThread(t); } runq->clear(); return 0; } string NFA::FormatCapture(const char** capture) { string s; for (int i = 0; i < ncapture_; i+=2) { if (capture[i] == NULL) StringAppendF(&s, "(?,?)"); else if (capture[i+1] == NULL) StringAppendF(&s, "(%d,?)", (int)(capture[i] - btext_)); else StringAppendF(&s, "(%d,%d)", (int)(capture[i] - btext_), (int)(capture[i+1] - btext_)); } return s; } // Returns whether haystack contains needle's memory. static bool StringPieceContains(const StringPiece haystack, const StringPiece needle) { return haystack.begin() <= needle.begin() && haystack.end() >= needle.end(); } bool NFA::Search(const StringPiece& text, const StringPiece& const_context, bool anchored, bool longest, StringPiece* submatch, int nsubmatch) { if (start_ == 0) return false; StringPiece context = const_context; if (context.begin() == NULL) context = text; if (!StringPieceContains(context, text)) { LOG(FATAL) << "Bad args: context does not contain text " << reinterpret_cast(context.begin()) << "+" << context.size() << " " << reinterpret_cast(text.begin()) << "+" << text.size(); return false; } if (prog_->anchor_start() && context.begin() != text.begin()) return false; if (prog_->anchor_end() && context.end() != text.end()) return false; anchored |= prog_->anchor_start(); if (prog_->anchor_end()) { longest = true; endmatch_ = true; etext_ = text.end(); } if (nsubmatch < 0) { LOG(DFATAL) << "Bad args: nsubmatch=" << nsubmatch; return false; } // Save search parameters. ncapture_ = 2*nsubmatch; longest_ = longest; if (nsubmatch == 0) { // We need to maintain match[0], both to distinguish the // longest match (if longest is true) and also to tell // whether we've seen any matches at all. ncapture_ = 2; } match_ = new const char*[ncapture_]; matched_ = false; memset(match_, 0, ncapture_*sizeof match_[0]); // For debugging prints. btext_ = context.begin(); if (Debug) { fprintf(stderr, "NFA::Search %s (context: %s) anchored=%d longest=%d\n", text.as_string().c_str(), context.as_string().c_str(), anchored, longest); } // Set up search. Threadq* runq = &q0_; Threadq* nextq = &q1_; runq->clear(); nextq->clear(); memset(&match_[0], 0, ncapture_*sizeof match_[0]); const char* bp = context.begin(); int c = -1; int wasword = 0; if (text.begin() > context.begin()) { c = text.begin()[-1] & 0xFF; wasword = Prog::IsWordChar(c); } // Loop over the text, stepping the machine. for (const char* p = text.begin();; p++) { // Check for empty-width specials. int flag = 0; // ^ and \A if (p == context.begin()) flag |= kEmptyBeginText | kEmptyBeginLine; else if (p <= context.end() && p[-1] == '\n') flag |= kEmptyBeginLine; // $ and \z if (p == context.end()) flag |= kEmptyEndText | kEmptyEndLine; else if (p < context.end() && p[0] == '\n') flag |= kEmptyEndLine; // \b and \B int isword = 0; if (p < context.end()) isword = Prog::IsWordChar(p[0] & 0xFF); if (isword != wasword) flag |= kEmptyWordBoundary; else flag |= kEmptyNonWordBoundary; if (Debug) { fprintf(stderr, "%c[%#x/%d/%d]:", p > text.end() ? '$' : p == bp ? '^' : c, flag, isword, wasword); for (Threadq::iterator i = runq->begin(); i != runq->end(); ++i) { Thread* t = i->second; if (t == NULL) continue; fprintf(stderr, " %d%s", t->id, FormatCapture((const char**)t->capture).c_str()); } fprintf(stderr, "\n"); } // Process previous character (waited until now to avoid // repeating the flag computation above). // This is a no-op the first time around the loop, because // runq is empty. int id = Step(runq, nextq, c, flag, p-1); DCHECK_EQ(runq->size(), 0); swap(nextq, runq); nextq->clear(); if (id != 0) { // We're done: full match ahead. p = text.end(); for (;;) { Prog::Inst* ip = prog_->inst(id); switch (ip->opcode()) { default: LOG(DFATAL) << "Unexpected opcode in short circuit: " << ip->opcode(); break; case kInstCapture: if (ip->cap() < ncapture_) match_[ip->cap()] = p; id = ip->out(); continue; case kInstNop: id = ip->out(); continue; case kInstMatch: match_[1] = p; matched_ = true; break; case kInstEmptyWidth: if (ip->empty() & ~(kEmptyEndLine|kEmptyEndText)) { LOG(DFATAL) << "Unexpected empty-width in short circuit: " << ip->empty(); break; } id = ip->out(); continue; } break; } break; } if (p > text.end()) break; // Start a new thread if there have not been any matches. // (No point in starting a new thread if there have been // matches, since it would be to the right of the match // we already found.) if (!matched_ && (!anchored || p == text.begin())) { // If there's a required first byte for an unanchored search // and we're not in the middle of any possible matches, // use memchr to search for the byte quickly. if (!anchored && first_byte_ >= 0 && runq->size() == 0 && p < text.end() && (p[0] & 0xFF) != first_byte_) { p = reinterpret_cast(memchr(p, first_byte_, text.end() - p)); if (p == NULL) { p = text.end(); isword = 0; } else { isword = Prog::IsWordChar(p[0] & 0xFF); } flag = Prog::EmptyFlags(context, p); } // Steal match storage (cleared but unused as of yet) // temporarily to hold match boundaries for new thread. match_[0] = p; AddToThreadq(runq, start_, flag, p, match_); match_[0] = NULL; } // If all the threads have died, stop early. if (runq->size() == 0) { if (Debug) fprintf(stderr, "dead\n"); break; } if (p == text.end()) c = 0; else c = *p & 0xFF; wasword = isword; // Will run step(runq, nextq, c, ...) on next iteration. See above. } for (Threadq::iterator i = runq->begin(); i != runq->end(); ++i) FreeThread(i->second); if (matched_) { for (int i = 0; i < nsubmatch; i++) submatch[i].set(match_[2*i], match_[2*i+1] - match_[2*i]); if (Debug) fprintf(stderr, "match (%d,%d)\n", static_cast(match_[0] - btext_), static_cast(match_[1] - btext_)); return true; } VLOG(1) << "No matches found"; return false; } // Computes whether all successful matches have a common first byte, // and if so, returns that byte. If not, returns -1. int NFA::ComputeFirstByte() { if (start_ == 0) return -1; int b = -1; // first byte, not yet computed typedef SparseSet Workq; Workq q(prog_->size()); q.insert(start_); for (Workq::iterator it = q.begin(); it != q.end(); ++it) { int id = *it; Prog::Inst* ip = prog_->inst(id); switch (ip->opcode()) { default: LOG(DFATAL) << "unhandled " << ip->opcode() << " in ComputeFirstByte"; break; case kInstMatch: // The empty string matches: no first byte. return -1; case kInstByteRange: // Must match only a single byte if (ip->lo() != ip->hi()) return -1; if (ip->foldcase() && 'a' <= ip->lo() && ip->lo() <= 'z') return -1; // If we haven't seen any bytes yet, record it; // otherwise must match the one we saw before. if (b == -1) b = ip->lo(); else if (b != ip->lo()) return -1; break; case kInstNop: case kInstCapture: case kInstEmptyWidth: // Continue on. // Ignore ip->empty() flags for kInstEmptyWidth // in order to be as conservative as possible // (assume all possible empty-width flags are true). if (ip->out()) q.insert(ip->out()); break; case kInstAlt: case kInstAltMatch: // Explore alternatives. if (ip->out()) q.insert(ip->out()); if (ip->out1()) q.insert(ip->out1()); break; case kInstFail: break; } } return b; } bool Prog::SearchNFA(const StringPiece& text, const StringPiece& context, Anchor anchor, MatchKind kind, StringPiece* match, int nmatch) { if (NFA::Debug) Dump(); NFA nfa(this); StringPiece sp; if (kind == kFullMatch) { anchor = kAnchored; if (nmatch == 0) { match = &sp; nmatch = 1; } } if (!nfa.Search(text, context, anchor == kAnchored, kind != kFirstMatch, match, nmatch)) return false; if (kind == kFullMatch && match[0].end() != text.end()) return false; return true; } // For each instruction i in the program reachable from the start, compute the // number of instructions reachable from i by following only empty transitions // and record that count as fanout[i]. // // fanout holds the results and is also the work queue for the outer iteration. // reachable holds the reached nodes for the inner iteration. void Prog::Fanout(SparseArray* fanout) { DCHECK_EQ(fanout->max_size(), size()); SparseSet reachable(size()); fanout->clear(); fanout->set_new(start(), 0); for (SparseArray::iterator i = fanout->begin(); i != fanout->end(); ++i) { int* count = &i->second; reachable.clear(); reachable.insert(i->index()); for (SparseSet::iterator j = reachable.begin(); j != reachable.end(); ++j) { Prog::Inst* ip = inst(*j); switch (ip->opcode()) { default: LOG(DFATAL) << "unhandled " << ip->opcode() << " in Prog::Fanout()"; break; case kInstByteRange: (*count)++; if (!fanout->has_index(ip->out())) { fanout->set_new(ip->out(), 0); } break; case kInstAlt: case kInstAltMatch: reachable.insert(ip->out1()); // fall through case kInstCapture: case kInstEmptyWidth: case kInstNop: reachable.insert(ip->out()); break; case kInstMatch: case kInstFail: break; } } } } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/onepass.cc000066400000000000000000000530531266464252400231730ustar00rootroot00000000000000// Copyright 2008 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Tested by search_test.cc. // // Prog::SearchOnePass is an efficient implementation of // regular expression search with submatch tracking for // what I call "one-pass regular expressions". (An alternate // name might be "backtracking-free regular expressions".) // // One-pass regular expressions have the property that // at each input byte during an anchored match, there may be // multiple alternatives but only one can proceed for any // given input byte. // // For example, the regexp /x*yx*/ is one-pass: you read // x's until a y, then you read the y, then you keep reading x's. // At no point do you have to guess what to do or back up // and try a different guess. // // On the other hand, /x*x/ is not one-pass: when you're // looking at an input "x", it's not clear whether you should // use it to extend the x* or as the final x. // // More examples: /([^ ]*) (.*)/ is one-pass; /(.*) (.*)/ is not. // /(\d+)-(\d+)/ is one-pass; /(\d+).(\d+)/ is not. // // A simple intuition for identifying one-pass regular expressions // is that it's always immediately obvious when a repetition ends. // It must also be immediately obvious which branch of an | to take: // // /x(y|z)/ is one-pass, but /(xy|xz)/ is not. // // The NFA-based search in nfa.cc does some bookkeeping to // avoid the need for backtracking and its associated exponential blowup. // But if we have a one-pass regular expression, there is no // possibility of backtracking, so there is no need for the // extra bookkeeping. Hence, this code. // // On a one-pass regular expression, the NFA code in nfa.cc // runs at about 1/20 of the backtracking-based PCRE speed. // In contrast, the code in this file runs at about the same // speed as PCRE. // // One-pass regular expressions get used a lot when RE is // used for parsing simple strings, so it pays off to // notice them and handle them efficiently. // // See also Anne Brüggemann-Klein and Derick Wood, // "One-unambiguous regular languages", Information and Computation 142(2). #include #include #include "util/util.h" #include "util/sparse_set.h" #include "re2/prog.h" #include "re2/stringpiece.h" namespace re2 { static const int Debug = 0; // The key insight behind this implementation is that the // non-determinism in an NFA for a one-pass regular expression // is contained. To explain what that means, first a // refresher about what regular expression programs look like // and how the usual NFA execution runs. // // In a regular expression program, only the kInstByteRange // instruction processes an input byte c and moves on to the // next byte in the string (it does so if c is in the given range). // The kInstByteRange instructions correspond to literal characters // and character classes in the regular expression. // // The kInstAlt instructions are used as wiring to connect the // kInstByteRange instructions together in interesting ways when // implementing | + and *. // The kInstAlt instruction forks execution, like a goto that // jumps to ip->out() and ip->out1() in parallel. Each of the // resulting computation paths is called a thread. // // The other instructions -- kInstEmptyWidth, kInstMatch, kInstCapture -- // are interesting in their own right but like kInstAlt they don't // advance the input pointer. Only kInstByteRange does. // // The automaton execution in nfa.cc runs all the possible // threads of execution in lock-step over the input. To process // a particular byte, each thread gets run until it either dies // or finds a kInstByteRange instruction matching the byte. // If the latter happens, the thread stops just past the // kInstByteRange instruction (at ip->out()) and waits for // the other threads to finish processing the input byte. // Then, once all the threads have processed that input byte, // the whole process repeats. The kInstAlt state instruction // might create new threads during input processing, but no // matter what, all the threads stop after a kInstByteRange // and wait for the other threads to "catch up". // Running in lock step like this ensures that the NFA reads // the input string only once. // // Each thread maintains its own set of capture registers // (the string positions at which it executed the kInstCapture // instructions corresponding to capturing parentheses in the // regular expression). Repeated copying of the capture registers // is the main performance bottleneck in the NFA implementation. // // A regular expression program is "one-pass" if, no matter what // the input string, there is only one thread that makes it // past a kInstByteRange instruction at each input byte. This means // that there is in some sense only one active thread throughout // the execution. Other threads might be created during the // processing of an input byte, but they are ephemeral: only one // thread is left to start processing the next input byte. // This is what I meant above when I said the non-determinism // was "contained". // // To execute a one-pass regular expression program, we can build // a DFA (no non-determinism) that has at most as many states as // the NFA (compare this to the possibly exponential number of states // in the general case). Each state records, for each possible // input byte, the next state along with the conditions required // before entering that state -- empty-width flags that must be true // and capture operations that must be performed. It also records // whether a set of conditions required to finish a match at that // point in the input rather than process the next byte. // A state in the one-pass NFA - just an array of actions indexed // by the bytemap_[] of the next input byte. (The bytemap // maps next input bytes into equivalence classes, to reduce // the memory footprint.) struct OneState { uint32 matchcond; // conditions to match right now. uint32 action[1]; }; // The uint32 conditions in the action are a combination of // condition and capture bits and the next state. The bottom 16 bits // are the condition and capture bits, and the top 16 are the index of // the next state. // // Bits 0-5 are the empty-width flags from prog.h. // Bit 6 is kMatchWins, which means the match takes // priority over moving to next in a first-match search. // The remaining bits mark capture registers that should // be set to the current input position. The capture bits // start at index 2, since the search loop can take care of // cap[0], cap[1] (the overall match position). // That means we can handle up to 5 capturing parens: $1 through $4, plus $0. // No input position can satisfy both kEmptyWordBoundary // and kEmptyNonWordBoundary, so we can use that as a sentinel // instead of needing an extra bit. static const int kIndexShift = 16; // number of bits below index static const int kEmptyShift = 6; // number of empty flags in prog.h static const int kRealCapShift = kEmptyShift + 1; static const int kRealMaxCap = (kIndexShift - kRealCapShift) / 2 * 2; // Parameters used to skip over cap[0], cap[1]. static const int kCapShift = kRealCapShift - 2; static const int kMaxCap = kRealMaxCap + 2; static const uint32 kMatchWins = 1 << kEmptyShift; static const uint32 kCapMask = ((1 << kRealMaxCap) - 1) << kRealCapShift; static const uint32 kImpossible = kEmptyWordBoundary | kEmptyNonWordBoundary; // Check, at compile time, that prog.h agrees with math above. // This function is never called. void OnePass_Checks() { COMPILE_ASSERT((1<( const_cast(nodes + statesize*nodeindex)); } bool Prog::SearchOnePass(const StringPiece& text, const StringPiece& const_context, Anchor anchor, MatchKind kind, StringPiece* match, int nmatch) { if (anchor != kAnchored && kind != kFullMatch) { LOG(DFATAL) << "Cannot use SearchOnePass for unanchored matches."; return false; } // Make sure we have at least cap[1], // because we use it to tell if we matched. int ncap = 2*nmatch; if (ncap < 2) ncap = 2; const char* cap[kMaxCap]; for (int i = 0; i < ncap; i++) cap[i] = NULL; const char* matchcap[kMaxCap]; for (int i = 0; i < ncap; i++) matchcap[i] = NULL; StringPiece context = const_context; if (context.begin() == NULL) context = text; if (anchor_start() && context.begin() != text.begin()) return false; if (anchor_end() && context.end() != text.end()) return false; if (anchor_end()) kind = kFullMatch; // State and act are marked volatile to // keep the compiler from re-ordering the // memory accesses walking over the NFA. // This is worth about 5%. volatile OneState* state = onepass_start_; volatile uint8* nodes = onepass_nodes_; volatile uint32 statesize = onepass_statesize_; uint8* bytemap = bytemap_; const char* bp = text.begin(); const char* ep = text.end(); const char* p; bool matched = false; matchcap[0] = bp; cap[0] = bp; uint32 nextmatchcond = state->matchcond; for (p = bp; p < ep; p++) { int c = bytemap[*p & 0xFF]; uint32 matchcond = nextmatchcond; uint32 cond = state->action[c]; // Determine whether we can reach act->next. // If so, advance state and nextmatchcond. if ((cond & kEmptyAllFlags) == 0 || Satisfy(cond, context, p)) { uint32 nextindex = cond >> kIndexShift; state = IndexToNode(nodes, statesize, nextindex); nextmatchcond = state->matchcond; } else { state = NULL; nextmatchcond = kImpossible; } // This code section is carefully tuned. // The goto sequence is about 10% faster than the // obvious rewrite as a large if statement in the // ASCIIMatchRE2 and DotMatchRE2 benchmarks. // Saving the match capture registers is expensive. // Is this intermediate match worth thinking about? // Not if we want a full match. if (kind == kFullMatch) goto skipmatch; // Not if it's impossible. if (matchcond == kImpossible) goto skipmatch; // Not if the possible match is beaten by the certain // match at the next byte. When this test is useless // (e.g., HTTPPartialMatchRE2) it slows the loop by // about 10%, but when it avoids work (e.g., DotMatchRE2), // it cuts the loop execution by about 45%. if ((cond & kMatchWins) == 0 && (nextmatchcond & kEmptyAllFlags) == 0) goto skipmatch; // Finally, the match conditions must be satisfied. if ((matchcond & kEmptyAllFlags) == 0 || Satisfy(matchcond, context, p)) { for (int i = 2; i < 2*nmatch; i++) matchcap[i] = cap[i]; if (nmatch > 1 && (matchcond & kCapMask)) ApplyCaptures(matchcond, p, matchcap, ncap); matchcap[1] = p; matched = true; // If we're in longest match mode, we have to keep // going and see if we find a longer match. // In first match mode, we can stop if the match // takes priority over the next state for this input byte. // That bit is per-input byte and thus in cond, not matchcond. if (kind == kFirstMatch && (cond & kMatchWins)) goto done; } skipmatch: if (state == NULL) goto done; if ((cond & kCapMask) && nmatch > 1) ApplyCaptures(cond, p, cap, ncap); } // Look for match at end of input. { uint32 matchcond = state->matchcond; if (matchcond != kImpossible && ((matchcond & kEmptyAllFlags) == 0 || Satisfy(matchcond, context, p))) { if (nmatch > 1 && (matchcond & kCapMask)) ApplyCaptures(matchcond, p, cap, ncap); for (int i = 2; i < ncap; i++) matchcap[i] = cap[i]; matchcap[1] = p; matched = true; } } done: if (!matched) return false; for (int i = 0; i < nmatch; i++) match[i].set(matchcap[2*i], matchcap[2*i+1] - matchcap[2*i]); return true; } // Analysis to determine whether a given regexp program is one-pass. // If ip is not on workq, adds ip to work queue and returns true. // If ip is already on work queue, does nothing and returns false. // If ip is NULL, does nothing and returns true (pretends to add it). typedef SparseSet Instq; static bool AddQ(Instq *q, int id) { if (id == 0) return true; if (q->contains(id)) return false; q->insert(id); return true; } struct InstCond { int id; uint32 cond; }; // Returns whether this is a one-pass program; that is, // returns whether it is safe to use SearchOnePass on this program. // These conditions must be true for any instruction ip: // // (1) for any other Inst nip, there is at most one input-free // path from ip to nip. // (2) there is at most one kInstByte instruction reachable from // ip that matches any particular byte c. // (3) there is at most one input-free path from ip to a kInstMatch // instruction. // // This is actually just a conservative approximation: it might // return false when the answer is true, when kInstEmptyWidth // instructions are involved. // Constructs and saves corresponding one-pass NFA on success. bool Prog::IsOnePass() { if (did_onepass_) return onepass_start_ != NULL; did_onepass_ = true; if (start() == 0) // no match return false; // Steal memory for the one-pass NFA from the overall DFA budget. // Willing to use at most 1/4 of the DFA budget (heuristic). // Limit max node count to 65000 as a conservative estimate to // avoid overflowing 16-bit node index in encoding. int maxnodes = 2 + byte_inst_count_; int statesize = sizeof(OneState) + (bytemap_range_-1)*sizeof(uint32); if (maxnodes >= 65000 || dfa_mem_ / 4 / statesize < maxnodes) return false; // Flood the graph starting at the start state, and check // that in each reachable state, each possible byte leads // to a unique next state. int size = this->size(); InstCond *stack = new InstCond[size]; int* nodebyid = new int[size]; // indexed by ip memset(nodebyid, 0xFF, size*sizeof nodebyid[0]); uint8* nodes = new uint8[maxnodes*statesize]; uint8* nodep = nodes; Instq tovisit(size), workq(size); AddQ(&tovisit, start()); nodebyid[start()] = 0; nodep += statesize; int nalloc = 1; for (Instq::iterator it = tovisit.begin(); it != tovisit.end(); ++it) { int id = *it; int nodeindex = nodebyid[id]; OneState* node = IndexToNode(nodes, statesize, nodeindex); // Flood graph using manual stack, filling in actions as found. // Default is none. for (int b = 0; b < bytemap_range_; b++) node->action[b] = kImpossible; node->matchcond = kImpossible; workq.clear(); bool matched = false; int nstack = 0; stack[nstack].id = id; stack[nstack++].cond = 0; while (nstack > 0) { int id = stack[--nstack].id; Prog::Inst* ip = inst(id); uint32 cond = stack[nstack].cond; switch (ip->opcode()) { case kInstAltMatch: // TODO(rsc): Ignoring kInstAltMatch optimization. // Should implement it in this engine, but it's subtle. // Fall through. case kInstAlt: // If already on work queue, (1) is violated: bail out. if (!AddQ(&workq, ip->out()) || !AddQ(&workq, ip->out1())) goto fail; stack[nstack].id = ip->out1(); stack[nstack++].cond = cond; stack[nstack].id = ip->out(); stack[nstack++].cond = cond; break; case kInstByteRange: { int nextindex = nodebyid[ip->out()]; if (nextindex == -1) { if (nalloc >= maxnodes) { if (Debug) LOG(ERROR) << StringPrintf("Not OnePass: hit node limit %d > %d", nalloc, maxnodes); goto fail; } nextindex = nalloc; nodep += statesize; nodebyid[ip->out()] = nextindex; nalloc++; AddQ(&tovisit, ip->out()); } if (matched) cond |= kMatchWins; for (int c = ip->lo(); c <= ip->hi(); c++) { int b = bytemap_[c]; c = unbytemap_[b]; // last c in byte class uint32 act = node->action[b]; uint32 newact = (nextindex << kIndexShift) | cond; if ((act & kImpossible) == kImpossible) { node->action[b] = newact; } else if (act != newact) { if (Debug) { LOG(ERROR) << StringPrintf("Not OnePass: conflict on byte " "%#x at state %d", c, *it); } goto fail; } } if (ip->foldcase()) { Rune lo = max(ip->lo(), 'a') + 'A' - 'a'; Rune hi = min(ip->hi(), 'z') + 'A' - 'a'; for (int c = lo; c <= hi; c++) { int b = bytemap_[c]; c = unbytemap_[b]; // last c in class uint32 act = node->action[b]; uint32 newact = (nextindex << kIndexShift) | cond; if ((act & kImpossible) == kImpossible) { node->action[b] = newact; } else if (act != newact) { if (Debug) { LOG(ERROR) << StringPrintf("Not OnePass: conflict on byte " "%#x at state %d", c, *it); } goto fail; } } } break; } case kInstCapture: if (ip->cap() < kMaxCap) cond |= (1 << kCapShift) << ip->cap(); goto QueueEmpty; case kInstEmptyWidth: cond |= ip->empty(); goto QueueEmpty; case kInstNop: QueueEmpty: // kInstCapture and kInstNop always proceed to ip->out(). // kInstEmptyWidth only sometimes proceeds to ip->out(), // but as a conservative approximation we assume it always does. // We could be a little more precise by looking at what c // is, but that seems like overkill. // If already on work queue, (1) is violated: bail out. if (!AddQ(&workq, ip->out())) { if (Debug) { LOG(ERROR) << StringPrintf("Not OnePass: multiple paths" " %d -> %d\n", *it, ip->out()); } goto fail; } stack[nstack].id = ip->out(); stack[nstack++].cond = cond; break; case kInstMatch: if (matched) { // (3) is violated if (Debug) { LOG(ERROR) << StringPrintf("Not OnePass: multiple matches" " from %d\n", *it); } goto fail; } matched = true; node->matchcond = cond; break; case kInstFail: break; } } } if (Debug) { // For debugging, dump one-pass NFA to LOG(ERROR). string dump = "prog dump:\n" + Dump() + "node dump\n"; map idmap; for (int i = 0; i < size; i++) if (nodebyid[i] != -1) idmap[nodebyid[i]] = i; StringAppendF(&dump, "byte ranges:\n"); int i = 0; for (int b = 0; b < bytemap_range_; b++) { int lo = i; while (bytemap_[i] == b) i++; StringAppendF(&dump, "\t%d: %#x-%#x\n", b, lo, i - 1); } for (Instq::iterator it = tovisit.begin(); it != tovisit.end(); ++it) { int id = *it; int nodeindex = nodebyid[id]; if (nodeindex == -1) continue; OneState* node = IndexToNode(nodes, statesize, nodeindex); string s; StringAppendF(&dump, "node %d id=%d: matchcond=%#x\n", nodeindex, id, node->matchcond); for (int i = 0; i < bytemap_range_; i++) { if ((node->action[i] & kImpossible) == kImpossible) continue; StringAppendF(&dump, " %d cond %#x -> %d id=%d\n", i, node->action[i] & 0xFFFF, node->action[i] >> kIndexShift, idmap[node->action[i] >> kIndexShift]); } } LOG(ERROR) << dump; } // Overallocated earlier; cut down to actual size. nodep = new uint8[nalloc*statesize]; memmove(nodep, nodes, nalloc*statesize); delete[] nodes; nodes = nodep; onepass_start_ = IndexToNode(nodes, statesize, nodebyid[start()]); onepass_nodes_ = nodes; onepass_statesize_ = statesize; dfa_mem_ -= nalloc*statesize; delete[] stack; delete[] nodebyid; return true; fail: delete[] stack; delete[] nodebyid; delete[] nodes; return false; } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/parse.cc000066400000000000000000002063451266464252400226410ustar00rootroot00000000000000// Copyright 2006 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Regular expression parser. // The parser is a simple precedence-based parser with a // manual stack. The parsing work is done by the methods // of the ParseState class. The Regexp::Parse function is // essentially just a lexer that calls the ParseState method // for each token. // The parser recognizes POSIX extended regular expressions // excluding backreferences, collating elements, and collating // classes. It also allows the empty string as a regular expression // and recognizes the Perl escape sequences \d, \s, \w, \D, \S, and \W. // See regexp.h for rationale. #include "util/util.h" #include "re2/regexp.h" #include "re2/stringpiece.h" #include "re2/unicode_casefold.h" #include "re2/unicode_groups.h" #include "re2/walker-inl.h" namespace re2 { // Regular expression parse state. // The list of parsed regexps so far is maintained as a vector of // Regexp pointers called the stack. Left parenthesis and vertical // bar markers are also placed on the stack, as Regexps with // non-standard opcodes. // Scanning a left parenthesis causes the parser to push a left parenthesis // marker on the stack. // Scanning a vertical bar causes the parser to pop the stack until it finds a // vertical bar or left parenthesis marker (not popping the marker), // concatenate all the popped results, and push them back on // the stack (DoConcatenation). // Scanning a right parenthesis causes the parser to act as though it // has seen a vertical bar, which then leaves the top of the stack in the // form LeftParen regexp VerticalBar regexp VerticalBar ... regexp VerticalBar. // The parser pops all this off the stack and creates an alternation of the // regexps (DoAlternation). class Regexp::ParseState { public: ParseState(ParseFlags flags, const StringPiece& whole_regexp, RegexpStatus* status); ~ParseState(); ParseFlags flags() { return flags_; } int rune_max() { return rune_max_; } // Parse methods. All public methods return a bool saying // whether parsing should continue. If a method returns // false, it has set fields in *status_, and the parser // should return NULL. // Pushes the given regular expression onto the stack. // Could check for too much memory used here. bool PushRegexp(Regexp* re); // Pushes the literal rune r onto the stack. bool PushLiteral(Rune r); // Pushes a regexp with the given op (and no args) onto the stack. bool PushSimpleOp(RegexpOp op); // Pushes a ^ onto the stack. bool PushCarat(); // Pushes a \b (word == true) or \B (word == false) onto the stack. bool PushWordBoundary(bool word); // Pushes a $ onto the stack. bool PushDollar(); // Pushes a . onto the stack bool PushDot(); // Pushes a repeat operator regexp onto the stack. // A valid argument for the operator must already be on the stack. // s is the name of the operator, for use in error messages. bool PushRepeatOp(RegexpOp op, const StringPiece& s, bool nongreedy); // Pushes a repetition regexp onto the stack. // A valid argument for the operator must already be on the stack. bool PushRepetition(int min, int max, const StringPiece& s, bool nongreedy); // Checks whether a particular regexp op is a marker. bool IsMarker(RegexpOp op); // Processes a left parenthesis in the input. // Pushes a marker onto the stack. bool DoLeftParen(const StringPiece& name); bool DoLeftParenNoCapture(); // Processes a vertical bar in the input. bool DoVerticalBar(); // Processes a right parenthesis in the input. bool DoRightParen(); // Processes the end of input, returning the final regexp. Regexp* DoFinish(); // Finishes the regexp if necessary, preparing it for use // in a more complicated expression. // If it is a CharClassBuilder, converts into a CharClass. Regexp* FinishRegexp(Regexp*); // These routines don't manipulate the parse stack // directly, but they do need to look at flags_. // ParseCharClass also manipulates the internals of Regexp // while creating *out_re. // Parse a character class into *out_re. // Removes parsed text from s. bool ParseCharClass(StringPiece* s, Regexp** out_re, RegexpStatus* status); // Parse a character class character into *rp. // Removes parsed text from s. bool ParseCCCharacter(StringPiece* s, Rune *rp, const StringPiece& whole_class, RegexpStatus* status); // Parse a character class range into rr. // Removes parsed text from s. bool ParseCCRange(StringPiece* s, RuneRange* rr, const StringPiece& whole_class, RegexpStatus* status); // Parse a Perl flag set or non-capturing group from s. bool ParsePerlFlags(StringPiece* s); // Finishes the current concatenation, // collapsing it into a single regexp on the stack. void DoConcatenation(); // Finishes the current alternation, // collapsing it to a single regexp on the stack. void DoAlternation(); // Generalized DoAlternation/DoConcatenation. void DoCollapse(RegexpOp op); // Maybe concatenate Literals into LiteralString. bool MaybeConcatString(int r, ParseFlags flags); private: ParseFlags flags_; StringPiece whole_regexp_; RegexpStatus* status_; Regexp* stacktop_; int ncap_; // number of capturing parens seen int rune_max_; // maximum char value for this encoding DISALLOW_COPY_AND_ASSIGN(ParseState); }; // Pseudo-operators - only on parse stack. const RegexpOp kLeftParen = static_cast(kMaxRegexpOp+1); const RegexpOp kVerticalBar = static_cast(kMaxRegexpOp+2); Regexp::ParseState::ParseState(ParseFlags flags, const StringPiece& whole_regexp, RegexpStatus* status) : flags_(flags), whole_regexp_(whole_regexp), status_(status), stacktop_(NULL), ncap_(0) { if (flags_ & Latin1) rune_max_ = 0xFF; else rune_max_ = Runemax; } // Cleans up by freeing all the regexps on the stack. Regexp::ParseState::~ParseState() { Regexp* next; for (Regexp* re = stacktop_; re != NULL; re = next) { next = re->down_; re->down_ = NULL; if (re->op() == kLeftParen) delete re->name_; re->Decref(); } } // Finishes the regexp if necessary, preparing it for use in // a more complex expression. // If it is a CharClassBuilder, converts into a CharClass. Regexp* Regexp::ParseState::FinishRegexp(Regexp* re) { if (re == NULL) return NULL; re->down_ = NULL; if (re->op_ == kRegexpCharClass && re->ccb_ != NULL) { CharClassBuilder* ccb = re->ccb_; re->ccb_ = NULL; re->cc_ = ccb->GetCharClass(); delete ccb; } return re; } // Pushes the given regular expression onto the stack. // Could check for too much memory used here. bool Regexp::ParseState::PushRegexp(Regexp* re) { MaybeConcatString(-1, NoParseFlags); // Special case: a character class of one character is just // a literal. This is a common idiom for escaping // single characters (e.g., [.] instead of \.), and some // analysis does better with fewer character classes. // Similarly, [Aa] can be rewritten as a literal A with ASCII case folding. if (re->op_ == kRegexpCharClass) { re->ccb_->RemoveAbove(rune_max_); if (re->ccb_->size() == 1) { Rune r = re->ccb_->begin()->lo; re->Decref(); re = new Regexp(kRegexpLiteral, flags_); re->rune_ = r; } else if (re->ccb_->size() == 2) { Rune r = re->ccb_->begin()->lo; if ('A' <= r && r <= 'Z' && re->ccb_->Contains(r + 'a' - 'A')) { re->Decref(); re = new Regexp(kRegexpLiteral, flags_ | FoldCase); re->rune_ = r + 'a' - 'A'; } } } if (!IsMarker(re->op())) re->simple_ = re->ComputeSimple(); re->down_ = stacktop_; stacktop_ = re; return true; } // Searches the case folding tables and returns the CaseFold* that contains r. // If there isn't one, returns the CaseFold* with smallest f->lo bigger than r. // If there isn't one, returns NULL. const CaseFold* LookupCaseFold(const CaseFold *f, int n, Rune r) { const CaseFold* ef = f + n; // Binary search for entry containing r. while (n > 0) { int m = n/2; if (f[m].lo <= r && r <= f[m].hi) return &f[m]; if (r < f[m].lo) { n = m; } else { f += m+1; n -= m+1; } } // There is no entry that contains r, but f points // where it would have been. Unless f points at // the end of the array, it points at the next entry // after r. if (f < ef) return f; // No entry contains r; no entry contains runes > r. return NULL; } // Returns the result of applying the fold f to the rune r. Rune ApplyFold(const CaseFold *f, Rune r) { switch (f->delta) { default: return r + f->delta; case EvenOddSkip: // even <-> odd but only applies to every other if ((r - f->lo) % 2) return r; // fall through case EvenOdd: // even <-> odd if (r%2 == 0) return r + 1; return r - 1; case OddEvenSkip: // odd <-> even but only applies to every other if ((r - f->lo) % 2) return r; // fall through case OddEven: // odd <-> even if (r%2 == 1) return r + 1; return r - 1; } } // Returns the next Rune in r's folding cycle (see unicode_casefold.h). // Examples: // CycleFoldRune('A') = 'a' // CycleFoldRune('a') = 'A' // // CycleFoldRune('K') = 'k' // CycleFoldRune('k') = 0x212A (Kelvin) // CycleFoldRune(0x212A) = 'K' // // CycleFoldRune('?') = '?' Rune CycleFoldRune(Rune r) { const CaseFold* f = LookupCaseFold(unicode_casefold, num_unicode_casefold, r); if (f == NULL || r < f->lo) return r; return ApplyFold(f, r); } // Add lo-hi to the class, along with their fold-equivalent characters. // If lo-hi is already in the class, assume that the fold-equivalent // chars are there too, so there's no work to do. static void AddFoldedRange(CharClassBuilder* cc, Rune lo, Rune hi, int depth) { // AddFoldedRange calls itself recursively for each rune in the fold cycle. // Most folding cycles are small: there aren't any bigger than four in the // current Unicode tables. make_unicode_casefold.py checks that // the cycles are not too long, and we double-check here using depth. if (depth > 10) { LOG(DFATAL) << "AddFoldedRange recurses too much."; return; } if (!cc->AddRange(lo, hi)) // lo-hi was already there? we're done return; while (lo <= hi) { const CaseFold* f = LookupCaseFold(unicode_casefold, num_unicode_casefold, lo); if (f == NULL) // lo has no fold, nor does anything above lo break; if (lo < f->lo) { // lo has no fold; next rune with a fold is f->lo lo = f->lo; continue; } // Add in the result of folding the range lo - f->hi // and that range's fold, recursively. Rune lo1 = lo; Rune hi1 = min(hi, f->hi); switch (f->delta) { default: lo1 += f->delta; hi1 += f->delta; break; case EvenOdd: if (lo1%2 == 1) lo1--; if (hi1%2 == 0) hi1++; break; case OddEven: if (lo1%2 == 0) lo1--; if (hi1%2 == 1) hi1++; break; } AddFoldedRange(cc, lo1, hi1, depth+1); // Pick up where this fold left off. lo = f->hi + 1; } } // Pushes the literal rune r onto the stack. bool Regexp::ParseState::PushLiteral(Rune r) { // Do case folding if needed. if ((flags_ & FoldCase) && CycleFoldRune(r) != r) { Regexp* re = new Regexp(kRegexpCharClass, flags_ & ~FoldCase); re->ccb_ = new CharClassBuilder; Rune r1 = r; do { if (!(flags_ & NeverNL) || r != '\n') { re->ccb_->AddRange(r, r); } r = CycleFoldRune(r); } while (r != r1); return PushRegexp(re); } // Exclude newline if applicable. if ((flags_ & NeverNL) && r == '\n') return PushRegexp(new Regexp(kRegexpNoMatch, flags_)); // No fancy stuff worked. Ordinary literal. if (MaybeConcatString(r, flags_)) return true; Regexp* re = new Regexp(kRegexpLiteral, flags_); re->rune_ = r; return PushRegexp(re); } // Pushes a ^ onto the stack. bool Regexp::ParseState::PushCarat() { if (flags_ & OneLine) { return PushSimpleOp(kRegexpBeginText); } return PushSimpleOp(kRegexpBeginLine); } // Pushes a \b or \B onto the stack. bool Regexp::ParseState::PushWordBoundary(bool word) { if (word) return PushSimpleOp(kRegexpWordBoundary); return PushSimpleOp(kRegexpNoWordBoundary); } // Pushes a $ onto the stack. bool Regexp::ParseState::PushDollar() { if (flags_ & OneLine) { // Clumsy marker so that MimicsPCRE() can tell whether // this kRegexpEndText was a $ and not a \z. Regexp::ParseFlags oflags = flags_; flags_ = flags_ | WasDollar; bool ret = PushSimpleOp(kRegexpEndText); flags_ = oflags; return ret; } return PushSimpleOp(kRegexpEndLine); } // Pushes a . onto the stack. bool Regexp::ParseState::PushDot() { if ((flags_ & DotNL) && !(flags_ & NeverNL)) return PushSimpleOp(kRegexpAnyChar); // Rewrite . into [^\n] Regexp* re = new Regexp(kRegexpCharClass, flags_ & ~FoldCase); re->ccb_ = new CharClassBuilder; re->ccb_->AddRange(0, '\n' - 1); re->ccb_->AddRange('\n' + 1, rune_max_); return PushRegexp(re); } // Pushes a regexp with the given op (and no args) onto the stack. bool Regexp::ParseState::PushSimpleOp(RegexpOp op) { Regexp* re = new Regexp(op, flags_); return PushRegexp(re); } // Pushes a repeat operator regexp onto the stack. // A valid argument for the operator must already be on the stack. // The char c is the name of the operator, for use in error messages. bool Regexp::ParseState::PushRepeatOp(RegexpOp op, const StringPiece& s, bool nongreedy) { if (stacktop_ == NULL || IsMarker(stacktop_->op())) { status_->set_code(kRegexpRepeatArgument); status_->set_error_arg(s); return false; } Regexp::ParseFlags fl = flags_; if (nongreedy) fl = fl ^ NonGreedy; Regexp* re = new Regexp(op, fl); re->AllocSub(1); re->down_ = stacktop_->down_; re->sub()[0] = FinishRegexp(stacktop_); re->simple_ = re->ComputeSimple(); stacktop_ = re; return true; } // RepetitionWalker reports whether the repetition regexp is valid. // Valid means that the combination of the top-level repetition // and any inner repetitions does not exceed n copies of the // innermost thing. // This rewalks the regexp tree and is called for every repetition, // so we have to worry about inducing quadratic behavior in the parser. // We avoid this by only using RepetitionWalker when min or max >= 2. // In that case the depth of any >= 2 nesting can only get to 9 without // triggering a parse error, so each subtree can only be rewalked 9 times. class RepetitionWalker : public Regexp::Walker { public: RepetitionWalker() {} virtual int PreVisit(Regexp* re, int parent_arg, bool* stop); virtual int PostVisit(Regexp* re, int parent_arg, int pre_arg, int* child_args, int nchild_args); virtual int ShortVisit(Regexp* re, int parent_arg); private: DISALLOW_COPY_AND_ASSIGN(RepetitionWalker); }; int RepetitionWalker::PreVisit(Regexp* re, int parent_arg, bool* stop) { int arg = parent_arg; if (re->op() == kRegexpRepeat) { int m = re->max(); if (m < 0) { m = re->min(); } if (m > 0) { arg /= m; } } return arg; } int RepetitionWalker::PostVisit(Regexp* re, int parent_arg, int pre_arg, int* child_args, int nchild_args) { int arg = pre_arg; for (int i = 0; i < nchild_args; i++) { if (child_args[i] < arg) { arg = child_args[i]; } } return arg; } int RepetitionWalker::ShortVisit(Regexp* re, int parent_arg) { // This should never be called, since we use Walk and not // WalkExponential. LOG(DFATAL) << "RepetitionWalker::ShortVisit called"; return 0; } // Pushes a repetition regexp onto the stack. // A valid argument for the operator must already be on the stack. bool Regexp::ParseState::PushRepetition(int min, int max, const StringPiece& s, bool nongreedy) { if ((max != -1 && max < min) || min > 1000 || max > 1000) { status_->set_code(kRegexpRepeatSize); status_->set_error_arg(s); return false; } if (stacktop_ == NULL || IsMarker(stacktop_->op())) { status_->set_code(kRegexpRepeatArgument); status_->set_error_arg(s); return false; } Regexp::ParseFlags fl = flags_; if (nongreedy) fl = fl ^ NonGreedy; Regexp* re = new Regexp(kRegexpRepeat, fl); re->min_ = min; re->max_ = max; re->AllocSub(1); re->down_ = stacktop_->down_; re->sub()[0] = FinishRegexp(stacktop_); re->simple_ = re->ComputeSimple(); stacktop_ = re; if (min >= 2 || max >= 2) { RepetitionWalker w; if (w.Walk(stacktop_, 1000) == 0) { status_->set_code(kRegexpRepeatSize); status_->set_error_arg(s); return false; } } return true; } // Checks whether a particular regexp op is a marker. bool Regexp::ParseState::IsMarker(RegexpOp op) { return op >= kLeftParen; } // Processes a left parenthesis in the input. // Pushes a marker onto the stack. bool Regexp::ParseState::DoLeftParen(const StringPiece& name) { Regexp* re = new Regexp(kLeftParen, flags_); re->cap_ = ++ncap_; if (name.data() != NULL) re->name_ = new string(name.as_string()); return PushRegexp(re); } // Pushes a non-capturing marker onto the stack. bool Regexp::ParseState::DoLeftParenNoCapture() { Regexp* re = new Regexp(kLeftParen, flags_); re->cap_ = -1; return PushRegexp(re); } // Adds r to cc, along with r's upper case if foldascii is set. static void AddLiteral(CharClassBuilder* cc, Rune r, bool foldascii) { cc->AddRange(r, r); if (foldascii && 'a' <= r && r <= 'z') cc->AddRange(r + 'A' - 'a', r + 'A' - 'a'); } // Processes a vertical bar in the input. bool Regexp::ParseState::DoVerticalBar() { MaybeConcatString(-1, NoParseFlags); DoConcatenation(); // Below the vertical bar is a list to alternate. // Above the vertical bar is a list to concatenate. // We just did the concatenation, so either swap // the result below the vertical bar or push a new // vertical bar on the stack. Regexp* r1; Regexp* r2; if ((r1 = stacktop_) != NULL && (r2 = stacktop_->down_) != NULL && r2->op() == kVerticalBar) { // If above and below vertical bar are literal or char class, // can merge into a single char class. Regexp* r3; if ((r1->op() == kRegexpLiteral || r1->op() == kRegexpCharClass || r1->op() == kRegexpAnyChar) && (r3 = r2->down_) != NULL) { Rune rune; switch (r3->op()) { case kRegexpLiteral: // convert to char class rune = r3->rune_; r3->op_ = kRegexpCharClass; r3->cc_ = NULL; r3->ccb_ = new CharClassBuilder; AddLiteral(r3->ccb_, rune, r3->parse_flags_ & Regexp::FoldCase); // fall through case kRegexpCharClass: if (r1->op() == kRegexpLiteral) AddLiteral(r3->ccb_, r1->rune_, r1->parse_flags_ & Regexp::FoldCase); else if (r1->op() == kRegexpCharClass) r3->ccb_->AddCharClass(r1->ccb_); if (r1->op() == kRegexpAnyChar || r3->ccb_->full()) { delete r3->ccb_; r3->ccb_ = NULL; r3->op_ = kRegexpAnyChar; } // fall through case kRegexpAnyChar: // pop r1 stacktop_ = r2; r1->Decref(); return true; default: break; } } // Swap r1 below vertical bar (r2). r1->down_ = r2->down_; r2->down_ = r1; stacktop_ = r2; return true; } return PushSimpleOp(kVerticalBar); } // Processes a right parenthesis in the input. bool Regexp::ParseState::DoRightParen() { // Finish the current concatenation and alternation. DoAlternation(); // The stack should be: LeftParen regexp // Remove the LeftParen, leaving the regexp, // parenthesized. Regexp* r1; Regexp* r2; if ((r1 = stacktop_) == NULL || (r2 = r1->down_) == NULL || r2->op() != kLeftParen) { status_->set_code(kRegexpMissingParen); status_->set_error_arg(whole_regexp_); return false; } // Pop off r1, r2. Will Decref or reuse below. stacktop_ = r2->down_; // Restore flags from when paren opened. Regexp* re = r2; flags_ = re->parse_flags(); // Rewrite LeftParen as capture if needed. if (re->cap_ > 0) { re->op_ = kRegexpCapture; // re->cap_ is already set re->AllocSub(1); re->sub()[0] = FinishRegexp(r1); re->simple_ = re->ComputeSimple(); } else { re->Decref(); re = r1; } return PushRegexp(re); } // Processes the end of input, returning the final regexp. Regexp* Regexp::ParseState::DoFinish() { DoAlternation(); Regexp* re = stacktop_; if (re != NULL && re->down_ != NULL) { status_->set_code(kRegexpMissingParen); status_->set_error_arg(whole_regexp_); return NULL; } stacktop_ = NULL; return FinishRegexp(re); } // Returns the leading regexp that re starts with. // The returned Regexp* points into a piece of re, // so it must not be used after the caller calls re->Decref(). Regexp* Regexp::LeadingRegexp(Regexp* re) { if (re->op() == kRegexpEmptyMatch) return NULL; if (re->op() == kRegexpConcat && re->nsub() >= 2) { Regexp** sub = re->sub(); if (sub[0]->op() == kRegexpEmptyMatch) return NULL; return sub[0]; } return re; } // Removes LeadingRegexp(re) from re and returns what's left. // Consumes the reference to re and may edit it in place. // If caller wants to hold on to LeadingRegexp(re), // must have already Incref'ed it. Regexp* Regexp::RemoveLeadingRegexp(Regexp* re) { if (re->op() == kRegexpEmptyMatch) return re; if (re->op() == kRegexpConcat && re->nsub() >= 2) { Regexp** sub = re->sub(); if (sub[0]->op() == kRegexpEmptyMatch) return re; sub[0]->Decref(); sub[0] = NULL; if (re->nsub() == 2) { // Collapse concatenation to single regexp. Regexp* nre = sub[1]; sub[1] = NULL; re->Decref(); return nre; } // 3 or more -> 2 or more. re->nsub_--; memmove(sub, sub + 1, re->nsub_ * sizeof sub[0]); return re; } Regexp::ParseFlags pf = re->parse_flags(); re->Decref(); return new Regexp(kRegexpEmptyMatch, pf); } // Returns the leading string that re starts with. // The returned Rune* points into a piece of re, // so it must not be used after the caller calls re->Decref(). Rune* Regexp::LeadingString(Regexp* re, int *nrune, Regexp::ParseFlags *flags) { while (re->op() == kRegexpConcat && re->nsub() > 0) re = re->sub()[0]; *flags = static_cast(re->parse_flags_ & Regexp::FoldCase); if (re->op() == kRegexpLiteral) { *nrune = 1; return &re->rune_; } if (re->op() == kRegexpLiteralString) { *nrune = re->nrunes_; return re->runes_; } *nrune = 0; return NULL; } // Removes the first n leading runes from the beginning of re. // Edits re in place. void Regexp::RemoveLeadingString(Regexp* re, int n) { // Chase down concats to find first string. // For regexps generated by parser, nested concats are // flattened except when doing so would overflow the 16-bit // limit on the size of a concatenation, so we should never // see more than two here. Regexp* stk[4]; int d = 0; while (re->op() == kRegexpConcat) { if (d < arraysize(stk)) stk[d++] = re; re = re->sub()[0]; } // Remove leading string from re. if (re->op() == kRegexpLiteral) { re->rune_ = 0; re->op_ = kRegexpEmptyMatch; } else if (re->op() == kRegexpLiteralString) { if (n >= re->nrunes_) { delete[] re->runes_; re->runes_ = NULL; re->nrunes_ = 0; re->op_ = kRegexpEmptyMatch; } else if (n == re->nrunes_ - 1) { Rune rune = re->runes_[re->nrunes_ - 1]; delete[] re->runes_; re->runes_ = NULL; re->nrunes_ = 0; re->rune_ = rune; re->op_ = kRegexpLiteral; } else { re->nrunes_ -= n; memmove(re->runes_, re->runes_ + n, re->nrunes_ * sizeof re->runes_[0]); } } // If re is now empty, concatenations might simplify too. while (d-- > 0) { re = stk[d]; Regexp** sub = re->sub(); if (sub[0]->op() == kRegexpEmptyMatch) { sub[0]->Decref(); sub[0] = NULL; // Delete first element of concat. switch (re->nsub()) { case 0: case 1: // Impossible. LOG(DFATAL) << "Concat of " << re->nsub(); re->submany_ = NULL; re->op_ = kRegexpEmptyMatch; break; case 2: { // Replace re with sub[1]. Regexp* old = sub[1]; sub[1] = NULL; re->Swap(old); old->Decref(); break; } default: // Slide down. re->nsub_--; memmove(sub, sub + 1, re->nsub_ * sizeof sub[0]); break; } } } } // Factors common prefixes from alternation. // For example, // ABC|ABD|AEF|BCX|BCY // simplifies to // A(B(C|D)|EF)|BC(X|Y) // which the normal parse state routines will further simplify to // A(B[CD]|EF)|BC[XY] // // Rewrites sub to contain simplified list to alternate and returns // the new length of sub. Adjusts reference counts accordingly // (incoming sub[i] decremented, outgoing sub[i] incremented). // It's too much of a pain to write this code with an explicit stack, // so instead we let the caller specify a maximum depth and // don't simplify beyond that. There are around 15 words of local // variables and parameters in the frame, so allowing 8 levels // on a 64-bit machine is still less than a kilobyte of stack and // probably enough benefit for practical uses. const int kFactorAlternationMaxDepth = 8; int Regexp::FactorAlternation( Regexp** sub, int n, Regexp::ParseFlags altflags) { return FactorAlternationRecursive(sub, n, altflags, kFactorAlternationMaxDepth); } int Regexp::FactorAlternationRecursive( Regexp** sub, int n, Regexp::ParseFlags altflags, int maxdepth) { if (maxdepth <= 0) return n; // Round 1: Factor out common literal prefixes. Rune *rune = NULL; int nrune = 0; Regexp::ParseFlags runeflags = Regexp::NoParseFlags; int start = 0; int out = 0; for (int i = 0; i <= n; i++) { // Invariant: what was in sub[0:start] has been Decref'ed // and that space has been reused for sub[0:out] (out <= start). // // Invariant: sub[start:i] consists of regexps that all begin // with the string rune[0:nrune]. Rune* rune_i = NULL; int nrune_i = 0; Regexp::ParseFlags runeflags_i = Regexp::NoParseFlags; if (i < n) { rune_i = LeadingString(sub[i], &nrune_i, &runeflags_i); if (runeflags_i == runeflags) { int same = 0; while (same < nrune && same < nrune_i && rune[same] == rune_i[same]) same++; if (same > 0) { // Matches at least one rune in current range. Keep going around. nrune = same; continue; } } } // Found end of a run with common leading literal string: // sub[start:i] all begin with rune[0:nrune] but sub[i] // does not even begin with rune[0]. // // Factor out common string and append factored expression to sub[0:out]. if (i == start) { // Nothing to do - first iteration. } else if (i == start+1) { // Just one: don't bother factoring. sub[out++] = sub[start]; } else { // Construct factored form: prefix(suffix1|suffix2|...) Regexp* x[2]; // x[0] = prefix, x[1] = suffix1|suffix2|... x[0] = LiteralString(rune, nrune, runeflags); for (int j = start; j < i; j++) RemoveLeadingString(sub[j], nrune); int nn = FactorAlternationRecursive(sub + start, i - start, altflags, maxdepth - 1); x[1] = AlternateNoFactor(sub + start, nn, altflags); sub[out++] = Concat(x, 2, altflags); } // Prepare for next round (if there is one). if (i < n) { start = i; rune = rune_i; nrune = nrune_i; runeflags = runeflags_i; } } n = out; // Round 2: Factor out common complex prefixes, // just the first piece of each concatenation, // whatever it is. This is good enough a lot of the time. start = 0; out = 0; Regexp* first = NULL; for (int i = 0; i <= n; i++) { // Invariant: what was in sub[0:start] has been Decref'ed // and that space has been reused for sub[0:out] (out <= start). // // Invariant: sub[start:i] consists of regexps that all begin with first. Regexp* first_i = NULL; if (i < n) { first_i = LeadingRegexp(sub[i]); if (first != NULL && Regexp::Equal(first, first_i)) { continue; } } // Found end of a run with common leading regexp: // sub[start:i] all begin with first but sub[i] does not. // // Factor out common regexp and append factored expression to sub[0:out]. if (i == start) { // Nothing to do - first iteration. } else if (i == start+1) { // Just one: don't bother factoring. sub[out++] = sub[start]; } else { // Construct factored form: prefix(suffix1|suffix2|...) Regexp* x[2]; // x[0] = prefix, x[1] = suffix1|suffix2|... x[0] = first->Incref(); for (int j = start; j < i; j++) sub[j] = RemoveLeadingRegexp(sub[j]); int nn = FactorAlternationRecursive(sub + start, i - start, altflags, maxdepth - 1); x[1] = AlternateNoFactor(sub + start, nn, altflags); sub[out++] = Concat(x, 2, altflags); } // Prepare for next round (if there is one). if (i < n) { start = i; first = first_i; } } n = out; // Round 3: Collapse runs of single literals into character classes. start = 0; out = 0; for (int i = 0; i <= n; i++) { // Invariant: what was in sub[0:start] has been Decref'ed // and that space has been reused for sub[0:out] (out <= start). // // Invariant: sub[start:i] consists of regexps that are either // literal runes or character classes. if (i < n && (sub[i]->op() == kRegexpLiteral || sub[i]->op() == kRegexpCharClass)) continue; // sub[i] is not a char or char class; // emit char class for sub[start:i]... if (i == start) { // Nothing to do. } else if (i == start+1) { sub[out++] = sub[start]; } else { // Make new char class. CharClassBuilder ccb; for (int j = start; j < i; j++) { Regexp* re = sub[j]; if (re->op() == kRegexpCharClass) { CharClass* cc = re->cc(); for (CharClass::iterator it = cc->begin(); it != cc->end(); ++it) ccb.AddRange(it->lo, it->hi); } else if (re->op() == kRegexpLiteral) { ccb.AddRangeFlags(re->rune(), re->rune(), re->parse_flags()); } else { LOG(DFATAL) << "RE2: unexpected op: " << re->op() << " " << re->ToString(); } re->Decref(); } sub[out++] = NewCharClass(ccb.GetCharClass(), altflags); } // ... and then emit sub[i]. if (i < n) sub[out++] = sub[i]; start = i+1; } n = out; // Round 4: Collapse runs of empty matches into single empty match. start = 0; out = 0; for (int i = 0; i < n; i++) { if (i + 1 < n && sub[i]->op() == kRegexpEmptyMatch && sub[i+1]->op() == kRegexpEmptyMatch) { sub[i]->Decref(); continue; } sub[out++] = sub[i]; } n = out; return n; } // Collapse the regexps on top of the stack, down to the // first marker, into a new op node (op == kRegexpAlternate // or op == kRegexpConcat). void Regexp::ParseState::DoCollapse(RegexpOp op) { // Scan backward to marker, counting children of composite. int n = 0; Regexp* next = NULL; Regexp* sub; for (sub = stacktop_; sub != NULL && !IsMarker(sub->op()); sub = next) { next = sub->down_; if (sub->op_ == op) n += sub->nsub_; else n++; } // If there's just one child, leave it alone. // (Concat of one thing is that one thing; alternate of one thing is same.) if (stacktop_ != NULL && stacktop_->down_ == next) return; // Construct op (alternation or concatenation), flattening op of op. Regexp** subs = new Regexp*[n]; next = NULL; int i = n; for (sub = stacktop_; sub != NULL && !IsMarker(sub->op()); sub = next) { next = sub->down_; if (sub->op_ == op) { Regexp** sub_subs = sub->sub(); for (int k = sub->nsub_ - 1; k >= 0; k--) subs[--i] = sub_subs[k]->Incref(); sub->Decref(); } else { subs[--i] = FinishRegexp(sub); } } Regexp* re = ConcatOrAlternate(op, subs, n, flags_, true); delete[] subs; re->simple_ = re->ComputeSimple(); re->down_ = next; stacktop_ = re; } // Finishes the current concatenation, // collapsing it into a single regexp on the stack. void Regexp::ParseState::DoConcatenation() { Regexp* r1 = stacktop_; if (r1 == NULL || IsMarker(r1->op())) { // empty concatenation is special case Regexp* re = new Regexp(kRegexpEmptyMatch, flags_); PushRegexp(re); } DoCollapse(kRegexpConcat); } // Finishes the current alternation, // collapsing it to a single regexp on the stack. void Regexp::ParseState::DoAlternation() { DoVerticalBar(); // Now stack top is kVerticalBar. Regexp* r1 = stacktop_; stacktop_ = r1->down_; r1->Decref(); DoCollapse(kRegexpAlternate); } // Incremental conversion of concatenated literals into strings. // If top two elements on stack are both literal or string, // collapse into single string. // Don't walk down the stack -- the parser calls this frequently // enough that below the bottom two is known to be collapsed. // Only called when another regexp is about to be pushed // on the stack, so that the topmost literal is not being considered. // (Otherwise ab* would turn into (ab)*.) // If r >= 0, consider pushing a literal r on the stack. // Return whether that happened. bool Regexp::ParseState::MaybeConcatString(int r, ParseFlags flags) { Regexp* re1; Regexp* re2; if ((re1 = stacktop_) == NULL || (re2 = re1->down_) == NULL) return false; if (re1->op_ != kRegexpLiteral && re1->op_ != kRegexpLiteralString) return false; if (re2->op_ != kRegexpLiteral && re2->op_ != kRegexpLiteralString) return false; if ((re1->parse_flags_ & FoldCase) != (re2->parse_flags_ & FoldCase)) return false; if (re2->op_ == kRegexpLiteral) { // convert into string Rune rune = re2->rune_; re2->op_ = kRegexpLiteralString; re2->nrunes_ = 0; re2->runes_ = NULL; re2->AddRuneToString(rune); } // push re1 into re2. if (re1->op_ == kRegexpLiteral) { re2->AddRuneToString(re1->rune_); } else { for (int i = 0; i < re1->nrunes_; i++) re2->AddRuneToString(re1->runes_[i]); re1->nrunes_ = 0; delete[] re1->runes_; re1->runes_ = NULL; } // reuse re1 if possible if (r >= 0) { re1->op_ = kRegexpLiteral; re1->rune_ = r; re1->parse_flags_ = flags; return true; } stacktop_ = re2; re1->Decref(); return false; } // Lexing routines. // Parses a decimal integer, storing it in *n. // Sets *s to span the remainder of the string. // Sets *out_re to the regexp for the class. static bool ParseInteger(StringPiece* s, int* np) { if (s->size() == 0 || !isdigit((*s)[0] & 0xFF)) return false; // Disallow leading zeros. if (s->size() >= 2 && (*s)[0] == '0' && isdigit((*s)[1] & 0xFF)) return false; int n = 0; int c; while (s->size() > 0 && isdigit(c = (*s)[0] & 0xFF)) { // Avoid overflow. if (n >= 100000000) return false; n = n*10 + c - '0'; s->remove_prefix(1); // digit } *np = n; return true; } // Parses a repetition suffix like {1,2} or {2} or {2,}. // Sets *s to span the remainder of the string on success. // Sets *lo and *hi to the given range. // In the case of {2,}, the high number is unbounded; // sets *hi to -1 to signify this. // {,2} is NOT a valid suffix. // The Maybe in the name signifies that the regexp parse // doesn't fail even if ParseRepetition does, so the StringPiece // s must NOT be edited unless MaybeParseRepetition returns true. static bool MaybeParseRepetition(StringPiece* sp, int* lo, int* hi) { StringPiece s = *sp; if (s.size() == 0 || s[0] != '{') return false; s.remove_prefix(1); // '{' if (!ParseInteger(&s, lo)) return false; if (s.size() == 0) return false; if (s[0] == ',') { s.remove_prefix(1); // ',' if (s.size() == 0) return false; if (s[0] == '}') { // {2,} means at least 2 *hi = -1; } else { // {2,4} means 2, 3, or 4. if (!ParseInteger(&s, hi)) return false; } } else { // {2} means exactly two *hi = *lo; } if (s.size() == 0 || s[0] != '}') return false; s.remove_prefix(1); // '}' *sp = s; return true; } // Removes the next Rune from the StringPiece and stores it in *r. // Returns number of bytes removed from sp. // Behaves as though there is a terminating NUL at the end of sp. // Argument order is backwards from usual Google style // but consistent with chartorune. static int StringPieceToRune(Rune *r, StringPiece *sp, RegexpStatus* status) { int n; if (fullrune(sp->data(), sp->size())) { n = chartorune(r, sp->data()); // Some copies of chartorune have a bug that accepts // encodings of values in (10FFFF, 1FFFFF] as valid. // Those values break the character class algorithm, // which assumes Runemax is the largest rune. if (*r > Runemax) { n = 1; *r = Runeerror; } if (!(n == 1 && *r == Runeerror)) { // no decoding error sp->remove_prefix(n); return n; } } status->set_code(kRegexpBadUTF8); status->set_error_arg(NULL); return -1; } // Return whether name is valid UTF-8. // If not, set status to kRegexpBadUTF8. static bool IsValidUTF8(const StringPiece& s, RegexpStatus* status) { StringPiece t = s; Rune r; while (t.size() > 0) { if (StringPieceToRune(&r, &t, status) < 0) return false; } return true; } // Is c a hex digit? static int IsHex(int c) { return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'); } // Convert hex digit to value. static int UnHex(int c) { if ('0' <= c && c <= '9') return c - '0'; if ('A' <= c && c <= 'F') return c - 'A' + 10; if ('a' <= c && c <= 'f') return c - 'a' + 10; LOG(DFATAL) << "Bad hex digit " << c; return 0; } // Parse an escape sequence (e.g., \n, \{). // Sets *s to span the remainder of the string. // Sets *rp to the named character. static bool ParseEscape(StringPiece* s, Rune* rp, RegexpStatus* status, int rune_max) { const char* begin = s->begin(); if (s->size() < 1 || (*s)[0] != '\\') { // Should not happen - caller always checks. status->set_code(kRegexpInternalError); status->set_error_arg(NULL); return false; } if (s->size() < 2) { status->set_code(kRegexpTrailingBackslash); status->set_error_arg(NULL); return false; } Rune c, c1; s->remove_prefix(1); // backslash if (StringPieceToRune(&c, s, status) < 0) return false; int code; switch (c) { default: if (c < Runeself && !isalpha(c) && !isdigit(c)) { // Escaped non-word characters are always themselves. // PCRE is not quite so rigorous: it accepts things like // \q, but we don't. We once rejected \_, but too many // programs and people insist on using it, so allow \_. *rp = c; return true; } goto BadEscape; // Octal escapes. case '1': case '2': case '3': case '4': case '5': case '6': case '7': // Single non-zero octal digit is a backreference; not supported. if (s->size() == 0 || (*s)[0] < '0' || (*s)[0] > '7') goto BadEscape; // fall through case '0': // consume up to three octal digits; already have one. code = c - '0'; if (s->size() > 0 && '0' <= (c = (*s)[0]) && c <= '7') { code = code * 8 + c - '0'; s->remove_prefix(1); // digit if (s->size() > 0) { c = (*s)[0]; if ('0' <= c && c <= '7') { code = code * 8 + c - '0'; s->remove_prefix(1); // digit } } } if (code > rune_max) goto BadEscape; *rp = code; return true; // Hexadecimal escapes case 'x': if (s->size() == 0) goto BadEscape; if (StringPieceToRune(&c, s, status) < 0) return false; if (c == '{') { // Any number of digits in braces. // Update n as we consume the string, so that // the whole thing gets shown in the error message. // Perl accepts any text at all; it ignores all text // after the first non-hex digit. We require only hex digits, // and at least one. if (StringPieceToRune(&c, s, status) < 0) return false; int nhex = 0; code = 0; while (IsHex(c)) { nhex++; code = code * 16 + UnHex(c); if (code > rune_max) goto BadEscape; if (s->size() == 0) goto BadEscape; if (StringPieceToRune(&c, s, status) < 0) return false; } if (c != '}' || nhex == 0) goto BadEscape; *rp = code; return true; } // Easy case: two hex digits. if (s->size() == 0) goto BadEscape; if (StringPieceToRune(&c1, s, status) < 0) return false; if (!IsHex(c) || !IsHex(c1)) goto BadEscape; *rp = UnHex(c) * 16 + UnHex(c1); return true; // C escapes. case 'n': *rp = '\n'; return true; case 'r': *rp = '\r'; return true; case 't': *rp = '\t'; return true; // Less common C escapes. case 'a': *rp = '\a'; return true; case 'f': *rp = '\f'; return true; case 'v': *rp = '\v'; return true; // This code is disabled to avoid misparsing // the Perl word-boundary \b as a backspace // when in POSIX regexp mode. Surprisingly, // in Perl, \b means word-boundary but [\b] // means backspace. We don't support that: // if you want a backspace embed a literal // backspace character or use \x08. // // case 'b': // *rp = '\b'; // return true; } LOG(DFATAL) << "Not reached in ParseEscape."; BadEscape: // Unrecognized escape sequence. status->set_code(kRegexpBadEscape); status->set_error_arg(StringPiece(begin, s->data() - begin)); return false; } // Add a range to the character class, but exclude newline if asked. // Also handle case folding. void CharClassBuilder::AddRangeFlags( Rune lo, Rune hi, Regexp::ParseFlags parse_flags) { // Take out \n if the flags say so. bool cutnl = !(parse_flags & Regexp::ClassNL) || (parse_flags & Regexp::NeverNL); if (cutnl && lo <= '\n' && '\n' <= hi) { if (lo < '\n') AddRangeFlags(lo, '\n' - 1, parse_flags); if (hi > '\n') AddRangeFlags('\n' + 1, hi, parse_flags); return; } // If folding case, add fold-equivalent characters too. if (parse_flags & Regexp::FoldCase) AddFoldedRange(this, lo, hi, 0); else AddRange(lo, hi); } // Look for a group with the given name. static const UGroup* LookupGroup(const StringPiece& name, const UGroup *groups, int ngroups) { // Simple name lookup. for (int i = 0; i < ngroups; i++) if (StringPiece(groups[i].name) == name) return &groups[i]; return NULL; } // Fake UGroup containing all Runes static URange16 any16[] = { { 0, 65535 } }; static URange32 any32[] = { { 65536, Runemax } }; static UGroup anygroup = { "Any", +1, any16, 1, any32, 1 }; // Look for a POSIX group with the given name (e.g., "[:^alpha:]") static const UGroup* LookupPosixGroup(const StringPiece& name) { return LookupGroup(name, posix_groups, num_posix_groups); } static const UGroup* LookupPerlGroup(const StringPiece& name) { return LookupGroup(name, perl_groups, num_perl_groups); } // Look for a Unicode group with the given name (e.g., "Han") static const UGroup* LookupUnicodeGroup(const StringPiece& name) { // Special case: "Any" means any. if (name == StringPiece("Any")) return &anygroup; return LookupGroup(name, unicode_groups, num_unicode_groups); } // Add a UGroup or its negation to the character class. static void AddUGroup(CharClassBuilder *cc, const UGroup *g, int sign, Regexp::ParseFlags parse_flags) { if (sign == +1) { for (int i = 0; i < g->nr16; i++) { cc->AddRangeFlags(g->r16[i].lo, g->r16[i].hi, parse_flags); } for (int i = 0; i < g->nr32; i++) { cc->AddRangeFlags(g->r32[i].lo, g->r32[i].hi, parse_flags); } } else { if (parse_flags & Regexp::FoldCase) { // Normally adding a case-folded group means // adding all the extra fold-equivalent runes too. // But if we're adding the negation of the group, // we have to exclude all the runes that are fold-equivalent // to what's already missing. Too hard, so do in two steps. CharClassBuilder ccb1; AddUGroup(&ccb1, g, +1, parse_flags); // If the flags say to take out \n, put it in, so that negating will take it out. // Normally AddRangeFlags does this, but we're bypassing AddRangeFlags. bool cutnl = !(parse_flags & Regexp::ClassNL) || (parse_flags & Regexp::NeverNL); if (cutnl) { ccb1.AddRange('\n', '\n'); } ccb1.Negate(); cc->AddCharClass(&ccb1); return; } int next = 0; for (int i = 0; i < g->nr16; i++) { if (next < g->r16[i].lo) cc->AddRangeFlags(next, g->r16[i].lo - 1, parse_flags); next = g->r16[i].hi + 1; } for (int i = 0; i < g->nr32; i++) { if (next < g->r32[i].lo) cc->AddRangeFlags(next, g->r32[i].lo - 1, parse_flags); next = g->r32[i].hi + 1; } if (next <= Runemax) cc->AddRangeFlags(next, Runemax, parse_flags); } } // Maybe parse a Perl character class escape sequence. // Only recognizes the Perl character classes (\d \s \w \D \S \W), // not the Perl empty-string classes (\b \B \A \Z \z). // On success, sets *s to span the remainder of the string // and returns the corresponding UGroup. // The StringPiece must *NOT* be edited unless the call succeeds. const UGroup* MaybeParsePerlCCEscape(StringPiece* s, Regexp::ParseFlags parse_flags) { if (!(parse_flags & Regexp::PerlClasses)) return NULL; if (s->size() < 2 || (*s)[0] != '\\') return NULL; // Could use StringPieceToRune, but there aren't // any non-ASCII Perl group names. StringPiece name(s->begin(), 2); const UGroup *g = LookupPerlGroup(name); if (g == NULL) return NULL; s->remove_prefix(name.size()); return g; } enum ParseStatus { kParseOk, // Did some parsing. kParseError, // Found an error. kParseNothing, // Decided not to parse. }; // Maybe parses a Unicode character group like \p{Han} or \P{Han} // (the latter is a negated group). ParseStatus ParseUnicodeGroup(StringPiece* s, Regexp::ParseFlags parse_flags, CharClassBuilder *cc, RegexpStatus* status) { // Decide whether to parse. if (!(parse_flags & Regexp::UnicodeGroups)) return kParseNothing; if (s->size() < 2 || (*s)[0] != '\\') return kParseNothing; Rune c = (*s)[1]; if (c != 'p' && c != 'P') return kParseNothing; // Committed to parse. Results: int sign = +1; // -1 = negated char class if (c == 'P') sign = -1; StringPiece seq = *s; // \p{Han} or \pL StringPiece name; // Han or L s->remove_prefix(2); // '\\', 'p' if (!StringPieceToRune(&c, s, status)) return kParseError; if (c != '{') { // Name is the bit of string we just skipped over for c. const char* p = seq.begin() + 2; name = StringPiece(p, s->begin() - p); } else { // Name is in braces. Look for closing } size_t end = s->find('}', 0); if (end == s->npos) { if (!IsValidUTF8(seq, status)) return kParseError; status->set_code(kRegexpBadCharRange); status->set_error_arg(seq); return kParseError; } name = StringPiece(s->begin(), end); // without '}' s->remove_prefix(end + 1); // with '}' if (!IsValidUTF8(name, status)) return kParseError; } // Chop seq where s now begins. seq = StringPiece(seq.begin(), s->begin() - seq.begin()); // Look up group if (name.size() > 0 && name[0] == '^') { sign = -sign; name.remove_prefix(1); // '^' } const UGroup *g = LookupUnicodeGroup(name); if (g == NULL) { status->set_code(kRegexpBadCharRange); status->set_error_arg(seq); return kParseError; } AddUGroup(cc, g, sign, parse_flags); return kParseOk; } // Parses a character class name like [:alnum:]. // Sets *s to span the remainder of the string. // Adds the ranges corresponding to the class to ranges. static ParseStatus ParseCCName(StringPiece* s, Regexp::ParseFlags parse_flags, CharClassBuilder *cc, RegexpStatus* status) { // Check begins with [: const char* p = s->data(); const char* ep = s->data() + s->size(); if (ep - p < 2 || p[0] != '[' || p[1] != ':') return kParseNothing; // Look for closing :]. const char* q; for (q = p+2; q <= ep-2 && (*q != ':' || *(q+1) != ']'); q++) ; // If no closing :], then ignore. if (q > ep-2) return kParseNothing; // Got it. Check that it's valid. q += 2; StringPiece name(p, q-p); const UGroup *g = LookupPosixGroup(name); if (g == NULL) { status->set_code(kRegexpBadCharRange); status->set_error_arg(name); return kParseError; } s->remove_prefix(name.size()); AddUGroup(cc, g, g->sign, parse_flags); return kParseOk; } // Parses a character inside a character class. // There are fewer special characters here than in the rest of the regexp. // Sets *s to span the remainder of the string. // Sets *rp to the character. bool Regexp::ParseState::ParseCCCharacter(StringPiece* s, Rune *rp, const StringPiece& whole_class, RegexpStatus* status) { if (s->size() == 0) { status->set_code(kRegexpMissingBracket); status->set_error_arg(whole_class); return false; } // Allow regular escape sequences even though // many need not be escaped in this context. if (s->size() >= 1 && (*s)[0] == '\\') return ParseEscape(s, rp, status, rune_max_); // Otherwise take the next rune. return StringPieceToRune(rp, s, status) >= 0; } // Parses a character class character, or, if the character // is followed by a hyphen, parses a character class range. // For single characters, rr->lo == rr->hi. // Sets *s to span the remainder of the string. // Sets *rp to the character. bool Regexp::ParseState::ParseCCRange(StringPiece* s, RuneRange* rr, const StringPiece& whole_class, RegexpStatus* status) { StringPiece os = *s; if (!ParseCCCharacter(s, &rr->lo, whole_class, status)) return false; // [a-] means (a|-), so check for final ]. if (s->size() >= 2 && (*s)[0] == '-' && (*s)[1] != ']') { s->remove_prefix(1); // '-' if (!ParseCCCharacter(s, &rr->hi, whole_class, status)) return false; if (rr->hi < rr->lo) { status->set_code(kRegexpBadCharRange); status->set_error_arg(StringPiece(os.data(), s->data() - os.data())); return false; } } else { rr->hi = rr->lo; } return true; } // Parses a possibly-negated character class expression like [^abx-z[:digit:]]. // Sets *s to span the remainder of the string. // Sets *out_re to the regexp for the class. bool Regexp::ParseState::ParseCharClass(StringPiece* s, Regexp** out_re, RegexpStatus* status) { StringPiece whole_class = *s; if (s->size() == 0 || (*s)[0] != '[') { // Caller checked this. status->set_code(kRegexpInternalError); status->set_error_arg(NULL); return false; } bool negated = false; Regexp* re = new Regexp(kRegexpCharClass, flags_ & ~FoldCase); re->ccb_ = new CharClassBuilder; s->remove_prefix(1); // '[' if (s->size() > 0 && (*s)[0] == '^') { s->remove_prefix(1); // '^' negated = true; if (!(flags_ & ClassNL) || (flags_ & NeverNL)) { // If NL can't match implicitly, then pretend // negated classes include a leading \n. re->ccb_->AddRange('\n', '\n'); } } bool first = true; // ] is okay as first char in class while (s->size() > 0 && ((*s)[0] != ']' || first)) { // - is only okay unescaped as first or last in class. // Except that Perl allows - anywhere. if ((*s)[0] == '-' && !first && !(flags_&PerlX) && (s->size() == 1 || (*s)[1] != ']')) { StringPiece t = *s; t.remove_prefix(1); // '-' Rune r; int n = StringPieceToRune(&r, &t, status); if (n < 0) { re->Decref(); return false; } status->set_code(kRegexpBadCharRange); status->set_error_arg(StringPiece(s->data(), 1+n)); re->Decref(); return false; } first = false; // Look for [:alnum:] etc. if (s->size() > 2 && (*s)[0] == '[' && (*s)[1] == ':') { switch (ParseCCName(s, flags_, re->ccb_, status)) { case kParseOk: continue; case kParseError: re->Decref(); return false; case kParseNothing: break; } } // Look for Unicode character group like \p{Han} if (s->size() > 2 && (*s)[0] == '\\' && ((*s)[1] == 'p' || (*s)[1] == 'P')) { switch (ParseUnicodeGroup(s, flags_, re->ccb_, status)) { case kParseOk: continue; case kParseError: re->Decref(); return false; case kParseNothing: break; } } // Look for Perl character class symbols (extension). const UGroup *g = MaybeParsePerlCCEscape(s, flags_); if (g != NULL) { AddUGroup(re->ccb_, g, g->sign, flags_); continue; } // Otherwise assume single character or simple range. RuneRange rr; if (!ParseCCRange(s, &rr, whole_class, status)) { re->Decref(); return false; } // AddRangeFlags is usually called in response to a class like // \p{Foo} or [[:foo:]]; for those, it filters \n out unless // Regexp::ClassNL is set. In an explicit range or singleton // like we just parsed, we do not filter \n out, so set ClassNL // in the flags. re->ccb_->AddRangeFlags(rr.lo, rr.hi, flags_ | Regexp::ClassNL); } if (s->size() == 0) { status->set_code(kRegexpMissingBracket); status->set_error_arg(whole_class); re->Decref(); return false; } s->remove_prefix(1); // ']' if (negated) re->ccb_->Negate(); *out_re = re; return true; } // Is this a valid capture name? [A-Za-z0-9_]+ // PCRE limits names to 32 bytes. // Python rejects names starting with digits. // We don't enforce either of those. static bool IsValidCaptureName(const StringPiece& name) { if (name.size() == 0) return false; for (int i = 0; i < name.size(); i++) { int c = name[i]; if (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_') continue; return false; } return true; } // Parses a Perl flag setting or non-capturing group or both, // like (?i) or (?: or (?i:. Removes from s, updates parse state. // The caller must check that s begins with "(?". // Returns true on success. If the Perl flag is not // well-formed or not supported, sets status_ and returns false. bool Regexp::ParseState::ParsePerlFlags(StringPiece* s) { StringPiece t = *s; // Caller is supposed to check this. if (!(flags_ & PerlX) || t.size() < 2 || t[0] != '(' || t[1] != '?') { LOG(DFATAL) << "Bad call to ParseState::ParsePerlFlags"; status_->set_code(kRegexpInternalError); return false; } t.remove_prefix(2); // "(?" // Check for named captures, first introduced in Python's regexp library. // As usual, there are three slightly different syntaxes: // // (?Pexpr) the original, introduced by Python // (?expr) the .NET alteration, adopted by Perl 5.10 // (?'name'expr) another .NET alteration, adopted by Perl 5.10 // // Perl 5.10 gave in and implemented the Python version too, // but they claim that the last two are the preferred forms. // PCRE and languages based on it (specifically, PHP and Ruby) // support all three as well. EcmaScript 4 uses only the Python form. // // In both the open source world (via Code Search) and the // Google source tree, (?Pname) is the dominant form, // so that's the one we implement. One is enough. if (t.size() > 2 && t[0] == 'P' && t[1] == '<') { // Pull out name. size_t end = t.find('>', 2); if (end == t.npos) { if (!IsValidUTF8(*s, status_)) return false; status_->set_code(kRegexpBadNamedCapture); status_->set_error_arg(*s); return false; } // t is "P...", t[end] == '>' StringPiece capture(t.begin()-2, end+3); // "(?P" StringPiece name(t.begin()+2, end-2); // "name" if (!IsValidUTF8(name, status_)) return false; if (!IsValidCaptureName(name)) { status_->set_code(kRegexpBadNamedCapture); status_->set_error_arg(capture); return false; } if (!DoLeftParen(name)) { // DoLeftParen's failure set status_. return false; } s->remove_prefix(capture.end() - s->begin()); return true; } bool negated = false; bool sawflags = false; int nflags = flags_; Rune c; for (bool done = false; !done; ) { if (t.size() == 0) goto BadPerlOp; if (StringPieceToRune(&c, &t, status_) < 0) return false; switch (c) { default: goto BadPerlOp; // Parse flags. case 'i': sawflags = true; if (negated) nflags &= ~FoldCase; else nflags |= FoldCase; break; case 'm': // opposite of our OneLine sawflags = true; if (negated) nflags |= OneLine; else nflags &= ~OneLine; break; case 's': sawflags = true; if (negated) nflags &= ~DotNL; else nflags |= DotNL; break; case 'U': sawflags = true; if (negated) nflags &= ~NonGreedy; else nflags |= NonGreedy; break; // Negation case '-': if (negated) goto BadPerlOp; negated = true; sawflags = false; break; // Open new group. case ':': if (!DoLeftParenNoCapture()) { // DoLeftParenNoCapture's failure set status_. return false; } done = true; break; // Finish flags. case ')': done = true; break; } } if (negated && !sawflags) goto BadPerlOp; flags_ = static_cast(nflags); *s = t; return true; BadPerlOp: status_->set_code(kRegexpBadPerlOp); status_->set_error_arg(StringPiece(s->begin(), t.begin() - s->begin())); return false; } // Converts latin1 (assumed to be encoded as Latin1 bytes) // into UTF8 encoding in string. // Can't use EncodingUtils::EncodeLatin1AsUTF8 because it is // deprecated and because it rejects code points 0x80-0x9F. void ConvertLatin1ToUTF8(const StringPiece& latin1, string* utf) { char buf[UTFmax]; utf->clear(); for (int i = 0; i < latin1.size(); i++) { Rune r = latin1[i] & 0xFF; int n = runetochar(buf, &r); utf->append(buf, n); } } // Parses the regular expression given by s, // returning the corresponding Regexp tree. // The caller must Decref the return value when done with it. // Returns NULL on error. Regexp* Regexp::Parse(const StringPiece& s, ParseFlags global_flags, RegexpStatus* status) { // Make status non-NULL (easier on everyone else). RegexpStatus xstatus; if (status == NULL) status = &xstatus; ParseState ps(global_flags, s, status); StringPiece t = s; // Convert regexp to UTF-8 (easier on the rest of the parser). if (global_flags & Latin1) { string* tmp = new string; ConvertLatin1ToUTF8(t, tmp); status->set_tmp(tmp); t = *tmp; } if (global_flags & Literal) { // Special parse loop for literal string. while (t.size() > 0) { Rune r; if (StringPieceToRune(&r, &t, status) < 0) return NULL; if (!ps.PushLiteral(r)) return NULL; } return ps.DoFinish(); } StringPiece lastunary = NULL; while (t.size() > 0) { StringPiece isunary = NULL; switch (t[0]) { default: { Rune r; if (StringPieceToRune(&r, &t, status) < 0) return NULL; if (!ps.PushLiteral(r)) return NULL; break; } case '(': // "(?" introduces Perl escape. if ((ps.flags() & PerlX) && (t.size() >= 2 && t[1] == '?')) { // Flag changes and non-capturing groups. if (!ps.ParsePerlFlags(&t)) return NULL; break; } if (ps.flags() & NeverCapture) { if (!ps.DoLeftParenNoCapture()) return NULL; } else { if (!ps.DoLeftParen(NULL)) return NULL; } t.remove_prefix(1); // '(' break; case '|': if (!ps.DoVerticalBar()) return NULL; t.remove_prefix(1); // '|' break; case ')': if (!ps.DoRightParen()) return NULL; t.remove_prefix(1); // ')' break; case '^': // Beginning of line. if (!ps.PushCarat()) return NULL; t.remove_prefix(1); // '^' break; case '$': // End of line. if (!ps.PushDollar()) return NULL; t.remove_prefix(1); // '$' break; case '.': // Any character (possibly except newline). if (!ps.PushDot()) return NULL; t.remove_prefix(1); // '.' break; case '[': { // Character class. Regexp* re; if (!ps.ParseCharClass(&t, &re, status)) return NULL; if (!ps.PushRegexp(re)) return NULL; break; } case '*': { // Zero or more. RegexpOp op; op = kRegexpStar; goto Rep; case '+': // One or more. op = kRegexpPlus; goto Rep; case '?': // Zero or one. op = kRegexpQuest; goto Rep; Rep: StringPiece opstr = t; bool nongreedy = false; t.remove_prefix(1); // '*' or '+' or '?' if (ps.flags() & PerlX) { if (t.size() > 0 && t[0] == '?') { nongreedy = true; t.remove_prefix(1); // '?' } if (lastunary.size() > 0) { // In Perl it is not allowed to stack repetition operators: // a** is a syntax error, not a double-star. // (and a++ means something else entirely, which we don't support!) status->set_code(kRegexpRepeatOp); status->set_error_arg(StringPiece(lastunary.begin(), t.begin() - lastunary.begin())); return NULL; } } opstr.set(opstr.data(), t.data() - opstr.data()); if (!ps.PushRepeatOp(op, opstr, nongreedy)) return NULL; isunary = opstr; break; } case '{': { // Counted repetition. int lo, hi; StringPiece opstr = t; if (!MaybeParseRepetition(&t, &lo, &hi)) { // Treat like a literal. if (!ps.PushLiteral('{')) return NULL; t.remove_prefix(1); // '{' break; } bool nongreedy = false; if (ps.flags() & PerlX) { if (t.size() > 0 && t[0] == '?') { nongreedy = true; t.remove_prefix(1); // '?' } if (lastunary.size() > 0) { // Not allowed to stack repetition operators. status->set_code(kRegexpRepeatOp); status->set_error_arg(StringPiece(lastunary.begin(), t.begin() - lastunary.begin())); return NULL; } } opstr.set(opstr.data(), t.data() - opstr.data()); if (!ps.PushRepetition(lo, hi, opstr, nongreedy)) return NULL; isunary = opstr; break; } case '\\': { // Escaped character or Perl sequence. // \b and \B: word boundary or not if ((ps.flags() & Regexp::PerlB) && t.size() >= 2 && (t[1] == 'b' || t[1] == 'B')) { if (!ps.PushWordBoundary(t[1] == 'b')) return NULL; t.remove_prefix(2); // '\\', 'b' break; } if ((ps.flags() & Regexp::PerlX) && t.size() >= 2) { if (t[1] == 'A') { if (!ps.PushSimpleOp(kRegexpBeginText)) return NULL; t.remove_prefix(2); // '\\', 'A' break; } if (t[1] == 'z') { if (!ps.PushSimpleOp(kRegexpEndText)) return NULL; t.remove_prefix(2); // '\\', 'z' break; } // Do not recognize \Z, because this library can't // implement the exact Perl/PCRE semantics. // (This library treats "(?-m)$" as \z, even though // in Perl and PCRE it is equivalent to \Z.) if (t[1] == 'C') { // \C: any byte [sic] if (!ps.PushSimpleOp(kRegexpAnyByte)) return NULL; t.remove_prefix(2); // '\\', 'C' break; } if (t[1] == 'Q') { // \Q ... \E: the ... is always literals t.remove_prefix(2); // '\\', 'Q' while (t.size() > 0) { if (t.size() >= 2 && t[0] == '\\' && t[1] == 'E') { t.remove_prefix(2); // '\\', 'E' break; } Rune r; if (StringPieceToRune(&r, &t, status) < 0) return NULL; if (!ps.PushLiteral(r)) return NULL; } break; } } if (t.size() >= 2 && (t[1] == 'p' || t[1] == 'P')) { Regexp* re = new Regexp(kRegexpCharClass, ps.flags() & ~FoldCase); re->ccb_ = new CharClassBuilder; switch (ParseUnicodeGroup(&t, ps.flags(), re->ccb_, status)) { case kParseOk: if (!ps.PushRegexp(re)) return NULL; goto Break2; case kParseError: re->Decref(); return NULL; case kParseNothing: re->Decref(); break; } } const UGroup *g = MaybeParsePerlCCEscape(&t, ps.flags()); if (g != NULL) { Regexp* re = new Regexp(kRegexpCharClass, ps.flags() & ~FoldCase); re->ccb_ = new CharClassBuilder; AddUGroup(re->ccb_, g, g->sign, ps.flags()); if (!ps.PushRegexp(re)) return NULL; break; } Rune r; if (!ParseEscape(&t, &r, status, ps.rune_max())) return NULL; if (!ps.PushLiteral(r)) return NULL; break; } } Break2: lastunary = isunary; } return ps.DoFinish(); } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/perl_groups.cc000066400000000000000000000055031266464252400240610ustar00rootroot00000000000000// GENERATED BY make_perl_groups.pl; DO NOT EDIT. // make_perl_groups.pl >perl_groups.cc #include "re2/unicode_groups.h" namespace re2 { static const URange16 code1[] = { /* \d */ { 0x30, 0x39 }, }; static const URange16 code2[] = { /* \s */ { 0x9, 0xa }, { 0xc, 0xd }, { 0x20, 0x20 }, }; static const URange16 code3[] = { /* \w */ { 0x30, 0x39 }, { 0x41, 0x5a }, { 0x5f, 0x5f }, { 0x61, 0x7a }, }; const UGroup perl_groups[] = { { "\\d", +1, code1, 1 }, { "\\D", -1, code1, 1 }, { "\\s", +1, code2, 3 }, { "\\S", -1, code2, 3 }, { "\\w", +1, code3, 4 }, { "\\W", -1, code3, 4 }, }; const int num_perl_groups = 6; static const URange16 code4[] = { /* [:alnum:] */ { 0x30, 0x39 }, { 0x41, 0x5a }, { 0x61, 0x7a }, }; static const URange16 code5[] = { /* [:alpha:] */ { 0x41, 0x5a }, { 0x61, 0x7a }, }; static const URange16 code6[] = { /* [:ascii:] */ { 0x0, 0x7f }, }; static const URange16 code7[] = { /* [:blank:] */ { 0x9, 0x9 }, { 0x20, 0x20 }, }; static const URange16 code8[] = { /* [:cntrl:] */ { 0x0, 0x1f }, { 0x7f, 0x7f }, }; static const URange16 code9[] = { /* [:digit:] */ { 0x30, 0x39 }, }; static const URange16 code10[] = { /* [:graph:] */ { 0x21, 0x7e }, }; static const URange16 code11[] = { /* [:lower:] */ { 0x61, 0x7a }, }; static const URange16 code12[] = { /* [:print:] */ { 0x20, 0x7e }, }; static const URange16 code13[] = { /* [:punct:] */ { 0x21, 0x2f }, { 0x3a, 0x40 }, { 0x5b, 0x60 }, { 0x7b, 0x7e }, }; static const URange16 code14[] = { /* [:space:] */ { 0x9, 0xd }, { 0x20, 0x20 }, }; static const URange16 code15[] = { /* [:upper:] */ { 0x41, 0x5a }, }; static const URange16 code16[] = { /* [:word:] */ { 0x30, 0x39 }, { 0x41, 0x5a }, { 0x5f, 0x5f }, { 0x61, 0x7a }, }; static const URange16 code17[] = { /* [:xdigit:] */ { 0x30, 0x39 }, { 0x41, 0x46 }, { 0x61, 0x66 }, }; const UGroup posix_groups[] = { { "[:alnum:]", +1, code4, 3 }, { "[:^alnum:]", -1, code4, 3 }, { "[:alpha:]", +1, code5, 2 }, { "[:^alpha:]", -1, code5, 2 }, { "[:ascii:]", +1, code6, 1 }, { "[:^ascii:]", -1, code6, 1 }, { "[:blank:]", +1, code7, 2 }, { "[:^blank:]", -1, code7, 2 }, { "[:cntrl:]", +1, code8, 2 }, { "[:^cntrl:]", -1, code8, 2 }, { "[:digit:]", +1, code9, 1 }, { "[:^digit:]", -1, code9, 1 }, { "[:graph:]", +1, code10, 1 }, { "[:^graph:]", -1, code10, 1 }, { "[:lower:]", +1, code11, 1 }, { "[:^lower:]", -1, code11, 1 }, { "[:print:]", +1, code12, 1 }, { "[:^print:]", -1, code12, 1 }, { "[:punct:]", +1, code13, 4 }, { "[:^punct:]", -1, code13, 4 }, { "[:space:]", +1, code14, 2 }, { "[:^space:]", -1, code14, 2 }, { "[:upper:]", +1, code15, 1 }, { "[:^upper:]", -1, code15, 1 }, { "[:word:]", +1, code16, 4 }, { "[:^word:]", -1, code16, 4 }, { "[:xdigit:]", +1, code17, 3 }, { "[:^xdigit:]", -1, code17, 3 }, }; const int num_posix_groups = 28; } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/prefilter.cc000066400000000000000000000420561266464252400235200ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "util/util.h" #include "re2/prefilter.h" #include "re2.h" #include "re2/unicode_casefold.h" #include "re2/walker-inl.h" namespace re2 { static const int Trace = false; typedef set::iterator SSIter; typedef set::const_iterator ConstSSIter; GLOBAL_MUTEX(alloc_id_mutex); static int alloc_id = 100000; // Used for debugging. // Initializes a Prefilter, allocating subs_ as necessary. Prefilter::Prefilter(Op op) { op_ = op; subs_ = NULL; if (op_ == AND || op_ == OR) subs_ = new vector; GLOBAL_MUTEX_LOCK(alloc_id_mutex); alloc_id_ = alloc_id++; GLOBAL_MUTEX_UNLOCK(alloc_id_mutex); VLOG(10) << "alloc_id: " << alloc_id_; } // Destroys a Prefilter. Prefilter::~Prefilter() { VLOG(10) << "Deleted: " << alloc_id_; if (subs_) { for (size_t i = 0; i < subs_->size(); i++) delete (*subs_)[i]; delete subs_; subs_ = NULL; } } // Simplify if the node is an empty Or or And. Prefilter* Prefilter::Simplify() { if (op_ != AND && op_ != OR) { return this; } // Nothing left in the AND/OR. if (subs_->size() == 0) { if (op_ == AND) op_ = ALL; // AND of nothing is true else op_ = NONE; // OR of nothing is false return this; } // Just one subnode: throw away wrapper. if (subs_->size() == 1) { Prefilter* a = (*subs_)[0]; subs_->clear(); delete this; return a->Simplify(); } return this; } // Combines two Prefilters together to create an "op" (AND or OR). // The passed Prefilters will be part of the returned Prefilter or deleted. // Does lots of work to avoid creating unnecessarily complicated structures. Prefilter* Prefilter::AndOr(Op op, Prefilter* a, Prefilter* b) { // If a, b can be rewritten as op, do so. a = a->Simplify(); b = b->Simplify(); // Canonicalize: a->op <= b->op. if (a->op() > b->op()) { Prefilter* t = a; a = b; b = t; } // Trivial cases. // ALL AND b = b // NONE OR b = b // ALL OR b = ALL // NONE AND b = NONE // Don't need to look at b, because of canonicalization above. // ALL and NONE are smallest opcodes. if (a->op() == ALL || a->op() == NONE) { if ((a->op() == ALL && op == AND) || (a->op() == NONE && op == OR)) { delete a; return b; } else { delete b; return a; } } // If a and b match op, merge their contents. if (a->op() == op && b->op() == op) { for (size_t i = 0; i < b->subs()->size(); i++) { Prefilter* bb = (*b->subs())[i]; a->subs()->push_back(bb); } b->subs()->clear(); delete b; return a; } // If a already has the same op as the op that is under construction // add in b (similarly if b already has the same op, add in a). if (b->op() == op) { Prefilter* t = a; a = b; b = t; } if (a->op() == op) { a->subs()->push_back(b); return a; } // Otherwise just return the op. Prefilter* c = new Prefilter(op); c->subs()->push_back(a); c->subs()->push_back(b); return c; } Prefilter* Prefilter::And(Prefilter* a, Prefilter* b) { return AndOr(AND, a, b); } Prefilter* Prefilter::Or(Prefilter* a, Prefilter* b) { return AndOr(OR, a, b); } static void SimplifyStringSet(set *ss) { // Now make sure that the strings aren't redundant. For example, if // we know "ab" is a required string, then it doesn't help at all to // know that "abc" is also a required string, so delete "abc". This // is because, when we are performing a string search to filter // regexps, matching ab will already allow this regexp to be a // candidate for match, so further matching abc is redundant. for (SSIter i = ss->begin(); i != ss->end(); ++i) { SSIter j = i; ++j; while (j != ss->end()) { // Increment j early so that we can erase the element it points to. SSIter old_j = j; ++j; if (old_j->find(*i) != string::npos) ss->erase(old_j); } } } Prefilter* Prefilter::OrStrings(set* ss) { SimplifyStringSet(ss); Prefilter* or_prefilter = NULL; if (!ss->empty()) { or_prefilter = new Prefilter(NONE); for (SSIter i = ss->begin(); i != ss->end(); ++i) or_prefilter = Or(or_prefilter, FromString(*i)); } return or_prefilter; } static Rune ToLowerRune(Rune r) { if (r < Runeself) { if ('A' <= r && r <= 'Z') r += 'a' - 'A'; return r; } const CaseFold *f = LookupCaseFold(unicode_tolower, num_unicode_tolower, r); if (f == NULL || r < f->lo) return r; return ApplyFold(f, r); } static Rune ToLowerRuneLatin1(Rune r) { if ('A' <= r && r <= 'Z') r += 'a' - 'A'; return r; } Prefilter* Prefilter::FromString(const string& str) { Prefilter* m = new Prefilter(Prefilter::ATOM); m->atom_ = str; return m; } // Information about a regexp used during computation of Prefilter. // Can be thought of as information about the set of strings matching // the given regular expression. class Prefilter::Info { public: Info(); ~Info(); // More constructors. They delete their Info* arguments. static Info* Alt(Info* a, Info* b); static Info* Concat(Info* a, Info* b); static Info* And(Info* a, Info* b); static Info* Star(Info* a); static Info* Plus(Info* a); static Info* Quest(Info* a); static Info* EmptyString(); static Info* NoMatch(); static Info* AnyChar(); static Info* CClass(CharClass* cc, bool latin1); static Info* Literal(Rune r); static Info* LiteralLatin1(Rune r); static Info* AnyMatch(); // Format Info as a string. string ToString(); // Caller takes ownership of the Prefilter. Prefilter* TakeMatch(); set& exact() { return exact_; } bool is_exact() const { return is_exact_; } class Walker; private: set exact_; // When is_exact_ is true, the strings that match // are placed in exact_. When it is no longer an exact // set of strings that match this RE, then is_exact_ // is false and the match_ contains the required match // criteria. bool is_exact_; // Accumulated Prefilter query that any // match for this regexp is guaranteed to match. Prefilter* match_; }; Prefilter::Info::Info() : is_exact_(false), match_(NULL) { } Prefilter::Info::~Info() { delete match_; } Prefilter* Prefilter::Info::TakeMatch() { if (is_exact_) { match_ = Prefilter::OrStrings(&exact_); is_exact_ = false; } Prefilter* m = match_; match_ = NULL; return m; } // Format a Info in string form. string Prefilter::Info::ToString() { if (is_exact_) { int n = 0; string s; for (set::iterator i = exact_.begin(); i != exact_.end(); ++i) { if (n++ > 0) s += ","; s += *i; } return s; } if (match_) return match_->DebugString(); return ""; } // Add the strings from src to dst. static void CopyIn(const set& src, set* dst) { for (ConstSSIter i = src.begin(); i != src.end(); ++i) dst->insert(*i); } // Add the cross-product of a and b to dst. // (For each string i in a and j in b, add i+j.) static void CrossProduct(const set& a, const set& b, set* dst) { for (ConstSSIter i = a.begin(); i != a.end(); ++i) for (ConstSSIter j = b.begin(); j != b.end(); ++j) dst->insert(*i + *j); } // Concats a and b. Requires that both are exact sets. // Forms an exact set that is a crossproduct of a and b. Prefilter::Info* Prefilter::Info::Concat(Info* a, Info* b) { if (a == NULL) return b; DCHECK(a->is_exact_); DCHECK(b && b->is_exact_); Info *ab = new Info(); CrossProduct(a->exact_, b->exact_, &ab->exact_); ab->is_exact_ = true; delete a; delete b; return ab; } // Constructs an inexact Info for ab given a and b. // Used only when a or b is not exact or when the // exact cross product is likely to be too big. Prefilter::Info* Prefilter::Info::And(Info* a, Info* b) { if (a == NULL) return b; if (b == NULL) return a; Info *ab = new Info(); ab->match_ = Prefilter::And(a->TakeMatch(), b->TakeMatch()); ab->is_exact_ = false; delete a; delete b; return ab; } // Constructs Info for a|b given a and b. Prefilter::Info* Prefilter::Info::Alt(Info* a, Info* b) { Info *ab = new Info(); if (a->is_exact_ && b->is_exact_) { CopyIn(a->exact_, &ab->exact_); CopyIn(b->exact_, &ab->exact_); ab->is_exact_ = true; } else { // Either a or b has is_exact_ = false. If the other // one has is_exact_ = true, we move it to match_ and // then create a OR of a,b. The resulting Info has // is_exact_ = false. ab->match_ = Prefilter::Or(a->TakeMatch(), b->TakeMatch()); ab->is_exact_ = false; } delete a; delete b; return ab; } // Constructs Info for a? given a. Prefilter::Info* Prefilter::Info::Quest(Info *a) { Info *ab = new Info(); ab->is_exact_ = false; ab->match_ = new Prefilter(ALL); delete a; return ab; } // Constructs Info for a* given a. // Same as a? -- not much to do. Prefilter::Info* Prefilter::Info::Star(Info *a) { return Quest(a); } // Constructs Info for a+ given a. If a was exact set, it isn't // anymore. Prefilter::Info* Prefilter::Info::Plus(Info *a) { Info *ab = new Info(); ab->match_ = a->TakeMatch(); ab->is_exact_ = false; delete a; return ab; } static string RuneToString(Rune r) { char buf[UTFmax]; int n = runetochar(buf, &r); return string(buf, n); } static string RuneToStringLatin1(Rune r) { char c = r & 0xff; return string(&c, 1); } // Constructs Info for literal rune. Prefilter::Info* Prefilter::Info::Literal(Rune r) { Info* info = new Info(); info->exact_.insert(RuneToString(ToLowerRune(r))); info->is_exact_ = true; return info; } // Constructs Info for literal rune for Latin1 encoded string. Prefilter::Info* Prefilter::Info::LiteralLatin1(Rune r) { Info* info = new Info(); info->exact_.insert(RuneToStringLatin1(ToLowerRuneLatin1(r))); info->is_exact_ = true; return info; } // Constructs Info for dot (any character). Prefilter::Info* Prefilter::Info::AnyChar() { Prefilter::Info* info = new Prefilter::Info(); info->match_ = new Prefilter(ALL); return info; } // Constructs Prefilter::Info for no possible match. Prefilter::Info* Prefilter::Info::NoMatch() { Prefilter::Info* info = new Prefilter::Info(); info->match_ = new Prefilter(NONE); return info; } // Constructs Prefilter::Info for any possible match. // This Prefilter::Info is valid for any regular expression, // since it makes no assertions whatsoever about the // strings being matched. Prefilter::Info* Prefilter::Info::AnyMatch() { Prefilter::Info *info = new Prefilter::Info(); info->match_ = new Prefilter(ALL); return info; } // Constructs Prefilter::Info for just the empty string. Prefilter::Info* Prefilter::Info::EmptyString() { Prefilter::Info* info = new Prefilter::Info(); info->is_exact_ = true; info->exact_.insert(""); return info; } // Constructs Prefilter::Info for a character class. typedef CharClass::iterator CCIter; Prefilter::Info* Prefilter::Info::CClass(CharClass *cc, bool latin1) { if (Trace) { VLOG(0) << "CharClassInfo:"; for (CCIter i = cc->begin(); i != cc->end(); ++i) VLOG(0) << " " << i->lo << "-" << i->hi; } // If the class is too large, it's okay to overestimate. if (cc->size() > 10) return AnyChar(); Prefilter::Info *a = new Prefilter::Info(); for (CCIter i = cc->begin(); i != cc->end(); ++i) for (Rune r = i->lo; r <= i->hi; r++) { if (latin1) { a->exact_.insert(RuneToStringLatin1(ToLowerRuneLatin1(r))); } else { a->exact_.insert(RuneToString(ToLowerRune(r))); } } a->is_exact_ = true; if (Trace) { VLOG(0) << " = " << a->ToString(); } return a; } class Prefilter::Info::Walker : public Regexp::Walker { public: Walker(bool latin1) : latin1_(latin1) {} virtual Info* PostVisit( Regexp* re, Info* parent_arg, Info* pre_arg, Info** child_args, int nchild_args); virtual Info* ShortVisit( Regexp* re, Info* parent_arg); bool latin1() { return latin1_; } private: bool latin1_; DISALLOW_COPY_AND_ASSIGN(Walker); }; Prefilter::Info* Prefilter::BuildInfo(Regexp* re) { if (Trace) { LOG(INFO) << "BuildPrefilter::Info: " << re->ToString(); } bool latin1 = re->parse_flags() & Regexp::Latin1; Prefilter::Info::Walker w(latin1); Prefilter::Info* info = w.WalkExponential(re, NULL, 100000); if (w.stopped_early()) { delete info; return NULL; } return info; } Prefilter::Info* Prefilter::Info::Walker::ShortVisit( Regexp* re, Prefilter::Info* parent_arg) { return AnyMatch(); } // Constructs the Prefilter::Info for the given regular expression. // Assumes re is simplified. Prefilter::Info* Prefilter::Info::Walker::PostVisit( Regexp* re, Prefilter::Info* parent_arg, Prefilter::Info* pre_arg, Prefilter::Info** child_args, int nchild_args) { Prefilter::Info *info; switch (re->op()) { default: case kRegexpRepeat: LOG(DFATAL) << "Bad regexp op " << re->op(); info = EmptyString(); break; case kRegexpNoMatch: info = NoMatch(); break; // These ops match the empty string: case kRegexpEmptyMatch: // anywhere case kRegexpBeginLine: // at beginning of line case kRegexpEndLine: // at end of line case kRegexpBeginText: // at beginning of text case kRegexpEndText: // at end of text case kRegexpWordBoundary: // at word boundary case kRegexpNoWordBoundary: // not at word boundary info = EmptyString(); break; case kRegexpLiteral: if (latin1()) { info = LiteralLatin1(re->rune()); } else { info = Literal(re->rune()); } break; case kRegexpLiteralString: if (re->nrunes() == 0) { info = NoMatch(); break; } if (latin1()) { info = LiteralLatin1(re->runes()[0]); for (int i = 1; i < re->nrunes(); i++) { info = Concat(info, LiteralLatin1(re->runes()[i])); } } else { info = Literal(re->runes()[0]); for (int i = 1; i < re->nrunes(); i++) { info = Concat(info, Literal(re->runes()[i])); } } break; case kRegexpConcat: { // Accumulate in info. // Exact is concat of recent contiguous exact nodes. info = NULL; Info* exact = NULL; for (int i = 0; i < nchild_args; i++) { Info* ci = child_args[i]; // child info if (!ci->is_exact() || (exact && ci->exact().size() * exact->exact().size() > 16)) { // Exact run is over. info = And(info, exact); exact = NULL; // Add this child's info. info = And(info, ci); } else { // Append to exact run. exact = Concat(exact, ci); } } info = And(info, exact); } break; case kRegexpAlternate: info = child_args[0]; for (int i = 1; i < nchild_args; i++) info = Alt(info, child_args[i]); VLOG(10) << "Alt: " << info->ToString(); break; case kRegexpStar: info = Star(child_args[0]); break; case kRegexpQuest: info = Quest(child_args[0]); break; case kRegexpPlus: info = Plus(child_args[0]); break; case kRegexpAnyChar: // Claim nothing, except that it's not empty. info = AnyChar(); break; case kRegexpCharClass: info = CClass(re->cc(), latin1()); break; case kRegexpCapture: // These don't affect the set of matching strings. info = child_args[0]; break; } if (Trace) { VLOG(0) << "BuildInfo " << re->ToString() << ": " << (info ? info->ToString() : ""); } return info; } Prefilter* Prefilter::FromRegexp(Regexp* re) { if (re == NULL) return NULL; Regexp* simple = re->Simplify(); Prefilter::Info *info = BuildInfo(simple); simple->Decref(); if (info == NULL) return NULL; Prefilter* m = info->TakeMatch(); delete info; return m; } string Prefilter::DebugString() const { switch (op_) { default: LOG(DFATAL) << "Bad op in Prefilter::DebugString: " << op_; return StringPrintf("op%d", op_); case NONE: return "*no-matches*"; case ATOM: return atom_; case ALL: return ""; case AND: { string s = ""; for (size_t i = 0; i < subs_->size(); i++) { if (i > 0) s += " "; Prefilter* sub = (*subs_)[i]; s += sub ? sub->DebugString() : ""; } return s; } case OR: { string s = "("; for (size_t i = 0; i < subs_->size(); i++) { if (i > 0) s += "|"; Prefilter* sub = (*subs_)[i]; s += sub ? sub->DebugString() : ""; } s += ")"; return s; } } } Prefilter* Prefilter::FromRE2(const RE2* re2) { if (re2 == NULL) return NULL; Regexp* regexp = re2->Regexp(); if (regexp == NULL) return NULL; return FromRegexp(regexp); } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/prefilter.h000066400000000000000000000055621266464252400233630ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Prefilter is the class used to extract string guards from regexps. // Rather than using Prefilter class directly, use FilteredRE2. // See filtered_re2.h #ifndef RE2_PREFILTER_H_ #define RE2_PREFILTER_H_ #include "util/util.h" namespace re2 { class RE2; class Regexp; class Prefilter { // Instead of using Prefilter directly, use FilteredRE2; see filtered_re2.h public: enum Op { ALL = 0, // Everything matches NONE, // Nothing matches ATOM, // The string atom() must match AND, // All in subs() must match OR, // One of subs() must match }; explicit Prefilter(Op op); ~Prefilter(); Op op() { return op_; } const string& atom() const { return atom_; } void set_unique_id(int id) { unique_id_ = id; } int unique_id() const { return unique_id_; } // The children of the Prefilter node. vector* subs() { CHECK(op_ == AND || op_ == OR); return subs_; } // Set the children vector. Prefilter takes ownership of subs and // subs_ will be deleted when Prefilter is deleted. void set_subs(vector* subs) { subs_ = subs; } // Given a RE2, return a Prefilter. The caller takes ownership of // the Prefilter and should deallocate it. Returns NULL if Prefilter // cannot be formed. static Prefilter* FromRE2(const RE2* re2); // Returns a readable debug string of the prefilter. string DebugString() const; private: class Info; // Combines two prefilters together to create an AND. The passed // Prefilters will be part of the returned Prefilter or deleted. static Prefilter* And(Prefilter* a, Prefilter* b); // Combines two prefilters together to create an OR. The passed // Prefilters will be part of the returned Prefilter or deleted. static Prefilter* Or(Prefilter* a, Prefilter* b); // Generalized And/Or static Prefilter* AndOr(Op op, Prefilter* a, Prefilter* b); static Prefilter* FromRegexp(Regexp* a); static Prefilter* FromString(const string& str); static Prefilter* OrStrings(set* ss); static Info* BuildInfo(Regexp* re); Prefilter* Simplify(); // Kind of Prefilter. Op op_; // Sub-matches for AND or OR Prefilter. vector* subs_; // Actual string to match in leaf node. string atom_; // If different prefilters have the same string atom, or if they are // structurally the same (e.g., OR of same atom strings) they are // considered the same unique nodes. This is the id for each unique // node. This field is populated with a unique id for every node, // and -1 for duplicate nodes. int unique_id_; // Used for debugging, helps in tracking memory leaks. int alloc_id_; DISALLOW_COPY_AND_ASSIGN(Prefilter); }; } // namespace re2 #endif // RE2_PREFILTER_H_ openalpr_2.2.4.orig/src/openalpr/support/re2/prefilter_tree.cc000066400000000000000000000273041266464252400245360ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "util/util.h" #include "util/flags.h" #include "re2/prefilter.h" #include "re2/prefilter_tree.h" #include "re2.h" DEFINE_int32(filtered_re2_min_atom_len, 3, "Strings less than this length are not stored as atoms"); namespace re2 { PrefilterTree::PrefilterTree() : compiled_(false) { } PrefilterTree::~PrefilterTree() { for (size_t i = 0; i < prefilter_vec_.size(); i++) delete prefilter_vec_[i]; for (size_t i = 0; i < entries_.size(); i++) delete entries_[i].parents; } // Functions used for adding and Compiling prefilters to the // PrefilterTree. static bool KeepPart(Prefilter* prefilter, int level) { if (prefilter == NULL) return false; switch (prefilter->op()) { default: LOG(DFATAL) << "Unexpected op in KeepPart: " << prefilter->op(); return false; case Prefilter::ALL: return false; case Prefilter::ATOM: return prefilter->atom().size() >= static_cast(FLAGS_filtered_re2_min_atom_len); case Prefilter::AND: { int j = 0; vector* subs = prefilter->subs(); for (size_t i = 0; i < subs->size(); i++) if (KeepPart((*subs)[i], level + 1)) (*subs)[j++] = (*subs)[i]; else delete (*subs)[i]; subs->resize(j); return j > 0; } case Prefilter::OR: for (size_t i = 0; i < prefilter->subs()->size(); i++) if (!KeepPart((*prefilter->subs())[i], level + 1)) return false; return true; } } void PrefilterTree::Add(Prefilter *f) { if (compiled_) { LOG(DFATAL) << "Add after Compile."; return; } if (f != NULL && !KeepPart(f, 0)) { delete f; f = NULL; } prefilter_vec_.push_back(f); } void PrefilterTree::Compile(vector* atom_vec) { if (compiled_) { LOG(DFATAL) << "Compile after Compile."; return; } // We do this check to support some legacy uses of // PrefilterTree that call Compile before adding any regexps, // and expect Compile not to have effect. if (prefilter_vec_.empty()) return; compiled_ = true; AssignUniqueIds(atom_vec); // Identify nodes that are too common among prefilters and are // triggering too many parents. Then get rid of them if possible. // Note that getting rid of a prefilter node simply means they are // no longer necessary for their parent to trigger; that is, we do // not miss out on any regexps triggering by getting rid of a // prefilter node. for (size_t i = 0; i < entries_.size(); i++) { StdIntMap* parents = entries_[i].parents; if (parents->size() > 8) { // This one triggers too many things. If all the parents are AND // nodes and have other things guarding them, then get rid of // this trigger. TODO(vsri): Adjust the threshold appropriately, // make it a function of total number of nodes? bool have_other_guard = true; for (StdIntMap::iterator it = parents->begin(); it != parents->end(); ++it) { have_other_guard = have_other_guard && (entries_[it->first].propagate_up_at_count > 1); } if (have_other_guard) { for (StdIntMap::iterator it = parents->begin(); it != parents->end(); ++it) entries_[it->first].propagate_up_at_count -= 1; parents->clear(); // Forget the parents } } } PrintDebugInfo(); } Prefilter* PrefilterTree::CanonicalNode(Prefilter* node) { string node_string = NodeString(node); map::iterator iter = node_map_.find(node_string); if (iter == node_map_.end()) return NULL; return (*iter).second; } static string Itoa(int n) { char buf[100]; snprintf(buf, sizeof buf, "%d", n); return string(buf); } string PrefilterTree::NodeString(Prefilter* node) const { // Adding the operation disambiguates AND/OR/atom nodes. string s = Itoa(node->op()) + ":"; if (node->op() == Prefilter::ATOM) { s += node->atom(); } else { for (size_t i = 0; i < node->subs()->size(); i++) { if (i > 0) s += ','; s += Itoa((*node->subs())[i]->unique_id()); } } return s; } void PrefilterTree::AssignUniqueIds(vector* atom_vec) { atom_vec->clear(); // Build vector of all filter nodes, sorted topologically // from top to bottom in v. vector v; // Add the top level nodes of each regexp prefilter. for (size_t i = 0; i < prefilter_vec_.size(); i++) { Prefilter* f = prefilter_vec_[i]; if (f == NULL) unfiltered_.push_back(i); // We push NULL also on to v, so that we maintain the // mapping of index==regexpid for level=0 prefilter nodes. v.push_back(f); } // Now add all the descendant nodes. for (size_t i = 0; i < v.size(); i++) { Prefilter* f = v[i]; if (f == NULL) continue; if (f->op() == Prefilter::AND || f->op() == Prefilter::OR) { const vector& subs = *f->subs(); for (size_t j = 0; j < subs.size(); j++) v.push_back(subs[j]); } } // Identify unique nodes. int unique_id = 0; for (int i = v.size() - 1; i >= 0; i--) { Prefilter *node = v[i]; if (node == NULL) continue; node->set_unique_id(-1); Prefilter* canonical = CanonicalNode(node); if (canonical == NULL) { // Any further nodes that have the same node string // will find this node as the canonical node. node_map_[NodeString(node)] = node; if (node->op() == Prefilter::ATOM) { atom_vec->push_back(node->atom()); atom_index_to_id_.push_back(unique_id); } node->set_unique_id(unique_id++); } else { node->set_unique_id(canonical->unique_id()); } } entries_.resize(node_map_.size()); // Create parent StdIntMap for the entries. for (int i = v.size() - 1; i >= 0; i--) { Prefilter* prefilter = v[i]; if (prefilter == NULL) continue; if (CanonicalNode(prefilter) != prefilter) continue; Entry* entry = &entries_[prefilter->unique_id()]; entry->parents = new StdIntMap(); } // Fill the entries. for (int i = v.size() - 1; i >= 0; i--) { Prefilter* prefilter = v[i]; if (prefilter == NULL) continue; if (CanonicalNode(prefilter) != prefilter) continue; Entry* entry = &entries_[prefilter->unique_id()]; switch (prefilter->op()) { default: case Prefilter::ALL: LOG(DFATAL) << "Unexpected op: " << prefilter->op(); return; case Prefilter::ATOM: entry->propagate_up_at_count = 1; break; case Prefilter::OR: case Prefilter::AND: { set uniq_child; for (size_t j = 0; j < prefilter->subs()->size(); j++) { Prefilter* child = (*prefilter->subs())[j]; Prefilter* canonical = CanonicalNode(child); if (canonical == NULL) { LOG(DFATAL) << "Null canonical node"; return; } int child_id = canonical->unique_id(); uniq_child.insert(child_id); // To the child, we want to add to parent indices. Entry* child_entry = &entries_[child_id]; if (child_entry->parents->find(prefilter->unique_id()) == child_entry->parents->end()) { (*child_entry->parents)[prefilter->unique_id()] = 1; } } entry->propagate_up_at_count = prefilter->op() == Prefilter::AND ? uniq_child.size() : 1; break; } } } // For top level nodes, populate regexp id. for (size_t i = 0; i < prefilter_vec_.size(); i++) { if (prefilter_vec_[i] == NULL) continue; int id = CanonicalNode(prefilter_vec_[i])->unique_id(); DCHECK_LE(0, id); Entry* entry = &entries_[id]; entry->regexps.push_back(i); } } // Functions for triggering during search. void PrefilterTree::RegexpsGivenStrings( const vector& matched_atoms, vector* regexps) const { regexps->clear(); if (!compiled_) { LOG(WARNING) << "Compile() not called"; for (size_t i = 0; i < prefilter_vec_.size(); ++i) regexps->push_back(i); } else { if (!prefilter_vec_.empty()) { IntMap regexps_map(prefilter_vec_.size()); vector matched_atom_ids; for (size_t j = 0; j < matched_atoms.size(); j++) { matched_atom_ids.push_back(atom_index_to_id_[matched_atoms[j]]); VLOG(10) << "Atom id:" << atom_index_to_id_[matched_atoms[j]]; } PropagateMatch(matched_atom_ids, ®exps_map); for (IntMap::iterator it = regexps_map.begin(); it != regexps_map.end(); ++it) regexps->push_back(it->index()); regexps->insert(regexps->end(), unfiltered_.begin(), unfiltered_.end()); } } sort(regexps->begin(), regexps->end()); } void PrefilterTree::PropagateMatch(const vector& atom_ids, IntMap* regexps) const { IntMap count(entries_.size()); IntMap work(entries_.size()); for (size_t i = 0; i < atom_ids.size(); i++) work.set(atom_ids[i], 1); for (IntMap::iterator it = work.begin(); it != work.end(); ++it) { const Entry& entry = entries_[it->index()]; VLOG(10) << "Processing: " << it->index(); // Record regexps triggered. for (size_t i = 0; i < entry.regexps.size(); i++) { VLOG(10) << "Regexp triggered: " << entry.regexps[i]; regexps->set(entry.regexps[i], 1); } int c; // Pass trigger up to parents. for (StdIntMap::iterator it = entry.parents->begin(); it != entry.parents->end(); ++it) { int j = it->first; const Entry& parent = entries_[j]; VLOG(10) << " parent= " << j << " trig= " << parent.propagate_up_at_count; // Delay until all the children have succeeded. if (parent.propagate_up_at_count > 1) { if (count.has_index(j)) { c = count.get_existing(j) + 1; count.set_existing(j, c); } else { c = 1; count.set_new(j, c); } if (c < parent.propagate_up_at_count) continue; } VLOG(10) << "Triggering: " << j; // Trigger the parent. work.set(j, 1); } } } // Debugging help. void PrefilterTree::PrintPrefilter(int regexpid) { LOG(INFO) << DebugNodeString(prefilter_vec_[regexpid]); } void PrefilterTree::PrintDebugInfo() { VLOG(10) << "#Unique Atoms: " << atom_index_to_id_.size(); VLOG(10) << "#Unique Nodes: " << entries_.size(); for (size_t i = 0; i < entries_.size(); ++i) { StdIntMap* parents = entries_[i].parents; const vector& regexps = entries_[i].regexps; VLOG(10) << "EntryId: " << i << " N: " << parents->size() << " R: " << regexps.size(); for (StdIntMap::iterator it = parents->begin(); it != parents->end(); ++it) VLOG(10) << it->first; } VLOG(10) << "Map:"; for (map::const_iterator iter = node_map_.begin(); iter != node_map_.end(); ++iter) VLOG(10) << "NodeId: " << (*iter).second->unique_id() << " Str: " << (*iter).first; } string PrefilterTree::DebugNodeString(Prefilter* node) const { string node_string = ""; if (node->op() == Prefilter::ATOM) { DCHECK(!node->atom().empty()); node_string += node->atom(); } else { // Adding the operation disambiguates AND and OR nodes. node_string += node->op() == Prefilter::AND ? "AND" : "OR"; node_string += "("; for (size_t i = 0; i < node->subs()->size(); i++) { if (i > 0) node_string += ','; node_string += Itoa((*node->subs())[i]->unique_id()); node_string += ":"; node_string += DebugNodeString((*node->subs())[i]); } node_string += ")"; } return node_string; } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/prefilter_tree.h000066400000000000000000000112601266464252400243720ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // The PrefilterTree class is used to form an AND-OR tree of strings // that would trigger each regexp. The 'prefilter' of each regexp is // added tp PrefilterTree, and then PrefilterTree is used to find all // the unique strings across the prefilters. During search, by using // matches from a string matching engine, PrefilterTree deduces the // set of regexps that are to be triggered. The 'string matching // engine' itself is outside of this class, and the caller can use any // favorite engine. PrefilterTree provides a set of strings (called // atoms) that the user of this class should use to do the string // matching. // #ifndef RE2_PREFILTER_TREE_H_ #define RE2_PREFILTER_TREE_H_ #include "util/util.h" #include "util/sparse_array.h" namespace re2 { typedef SparseArray IntMap; typedef map StdIntMap; class Prefilter; class PrefilterTree { public: PrefilterTree(); ~PrefilterTree(); // Adds the prefilter for the next regexp. Note that we assume that // Add called sequentially for all regexps. All Add calls // must precede Compile. void Add(Prefilter* prefilter); // The Compile returns a vector of string in atom_vec. // Call this after all the prefilters are added through Add. // No calls to Add after Compile are allowed. // The caller should use the returned set of strings to do string matching. // Each time a string matches, the corresponding index then has to be // and passed to RegexpsGivenStrings below. void Compile(vector* atom_vec); // Given the indices of the atoms that matched, returns the indexes // of regexps that should be searched. The matched_atoms should // contain all the ids of string atoms that were found to match the // content. The caller can use any string match engine to perform // this function. This function is thread safe. void RegexpsGivenStrings(const vector& matched_atoms, vector* regexps) const; // Print debug prefilter. Also prints unique ids associated with // nodes of the prefilter of the regexp. void PrintPrefilter(int regexpid); // Each unique node has a corresponding Entry that helps in // passing the matching trigger information along the tree. struct Entry { public: // How many children should match before this node triggers the // parent. For an atom and an OR node, this is 1 and for an AND // node, it is the number of unique children. int propagate_up_at_count; // When this node is ready to trigger the parent, what are the indices // of the parent nodes to trigger. The reason there may be more than // one is because of sharing. For example (abc | def) and (xyz | def) // are two different nodes, but they share the atom 'def'. So when // 'def' matches, it triggers two parents, corresponding to the two // different OR nodes. StdIntMap* parents; // When this node is ready to trigger the parent, what are the // regexps that are triggered. vector regexps; }; private: // This function assigns unique ids to various parts of the // prefilter, by looking at if these nodes are already in the // PrefilterTree. void AssignUniqueIds(vector* atom_vec); // Given the matching atoms, find the regexps to be triggered. void PropagateMatch(const vector& atom_ids, IntMap* regexps) const; // Returns the prefilter node that has the same NodeString as this // node. For the canonical node, returns node. Prefilter* CanonicalNode(Prefilter* node); // A string that uniquely identifies the node. Assumes that the // children of node has already been assigned unique ids. string NodeString(Prefilter* node) const; // Recursively constructs a readable prefilter string. string DebugNodeString(Prefilter* node) const; // Used for debugging. void PrintDebugInfo(); // These are all the nodes formed by Compile. Essentially, there is // one node for each unique atom and each unique AND/OR node. vector entries_; // Map node string to canonical Prefilter node. map node_map_; // indices of regexps that always pass through the filter (since we // found no required literals in these regexps). vector unfiltered_; // vector of Prefilter for all regexps. vector prefilter_vec_; // Atom index in returned strings to entry id mapping. vector atom_index_to_id_; // Has the prefilter tree been compiled. bool compiled_; DISALLOW_COPY_AND_ASSIGN(PrefilterTree); }; } // namespace #endif // RE2_PREFILTER_TREE_H_ openalpr_2.2.4.orig/src/openalpr/support/re2/prog.cc000066400000000000000000000200051266464252400224610ustar00rootroot00000000000000// Copyright 2007 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Compiled regular expression representation. // Tested by compile_test.cc #include "util/util.h" #include "util/sparse_set.h" #include "re2/prog.h" #include "re2/stringpiece.h" namespace re2 { // Constructors per Inst opcode void Prog::Inst::InitAlt(uint32 out, uint32 out1) { DCHECK_EQ(out_opcode_, 0); set_out_opcode(out, kInstAlt); out1_ = out1; } void Prog::Inst::InitByteRange(int lo, int hi, int foldcase, uint32 out) { DCHECK_EQ(out_opcode_, 0); set_out_opcode(out, kInstByteRange); lo_ = lo & 0xFF; hi_ = hi & 0xFF; foldcase_ = foldcase; } void Prog::Inst::InitCapture(int cap, uint32 out) { DCHECK_EQ(out_opcode_, 0); set_out_opcode(out, kInstCapture); cap_ = cap; } void Prog::Inst::InitEmptyWidth(EmptyOp empty, uint32 out) { DCHECK_EQ(out_opcode_, 0); set_out_opcode(out, kInstEmptyWidth); empty_ = empty; } void Prog::Inst::InitMatch(int32 id) { DCHECK_EQ(out_opcode_, 0); set_opcode(kInstMatch); match_id_ = id; } void Prog::Inst::InitNop(uint32 out) { DCHECK_EQ(out_opcode_, 0); set_opcode(kInstNop); } void Prog::Inst::InitFail() { DCHECK_EQ(out_opcode_, 0); set_opcode(kInstFail); } string Prog::Inst::Dump() { switch (opcode()) { default: return StringPrintf("opcode %d", static_cast(opcode())); case kInstAlt: return StringPrintf("alt -> %d | %d", out(), out1_); case kInstAltMatch: return StringPrintf("altmatch -> %d | %d", out(), out1_); case kInstByteRange: return StringPrintf("byte%s [%02x-%02x] -> %d", foldcase_ ? "/i" : "", lo_, hi_, out()); case kInstCapture: return StringPrintf("capture %d -> %d", cap_, out()); case kInstEmptyWidth: return StringPrintf("emptywidth %#x -> %d", static_cast(empty_), out()); case kInstMatch: return StringPrintf("match! %d", match_id()); case kInstNop: return StringPrintf("nop -> %d", out()); case kInstFail: return StringPrintf("fail"); } } Prog::Prog() : anchor_start_(false), anchor_end_(false), reversed_(false), did_onepass_(false), start_(0), start_unanchored_(0), size_(0), byte_inst_count_(0), bytemap_range_(0), flags_(0), onepass_statesize_(0), inst_(NULL), dfa_first_(NULL), dfa_longest_(NULL), dfa_mem_(0), delete_dfa_(NULL), unbytemap_(NULL), onepass_nodes_(NULL), onepass_start_(NULL) { } Prog::~Prog() { if (delete_dfa_) { if (dfa_first_) delete_dfa_(dfa_first_); if (dfa_longest_) delete_dfa_(dfa_longest_); } delete[] onepass_nodes_; delete[] inst_; delete[] unbytemap_; } typedef SparseSet Workq; static inline void AddToQueue(Workq* q, int id) { if (id != 0) q->insert(id); } static string ProgToString(Prog* prog, Workq* q) { string s; for (Workq::iterator i = q->begin(); i != q->end(); ++i) { int id = *i; Prog::Inst* ip = prog->inst(id); StringAppendF(&s, "%d. %s\n", id, ip->Dump().c_str()); AddToQueue(q, ip->out()); if (ip->opcode() == kInstAlt || ip->opcode() == kInstAltMatch) AddToQueue(q, ip->out1()); } return s; } string Prog::Dump() { string map; if (false) { // Debugging int lo = 0; StringAppendF(&map, "byte map:\n"); for (int i = 0; i < bytemap_range_; i++) { StringAppendF(&map, "\t%d. [%02x-%02x]\n", i, lo, unbytemap_[i]); lo = unbytemap_[i] + 1; } StringAppendF(&map, "\n"); } Workq q(size_); AddToQueue(&q, start_); return map + ProgToString(this, &q); } string Prog::DumpUnanchored() { Workq q(size_); AddToQueue(&q, start_unanchored_); return ProgToString(this, &q); } static bool IsMatch(Prog*, Prog::Inst*); // Peep-hole optimizer. void Prog::Optimize() { Workq q(size_); // Eliminate nops. Most are taken out during compilation // but a few are hard to avoid. q.clear(); AddToQueue(&q, start_); for (Workq::iterator i = q.begin(); i != q.end(); ++i) { int id = *i; Inst* ip = inst(id); int j = ip->out(); Inst* jp; while (j != 0 && (jp=inst(j))->opcode() == kInstNop) { j = jp->out(); } ip->set_out(j); AddToQueue(&q, ip->out()); if (ip->opcode() == kInstAlt) { j = ip->out1(); while (j != 0 && (jp=inst(j))->opcode() == kInstNop) { j = jp->out(); } ip->out1_ = j; AddToQueue(&q, ip->out1()); } } // Insert kInstAltMatch instructions // Look for // ip: Alt -> j | k // j: ByteRange [00-FF] -> ip // k: Match // or the reverse (the above is the greedy one). // Rewrite Alt to AltMatch. q.clear(); AddToQueue(&q, start_); for (Workq::iterator i = q.begin(); i != q.end(); ++i) { int id = *i; Inst* ip = inst(id); AddToQueue(&q, ip->out()); if (ip->opcode() == kInstAlt) AddToQueue(&q, ip->out1()); if (ip->opcode() == kInstAlt) { Inst* j = inst(ip->out()); Inst* k = inst(ip->out1()); if (j->opcode() == kInstByteRange && j->out() == id && j->lo() == 0x00 && j->hi() == 0xFF && IsMatch(this, k)) { ip->set_opcode(kInstAltMatch); continue; } if (IsMatch(this, j) && k->opcode() == kInstByteRange && k->out() == id && k->lo() == 0x00 && k->hi() == 0xFF) { ip->set_opcode(kInstAltMatch); } } } } // Is ip a guaranteed match at end of text, perhaps after some capturing? static bool IsMatch(Prog* prog, Prog::Inst* ip) { for (;;) { switch (ip->opcode()) { default: LOG(DFATAL) << "Unexpected opcode in IsMatch: " << ip->opcode(); return false; case kInstAlt: case kInstAltMatch: case kInstByteRange: case kInstFail: case kInstEmptyWidth: return false; case kInstCapture: case kInstNop: ip = prog->inst(ip->out()); break; case kInstMatch: return true; } } } uint32 Prog::EmptyFlags(const StringPiece& text, const char* p) { int flags = 0; // ^ and \A if (p == text.begin()) flags |= kEmptyBeginText | kEmptyBeginLine; else if (p[-1] == '\n') flags |= kEmptyBeginLine; // $ and \z if (p == text.end()) flags |= kEmptyEndText | kEmptyEndLine; else if (p < text.end() && p[0] == '\n') flags |= kEmptyEndLine; // \b and \B if (p == text.begin() && p == text.end()) { // no word boundary here } else if (p == text.begin()) { if (IsWordChar(p[0])) flags |= kEmptyWordBoundary; } else if (p == text.end()) { if (IsWordChar(p[-1])) flags |= kEmptyWordBoundary; } else { if (IsWordChar(p[-1]) != IsWordChar(p[0])) flags |= kEmptyWordBoundary; } if (!(flags & kEmptyWordBoundary)) flags |= kEmptyNonWordBoundary; return flags; } void Prog::MarkByteRange(int lo, int hi) { DCHECK_GE(lo, 0); DCHECK_GE(hi, 0); DCHECK_LE(lo, 255); DCHECK_LE(hi, 255); DCHECK_LE(lo, hi); if (0 < lo && lo <= 255) byterange_.Set(lo - 1); if (0 <= hi && hi <= 255) byterange_.Set(hi); } void Prog::ComputeByteMap() { // Fill in bytemap with byte classes for prog_. // Ranges of bytes that are treated as indistinguishable // by the regexp program are mapped to a single byte class. // The vector prog_->byterange() marks the end of each // such range. const Bitmap<256>& v = byterange(); COMPILE_ASSERT(8*sizeof(v.Word(0)) == 32, wordsize); uint8 n = 0; uint32 bits = 0; for (int i = 0; i < 256; i++) { if ((i&31) == 0) bits = v.Word(i >> 5); bytemap_[i] = n; n += bits & 1; bits >>= 1; } bytemap_range_ = bytemap_[255] + 1; unbytemap_ = new uint8[bytemap_range_]; for (int i = 0; i < 256; i++) unbytemap_[bytemap_[i]] = i; if (0) { // For debugging: use trivial byte map. for (int i = 0; i < 256; i++) { bytemap_[i] = i; unbytemap_[i] = i; } bytemap_range_ = 256; LOG(INFO) << "Using trivial bytemap."; } } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/prog.h000066400000000000000000000342531266464252400223350ustar00rootroot00000000000000// Copyright 2007 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Compiled representation of regular expressions. // See regexp.h for the Regexp class, which represents a regular // expression symbolically. #ifndef RE2_PROG_H__ #define RE2_PROG_H__ #include "re2/util/util.h" #include "re2/util/sparse_array.h" #include "re2.h" namespace re2 { // Simple fixed-size bitmap. template class Bitmap { public: Bitmap() { Reset(); } int Size() { return Bits; } void Reset() { for (int i = 0; i < Words; i++) w_[i] = 0; } bool Get(int k) const { return w_[k >> WordLog] & (1<<(k & 31)); } void Set(int k) { w_[k >> WordLog] |= 1<<(k & 31); } void Clear(int k) { w_[k >> WordLog] &= ~(1<<(k & 31)); } uint32 Word(int i) const { return w_[i]; } private: static const int WordLog = 5; static const int Words = (Bits+31)/32; uint32 w_[Words]; DISALLOW_COPY_AND_ASSIGN(Bitmap); }; // Opcodes for Inst enum InstOp { kInstAlt = 0, // choose between out_ and out1_ kInstAltMatch, // Alt: out_ is [00-FF] and back, out1_ is match; or vice versa. kInstByteRange, // next (possible case-folded) byte must be in [lo_, hi_] kInstCapture, // capturing parenthesis number cap_ kInstEmptyWidth, // empty-width special (^ $ ...); bit(s) set in empty_ kInstMatch, // found a match! kInstNop, // no-op; occasionally unavoidable kInstFail, // never match; occasionally unavoidable }; // Bit flags for empty-width specials enum EmptyOp { kEmptyBeginLine = 1<<0, // ^ - beginning of line kEmptyEndLine = 1<<1, // $ - end of line kEmptyBeginText = 1<<2, // \A - beginning of text kEmptyEndText = 1<<3, // \z - end of text kEmptyWordBoundary = 1<<4, // \b - word boundary kEmptyNonWordBoundary = 1<<5, // \B - not \b kEmptyAllFlags = (1<<6)-1, }; class Regexp; class DFA; struct OneState; // Compiled form of regexp program. class Prog { public: Prog(); ~Prog(); // Single instruction in regexp program. class Inst { public: Inst() : out_opcode_(0), out1_(0) { } // Constructors per opcode void InitAlt(uint32 out, uint32 out1); void InitByteRange(int lo, int hi, int foldcase, uint32 out); void InitCapture(int cap, uint32 out); void InitEmptyWidth(EmptyOp empty, uint32 out); void InitMatch(int id); void InitNop(uint32 out); void InitFail(); // Getters int id(Prog* p) { return this - p->inst_; } InstOp opcode() { return static_cast(out_opcode_&7); } int out() { return out_opcode_>>3; } int out1() { DCHECK(opcode() == kInstAlt || opcode() == kInstAltMatch); return out1_; } int cap() { DCHECK_EQ(opcode(), kInstCapture); return cap_; } int lo() { DCHECK_EQ(opcode(), kInstByteRange); return lo_; } int hi() { DCHECK_EQ(opcode(), kInstByteRange); return hi_; } int foldcase() { DCHECK_EQ(opcode(), kInstByteRange); return foldcase_; } int match_id() { DCHECK_EQ(opcode(), kInstMatch); return match_id_; } EmptyOp empty() { DCHECK_EQ(opcode(), kInstEmptyWidth); return empty_; } bool greedy(Prog *p) { DCHECK_EQ(opcode(), kInstAltMatch); return p->inst(out())->opcode() == kInstByteRange; } // Does this inst (an kInstByteRange) match c? inline bool Matches(int c) { DCHECK_EQ(opcode(), kInstByteRange); if (foldcase_ && 'A' <= c && c <= 'Z') c += 'a' - 'A'; return lo_ <= c && c <= hi_; } // Returns string representation for debugging. string Dump(); // Maximum instruction id. // (Must fit in out_opcode_, and PatchList steals another bit.) static const int kMaxInst = (1<<28) - 1; private: void set_opcode(InstOp opcode) { out_opcode_ = (out()<<3) | opcode; } void set_out(int out) { out_opcode_ = (out<<3) | opcode(); } void set_out_opcode(int out, InstOp opcode) { out_opcode_ = (out<<3) | opcode; } uint32 out_opcode_; // 29 bits of out, 3 (low) bits opcode union { // additional instruction arguments: uint32 out1_; // opcode == kInstAlt // alternate next instruction int32 cap_; // opcode == kInstCapture // Index of capture register (holds text // position recorded by capturing parentheses). // For \n (the submatch for the nth parentheses), // the left parenthesis captures into register 2*n // and the right one captures into register 2*n+1. int32 match_id_; // opcode == kInstMatch // Match ID to identify this match (for re2::Set). struct { // opcode == kInstByteRange uint8 lo_; // byte range is lo_-hi_ inclusive uint8 hi_; // uint8 foldcase_; // convert A-Z to a-z before checking range. }; EmptyOp empty_; // opcode == kInstEmptyWidth // empty_ is bitwise OR of kEmpty* flags above. }; friend class Compiler; friend struct PatchList; friend class Prog; DISALLOW_COPY_AND_ASSIGN(Inst); }; // Whether to anchor the search. enum Anchor { kUnanchored, // match anywhere kAnchored, // match only starting at beginning of text }; // Kind of match to look for (for anchor != kFullMatch) // // kLongestMatch mode finds the overall longest // match but still makes its submatch choices the way // Perl would, not in the way prescribed by POSIX. // The POSIX rules are much more expensive to implement, // and no one has needed them. // // kFullMatch is not strictly necessary -- we could use // kLongestMatch and then check the length of the match -- but // the matching code can run faster if it knows to consider only // full matches. enum MatchKind { kFirstMatch, // like Perl, PCRE kLongestMatch, // like egrep or POSIX kFullMatch, // match only entire text; implies anchor==kAnchored kManyMatch // for SearchDFA, records set of matches }; Inst *inst(int id) { return &inst_[id]; } int start() { return start_; } int start_unanchored() { return start_unanchored_; } void set_start(int start) { start_ = start; } void set_start_unanchored(int start) { start_unanchored_ = start; } int64 size() { return size_; } bool reversed() { return reversed_; } void set_reversed(bool reversed) { reversed_ = reversed; } int64 byte_inst_count() { return byte_inst_count_; } const Bitmap<256>& byterange() { return byterange_; } void set_dfa_mem(int64 dfa_mem) { dfa_mem_ = dfa_mem; } int64 dfa_mem() { return dfa_mem_; } int flags() { return flags_; } void set_flags(int flags) { flags_ = flags; } bool anchor_start() { return anchor_start_; } void set_anchor_start(bool b) { anchor_start_ = b; } bool anchor_end() { return anchor_end_; } void set_anchor_end(bool b) { anchor_end_ = b; } int bytemap_range() { return bytemap_range_; } const uint8* bytemap() { return bytemap_; } // Returns string representation of program for debugging. string Dump(); string DumpUnanchored(); // Record that at some point in the prog, the bytes in the range // lo-hi (inclusive) are treated as different from bytes outside the range. // Tracking this lets the DFA collapse commonly-treated byte ranges // when recording state pointers, greatly reducing its memory footprint. void MarkByteRange(int lo, int hi); // Returns the set of kEmpty flags that are in effect at // position p within context. static uint32 EmptyFlags(const StringPiece& context, const char* p); // Returns whether byte c is a word character: ASCII only. // Used by the implementation of \b and \B. // This is not right for Unicode, but: // - it's hard to get right in a byte-at-a-time matching world // (the DFA has only one-byte lookahead). // - even if the lookahead were possible, the Progs would be huge. // This crude approximation is the same one PCRE uses. static bool IsWordChar(uint8 c) { return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_'; } // Execution engines. They all search for the regexp (run the prog) // in text, which is in the larger context (used for ^ $ \b etc). // Anchor and kind control the kind of search. // Returns true if match found, false if not. // If match found, fills match[0..nmatch-1] with submatch info. // match[0] is overall match, match[1] is first set of parens, etc. // If a particular submatch is not matched during the regexp match, // it is set to NULL. // // Matching text == StringPiece(NULL, 0) is treated as any other empty // string, but note that on return, it will not be possible to distinguish // submatches that matched that empty string from submatches that didn't // match anything. Either way, match[i] == NULL. // Search using NFA: can find submatches but kind of slow. bool SearchNFA(const StringPiece& text, const StringPiece& context, Anchor anchor, MatchKind kind, StringPiece* match, int nmatch); // Search using DFA: much faster than NFA but only finds // end of match and can use a lot more memory. // Returns whether a match was found. // If the DFA runs out of memory, sets *failed to true and returns false. // If matches != NULL and kind == kManyMatch and there is a match, // SearchDFA fills matches with the match IDs of the final matching state. bool SearchDFA(const StringPiece& text, const StringPiece& context, Anchor anchor, MatchKind kind, StringPiece* match0, bool* failed, vector* matches); // Build the entire DFA for the given match kind. FOR TESTING ONLY. // Usually the DFA is built out incrementally, as needed, which // avoids lots of unnecessary work. This function is useful only // for testing purposes. Returns number of states. int BuildEntireDFA(MatchKind kind); // Compute byte map. void ComputeByteMap(); // Run peep-hole optimizer on program. void Optimize(); // One-pass NFA: only correct if IsOnePass() is true, // but much faster than NFA (competitive with PCRE) // for those expressions. bool IsOnePass(); bool SearchOnePass(const StringPiece& text, const StringPiece& context, Anchor anchor, MatchKind kind, StringPiece* match, int nmatch); // Bit-state backtracking. Fast on small cases but uses memory // proportional to the product of the program size and the text size. bool SearchBitState(const StringPiece& text, const StringPiece& context, Anchor anchor, MatchKind kind, StringPiece* match, int nmatch); static const int kMaxOnePassCapture = 5; // $0 through $4 // Backtracking search: the gold standard against which the other // implementations are checked. FOR TESTING ONLY. // It allocates a ton of memory to avoid running forever. // It is also recursive, so can't use in production (will overflow stacks). // The name "Unsafe" here is supposed to be a flag that // you should not be using this function. bool UnsafeSearchBacktrack(const StringPiece& text, const StringPiece& context, Anchor anchor, MatchKind kind, StringPiece* match, int nmatch); // Computes range for any strings matching regexp. The min and max can in // some cases be arbitrarily precise, so the caller gets to specify the // maximum desired length of string returned. // // Assuming PossibleMatchRange(&min, &max, N) returns successfully, any // string s that is an anchored match for this regexp satisfies // min <= s && s <= max. // // Note that PossibleMatchRange() will only consider the first copy of an // infinitely repeated element (i.e., any regexp element followed by a '*' or // '+' operator). Regexps with "{N}" constructions are not affected, as those // do not compile down to infinite repetitions. // // Returns true on success, false on error. bool PossibleMatchRange(string* min, string* max, int maxlen); // EXPERIMENTAL! SUBJECT TO CHANGE! // Outputs the program fanout into the given sparse array. void Fanout(SparseArray* fanout); // Compiles a collection of regexps to Prog. Each regexp will have // its own Match instruction recording the index in the vector. static Prog* CompileSet(const RE2::Options& options, RE2::Anchor anchor, Regexp* re); private: friend class Compiler; DFA* GetDFA(MatchKind kind); bool anchor_start_; // regexp has explicit start anchor bool anchor_end_; // regexp has explicit end anchor bool reversed_; // whether program runs backward over input bool did_onepass_; // has IsOnePass been called? int start_; // entry point for program int start_unanchored_; // unanchored entry point for program int size_; // number of instructions int byte_inst_count_; // number of kInstByteRange instructions int bytemap_range_; // bytemap_[x] < bytemap_range_ int flags_; // regexp parse flags int onepass_statesize_; // byte size of each OneState* node Inst* inst_; // pointer to instruction array Mutex dfa_mutex_; // Protects dfa_first_, dfa_longest_ DFA* volatile dfa_first_; // DFA cached for kFirstMatch DFA* volatile dfa_longest_; // DFA cached for kLongestMatch and kFullMatch int64 dfa_mem_; // Maximum memory for DFAs. void (*delete_dfa_)(DFA* dfa); Bitmap<256> byterange_; // byterange.Get(x) true if x ends a // commonly-treated byte range. uint8 bytemap_[256]; // map from input bytes to byte classes uint8 *unbytemap_; // bytemap_[unbytemap_[x]] == x uint8* onepass_nodes_; // data for OnePass nodes OneState* onepass_start_; // start node for OnePass program DISALLOW_COPY_AND_ASSIGN(Prog); }; } // namespace re2 #endif // RE2_PROG_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/re2.cc000066400000000000000000001112761266464252400222150ustar00rootroot00000000000000// Copyright 2003-2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Regular expression interface RE2. // // Originally the PCRE C++ wrapper, but adapted to use // the new automata-based regular expression engines. #include "re2.h" #include #include #ifndef WIN32 #include #endif #include #include "util/atomicops.h" #include "util/util.h" #include "util/flags.h" #include "util/sparse_array.h" #include "re2/prog.h" #include "re2/regexp.h" DEFINE_bool(trace_re2, false, "trace RE2 execution"); namespace re2 { // Maximum number of args we can set static const int kMaxArgs = 16; static const int kVecSize = 1+kMaxArgs; const VariadicFunction2 RE2::FullMatch = {}; const VariadicFunction2 RE2::PartialMatch = {}; const VariadicFunction2 RE2::Consume = {}; const VariadicFunction2 RE2::FindAndConsume = {}; // This will trigger LNK2005 error in MSVC. #ifndef WIN32 const int RE2::Options::kDefaultMaxMem; // initialized in re2.h #endif // COMPILER_MSVC RE2::Options::Options(RE2::CannedOptions opt) : encoding_(opt == RE2::Latin1 ? EncodingLatin1 : EncodingUTF8), posix_syntax_(opt == RE2::POSIX), longest_match_(opt == RE2::POSIX), log_errors_(opt != RE2::Quiet), max_mem_(kDefaultMaxMem), literal_(false), never_nl_(false), dot_nl_(false), never_capture_(false), case_sensitive_(true), perl_classes_(false), word_boundary_(false), one_line_(false) { } // static empty things for use as const references. // To avoid global constructors, initialized on demand. GLOBAL_MUTEX(empty_mutex); static const string *empty_string; static const map *empty_named_groups; static const map *empty_group_names; static void InitEmpty() { GLOBAL_MUTEX_LOCK(empty_mutex); if (empty_string == NULL) { empty_string = new string; empty_named_groups = new map; empty_group_names = new map; } GLOBAL_MUTEX_UNLOCK(empty_mutex); } // Converts from Regexp error code to RE2 error code. // Maybe some day they will diverge. In any event, this // hides the existence of Regexp from RE2 users. static RE2::ErrorCode RegexpErrorToRE2(re2::RegexpStatusCode code) { switch (code) { case re2::kRegexpSuccess: return RE2::NoError; case re2::kRegexpInternalError: return RE2::ErrorInternal; case re2::kRegexpBadEscape: return RE2::ErrorBadEscape; case re2::kRegexpBadCharClass: return RE2::ErrorBadCharClass; case re2::kRegexpBadCharRange: return RE2::ErrorBadCharRange; case re2::kRegexpMissingBracket: return RE2::ErrorMissingBracket; case re2::kRegexpMissingParen: return RE2::ErrorMissingParen; case re2::kRegexpTrailingBackslash: return RE2::ErrorTrailingBackslash; case re2::kRegexpRepeatArgument: return RE2::ErrorRepeatArgument; case re2::kRegexpRepeatSize: return RE2::ErrorRepeatSize; case re2::kRegexpRepeatOp: return RE2::ErrorRepeatOp; case re2::kRegexpBadPerlOp: return RE2::ErrorBadPerlOp; case re2::kRegexpBadUTF8: return RE2::ErrorBadUTF8; case re2::kRegexpBadNamedCapture: return RE2::ErrorBadNamedCapture; } return RE2::ErrorInternal; } static string trunc(const StringPiece& pattern) { if (pattern.size() < 100) return pattern.as_string(); return pattern.substr(0, 100).as_string() + "..."; } RE2::RE2(const char* pattern) { Init(pattern, DefaultOptions); } RE2::RE2(const string& pattern) { Init(pattern, DefaultOptions); } RE2::RE2(const StringPiece& pattern) { Init(pattern, DefaultOptions); } RE2::RE2(const StringPiece& pattern, const Options& options) { Init(pattern, options); } int RE2::Options::ParseFlags() const { int flags = Regexp::ClassNL; switch (encoding()) { default: if (log_errors()) LOG(ERROR) << "Unknown encoding " << encoding(); break; case RE2::Options::EncodingUTF8: break; case RE2::Options::EncodingLatin1: flags |= Regexp::Latin1; break; } if (!posix_syntax()) flags |= Regexp::LikePerl; if (literal()) flags |= Regexp::Literal; if (never_nl()) flags |= Regexp::NeverNL; if (dot_nl()) flags |= Regexp::DotNL; if (never_capture()) flags |= Regexp::NeverCapture; if (!case_sensitive()) flags |= Regexp::FoldCase; if (perl_classes()) flags |= Regexp::PerlClasses; if (word_boundary()) flags |= Regexp::PerlB; if (one_line()) flags |= Regexp::OneLine; return flags; } void RE2::Init(const StringPiece& pattern, const Options& options) { mutex_ = new Mutex; pattern_ = pattern.as_string(); options_.Copy(options); InitEmpty(); error_ = empty_string; error_code_ = NoError; suffix_regexp_ = NULL; entire_regexp_ = NULL; prog_ = NULL; rprog_ = NULL; named_groups_ = NULL; group_names_ = NULL; num_captures_ = -1; RegexpStatus status; entire_regexp_ = Regexp::Parse( pattern_, static_cast(options_.ParseFlags()), &status); if (entire_regexp_ == NULL) { if (error_ == empty_string) error_ = new string(status.Text()); if (options_.log_errors()) { LOG(ERROR) << "Error parsing '" << trunc(pattern_) << "': " << status.Text(); } error_arg_ = status.error_arg().as_string(); error_code_ = RegexpErrorToRE2(status.code()); return; } prefix_.clear(); prefix_foldcase_ = false; re2::Regexp* suffix; if (entire_regexp_->RequiredPrefix(&prefix_, &prefix_foldcase_, &suffix)) suffix_regexp_ = suffix; else suffix_regexp_ = entire_regexp_->Incref(); // Two thirds of the memory goes to the forward Prog, // one third to the reverse prog, because the forward // Prog has two DFAs but the reverse prog has one. prog_ = suffix_regexp_->CompileToProg(options_.max_mem()*2/3); if (prog_ == NULL) { if (options_.log_errors()) LOG(ERROR) << "Error compiling '" << trunc(pattern_) << "'"; error_ = new string("pattern too large - compile failed"); error_code_ = RE2::ErrorPatternTooLarge; return; } // Could delay this until the first match call that // cares about submatch information, but the one-pass // machine's memory gets cut from the DFA memory budget, // and that is harder to do if the DFA has already // been built. is_one_pass_ = prog_->IsOnePass(); } // Returns rprog_, computing it if needed. re2::Prog* RE2::ReverseProg() const { MutexLock l(mutex_); if (rprog_ == NULL && error_ == empty_string) { rprog_ = suffix_regexp_->CompileToReverseProg(options_.max_mem()/3); if (rprog_ == NULL) { if (options_.log_errors()) LOG(ERROR) << "Error reverse compiling '" << trunc(pattern_) << "'"; error_ = new string("pattern too large - reverse compile failed"); error_code_ = RE2::ErrorPatternTooLarge; return NULL; } } return rprog_; } RE2::~RE2() { if (suffix_regexp_) suffix_regexp_->Decref(); if (entire_regexp_) entire_regexp_->Decref(); delete mutex_; delete prog_; delete rprog_; if (error_ != empty_string) delete error_; if (named_groups_ != NULL && named_groups_ != empty_named_groups) delete named_groups_; if (group_names_ != NULL && group_names_ != empty_group_names) delete group_names_; } int RE2::ProgramSize() const { if (prog_ == NULL) return -1; return prog_->size(); } int RE2::ProgramFanout(map* histogram) const { if (prog_ == NULL) return -1; SparseArray fanout(prog_->size()); prog_->Fanout(&fanout); histogram->clear(); for (SparseArray::iterator i = fanout.begin(); i != fanout.end(); ++i) { // TODO(junyer): Optimise this? int bucket = 0; while (1 << bucket < i->second) { bucket++; } (*histogram)[bucket]++; } return histogram->rbegin()->first; } // Returns named_groups_, computing it if needed. const map& RE2::NamedCapturingGroups() const { MutexLock l(mutex_); if (!ok()) return *empty_named_groups; if (named_groups_ == NULL) { named_groups_ = suffix_regexp_->NamedCaptures(); if (named_groups_ == NULL) named_groups_ = empty_named_groups; } return *named_groups_; } // Returns group_names_, computing it if needed. const map& RE2::CapturingGroupNames() const { MutexLock l(mutex_); if (!ok()) return *empty_group_names; if (group_names_ == NULL) { group_names_ = suffix_regexp_->CaptureNames(); if (group_names_ == NULL) group_names_ = empty_group_names; } return *group_names_; } /***** Convenience interfaces *****/ bool RE2::FullMatchN(const StringPiece& text, const RE2& re, const Arg* const args[], int n) { return re.DoMatch(text, ANCHOR_BOTH, NULL, args, n); } bool RE2::PartialMatchN(const StringPiece& text, const RE2& re, const Arg* const args[], int n) { return re.DoMatch(text, UNANCHORED, NULL, args, n); } bool RE2::ConsumeN(StringPiece* input, const RE2& re, const Arg* const args[], int n) { int consumed; if (re.DoMatch(*input, ANCHOR_START, &consumed, args, n)) { input->remove_prefix(consumed); return true; } else { return false; } } bool RE2::FindAndConsumeN(StringPiece* input, const RE2& re, const Arg* const args[], int n) { int consumed; if (re.DoMatch(*input, UNANCHORED, &consumed, args, n)) { input->remove_prefix(consumed); return true; } else { return false; } } // Returns the maximum submatch needed for the rewrite to be done by Replace(). // E.g. if rewrite == "foo \\2,\\1", returns 2. int RE2::MaxSubmatch(const StringPiece& rewrite) { int max = 0; for (const char *s = rewrite.data(), *end = s + rewrite.size(); s < end; s++) { if (*s == '\\') { s++; int c = (s < end) ? *s : -1; if (isdigit(c)) { int n = (c - '0'); if (n > max) max = n; } } } return max; } bool RE2::Replace(string *str, const RE2& re, const StringPiece& rewrite) { StringPiece vec[kVecSize]; int nvec = 1 + MaxSubmatch(rewrite); if (nvec > arraysize(vec)) return false; if (!re.Match(*str, 0, str->size(), UNANCHORED, vec, nvec)) return false; string s; if (!re.Rewrite(&s, rewrite, vec, nvec)) return false; assert(vec[0].begin() >= str->data()); assert(vec[0].end() <= str->data()+str->size()); str->replace(vec[0].data() - str->data(), vec[0].size(), s); return true; } int RE2::GlobalReplace(string *str, const RE2& re, const StringPiece& rewrite) { StringPiece vec[kVecSize]; int nvec = 1 + MaxSubmatch(rewrite); if (nvec > arraysize(vec)) return false; const char* p = str->data(); const char* ep = p + str->size(); const char* lastend = NULL; string out; int count = 0; while (p <= ep) { if (!re.Match(*str, p - str->data(), str->size(), UNANCHORED, vec, nvec)) break; if (p < vec[0].begin()) out.append(p, vec[0].begin() - p); if (vec[0].begin() == lastend && vec[0].size() == 0) { // Disallow empty match at end of last match: skip ahead. if (p < ep) out.append(p, 1); p++; continue; } re.Rewrite(&out, rewrite, vec, nvec); p = vec[0].end(); lastend = p; count++; } if (count == 0) return 0; if (p < ep) out.append(p, ep - p); swap(out, *str); return count; } bool RE2::Extract(const StringPiece &text, const RE2& re, const StringPiece &rewrite, string *out) { StringPiece vec[kVecSize]; int nvec = 1 + MaxSubmatch(rewrite); if (nvec > arraysize(vec)) return false; if (!re.Match(text, 0, text.size(), UNANCHORED, vec, nvec)) return false; out->clear(); return re.Rewrite(out, rewrite, vec, nvec); } string RE2::QuoteMeta(const StringPiece& unquoted) { string result; result.reserve(unquoted.size() << 1); // Escape any ascii character not in [A-Za-z_0-9]. // // Note that it's legal to escape a character even if it has no // special meaning in a regular expression -- so this function does // that. (This also makes it identical to the perl function of the // same name except for the null-character special case; // see `perldoc -f quotemeta`.) for (int ii = 0; ii < unquoted.length(); ++ii) { // Note that using 'isalnum' here raises the benchmark time from // 32ns to 58ns: if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') && (unquoted[ii] < 'A' || unquoted[ii] > 'Z') && (unquoted[ii] < '0' || unquoted[ii] > '9') && unquoted[ii] != '_' && // If this is the part of a UTF8 or Latin1 character, we need // to copy this byte without escaping. Experimentally this is // what works correctly with the regexp library. !(unquoted[ii] & 128)) { if (unquoted[ii] == '\0') { // Special handling for null chars. // Note that this special handling is not strictly required for RE2, // but this quoting is required for other regexp libraries such as // PCRE. // Can't use "\\0" since the next character might be a digit. result += "\\x00"; continue; } result += '\\'; } result += unquoted[ii]; } return result; } bool RE2::PossibleMatchRange(string* min, string* max, int maxlen) const { if (prog_ == NULL) return false; int n = prefix_.size(); if (n > maxlen) n = maxlen; // Determine initial min max from prefix_ literal. string pmin, pmax; pmin = prefix_.substr(0, n); pmax = prefix_.substr(0, n); if (prefix_foldcase_) { // prefix is ASCII lowercase; change pmin to uppercase. for (int i = 0; i < n; i++) { if ('a' <= pmin[i] && pmin[i] <= 'z') pmin[i] += 'A' - 'a'; } } // Add to prefix min max using PossibleMatchRange on regexp. string dmin, dmax; maxlen -= n; if (maxlen > 0 && prog_->PossibleMatchRange(&dmin, &dmax, maxlen)) { pmin += dmin; pmax += dmax; } else if (pmax.size() > 0) { // prog_->PossibleMatchRange has failed us, // but we still have useful information from prefix_. // Round up pmax to allow any possible suffix. pmax = PrefixSuccessor(pmax); } else { // Nothing useful. *min = ""; *max = ""; return false; } *min = pmin; *max = pmax; return true; } // Avoid possible locale nonsense in standard strcasecmp. // The string a is known to be all lowercase. static int ascii_strcasecmp(const char* a, const char* b, int len) { const char *ae = a + len; for (; a < ae; a++, b++) { uint8 x = *a; uint8 y = *b; if ('A' <= y && y <= 'Z') y += 'a' - 'A'; if (x != y) return x - y; } return 0; } /***** Actual matching and rewriting code *****/ bool RE2::Match(const StringPiece& text, int startpos, int endpos, Anchor re_anchor, StringPiece* submatch, int nsubmatch) const { if (!ok() || suffix_regexp_ == NULL) { if (options_.log_errors()) LOG(ERROR) << "Invalid RE2: " << *error_; return false; } if (startpos < 0 || startpos > endpos || endpos > text.size()) { if (options_.log_errors()) LOG(ERROR) << "RE2: invalid startpos, endpos pair. [" << "startpos: " << startpos << ", " << "endpos: " << endpos << ", " << "text size: " << text.size() << "]"; return false; } StringPiece subtext = text; subtext.remove_prefix(startpos); subtext.remove_suffix(text.size() - endpos); // Use DFAs to find exact location of match, filter out non-matches. // Don't ask for the location if we won't use it. // SearchDFA can do extra optimizations in that case. StringPiece match; StringPiece* matchp = &match; if (nsubmatch == 0) matchp = NULL; int ncap = 1 + NumberOfCapturingGroups(); if (ncap > nsubmatch) ncap = nsubmatch; // If the regexp is anchored explicitly, must not be in middle of text. if (prog_->anchor_start() && startpos != 0) return false; // If the regexp is anchored explicitly, update re_anchor // so that we can potentially fall into a faster case below. if (prog_->anchor_start() && prog_->anchor_end()) re_anchor = ANCHOR_BOTH; else if (prog_->anchor_start() && re_anchor != ANCHOR_BOTH) re_anchor = ANCHOR_START; // Check for the required prefix, if any. int prefixlen = 0; if (!prefix_.empty()) { if (startpos != 0) return false; prefixlen = prefix_.size(); if (prefixlen > subtext.size()) return false; if (prefix_foldcase_) { if (ascii_strcasecmp(&prefix_[0], subtext.data(), prefixlen) != 0) return false; } else { if (memcmp(&prefix_[0], subtext.data(), prefixlen) != 0) return false; } subtext.remove_prefix(prefixlen); // If there is a required prefix, the anchor must be at least ANCHOR_START. if (re_anchor != ANCHOR_BOTH) re_anchor = ANCHOR_START; } Prog::Anchor anchor = Prog::kUnanchored; Prog::MatchKind kind = Prog::kFirstMatch; if (options_.longest_match()) kind = Prog::kLongestMatch; bool skipped_test = false; bool can_one_pass = (is_one_pass_ && ncap <= Prog::kMaxOnePassCapture); // SearchBitState allocates a bit vector of size prog_->size() * text.size(). // It also allocates a stack of 3-word structures which could potentially // grow as large as prog_->size() * text.size() but in practice is much // smaller. // Conditions for using SearchBitState: const int MaxBitStateProg = 500; // prog_->size() <= Max. const int MaxBitStateVector = 256*1024; // bit vector size <= Max (bits) bool can_bit_state = prog_->size() <= MaxBitStateProg; int bit_state_text_max = MaxBitStateVector / prog_->size(); bool dfa_failed = false; switch (re_anchor) { default: case UNANCHORED: { if (!prog_->SearchDFA(subtext, text, anchor, kind, matchp, &dfa_failed, NULL)) { if (dfa_failed) { // Fall back to NFA below. skipped_test = true; if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " DFA failed."; break; } if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " used DFA - no match."; return false; } if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " used DFA - match"; if (matchp == NULL) // Matched. Don't care where return true; // SearchDFA set match[0].end() but didn't know where the // match started. Run the regexp backward from match[0].end() // to find the longest possible match -- that's where it started. Prog* prog = ReverseProg(); if (prog == NULL) return false; if (!prog->SearchDFA(match, text, Prog::kAnchored, Prog::kLongestMatch, &match, &dfa_failed, NULL)) { if (dfa_failed) { // Fall back to NFA below. skipped_test = true; if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " reverse DFA failed."; break; } if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " DFA inconsistency."; if (options_.log_errors()) LOG(ERROR) << "DFA inconsistency"; return false; } if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " used reverse DFA."; break; } case ANCHOR_BOTH: case ANCHOR_START: if (re_anchor == ANCHOR_BOTH) kind = Prog::kFullMatch; anchor = Prog::kAnchored; // If only a small amount of text and need submatch // information anyway and we're going to use OnePass or BitState // to get it, we might as well not even bother with the DFA: // OnePass or BitState will be fast enough. // On tiny texts, OnePass outruns even the DFA, and // it doesn't have the shared state and occasional mutex that // the DFA does. if (can_one_pass && text.size() <= 4096 && (ncap > 1 || text.size() <= 8)) { if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " skipping DFA for OnePass."; skipped_test = true; break; } if (can_bit_state && text.size() <= bit_state_text_max && ncap > 1) { if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " skipping DFA for BitState."; skipped_test = true; break; } if (!prog_->SearchDFA(subtext, text, anchor, kind, &match, &dfa_failed, NULL)) { if (dfa_failed) { if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " DFA failed."; skipped_test = true; break; } if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " used DFA - no match."; return false; } break; } if (!skipped_test && ncap <= 1) { // We know exactly where it matches. That's enough. if (ncap == 1) submatch[0] = match; } else { StringPiece subtext1; if (skipped_test) { // DFA ran out of memory or was skipped: // need to search in entire original text. subtext1 = subtext; } else { // DFA found the exact match location: // let NFA run an anchored, full match search // to find submatch locations. subtext1 = match; anchor = Prog::kAnchored; kind = Prog::kFullMatch; } if (can_one_pass && anchor != Prog::kUnanchored) { if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " using OnePass."; if (!prog_->SearchOnePass(subtext1, text, anchor, kind, submatch, ncap)) { if (!skipped_test && options_.log_errors()) LOG(ERROR) << "SearchOnePass inconsistency"; return false; } } else if (can_bit_state && subtext1.size() <= bit_state_text_max) { if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " using BitState."; if (!prog_->SearchBitState(subtext1, text, anchor, kind, submatch, ncap)) { if (!skipped_test && options_.log_errors()) LOG(ERROR) << "SearchBitState inconsistency"; return false; } } else { if (FLAGS_trace_re2) LOG(INFO) << "Match " << trunc(pattern_) << " [" << CEscape(subtext) << "]" << " using NFA."; if (!prog_->SearchNFA(subtext1, text, anchor, kind, submatch, ncap)) { if (!skipped_test && options_.log_errors()) LOG(ERROR) << "SearchNFA inconsistency"; return false; } } } // Adjust overall match for required prefix that we stripped off. if (prefixlen > 0 && nsubmatch > 0) submatch[0] = StringPiece(submatch[0].begin() - prefixlen, submatch[0].size() + prefixlen); // Zero submatches that don't exist in the regexp. for (int i = ncap; i < nsubmatch; i++) submatch[i] = NULL; return true; } // Internal matcher - like Match() but takes Args not StringPieces. bool RE2::DoMatch(const StringPiece& text, Anchor anchor, int* consumed, const Arg* const* args, int n) const { if (!ok()) { if (options_.log_errors()) LOG(ERROR) << "Invalid RE2: " << *error_; return false; } // Count number of capture groups needed. int nvec; if (n == 0 && consumed == NULL) nvec = 0; else nvec = n+1; StringPiece* vec; StringPiece stkvec[kVecSize]; StringPiece* heapvec = NULL; if (nvec <= arraysize(stkvec)) { vec = stkvec; } else { vec = new StringPiece[nvec]; heapvec = vec; } if (!Match(text, 0, text.size(), anchor, vec, nvec)) { delete[] heapvec; return false; } if(consumed != NULL) *consumed = vec[0].end() - text.begin(); if (n == 0 || args == NULL) { // We are not interested in results delete[] heapvec; return true; } int ncap = NumberOfCapturingGroups(); if (ncap < n) { // RE has fewer capturing groups than number of arg pointers passed in VLOG(1) << "Asked for " << n << " but only have " << ncap; delete[] heapvec; return false; } // If we got here, we must have matched the whole pattern. for (int i = 0; i < n; i++) { const StringPiece& s = vec[i+1]; if (!args[i]->Parse(s.data(), s.size())) { // TODO: Should we indicate what the error was? VLOG(1) << "Parse error on #" << i << " " << s << " " << (void*)s.data() << "/" << s.size(); delete[] heapvec; return false; } } delete[] heapvec; return true; } // Append the "rewrite" string, with backslash subsitutions from "vec", // to string "out". bool RE2::Rewrite(string *out, const StringPiece &rewrite, const StringPiece *vec, int veclen) const { for (const char *s = rewrite.data(), *end = s + rewrite.size(); s < end; s++) { int c = *s; if (c == '\\') { s++; c = (s < end) ? *s : -1; if (isdigit(c)) { int n = (c - '0'); if (n >= veclen) { if (options_.log_errors()) { LOG(ERROR) << "requested group " << n << " in regexp " << rewrite.data(); } return false; } StringPiece snip = vec[n]; if (snip.size() > 0) out->append(snip.data(), snip.size()); } else if (c == '\\') { out->push_back('\\'); } else { if (options_.log_errors()) LOG(ERROR) << "invalid rewrite pattern: " << rewrite.data(); return false; } } else { out->push_back(c); } } return true; } // Return the number of capturing subpatterns, or -1 if the // regexp wasn't valid on construction. int RE2::NumberOfCapturingGroups() const { if (suffix_regexp_ == NULL) return -1; int n; ATOMIC_LOAD_RELAXED(n, &num_captures_); if (n == -1) { n = suffix_regexp_->NumCaptures(); ATOMIC_STORE_RELAXED(&num_captures_, n); } return n; } // Checks that the rewrite string is well-formed with respect to this // regular expression. bool RE2::CheckRewriteString(const StringPiece& rewrite, string* error) const { int max_token = -1; for (const char *s = rewrite.data(), *end = s + rewrite.size(); s < end; s++) { int c = *s; if (c != '\\') { continue; } if (++s == end) { *error = "Rewrite schema error: '\\' not allowed at end."; return false; } c = *s; if (c == '\\') { continue; } if (!isdigit(c)) { *error = "Rewrite schema error: " "'\\' must be followed by a digit or '\\'."; return false; } int n = (c - '0'); if (max_token < n) { max_token = n; } } if (max_token > NumberOfCapturingGroups()) { SStringPrintf(error, "Rewrite schema requests %d matches, " "but the regexp only has %d parenthesized subexpressions.", max_token, NumberOfCapturingGroups()); return false; } return true; } /***** Parsers for various types *****/ bool RE2::Arg::parse_null(const char* str, int n, void* dest) { // We fail if somebody asked us to store into a non-NULL void* pointer return (dest == NULL); } bool RE2::Arg::parse_string(const char* str, int n, void* dest) { if (dest == NULL) return true; reinterpret_cast(dest)->assign(str, n); return true; } bool RE2::Arg::parse_stringpiece(const char* str, int n, void* dest) { if (dest == NULL) return true; reinterpret_cast(dest)->set(str, n); return true; } bool RE2::Arg::parse_char(const char* str, int n, void* dest) { if (n != 1) return false; if (dest == NULL) return true; *(reinterpret_cast(dest)) = str[0]; return true; } bool RE2::Arg::parse_uchar(const char* str, int n, void* dest) { if (n != 1) return false; if (dest == NULL) return true; *(reinterpret_cast(dest)) = str[0]; return true; } // Largest number spec that we are willing to parse static const int kMaxNumberLength = 32; // REQUIRES "buf" must have length at least nbuf. // Copies "str" into "buf" and null-terminates. // Overwrites *np with the new length. static const char* TerminateNumber(char* buf, int nbuf, const char* str, int* np, bool accept_spaces) { int n = *np; if (n <= 0) return ""; if (n > 0 && isspace(*str)) { // We are less forgiving than the strtoxxx() routines and do not // allow leading spaces. We do allow leading spaces for floats. if (!accept_spaces) { return ""; } while (n > 0 && isspace(*str)) { n--; str++; } } // Although buf has a fixed maximum size, we can still handle // arbitrarily large integers correctly by omitting leading zeros. // (Numbers that are still too long will be out of range.) // Before deciding whether str is too long, // remove leading zeros with s/000+/00/. // Leaving the leading two zeros in place means that // we don't change 0000x123 (invalid) into 0x123 (valid). // Skip over leading - before replacing. bool neg = false; if (n >= 1 && str[0] == '-') { neg = true; n--; str++; } if (n >= 3 && str[0] == '0' && str[1] == '0') { while (n >= 3 && str[2] == '0') { n--; str++; } } if (neg) { // make room in buf for - n++; str--; } if (n > nbuf-1) return ""; memmove(buf, str, n); if (neg) { buf[0] = '-'; } buf[n] = '\0'; *np = n; return buf; } bool RE2::Arg::parse_long_radix(const char* str, int n, void* dest, int radix) { if (n == 0) return false; char buf[kMaxNumberLength+1]; str = TerminateNumber(buf, sizeof buf, str, &n, false); char* end; errno = 0; long r = strtol(str, &end, radix); if (end != str + n) return false; // Leftover junk if (errno) return false; if (dest == NULL) return true; *(reinterpret_cast(dest)) = r; return true; } bool RE2::Arg::parse_ulong_radix(const char* str, int n, void* dest, int radix) { if (n == 0) return false; char buf[kMaxNumberLength+1]; str = TerminateNumber(buf, sizeof buf, str, &n, false); if (str[0] == '-') { // strtoul() will silently accept negative numbers and parse // them. This module is more strict and treats them as errors. return false; } char* end; errno = 0; unsigned long r = strtoul(str, &end, radix); if (end != str + n) return false; // Leftover junk if (errno) return false; if (dest == NULL) return true; *(reinterpret_cast(dest)) = r; return true; } bool RE2::Arg::parse_short_radix(const char* str, int n, void* dest, int radix) { long r; if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse if ((short)r != r) return false; // Out of range if (dest == NULL) return true; *(reinterpret_cast(dest)) = r; return true; } bool RE2::Arg::parse_ushort_radix(const char* str, int n, void* dest, int radix) { unsigned long r; if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse if ((ushort)r != r) return false; // Out of range if (dest == NULL) return true; *(reinterpret_cast(dest)) = r; return true; } bool RE2::Arg::parse_int_radix(const char* str, int n, void* dest, int radix) { long r; if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse if ((int)r != r) return false; // Out of range if (dest == NULL) return true; *(reinterpret_cast(dest)) = r; return true; } bool RE2::Arg::parse_uint_radix(const char* str, int n, void* dest, int radix) { unsigned long r; if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse if ((uint)r != r) return false; // Out of range if (dest == NULL) return true; *(reinterpret_cast(dest)) = r; return true; } #if RE2_HAVE_LONGLONG bool RE2::Arg::parse_longlong_radix(const char* str, int n, void* dest, int radix) { if (n == 0) return false; char buf[kMaxNumberLength+1]; str = TerminateNumber(buf, sizeof buf, str, &n, false); char* end; errno = 0; int64 r = strtoll(str, &end, radix); if (end != str + n) return false; // Leftover junk if (errno) return false; if (dest == NULL) return true; *(reinterpret_cast(dest)) = r; return true; } bool RE2::Arg::parse_ulonglong_radix(const char* str, int n, void* dest, int radix) { if (n == 0) return false; char buf[kMaxNumberLength+1]; str = TerminateNumber(buf, sizeof buf, str, &n, false); if (str[0] == '-') { // strtoull() will silently accept negative numbers and parse // them. This module is more strict and treats them as errors. return false; } char* end; errno = 0; uint64 r = strtoull(str, &end, radix); if (end != str + n) return false; // Leftover junk if (errno) return false; if (dest == NULL) return true; *(reinterpret_cast(dest)) = r; return true; } #endif static bool parse_double_float(const char* str, int n, bool isfloat, void *dest) { if (n == 0) return false; static const int kMaxLength = 200; char buf[kMaxLength+1]; str = TerminateNumber(buf, sizeof buf, str, &n, true); char* end; errno = 0; double r; if (isfloat) { r = strtof(str, &end); } else { r = strtod(str, &end); } if (end != str + n) return false; // Leftover junk if (errno) return false; if (dest == NULL) return true; if (isfloat) { *(reinterpret_cast(dest)) = r; } else { *(reinterpret_cast(dest)) = r; } return true; } bool RE2::Arg::parse_double(const char* str, int n, void* dest) { return parse_double_float(str, n, false, dest); } bool RE2::Arg::parse_float(const char* str, int n, void* dest) { return parse_double_float(str, n, true, dest); } #define DEFINE_INTEGER_PARSERS(name) \ bool RE2::Arg::parse_##name(const char* str, int n, void* dest) { \ return parse_##name##_radix(str, n, dest, 10); \ } \ bool RE2::Arg::parse_##name##_hex(const char* str, int n, void* dest) { \ return parse_##name##_radix(str, n, dest, 16); \ } \ bool RE2::Arg::parse_##name##_octal(const char* str, int n, void* dest) { \ return parse_##name##_radix(str, n, dest, 8); \ } \ bool RE2::Arg::parse_##name##_cradix(const char* str, int n, void* dest) { \ return parse_##name##_radix(str, n, dest, 0); \ } DEFINE_INTEGER_PARSERS(short); DEFINE_INTEGER_PARSERS(ushort); DEFINE_INTEGER_PARSERS(int); DEFINE_INTEGER_PARSERS(uint); DEFINE_INTEGER_PARSERS(long); DEFINE_INTEGER_PARSERS(ulong); DEFINE_INTEGER_PARSERS(longlong); DEFINE_INTEGER_PARSERS(ulonglong); #undef DEFINE_INTEGER_PARSERS } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/regexp.cc000066400000000000000000000572431266464252400230220ustar00rootroot00000000000000// Copyright 2006 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Regular expression representation. // Tested by parse_test.cc #include "util/util.h" #include "re2/regexp.h" #include "re2/stringpiece.h" #include "re2/walker-inl.h" namespace re2 { // Constructor. Allocates vectors as appropriate for operator. Regexp::Regexp(RegexpOp op, ParseFlags parse_flags) : op_(op), simple_(false), parse_flags_(static_cast(parse_flags)), ref_(1), nsub_(0), down_(NULL) { subone_ = NULL; memset(the_union_, 0, sizeof the_union_); } // Destructor. Assumes already cleaned up children. // Private: use Decref() instead of delete to destroy Regexps. // Can't call Decref on the sub-Regexps here because // that could cause arbitrarily deep recursion, so // required Decref() to have handled them for us. Regexp::~Regexp() { if (nsub_ > 0) LOG(DFATAL) << "Regexp not destroyed."; switch (op_) { default: break; case kRegexpCapture: delete name_; break; case kRegexpLiteralString: delete[] runes_; break; case kRegexpCharClass: if (cc_) cc_->Delete(); delete ccb_; break; } } // If it's possible to destroy this regexp without recurring, // do so and return true. Else return false. bool Regexp::QuickDestroy() { if (nsub_ == 0) { delete this; return true; } return false; } static map *ref_map; GLOBAL_MUTEX(ref_mutex); int Regexp::Ref() { if (ref_ < kMaxRef) return ref_; GLOBAL_MUTEX_LOCK(ref_mutex); int r = 0; if (ref_map != NULL) { r = (*ref_map)[this]; } GLOBAL_MUTEX_UNLOCK(ref_mutex); return r; } // Increments reference count, returns object as convenience. Regexp* Regexp::Incref() { if (ref_ >= kMaxRef-1) { // Store ref count in overflow map. GLOBAL_MUTEX_LOCK(ref_mutex); if (ref_map == NULL) { ref_map = new map; } if (ref_ == kMaxRef) { // already overflowed (*ref_map)[this]++; } else { // overflowing now (*ref_map)[this] = kMaxRef; ref_ = kMaxRef; } GLOBAL_MUTEX_UNLOCK(ref_mutex); return this; } ref_++; return this; } // Decrements reference count and deletes this object if count reaches 0. void Regexp::Decref() { if (ref_ == kMaxRef) { // Ref count is stored in overflow map. GLOBAL_MUTEX_LOCK(ref_mutex); int r = (*ref_map)[this] - 1; if (r < kMaxRef) { ref_ = r; ref_map->erase(this); } else { (*ref_map)[this] = r; } GLOBAL_MUTEX_UNLOCK(ref_mutex); return; } ref_--; if (ref_ == 0) Destroy(); } // Deletes this object; ref count has count reached 0. void Regexp::Destroy() { if (QuickDestroy()) return; // Handle recursive Destroy with explicit stack // to avoid arbitrarily deep recursion on process stack [sigh]. down_ = NULL; Regexp* stack = this; while (stack != NULL) { Regexp* re = stack; stack = re->down_; if (re->ref_ != 0) LOG(DFATAL) << "Bad reference count " << re->ref_; if (re->nsub_ > 0) { Regexp** subs = re->sub(); for (int i = 0; i < re->nsub_; i++) { Regexp* sub = subs[i]; if (sub == NULL) continue; if (sub->ref_ == kMaxRef) sub->Decref(); else --sub->ref_; if (sub->ref_ == 0 && !sub->QuickDestroy()) { sub->down_ = stack; stack = sub; } } if (re->nsub_ > 1) delete[] subs; re->nsub_ = 0; } delete re; } } void Regexp::AddRuneToString(Rune r) { DCHECK(op_ == kRegexpLiteralString); if (nrunes_ == 0) { // start with 8 runes_ = new Rune[8]; } else if (nrunes_ >= 8 && (nrunes_ & (nrunes_ - 1)) == 0) { // double on powers of two Rune *old = runes_; runes_ = new Rune[nrunes_ * 2]; for (int i = 0; i < nrunes_; i++) runes_[i] = old[i]; delete[] old; } runes_[nrunes_++] = r; } Regexp* Regexp::HaveMatch(int match_id, ParseFlags flags) { Regexp* re = new Regexp(kRegexpHaveMatch, flags); re->match_id_ = match_id; return re; } Regexp* Regexp::Plus(Regexp* sub, ParseFlags flags) { if (sub->op() == kRegexpPlus && sub->parse_flags() == flags) return sub; Regexp* re = new Regexp(kRegexpPlus, flags); re->AllocSub(1); re->sub()[0] = sub; return re; } Regexp* Regexp::Star(Regexp* sub, ParseFlags flags) { if (sub->op() == kRegexpStar && sub->parse_flags() == flags) return sub; Regexp* re = new Regexp(kRegexpStar, flags); re->AllocSub(1); re->sub()[0] = sub; return re; } Regexp* Regexp::Quest(Regexp* sub, ParseFlags flags) { if (sub->op() == kRegexpQuest && sub->parse_flags() == flags) return sub; Regexp* re = new Regexp(kRegexpQuest, flags); re->AllocSub(1); re->sub()[0] = sub; return re; } Regexp* Regexp::ConcatOrAlternate(RegexpOp op, Regexp** sub, int nsub, ParseFlags flags, bool can_factor) { if (nsub == 1) return sub[0]; if (nsub == 0) { if (op == kRegexpAlternate) return new Regexp(kRegexpNoMatch, flags); else return new Regexp(kRegexpEmptyMatch, flags); } Regexp** subcopy = NULL; if (op == kRegexpAlternate && can_factor) { // Going to edit sub; make a copy so we don't step on caller. subcopy = new Regexp*[nsub]; memmove(subcopy, sub, nsub * sizeof sub[0]); sub = subcopy; nsub = FactorAlternation(sub, nsub, flags); if (nsub == 1) { Regexp* re = sub[0]; delete[] subcopy; return re; } } if (nsub > kMaxNsub) { // Too many subexpressions to fit in a single Regexp. // Make a two-level tree. Two levels gets us to 65535^2. int nbigsub = (nsub+kMaxNsub-1)/kMaxNsub; Regexp* re = new Regexp(op, flags); re->AllocSub(nbigsub); Regexp** subs = re->sub(); for (int i = 0; i < nbigsub - 1; i++) subs[i] = ConcatOrAlternate(op, sub+i*kMaxNsub, kMaxNsub, flags, false); subs[nbigsub - 1] = ConcatOrAlternate(op, sub+(nbigsub-1)*kMaxNsub, nsub - (nbigsub-1)*kMaxNsub, flags, false); delete[] subcopy; return re; } Regexp* re = new Regexp(op, flags); re->AllocSub(nsub); Regexp** subs = re->sub(); for (int i = 0; i < nsub; i++) subs[i] = sub[i]; delete[] subcopy; return re; } Regexp* Regexp::Concat(Regexp** sub, int nsub, ParseFlags flags) { return ConcatOrAlternate(kRegexpConcat, sub, nsub, flags, false); } Regexp* Regexp::Alternate(Regexp** sub, int nsub, ParseFlags flags) { return ConcatOrAlternate(kRegexpAlternate, sub, nsub, flags, true); } Regexp* Regexp::AlternateNoFactor(Regexp** sub, int nsub, ParseFlags flags) { return ConcatOrAlternate(kRegexpAlternate, sub, nsub, flags, false); } Regexp* Regexp::Capture(Regexp* sub, ParseFlags flags, int cap) { Regexp* re = new Regexp(kRegexpCapture, flags); re->AllocSub(1); re->sub()[0] = sub; re->cap_ = cap; return re; } Regexp* Regexp::Repeat(Regexp* sub, ParseFlags flags, int min, int max) { Regexp* re = new Regexp(kRegexpRepeat, flags); re->AllocSub(1); re->sub()[0] = sub; re->min_ = min; re->max_ = max; return re; } Regexp* Regexp::NewLiteral(Rune rune, ParseFlags flags) { Regexp* re = new Regexp(kRegexpLiteral, flags); re->rune_ = rune; return re; } Regexp* Regexp::LiteralString(Rune* runes, int nrunes, ParseFlags flags) { if (nrunes <= 0) return new Regexp(kRegexpEmptyMatch, flags); if (nrunes == 1) return NewLiteral(runes[0], flags); Regexp* re = new Regexp(kRegexpLiteralString, flags); for (int i = 0; i < nrunes; i++) re->AddRuneToString(runes[i]); return re; } Regexp* Regexp::NewCharClass(CharClass* cc, ParseFlags flags) { Regexp* re = new Regexp(kRegexpCharClass, flags); re->cc_ = cc; return re; } // Swaps this and that in place. void Regexp::Swap(Regexp* that) { // Can use memmove because Regexp is just a struct (no vtable). char tmp[sizeof *this]; memmove(tmp, this, sizeof tmp); memmove(this, that, sizeof tmp); memmove(that, tmp, sizeof tmp); } // Tests equality of all top-level structure but not subregexps. static bool TopEqual(Regexp* a, Regexp* b) { if (a->op() != b->op()) return false; switch (a->op()) { case kRegexpNoMatch: case kRegexpEmptyMatch: case kRegexpAnyChar: case kRegexpAnyByte: case kRegexpBeginLine: case kRegexpEndLine: case kRegexpWordBoundary: case kRegexpNoWordBoundary: case kRegexpBeginText: return true; case kRegexpEndText: // The parse flags remember whether it's \z or (?-m:$), // which matters when testing against PCRE. return ((a->parse_flags() ^ b->parse_flags()) & Regexp::WasDollar) == 0; case kRegexpLiteral: return a->rune() == b->rune() && ((a->parse_flags() ^ b->parse_flags()) & Regexp::FoldCase) == 0; case kRegexpLiteralString: return a->nrunes() == b->nrunes() && ((a->parse_flags() ^ b->parse_flags()) & Regexp::FoldCase) == 0 && memcmp(a->runes(), b->runes(), a->nrunes() * sizeof a->runes()[0]) == 0; case kRegexpAlternate: case kRegexpConcat: return a->nsub() == b->nsub(); case kRegexpStar: case kRegexpPlus: case kRegexpQuest: return ((a->parse_flags() ^ b->parse_flags()) & Regexp::NonGreedy) == 0; case kRegexpRepeat: return ((a->parse_flags() ^ b->parse_flags()) & Regexp::NonGreedy) == 0 && a->min() == b->min() && a->max() == b->max(); case kRegexpCapture: return a->cap() == b->cap() && a->name() == b->name(); case kRegexpHaveMatch: return a->match_id() == b->match_id(); case kRegexpCharClass: { CharClass* acc = a->cc(); CharClass* bcc = b->cc(); return acc->size() == bcc->size() && acc->end() - acc->begin() == bcc->end() - bcc->begin() && memcmp(acc->begin(), bcc->begin(), (acc->end() - acc->begin()) * sizeof acc->begin()[0]) == 0; } } LOG(DFATAL) << "Unexpected op in Regexp::Equal: " << a->op(); return 0; } bool Regexp::Equal(Regexp* a, Regexp* b) { if (a == NULL || b == NULL) return a == b; if (!TopEqual(a, b)) return false; // Fast path: // return without allocating vector if there are no subregexps. switch (a->op()) { case kRegexpAlternate: case kRegexpConcat: case kRegexpStar: case kRegexpPlus: case kRegexpQuest: case kRegexpRepeat: case kRegexpCapture: break; default: return true; } // Committed to doing real work. // The stack (vector) has pairs of regexps waiting to // be compared. The regexps are only equal if // all the pairs end up being equal. vector stk; for (;;) { // Invariant: TopEqual(a, b) == true. Regexp* a2; Regexp* b2; switch (a->op()) { default: break; case kRegexpAlternate: case kRegexpConcat: for (int i = 0; i < a->nsub(); i++) { a2 = a->sub()[i]; b2 = b->sub()[i]; if (!TopEqual(a2, b2)) return false; stk.push_back(a2); stk.push_back(b2); } break; case kRegexpStar: case kRegexpPlus: case kRegexpQuest: case kRegexpRepeat: case kRegexpCapture: a2 = a->sub()[0]; b2 = b->sub()[0]; if (!TopEqual(a2, b2)) return false; // Really: // stk.push_back(a2); // stk.push_back(b2); // break; // but faster to assign directly and loop. a = a2; b = b2; continue; } int n = stk.size(); if (n == 0) break; a = stk[n-2]; b = stk[n-1]; stk.resize(n-2); } return true; } // Keep in sync with enum RegexpStatusCode in regexp.h static const char *kErrorStrings[] = { "no error", "unexpected error", "invalid escape sequence", "invalid character class", "invalid character class range", "missing ]", "missing )", "trailing \\", "no argument for repetition operator", "invalid repetition size", "bad repetition operator", "invalid perl operator", "invalid UTF-8", "invalid named capture group", }; string RegexpStatus::CodeText(enum RegexpStatusCode code) { if (code < 0 || code >= arraysize(kErrorStrings)) code = kRegexpInternalError; return kErrorStrings[code]; } string RegexpStatus::Text() const { if (error_arg_.empty()) return CodeText(code_); string s; s.append(CodeText(code_)); s.append(": "); s.append(error_arg_.data(), error_arg_.size()); return s; } void RegexpStatus::Copy(const RegexpStatus& status) { code_ = status.code_; error_arg_ = status.error_arg_; } typedef int Ignored; // Walker doesn't exist // Walker subclass to count capturing parens in regexp. class NumCapturesWalker : public Regexp::Walker { public: NumCapturesWalker() : ncapture_(0) {} int ncapture() { return ncapture_; } virtual Ignored PreVisit(Regexp* re, Ignored ignored, bool* stop) { if (re->op() == kRegexpCapture) ncapture_++; return ignored; } virtual Ignored ShortVisit(Regexp* re, Ignored ignored) { // Should never be called: we use Walk not WalkExponential. LOG(DFATAL) << "NumCapturesWalker::ShortVisit called"; return ignored; } private: int ncapture_; DISALLOW_COPY_AND_ASSIGN(NumCapturesWalker); }; int Regexp::NumCaptures() { NumCapturesWalker w; w.Walk(this, 0); return w.ncapture(); } // Walker class to build map of named capture groups and their indices. class NamedCapturesWalker : public Regexp::Walker { public: NamedCapturesWalker() : map_(NULL) {} ~NamedCapturesWalker() { delete map_; } map* TakeMap() { map* m = map_; map_ = NULL; return m; } Ignored PreVisit(Regexp* re, Ignored ignored, bool* stop) { if (re->op() == kRegexpCapture && re->name() != NULL) { // Allocate map once we find a name. if (map_ == NULL) map_ = new map; // Record first occurrence of each name. // (The rule is that if you have the same name // multiple times, only the leftmost one counts.) if (map_->find(*re->name()) == map_->end()) (*map_)[*re->name()] = re->cap(); } return ignored; } virtual Ignored ShortVisit(Regexp* re, Ignored ignored) { // Should never be called: we use Walk not WalkExponential. LOG(DFATAL) << "NamedCapturesWalker::ShortVisit called"; return ignored; } private: map* map_; DISALLOW_COPY_AND_ASSIGN(NamedCapturesWalker); }; map* Regexp::NamedCaptures() { NamedCapturesWalker w; w.Walk(this, 0); return w.TakeMap(); } // Walker class to build map from capture group indices to their names. class CaptureNamesWalker : public Regexp::Walker { public: CaptureNamesWalker() : map_(NULL) {} ~CaptureNamesWalker() { delete map_; } map* TakeMap() { map* m = map_; map_ = NULL; return m; } Ignored PreVisit(Regexp* re, Ignored ignored, bool* stop) { if (re->op() == kRegexpCapture && re->name() != NULL) { // Allocate map once we find a name. if (map_ == NULL) map_ = new map; (*map_)[re->cap()] = *re->name(); } return ignored; } virtual Ignored ShortVisit(Regexp* re, Ignored ignored) { // Should never be called: we use Walk not WalkExponential. LOG(DFATAL) << "CaptureNamesWalker::ShortVisit called"; return ignored; } private: map* map_; DISALLOW_COPY_AND_ASSIGN(CaptureNamesWalker); }; map* Regexp::CaptureNames() { CaptureNamesWalker w; w.Walk(this, 0); return w.TakeMap(); } // Determines whether regexp matches must be anchored // with a fixed string prefix. If so, returns the prefix and // the regexp that remains after the prefix. The prefix might // be ASCII case-insensitive. bool Regexp::RequiredPrefix(string *prefix, bool *foldcase, Regexp** suffix) { // No need for a walker: the regexp must be of the form // 1. some number of ^ anchors // 2. a literal char or string // 3. the rest prefix->clear(); *foldcase = false; *suffix = NULL; if (op_ != kRegexpConcat) return false; // Some number of anchors, then a literal or concatenation. int i = 0; Regexp** sub = this->sub(); while (i < nsub_ && sub[i]->op_ == kRegexpBeginText) i++; if (i == 0 || i >= nsub_) return false; Regexp* re = sub[i]; switch (re->op_) { default: return false; case kRegexpLiteralString: // Convert to string in proper encoding. if (re->parse_flags() & Latin1) { prefix->resize(re->nrunes_); for (int j = 0; j < re->nrunes_; j++) (*prefix)[j] = re->runes_[j]; } else { // Convert to UTF-8 in place. // Assume worst-case space and then trim. prefix->resize(re->nrunes_ * UTFmax); char *p = &(*prefix)[0]; for (int j = 0; j < re->nrunes_; j++) { Rune r = re->runes_[j]; if (r < Runeself) *p++ = r; else p += runetochar(p, &r); } prefix->resize(p - &(*prefix)[0]); } break; case kRegexpLiteral: if ((re->parse_flags() & Latin1) || re->rune_ < Runeself) { prefix->append(1, re->rune_); } else { char buf[UTFmax]; prefix->append(buf, runetochar(buf, &re->rune_)); } break; } *foldcase = (sub[i]->parse_flags() & FoldCase); i++; // The rest. if (i < nsub_) { for (int j = i; j < nsub_; j++) sub[j]->Incref(); re = Concat(sub + i, nsub_ - i, parse_flags()); } else { re = new Regexp(kRegexpEmptyMatch, parse_flags()); } *suffix = re; return true; } // Character class builder is a balanced binary tree (STL set) // containing non-overlapping, non-abutting RuneRanges. // The less-than operator used in the tree treats two // ranges as equal if they overlap at all, so that // lookups for a particular Rune are possible. CharClassBuilder::CharClassBuilder() { nrunes_ = 0; upper_ = 0; lower_ = 0; } // Add lo-hi to the class; return whether class got bigger. bool CharClassBuilder::AddRange(Rune lo, Rune hi) { if (hi < lo) return false; if (lo <= 'z' && hi >= 'A') { // Overlaps some alpha, maybe not all. // Update bitmaps telling which ASCII letters are in the set. Rune lo1 = max(lo, 'A'); Rune hi1 = min(hi, 'Z'); if (lo1 <= hi1) upper_ |= ((1 << (hi1 - lo1 + 1)) - 1) << (lo1 - 'A'); lo1 = max(lo, 'a'); hi1 = min(hi, 'z'); if (lo1 <= hi1) lower_ |= ((1 << (hi1 - lo1 + 1)) - 1) << (lo1 - 'a'); } { // Check whether lo, hi is already in the class. iterator it = ranges_.find(RuneRange(lo, lo)); if (it != end() && it->lo <= lo && hi <= it->hi) return false; } // Look for a range abutting lo on the left. // If it exists, take it out and increase our range. if (lo > 0) { iterator it = ranges_.find(RuneRange(lo-1, lo-1)); if (it != end()) { lo = it->lo; if (it->hi > hi) hi = it->hi; nrunes_ -= it->hi - it->lo + 1; ranges_.erase(it); } } // Look for a range abutting hi on the right. // If it exists, take it out and increase our range. if (hi < Runemax) { iterator it = ranges_.find(RuneRange(hi+1, hi+1)); if (it != end()) { hi = it->hi; nrunes_ -= it->hi - it->lo + 1; ranges_.erase(it); } } // Look for ranges between lo and hi. Take them out. // This is only safe because the set has no overlapping ranges. // We've already removed any ranges abutting lo and hi, so // any that overlap [lo, hi] must be contained within it. for (;;) { iterator it = ranges_.find(RuneRange(lo, hi)); if (it == end()) break; nrunes_ -= it->hi - it->lo + 1; ranges_.erase(it); } // Finally, add [lo, hi]. nrunes_ += hi - lo + 1; ranges_.insert(RuneRange(lo, hi)); return true; } void CharClassBuilder::AddCharClass(CharClassBuilder *cc) { for (iterator it = cc->begin(); it != cc->end(); ++it) AddRange(it->lo, it->hi); } bool CharClassBuilder::Contains(Rune r) { return ranges_.find(RuneRange(r, r)) != end(); } // Does the character class behave the same on A-Z as on a-z? bool CharClassBuilder::FoldsASCII() { return ((upper_ ^ lower_) & AlphaMask) == 0; } CharClassBuilder* CharClassBuilder::Copy() { CharClassBuilder* cc = new CharClassBuilder; for (iterator it = begin(); it != end(); ++it) cc->ranges_.insert(RuneRange(it->lo, it->hi)); cc->upper_ = upper_; cc->lower_ = lower_; cc->nrunes_ = nrunes_; return cc; } void CharClassBuilder::RemoveAbove(Rune r) { if (r >= Runemax) return; if (r < 'z') { if (r < 'a') lower_ = 0; else lower_ &= AlphaMask >> ('z' - r); } if (r < 'Z') { if (r < 'A') upper_ = 0; else upper_ &= AlphaMask >> ('Z' - r); } for (;;) { iterator it = ranges_.find(RuneRange(r + 1, Runemax)); if (it == end()) break; RuneRange rr = *it; ranges_.erase(it); nrunes_ -= rr.hi - rr.lo + 1; if (rr.lo <= r) { rr.hi = r; ranges_.insert(rr); nrunes_ += rr.hi - rr.lo + 1; } } } void CharClassBuilder::Negate() { // Build up negation and then copy in. // Could edit ranges in place, but C++ won't let me. vector v; v.reserve(ranges_.size() + 1); // In negation, first range begins at 0, unless // the current class begins at 0. iterator it = begin(); if (it == end()) { v.push_back(RuneRange(0, Runemax)); } else { int nextlo = 0; if (it->lo == 0) { nextlo = it->hi + 1; ++it; } for (; it != end(); ++it) { v.push_back(RuneRange(nextlo, it->lo - 1)); nextlo = it->hi + 1; } if (nextlo <= Runemax) v.push_back(RuneRange(nextlo, Runemax)); } ranges_.clear(); for (size_t i = 0; i < v.size(); i++) ranges_.insert(v[i]); upper_ = AlphaMask & ~upper_; lower_ = AlphaMask & ~lower_; nrunes_ = Runemax+1 - nrunes_; } // Character class is a sorted list of ranges. // The ranges are allocated in the same block as the header, // necessitating a special allocator and Delete method. CharClass* CharClass::New(int maxranges) { CharClass* cc; uint8* data = new uint8[sizeof *cc + maxranges*sizeof cc->ranges_[0]]; cc = reinterpret_cast(data); cc->ranges_ = reinterpret_cast(data + sizeof *cc); cc->nranges_ = 0; cc->folds_ascii_ = false; cc->nrunes_ = 0; return cc; } void CharClass::Delete() { uint8 *data = reinterpret_cast(this); delete[] data; } CharClass* CharClass::Negate() { CharClass* cc = CharClass::New(nranges_+1); cc->folds_ascii_ = folds_ascii_; cc->nrunes_ = Runemax + 1 - nrunes_; int n = 0; int nextlo = 0; for (CharClass::iterator it = begin(); it != end(); ++it) { if (it->lo == nextlo) { nextlo = it->hi + 1; } else { cc->ranges_[n++] = RuneRange(nextlo, it->lo - 1); nextlo = it->hi + 1; } } if (nextlo <= Runemax) cc->ranges_[n++] = RuneRange(nextlo, Runemax); cc->nranges_ = n; return cc; } bool CharClass::Contains(Rune r) { RuneRange* rr = ranges_; int n = nranges_; while (n > 0) { int m = n/2; if (rr[m].hi < r) { rr += m+1; n -= m+1; } else if (r < rr[m].lo) { n = m; } else { // rr[m].lo <= r && r <= rr[m].hi return true; } } return false; } CharClass* CharClassBuilder::GetCharClass() { CharClass* cc = CharClass::New(ranges_.size()); size_t n = 0; for (iterator it = begin(); it != end(); ++it) cc->ranges_[n++] = *it; cc->nranges_ = n; DCHECK_LE(static_cast(n), ranges_.size()); cc->nrunes_ = nrunes_; cc->folds_ascii_ = FoldsASCII(); return cc; } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/regexp.h000066400000000000000000000542461266464252400226640ustar00rootroot00000000000000// Copyright 2006 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // --- SPONSORED LINK -------------------------------------------------- // If you want to use this library for regular expression matching, // you should use re2/re2.h, which provides a class RE2 that // mimics the PCRE interface provided by PCRE's C++ wrappers. // This header describes the low-level interface used to implement RE2 // and may change in backwards-incompatible ways from time to time. // In contrast, RE2's interface will not. // --------------------------------------------------------------------- // Regular expression library: parsing, execution, and manipulation // of regular expressions. // // Any operation that traverses the Regexp structures should be written // using Regexp::Walker (see walker-inl.h), not recursively, because deeply nested // regular expressions such as x++++++++++++++++++++... might cause recursive // traversals to overflow the stack. // // It is the caller's responsibility to provide appropriate mutual exclusion // around manipulation of the regexps. RE2 does this. // // PARSING // // Regexp::Parse parses regular expressions encoded in UTF-8. // The default syntax is POSIX extended regular expressions, // with the following changes: // // 1. Backreferences (optional in POSIX EREs) are not supported. // (Supporting them precludes the use of DFA-based // matching engines.) // // 2. Collating elements and collation classes are not supported. // (No one has needed or wanted them.) // // The exact syntax accepted can be modified by passing flags to // Regexp::Parse. In particular, many of the basic Perl additions // are available. The flags are documented below (search for LikePerl). // // If parsed with the flag Regexp::Latin1, both the regular expression // and the input to the matching routines are assumed to be encoded in // Latin-1, not UTF-8. // // EXECUTION // // Once Regexp has parsed a regular expression, it provides methods // to search text using that regular expression. These methods are // implemented via calling out to other regular expression libraries. // (Let's call them the sublibraries.) // // To call a sublibrary, Regexp does not simply prepare a // string version of the regular expression and hand it to the // sublibrary. Instead, Regexp prepares, from its own parsed form, the // corresponding internal representation used by the sublibrary. // This has the drawback of needing to know the internal representation // used by the sublibrary, but it has two important benefits: // // 1. The syntax and meaning of regular expressions is guaranteed // to be that used by Regexp's parser, not the syntax expected // by the sublibrary. Regexp might accept a restricted or // expanded syntax for regular expressions as compared with // the sublibrary. As long as Regexp can translate from its // internal form into the sublibrary's, clients need not know // exactly which sublibrary they are using. // // 2. The sublibrary parsers are bypassed. For whatever reason, // sublibrary regular expression parsers often have security // problems. For example, plan9grep's regular expression parser // has a buffer overflow in its handling of large character // classes, and PCRE's parser has had buffer overflow problems // in the past. Security-team requires sandboxing of sublibrary // regular expression parsers. Avoiding the sublibrary parsers // avoids the sandbox. // // The execution methods we use now are provided by the compiled form, // Prog, described in prog.h // // MANIPULATION // // Unlike other regular expression libraries, Regexp makes its parsed // form accessible to clients, so that client code can analyze the // parsed regular expressions. #ifndef RE2_REGEXP_H__ #define RE2_REGEXP_H__ #include "util/util.h" #include "re2/stringpiece.h" namespace re2 { // Keep in sync with string list kOpcodeNames[] in testing/dump.cc enum RegexpOp { // Matches no strings. kRegexpNoMatch = 1, // Matches empty string. kRegexpEmptyMatch, // Matches rune_. kRegexpLiteral, // Matches runes_. kRegexpLiteralString, // Matches concatenation of sub_[0..nsub-1]. kRegexpConcat, // Matches union of sub_[0..nsub-1]. kRegexpAlternate, // Matches sub_[0] zero or more times. kRegexpStar, // Matches sub_[0] one or more times. kRegexpPlus, // Matches sub_[0] zero or one times. kRegexpQuest, // Matches sub_[0] at least min_ times, at most max_ times. // max_ == -1 means no upper limit. kRegexpRepeat, // Parenthesized (capturing) subexpression. Index is cap_. // Optionally, capturing name is name_. kRegexpCapture, // Matches any character. kRegexpAnyChar, // Matches any byte [sic]. kRegexpAnyByte, // Matches empty string at beginning of line. kRegexpBeginLine, // Matches empty string at end of line. kRegexpEndLine, // Matches word boundary "\b". kRegexpWordBoundary, // Matches not-a-word boundary "\B". kRegexpNoWordBoundary, // Matches empty string at beginning of text. kRegexpBeginText, // Matches empty string at end of text. kRegexpEndText, // Matches character class given by cc_. kRegexpCharClass, // Forces match of entire expression right now, // with match ID match_id_ (used by RE2::Set). kRegexpHaveMatch, kMaxRegexpOp = kRegexpHaveMatch, }; // Keep in sync with string list in regexp.cc enum RegexpStatusCode { // No error kRegexpSuccess = 0, // Unexpected error kRegexpInternalError, // Parse errors kRegexpBadEscape, // bad escape sequence kRegexpBadCharClass, // bad character class kRegexpBadCharRange, // bad character class range kRegexpMissingBracket, // missing closing ] kRegexpMissingParen, // missing closing ) kRegexpTrailingBackslash, // at end of regexp kRegexpRepeatArgument, // repeat argument missing, e.g. "*" kRegexpRepeatSize, // bad repetition argument kRegexpRepeatOp, // bad repetition operator kRegexpBadPerlOp, // bad perl operator kRegexpBadUTF8, // invalid UTF-8 in regexp kRegexpBadNamedCapture, // bad named capture }; // Error status for certain operations. class RegexpStatus { public: RegexpStatus() : code_(kRegexpSuccess), tmp_(NULL) {} ~RegexpStatus() { delete tmp_; } void set_code(enum RegexpStatusCode code) { code_ = code; } void set_error_arg(const StringPiece& error_arg) { error_arg_ = error_arg; } void set_tmp(string* tmp) { delete tmp_; tmp_ = tmp; } enum RegexpStatusCode code() const { return code_; } const StringPiece& error_arg() const { return error_arg_; } bool ok() const { return code() == kRegexpSuccess; } // Copies state from status. void Copy(const RegexpStatus& status); // Returns text equivalent of code, e.g.: // "Bad character class" static string CodeText(enum RegexpStatusCode code); // Returns text describing error, e.g.: // "Bad character class: [z-a]" string Text() const; private: enum RegexpStatusCode code_; // Kind of error StringPiece error_arg_; // Piece of regexp containing syntax error. string* tmp_; // Temporary storage, possibly where error_arg_ is. DISALLOW_COPY_AND_ASSIGN(RegexpStatus); }; // Walker to implement Simplify. class SimplifyWalker; // Compiled form; see prog.h class Prog; struct RuneRange { RuneRange() : lo(0), hi(0) { } RuneRange(int l, int h) : lo(l), hi(h) { } Rune lo; Rune hi; }; // Less-than on RuneRanges treats a == b if they overlap at all. // This lets us look in a set to find the range covering a particular Rune. struct RuneRangeLess { bool operator()(const RuneRange& a, const RuneRange& b) const { return a.hi < b.lo; } }; class CharClassBuilder; class CharClass { public: void Delete(); typedef RuneRange* iterator; iterator begin() { return ranges_; } iterator end() { return ranges_ + nranges_; } int size() { return nrunes_; } bool empty() { return nrunes_ == 0; } bool full() { return nrunes_ == Runemax+1; } bool FoldsASCII() { return folds_ascii_; } bool Contains(Rune r); CharClass* Negate(); private: CharClass(); // not implemented ~CharClass(); // not implemented static CharClass* New(int maxranges); friend class CharClassBuilder; bool folds_ascii_; int nrunes_; RuneRange *ranges_; int nranges_; DISALLOW_COPY_AND_ASSIGN(CharClass); }; class Regexp { public: // Flags for parsing. Can be ORed together. enum ParseFlags { NoParseFlags = 0, FoldCase = 1<<0, // Fold case during matching (case-insensitive). Literal = 1<<1, // Treat s as literal string instead of a regexp. ClassNL = 1<<2, // Allow char classes like [^a-z] and \D and \s // and [[:space:]] to match newline. DotNL = 1<<3, // Allow . to match newline. MatchNL = ClassNL | DotNL, OneLine = 1<<4, // Treat ^ and $ as only matching at beginning and // end of text, not around embedded newlines. // (Perl's default) Latin1 = 1<<5, // Regexp and text are in Latin1, not UTF-8. NonGreedy = 1<<6, // Repetition operators are non-greedy by default. PerlClasses = 1<<7, // Allow Perl character classes like \d. PerlB = 1<<8, // Allow Perl's \b and \B. PerlX = 1<<9, // Perl extensions: // non-capturing parens - (?: ) // non-greedy operators - *? +? ?? {}? // flag edits - (?i) (?-i) (?i: ) // i - FoldCase // m - !OneLine // s - DotNL // U - NonGreedy // line ends: \A \z // \Q and \E to disable/enable metacharacters // (?Pexpr) for named captures // \C to match any single byte UnicodeGroups = 1<<10, // Allow \p{Han} for Unicode Han group // and \P{Han} for its negation. NeverNL = 1<<11, // Never match NL, even if the regexp mentions // it explicitly. NeverCapture = 1<<12, // Parse all parens as non-capturing. // As close to Perl as we can get. LikePerl = ClassNL | OneLine | PerlClasses | PerlB | PerlX | UnicodeGroups, // Internal use only. WasDollar = 1<<15, // on kRegexpEndText: was $ in regexp text }; // Get. No set, Regexps are logically immutable once created. RegexpOp op() { return static_cast(op_); } int nsub() { return nsub_; } bool simple() { return simple_; } enum ParseFlags parse_flags() { return static_cast(parse_flags_); } int Ref(); // For testing. Regexp** sub() { if(nsub_ <= 1) return &subone_; else return submany_; } int min() { DCHECK_EQ(op_, kRegexpRepeat); return min_; } int max() { DCHECK_EQ(op_, kRegexpRepeat); return max_; } Rune rune() { DCHECK_EQ(op_, kRegexpLiteral); return rune_; } CharClass* cc() { DCHECK_EQ(op_, kRegexpCharClass); return cc_; } int cap() { DCHECK_EQ(op_, kRegexpCapture); return cap_; } const string* name() { DCHECK_EQ(op_, kRegexpCapture); return name_; } Rune* runes() { DCHECK_EQ(op_, kRegexpLiteralString); return runes_; } int nrunes() { DCHECK_EQ(op_, kRegexpLiteralString); return nrunes_; } int match_id() { DCHECK_EQ(op_, kRegexpHaveMatch); return match_id_; } // Increments reference count, returns object as convenience. Regexp* Incref(); // Decrements reference count and deletes this object if count reaches 0. void Decref(); // Parses string s to produce regular expression, returned. // Caller must release return value with re->Decref(). // On failure, sets *status (if status != NULL) and returns NULL. static Regexp* Parse(const StringPiece& s, ParseFlags flags, RegexpStatus* status); // Returns a _new_ simplified version of the current regexp. // Does not edit the current regexp. // Caller must release return value with re->Decref(). // Simplified means that counted repetition has been rewritten // into simpler terms and all Perl/POSIX features have been // removed. The result will capture exactly the same // subexpressions the original did, unless formatted with ToString. Regexp* Simplify(); friend class SimplifyWalker; // Parses the regexp src and then simplifies it and sets *dst to the // string representation of the simplified form. Returns true on success. // Returns false and sets *status (if status != NULL) on parse error. static bool SimplifyRegexp(const StringPiece& src, ParseFlags flags, string* dst, RegexpStatus* status); // Returns the number of capturing groups in the regexp. int NumCaptures(); friend class NumCapturesWalker; // Returns a map from names to capturing group indices, // or NULL if the regexp contains no named capture groups. // The caller is responsible for deleting the map. map* NamedCaptures(); // Returns a map from capturing group indices to capturing group // names or NULL if the regexp contains no named capture groups. The // caller is responsible for deleting the map. map* CaptureNames(); // Returns a string representation of the current regexp, // using as few parentheses as possible. string ToString(); // Convenience functions. They consume the passed reference, // so in many cases you should use, e.g., Plus(re->Incref(), flags). // They do not consume allocated arrays like subs or runes. static Regexp* Plus(Regexp* sub, ParseFlags flags); static Regexp* Star(Regexp* sub, ParseFlags flags); static Regexp* Quest(Regexp* sub, ParseFlags flags); static Regexp* Concat(Regexp** subs, int nsubs, ParseFlags flags); static Regexp* Alternate(Regexp** subs, int nsubs, ParseFlags flags); static Regexp* Capture(Regexp* sub, ParseFlags flags, int cap); static Regexp* Repeat(Regexp* sub, ParseFlags flags, int min, int max); static Regexp* NewLiteral(Rune rune, ParseFlags flags); static Regexp* NewCharClass(CharClass* cc, ParseFlags flags); static Regexp* LiteralString(Rune* runes, int nrunes, ParseFlags flags); static Regexp* HaveMatch(int match_id, ParseFlags flags); // Like Alternate but does not factor out common prefixes. static Regexp* AlternateNoFactor(Regexp** subs, int nsubs, ParseFlags flags); // Debugging function. Returns string format for regexp // that makes structure clear. Does NOT use regexp syntax. string Dump(); // Helper traversal class, defined fully in walker-inl.h. template class Walker; // Compile to Prog. See prog.h // Reverse prog expects to be run over text backward. // Construction and execution of prog will // stay within approximately max_mem bytes of memory. // If max_mem <= 0, a reasonable default is used. Prog* CompileToProg(int64 max_mem); Prog* CompileToReverseProg(int64 max_mem); // Whether to expect this library to find exactly the same answer as PCRE // when running this regexp. Most regexps do mimic PCRE exactly, but a few // obscure cases behave differently. Technically this is more a property // of the Prog than the Regexp, but the computation is much easier to do // on the Regexp. See mimics_pcre.cc for the exact conditions. bool MimicsPCRE(); // Benchmarking function. void NullWalk(); // Whether every match of this regexp must be anchored and // begin with a non-empty fixed string (perhaps after ASCII // case-folding). If so, returns the prefix and the sub-regexp that // follows it. bool RequiredPrefix(string* prefix, bool *foldcase, Regexp** suffix); private: // Constructor allocates vectors as appropriate for operator. explicit Regexp(RegexpOp op, ParseFlags parse_flags); // Use Decref() instead of delete to release Regexps. // This is private to catch deletes at compile time. ~Regexp(); void Destroy(); bool QuickDestroy(); // Helpers for Parse. Listed here so they can edit Regexps. class ParseState; friend class ParseState; friend bool ParseCharClass(StringPiece* s, Regexp** out_re, RegexpStatus* status); // Helper for testing [sic]. friend bool RegexpEqualTestingOnly(Regexp*, Regexp*); // Computes whether Regexp is already simple. bool ComputeSimple(); // Constructor that generates a concatenation or alternation, // enforcing the limit on the number of subexpressions for // a particular Regexp. static Regexp* ConcatOrAlternate(RegexpOp op, Regexp** subs, int nsubs, ParseFlags flags, bool can_factor); // Returns the leading string that re starts with. // The returned Rune* points into a piece of re, // so it must not be used after the caller calls re->Decref(). static Rune* LeadingString(Regexp* re, int* nrune, ParseFlags* flags); // Removes the first n leading runes from the beginning of re. // Edits re in place. static void RemoveLeadingString(Regexp* re, int n); // Returns the leading regexp in re's top-level concatenation. // The returned Regexp* points at re or a sub-expression of re, // so it must not be used after the caller calls re->Decref(). static Regexp* LeadingRegexp(Regexp* re); // Removes LeadingRegexp(re) from re and returns the remainder. // Might edit re in place. static Regexp* RemoveLeadingRegexp(Regexp* re); // Simplifies an alternation of literal strings by factoring out // common prefixes. static int FactorAlternation(Regexp** sub, int nsub, ParseFlags flags); static int FactorAlternationRecursive(Regexp** sub, int nsub, ParseFlags flags, int maxdepth); // Is a == b? Only efficient on regexps that have not been through // Simplify yet - the expansion of a kRegexpRepeat will make this // take a long time. Do not call on such regexps, hence private. static bool Equal(Regexp* a, Regexp* b); // Allocate space for n sub-regexps. void AllocSub(int n) { if (n < 0 || static_cast(n) != n) LOG(FATAL) << "Cannot AllocSub " << n; if (n > 1) submany_ = new Regexp*[n]; nsub_ = n; } // Add Rune to LiteralString void AddRuneToString(Rune r); // Swaps this with that, in place. void Swap(Regexp *that); // Operator. See description of operators above. // uint8 instead of RegexpOp to control space usage. uint8 op_; // Is this regexp structure already simple // (has it been returned by Simplify)? // uint8 instead of bool to control space usage. uint8 simple_; // Flags saved from parsing and used during execution. // (Only FoldCase is used.) // uint16 instead of ParseFlags to control space usage. uint16 parse_flags_; // Reference count. Exists so that SimplifyRegexp can build // regexp structures that are dags rather than trees to avoid // exponential blowup in space requirements. // uint16 to control space usage. // The standard regexp routines will never generate a // ref greater than the maximum repeat count (100), // but even so, Incref and Decref consult an overflow map // when ref_ reaches kMaxRef. uint16 ref_; static const uint16 kMaxRef = 0xffff; // Subexpressions. // uint16 to control space usage. // Concat and Alternate handle larger numbers of subexpressions // by building concatenation or alternation trees. // Other routines should call Concat or Alternate instead of // filling in sub() by hand. uint16 nsub_; static const uint16 kMaxNsub = 0xffff; union { Regexp** submany_; // if nsub_ > 1 Regexp* subone_; // if nsub_ == 1 }; // Extra space for parse and teardown stacks. Regexp* down_; // Arguments to operator. See description of operators above. union { struct { // Repeat int max_; int min_; }; struct { // Capture int cap_; string* name_; }; struct { // LiteralString int nrunes_; Rune* runes_; }; struct { // CharClass // These two could be in separate union members, // but it wouldn't save any space (there are other two-word structs) // and keeping them separate avoids confusion during parsing. CharClass* cc_; CharClassBuilder* ccb_; }; Rune rune_; // Literal int match_id_; // HaveMatch void *the_union_[2]; // as big as any other element, for memset }; DISALLOW_COPY_AND_ASSIGN(Regexp); }; // Character class set: contains non-overlapping, non-abutting RuneRanges. typedef set RuneRangeSet; class CharClassBuilder { public: CharClassBuilder(); typedef RuneRangeSet::iterator iterator; iterator begin() { return ranges_.begin(); } iterator end() { return ranges_.end(); } int size() { return nrunes_; } bool empty() { return nrunes_ == 0; } bool full() { return nrunes_ == Runemax+1; } bool Contains(Rune r); bool FoldsASCII(); bool AddRange(Rune lo, Rune hi); // returns whether class changed CharClassBuilder* Copy(); void AddCharClass(CharClassBuilder* cc); void Negate(); void RemoveAbove(Rune r); CharClass* GetCharClass(); void AddRangeFlags(Rune lo, Rune hi, Regexp::ParseFlags parse_flags); private: static const uint32 AlphaMask = (1<<26) - 1; uint32 upper_; // bitmap of A-Z uint32 lower_; // bitmap of a-z int nrunes_; RuneRangeSet ranges_; DISALLOW_COPY_AND_ASSIGN(CharClassBuilder); }; // Tell g++ that bitwise ops on ParseFlags produce ParseFlags. inline Regexp::ParseFlags operator|(Regexp::ParseFlags a, Regexp::ParseFlags b) { return static_cast(static_cast(a) | static_cast(b)); } inline Regexp::ParseFlags operator^(Regexp::ParseFlags a, Regexp::ParseFlags b) { return static_cast(static_cast(a) ^ static_cast(b)); } inline Regexp::ParseFlags operator&(Regexp::ParseFlags a, Regexp::ParseFlags b) { return static_cast(static_cast(a) & static_cast(b)); } inline Regexp::ParseFlags operator~(Regexp::ParseFlags a) { return static_cast(~static_cast(a)); } } // namespace re2 #endif // RE2_REGEXP_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/set.cc000066400000000000000000000055161266464252400223170ustar00rootroot00000000000000// Copyright 2010 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "re2/set.h" #include "util/util.h" #include "re2/stringpiece.h" #include "re2/prog.h" #include "re2.h" #include "re2/regexp.h" using namespace re2; RE2::Set::Set(const RE2::Options& options, RE2::Anchor anchor) { options_.Copy(options); anchor_ = anchor; prog_ = NULL; compiled_ = false; } RE2::Set::~Set() { for (size_t i = 0; i < re_.size(); i++) re_[i]->Decref(); delete prog_; } int RE2::Set::Add(const StringPiece& pattern, string* error) { if (compiled_) { LOG(DFATAL) << "RE2::Set::Add after Compile"; return -1; } Regexp::ParseFlags pf = static_cast( options_.ParseFlags()); RegexpStatus status; re2::Regexp* re = Regexp::Parse(pattern, pf, &status); if (re == NULL) { if (error != NULL) *error = status.Text(); if (options_.log_errors()) LOG(ERROR) << "Error parsing '" << pattern << "': " << status.Text(); return -1; } // Concatenate with match index and push on vector. int n = re_.size(); re2::Regexp* m = re2::Regexp::HaveMatch(n, pf); if (re->op() == kRegexpConcat) { int nsub = re->nsub(); re2::Regexp** sub = new re2::Regexp*[nsub + 1]; for (int i = 0; i < nsub; i++) sub[i] = re->sub()[i]->Incref(); sub[nsub] = m; re->Decref(); re = re2::Regexp::Concat(sub, nsub + 1, pf); delete[] sub; } else { re2::Regexp* sub[2]; sub[0] = re; sub[1] = m; re = re2::Regexp::Concat(sub, 2, pf); } re_.push_back(re); return n; } bool RE2::Set::Compile() { if (compiled_) { LOG(DFATAL) << "RE2::Set::Compile multiple times"; return false; } compiled_ = true; Regexp::ParseFlags pf = static_cast( options_.ParseFlags()); re2::Regexp* re = re2::Regexp::Alternate(const_cast(&re_[0]), re_.size(), pf); re_.clear(); re2::Regexp* sre = re->Simplify(); re->Decref(); re = sre; if (re == NULL) { if (options_.log_errors()) LOG(ERROR) << "Error simplifying during Compile."; return false; } prog_ = Prog::CompileSet(options_, anchor_, re); return prog_ != NULL; } bool RE2::Set::Match(const StringPiece& text, vector* v) const { if (!compiled_) { LOG(DFATAL) << "RE2::Set::Match without Compile"; return false; } v->clear(); bool failed; bool ret = prog_->SearchDFA(text, text, Prog::kAnchored, Prog::kManyMatch, NULL, &failed, v); if (failed) LOG(DFATAL) << "RE2::Set::Match: DFA ran out of cache space"; if (ret == false) return false; if (v->size() == 0) { LOG(DFATAL) << "RE2::Set::Match: match but unknown regexp set"; return false; } return true; } openalpr_2.2.4.orig/src/openalpr/support/re2/set.h000066400000000000000000000032221266464252400221510ustar00rootroot00000000000000// Copyright 2010 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #ifndef RE2_SET_H #define RE2_SET_H #include #include #include "re2.h" namespace re2 { using std::vector; // An RE2::Set represents a collection of regexps that can // be searched for simultaneously. class RE2::Set { public: Set(const RE2::Options& options, RE2::Anchor anchor); ~Set(); // Add adds regexp pattern to the set, interpreted using the RE2 options. // (The RE2 constructor's default options parameter is RE2::UTF8.) // Add returns the regexp index that will be used to identify // it in the result of Match, or -1 if the regexp cannot be parsed. // Indices are assigned in sequential order starting from 0. // Error returns do not increment the index. // If an error occurs and error != NULL, *error will hold an error message. int Add(const StringPiece& pattern, string* error); // Compile prepares the Set for matching. // Add must not be called again after Compile. // Compile must be called before FullMatch or PartialMatch. // Compile may return false if it runs out of memory. bool Compile(); // Match returns true if text matches any of the regexps in the set. // If so, it fills v with the indices of the matching regexps. bool Match(const StringPiece& text, vector* v) const; private: RE2::Options options_; RE2::Anchor anchor_; vector re_; re2::Prog* prog_; bool compiled_; //DISALLOW_COPY_AND_ASSIGN(Set); Set(const Set&); void operator=(const Set&); }; } // namespace re2 #endif // RE2_SET_H openalpr_2.2.4.orig/src/openalpr/support/re2/simplify.cc000066400000000000000000000275071266464252400233640ustar00rootroot00000000000000// Copyright 2006 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Rewrite POSIX and other features in re // to use simple extended regular expression features. // Also sort and simplify character classes. #include "util/util.h" #include "re2/regexp.h" #include "re2/walker-inl.h" namespace re2 { // Parses the regexp src and then simplifies it and sets *dst to the // string representation of the simplified form. Returns true on success. // Returns false and sets *error (if error != NULL) on error. bool Regexp::SimplifyRegexp(const StringPiece& src, ParseFlags flags, string* dst, RegexpStatus* status) { Regexp* re = Parse(src, flags, status); if (re == NULL) return false; Regexp* sre = re->Simplify(); re->Decref(); if (sre == NULL) { // Should not happen, since Simplify never fails. LOG(ERROR) << "Simplify failed on " << src; if (status) { status->set_code(kRegexpInternalError); status->set_error_arg(src); } return false; } *dst = sre->ToString(); sre->Decref(); return true; } // Assuming the simple_ flags on the children are accurate, // is this Regexp* simple? bool Regexp::ComputeSimple() { Regexp** subs; switch (op_) { case kRegexpNoMatch: case kRegexpEmptyMatch: case kRegexpLiteral: case kRegexpLiteralString: case kRegexpBeginLine: case kRegexpEndLine: case kRegexpBeginText: case kRegexpWordBoundary: case kRegexpNoWordBoundary: case kRegexpEndText: case kRegexpAnyChar: case kRegexpAnyByte: case kRegexpHaveMatch: return true; case kRegexpConcat: case kRegexpAlternate: // These are simple as long as the subpieces are simple. subs = sub(); for (int i = 0; i < nsub_; i++) if (!subs[i]->simple_) return false; return true; case kRegexpCharClass: // Simple as long as the char class is not empty, not full. if (ccb_ != NULL) return !ccb_->empty() && !ccb_->full(); return !cc_->empty() && !cc_->full(); case kRegexpCapture: subs = sub(); return subs[0]->simple_; case kRegexpStar: case kRegexpPlus: case kRegexpQuest: subs = sub(); if (!subs[0]->simple_) return false; switch (subs[0]->op_) { case kRegexpStar: case kRegexpPlus: case kRegexpQuest: case kRegexpEmptyMatch: case kRegexpNoMatch: return false; default: break; } return true; case kRegexpRepeat: return false; } LOG(DFATAL) << "Case not handled in ComputeSimple: " << op_; return false; } // Walker subclass used by Simplify. // The simplify walk is purely post-recursive: given the simplified children, // PostVisit creates the simplified result. // The child_args are simplified Regexp*s. class SimplifyWalker : public Regexp::Walker { public: SimplifyWalker() {} virtual Regexp* PreVisit(Regexp* re, Regexp* parent_arg, bool* stop); virtual Regexp* PostVisit(Regexp* re, Regexp* parent_arg, Regexp* pre_arg, Regexp** child_args, int nchild_args); virtual Regexp* Copy(Regexp* re); virtual Regexp* ShortVisit(Regexp* re, Regexp* parent_arg); private: // These functions are declared inside SimplifyWalker so that // they can edit the private fields of the Regexps they construct. // Creates a concatenation of two Regexp, consuming refs to re1 and re2. // Caller must Decref return value when done with it. static Regexp* Concat2(Regexp* re1, Regexp* re2, Regexp::ParseFlags flags); // Simplifies the expression re{min,max} in terms of *, +, and ?. // Returns a new regexp. Does not edit re. Does not consume reference to re. // Caller must Decref return value when done with it. static Regexp* SimplifyRepeat(Regexp* re, int min, int max, Regexp::ParseFlags parse_flags); // Simplifies a character class by expanding any named classes // into rune ranges. Does not edit re. Does not consume ref to re. // Caller must Decref return value when done with it. static Regexp* SimplifyCharClass(Regexp* re); DISALLOW_COPY_AND_ASSIGN(SimplifyWalker); }; // Simplifies a regular expression, returning a new regexp. // The new regexp uses traditional Unix egrep features only, // plus the Perl (?:) non-capturing parentheses. // Otherwise, no POSIX or Perl additions. The new regexp // captures exactly the same subexpressions (with the same indices) // as the original. // Does not edit current object. // Caller must Decref() return value when done with it. Regexp* Regexp::Simplify() { if (simple_) return Incref(); SimplifyWalker w; return w.Walk(this, NULL); } #define Simplify DontCallSimplify // Avoid accidental recursion Regexp* SimplifyWalker::Copy(Regexp* re) { return re->Incref(); } Regexp* SimplifyWalker::ShortVisit(Regexp* re, Regexp* parent_arg) { // This should never be called, since we use Walk and not // WalkExponential. LOG(DFATAL) << "SimplifyWalker::ShortVisit called"; return re->Incref(); } Regexp* SimplifyWalker::PreVisit(Regexp* re, Regexp* parent_arg, bool* stop) { if (re->simple_) { *stop = true; return re->Incref(); } return NULL; } Regexp* SimplifyWalker::PostVisit(Regexp* re, Regexp* parent_arg, Regexp* pre_arg, Regexp** child_args, int nchild_args) { switch (re->op()) { case kRegexpNoMatch: case kRegexpEmptyMatch: case kRegexpLiteral: case kRegexpLiteralString: case kRegexpBeginLine: case kRegexpEndLine: case kRegexpBeginText: case kRegexpWordBoundary: case kRegexpNoWordBoundary: case kRegexpEndText: case kRegexpAnyChar: case kRegexpAnyByte: case kRegexpHaveMatch: // All these are always simple. re->simple_ = true; return re->Incref(); case kRegexpConcat: case kRegexpAlternate: { // These are simple as long as the subpieces are simple. // Two passes to avoid allocation in the common case. bool changed = false; Regexp** subs = re->sub(); for (int i = 0; i < re->nsub_; i++) { Regexp* sub = subs[i]; Regexp* newsub = child_args[i]; if (newsub != sub) { changed = true; break; } } if (!changed) { for (int i = 0; i < re->nsub_; i++) { Regexp* newsub = child_args[i]; newsub->Decref(); } re->simple_ = true; return re->Incref(); } Regexp* nre = new Regexp(re->op(), re->parse_flags()); nre->AllocSub(re->nsub_); Regexp** nre_subs = nre->sub(); for (int i = 0; i nsub_; i++) nre_subs[i] = child_args[i]; nre->simple_ = true; return nre; } case kRegexpCapture: { Regexp* newsub = child_args[0]; if (newsub == re->sub()[0]) { newsub->Decref(); re->simple_ = true; return re->Incref(); } Regexp* nre = new Regexp(kRegexpCapture, re->parse_flags()); nre->AllocSub(1); nre->sub()[0] = newsub; nre->cap_ = re->cap_; nre->simple_ = true; return nre; } case kRegexpStar: case kRegexpPlus: case kRegexpQuest: { Regexp* newsub = child_args[0]; // Special case: repeat the empty string as much as // you want, but it's still the empty string. if (newsub->op() == kRegexpEmptyMatch) return newsub; // These are simple as long as the subpiece is simple. if (newsub == re->sub()[0]) { newsub->Decref(); re->simple_ = true; return re->Incref(); } // These are also idempotent if flags are constant. if (re->op() == newsub->op() && re->parse_flags() == newsub->parse_flags()) return newsub; Regexp* nre = new Regexp(re->op(), re->parse_flags()); nre->AllocSub(1); nre->sub()[0] = newsub; nre->simple_ = true; return nre; } case kRegexpRepeat: { Regexp* newsub = child_args[0]; // Special case: repeat the empty string as much as // you want, but it's still the empty string. if (newsub->op() == kRegexpEmptyMatch) return newsub; Regexp* nre = SimplifyRepeat(newsub, re->min_, re->max_, re->parse_flags()); newsub->Decref(); nre->simple_ = true; return nre; } case kRegexpCharClass: { Regexp* nre = SimplifyCharClass(re); nre->simple_ = true; return nre; } } LOG(ERROR) << "Simplify case not handled: " << re->op(); return re->Incref(); } // Creates a concatenation of two Regexp, consuming refs to re1 and re2. // Returns a new Regexp, handing the ref to the caller. Regexp* SimplifyWalker::Concat2(Regexp* re1, Regexp* re2, Regexp::ParseFlags parse_flags) { Regexp* re = new Regexp(kRegexpConcat, parse_flags); re->AllocSub(2); Regexp** subs = re->sub(); subs[0] = re1; subs[1] = re2; return re; } // Simplifies the expression re{min,max} in terms of *, +, and ?. // Returns a new regexp. Does not edit re. Does not consume reference to re. // Caller must Decref return value when done with it. // The result will *not* necessarily have the right capturing parens // if you call ToString() and re-parse it: (x){2} becomes (x)(x), // but in the Regexp* representation, both (x) are marked as $1. Regexp* SimplifyWalker::SimplifyRepeat(Regexp* re, int min, int max, Regexp::ParseFlags f) { // x{n,} means at least n matches of x. if (max == -1) { // Special case: x{0,} is x* if (min == 0) return Regexp::Star(re->Incref(), f); // Special case: x{1,} is x+ if (min == 1) return Regexp::Plus(re->Incref(), f); // General case: x{4,} is xxxx+ Regexp* nre = new Regexp(kRegexpConcat, f); nre->AllocSub(min); Regexp** nre_subs = nre->sub(); for (int i = 0; i < min-1; i++) nre_subs[i] = re->Incref(); nre_subs[min-1] = Regexp::Plus(re->Incref(), f); return nre; } // Special case: (x){0} matches only empty string. if (min == 0 && max == 0) return new Regexp(kRegexpEmptyMatch, f); // Special case: x{1} is just x. if (min == 1 && max == 1) return re->Incref(); // General case: x{n,m} means n copies of x and m copies of x?. // The machine will do less work if we nest the final m copies, // so that x{2,5} = xx(x(x(x)?)?)? // Build leading prefix: xx. Capturing only on the last one. Regexp* nre = NULL; if (min > 0) { nre = new Regexp(kRegexpConcat, f); nre->AllocSub(min); Regexp** nre_subs = nre->sub(); for (int i = 0; i < min; i++) nre_subs[i] = re->Incref(); } // Build and attach suffix: (x(x(x)?)?)? if (max > min) { Regexp* suf = Regexp::Quest(re->Incref(), f); for (int i = min+1; i < max; i++) suf = Regexp::Quest(Concat2(re->Incref(), suf, f), f); if (nre == NULL) nre = suf; else nre = Concat2(nre, suf, f); } if (nre == NULL) { // Some degenerate case, like min > max, or min < max < 0. // This shouldn't happen, because the parser rejects such regexps. LOG(DFATAL) << "Malformed repeat " << re->ToString() << " " << min << " " << max; return new Regexp(kRegexpNoMatch, f); } return nre; } // Simplifies a character class. // Caller must Decref return value when done with it. Regexp* SimplifyWalker::SimplifyCharClass(Regexp* re) { CharClass* cc = re->cc(); // Special cases if (cc->empty()) return new Regexp(kRegexpNoMatch, re->parse_flags()); if (cc->full()) return new Regexp(kRegexpAnyChar, re->parse_flags()); return re->Incref(); } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/stringpiece.cc000066400000000000000000000052401266464252400240320ustar00rootroot00000000000000// Copyright 2004 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "re2/stringpiece.h" #include "util/util.h" using re2::StringPiece; std::ostream& operator<<(std::ostream& o, const StringPiece& piece) { o.write(piece.data(), piece.size()); return o; } bool StringPiece::_equal(const StringPiece& x, const StringPiece& y) { int len = x.size(); if (len != y.size()) { return false; } const char* p = x.data(); const char* p2 = y.data(); // Test last byte in case strings share large common prefix if ((len > 0) && (p[len-1] != p2[len-1])) return false; const char* p_limit = p + len; for (; p < p_limit; p++, p2++) { if (*p != *p2) return false; } return true; } void StringPiece::CopyToString(string* target) const { target->assign(ptr_, length_); } int StringPiece::copy(char* buf, size_type n, size_type pos) const { int ret = min(length_ - pos, n); memcpy(buf, ptr_ + pos, ret); return ret; } bool StringPiece::contains(StringPiece s) const { return (size_t)find(s, 0) != npos; } int StringPiece::find(const StringPiece& s, size_type pos) const { if (length_ < 0 || pos > static_cast(length_)) return npos; const char* result = std::search(ptr_ + pos, ptr_ + length_, s.ptr_, s.ptr_ + s.length_); const size_type xpos = result - ptr_; return xpos + s.length_ <= static_cast(length_) ? xpos : npos; } int StringPiece::find(char c, size_type pos) const { if (length_ <= 0 || pos >= static_cast(length_)) { return npos; } const char* result = std::find(ptr_ + pos, ptr_ + length_, c); return result != ptr_ + length_ ? result - ptr_ : npos; } int StringPiece::rfind(const StringPiece& s, size_type pos) const { if (length_ < s.length_) return npos; const size_t ulen = length_; if (s.length_ == 0) return min(ulen, pos); const char* last = ptr_ + min(ulen - s.length_, pos) + s.length_; const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_); return result != last ? result - ptr_ : npos; } int StringPiece::rfind(char c, size_type pos) const { if (length_ <= 0) return npos; for (int i = min(pos, static_cast(length_ - 1)); i >= 0; --i) { if (ptr_[i] == c) { return i; } } return npos; } StringPiece StringPiece::substr(size_type pos, size_type n) const { if (pos > static_cast(length_)) pos = static_cast(length_); if (n > length_ - pos) n = length_ - pos; return StringPiece(ptr_ + pos, n); } const StringPiece::size_type StringPiece::npos = size_type(-1); openalpr_2.2.4.orig/src/openalpr/support/re2/stringpiece.h000066400000000000000000000134271266464252400237020ustar00rootroot00000000000000// Copyright 2001-2010 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // A string-like object that points to a sized piece of memory. // // Functions or methods may use const StringPiece& parameters to accept either // a "const char*" or a "string" value that will be implicitly converted to // a StringPiece. The implicit conversion means that it is often appropriate // to include this .h file in other files rather than forward-declaring // StringPiece as would be appropriate for most other Google classes. // // Systematic usage of StringPiece is encouraged as it will reduce unnecessary // conversions from "const char*" to "string" and back again. // // // Arghh! I wish C++ literals were "string". #ifndef STRINGS_STRINGPIECE_H__ #define STRINGS_STRINGPIECE_H__ #include #include #include #include #include namespace re2 { class StringPiece { private: const char* ptr_; int length_; public: // We provide non-explicit singleton constructors so users can pass // in a "const char*" or a "string" wherever a "StringPiece" is // expected. StringPiece() : ptr_(NULL), length_(0) { } StringPiece(const char* str) : ptr_(str), length_((str == NULL) ? 0 : static_cast(strlen(str))) { } StringPiece(const std::string& str) : ptr_(str.data()), length_(static_cast(str.size())) { } StringPiece(const char* offset, int len) : ptr_(offset), length_(len) { } // data() may return a pointer to a buffer with embedded NULs, and the // returned buffer may or may not be null terminated. Therefore it is // typically a mistake to pass data() to a routine that expects a NUL // terminated string. const char* data() const { return ptr_; } int size() const { return length_; } int length() const { return length_; } bool empty() const { return length_ == 0; } void clear() { ptr_ = NULL; length_ = 0; } void set(const char* data, int len) { ptr_ = data; length_ = len; } void set(const char* str) { ptr_ = str; if (str != NULL) length_ = static_cast(strlen(str)); else length_ = 0; } void set(const void* data, int len) { ptr_ = reinterpret_cast(data); length_ = len; } char operator[](int i) const { return ptr_[i]; } void remove_prefix(int n) { ptr_ += n; length_ -= n; } void remove_suffix(int n) { length_ -= n; } int compare(const StringPiece& x) const { int r = memcmp(ptr_, x.ptr_, std::min(length_, x.length_)); if (r == 0) { if (length_ < x.length_) r = -1; else if (length_ > x.length_) r = +1; } return r; } std::string as_string() const { return std::string(data(), size()); } // We also define ToString() here, since many other string-like // interfaces name the routine that converts to a C++ string // "ToString", and it's confusing to have the method that does that // for a StringPiece be called "as_string()". We also leave the // "as_string()" method defined here for existing code. std::string ToString() const { return std::string(data(), size()); } void CopyToString(std::string* target) const; void AppendToString(std::string* target) const; // Does "this" start with "x" bool starts_with(const StringPiece& x) const { return ((length_ >= x.length_) && (memcmp(ptr_, x.ptr_, x.length_) == 0)); } // Does "this" end with "x" bool ends_with(const StringPiece& x) const { return ((length_ >= x.length_) && (memcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0)); } // standard STL container boilerplate typedef char value_type; typedef const char* pointer; typedef const char& reference; typedef const char& const_reference; typedef size_t size_type; typedef ptrdiff_t difference_type; static const size_type npos; typedef const char* const_iterator; typedef const char* iterator; typedef std::reverse_iterator const_reverse_iterator; typedef std::reverse_iterator reverse_iterator; iterator begin() const { return ptr_; } iterator end() const { return ptr_ + length_; } const_reverse_iterator rbegin() const { return const_reverse_iterator(ptr_ + length_); } const_reverse_iterator rend() const { return const_reverse_iterator(ptr_); } // STLS says return size_type, but Google says return int int max_size() const { return length_; } int capacity() const { return length_; } int copy(char* buf, size_type n, size_type pos = 0) const; bool contains(StringPiece s) const; int find(const StringPiece& s, size_type pos = 0) const; int find(char c, size_type pos = 0) const; int rfind(const StringPiece& s, size_type pos = npos) const; int rfind(char c, size_type pos = npos) const; StringPiece substr(size_type pos, size_type n = npos) const; static bool _equal(const StringPiece&, const StringPiece&); }; inline bool operator==(const StringPiece& x, const StringPiece& y) { return StringPiece::_equal(x, y); } inline bool operator!=(const StringPiece& x, const StringPiece& y) { return !(x == y); } inline bool operator<(const StringPiece& x, const StringPiece& y) { const int r = memcmp(x.data(), y.data(), std::min(x.size(), y.size())); return ((r < 0) || ((r == 0) && (x.size() < y.size()))); } inline bool operator>(const StringPiece& x, const StringPiece& y) { return y < x; } inline bool operator<=(const StringPiece& x, const StringPiece& y) { return !(x > y); } inline bool operator>=(const StringPiece& x, const StringPiece& y) { return !(x < y); } } // namespace re2 // allow StringPiece to be logged extern std::ostream& operator<<(std::ostream& o, const re2::StringPiece& piece); #endif // STRINGS_STRINGPIECE_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/tostring.cc000066400000000000000000000201121266464252400233620ustar00rootroot00000000000000// Copyright 2006 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Format a regular expression structure as a string. // Tested by parse_test.cc #include "util/util.h" #include "re2/regexp.h" #include "re2/walker-inl.h" namespace re2 { enum { PrecAtom, PrecUnary, PrecConcat, PrecAlternate, PrecEmpty, PrecParen, PrecToplevel, }; // Helper function. See description below. static void AppendCCRange(string* t, Rune lo, Rune hi); // Walker to generate string in s_. // The arg pointers are actually integers giving the // context precedence. // The child_args are always NULL. class ToStringWalker : public Regexp::Walker { public: explicit ToStringWalker(string* t) : t_(t) {} virtual int PreVisit(Regexp* re, int parent_arg, bool* stop); virtual int PostVisit(Regexp* re, int parent_arg, int pre_arg, int* child_args, int nchild_args); virtual int ShortVisit(Regexp* re, int parent_arg) { return 0; } private: string* t_; // The string the walker appends to. DISALLOW_COPY_AND_ASSIGN(ToStringWalker); }; string Regexp::ToString() { string t; ToStringWalker w(&t); w.WalkExponential(this, PrecToplevel, 100000); if (w.stopped_early()) t += " [truncated]"; return t; } #define ToString DontCallToString // Avoid accidental recursion. // Visits re before children are processed. // Appends ( if needed and passes new precedence to children. int ToStringWalker::PreVisit(Regexp* re, int parent_arg, bool* stop) { int prec = parent_arg; int nprec = PrecAtom; switch (re->op()) { case kRegexpNoMatch: case kRegexpEmptyMatch: case kRegexpLiteral: case kRegexpAnyChar: case kRegexpAnyByte: case kRegexpBeginLine: case kRegexpEndLine: case kRegexpBeginText: case kRegexpEndText: case kRegexpWordBoundary: case kRegexpNoWordBoundary: case kRegexpCharClass: case kRegexpHaveMatch: nprec = PrecAtom; break; case kRegexpConcat: case kRegexpLiteralString: if (prec < PrecConcat) t_->append("(?:"); nprec = PrecConcat; break; case kRegexpAlternate: if (prec < PrecAlternate) t_->append("(?:"); nprec = PrecAlternate; break; case kRegexpCapture: t_->append("("); if (re->name()) { t_->append("?P<"); t_->append(*re->name()); t_->append(">"); } nprec = PrecParen; break; case kRegexpStar: case kRegexpPlus: case kRegexpQuest: case kRegexpRepeat: if (prec < PrecUnary) t_->append("(?:"); // The subprecedence here is PrecAtom instead of PrecUnary // because PCRE treats two unary ops in a row as a parse error. nprec = PrecAtom; break; } return nprec; } static void AppendLiteral(string *t, Rune r, bool foldcase) { if (r != 0 && r < 0x80 && strchr("(){}[]*+?|.^$\\", r)) { t->append(1, '\\'); t->append(1, r); } else if (foldcase && 'a' <= r && r <= 'z') { if ('a' <= r && r <= 'z') r += 'A' - 'a'; t->append(1, '['); t->append(1, r); t->append(1, r + 'a' - 'A'); t->append(1, ']'); } else { AppendCCRange(t, r, r); } } // Visits re after children are processed. // For childless regexps, all the work is done here. // For regexps with children, append any unary suffixes or ). int ToStringWalker::PostVisit(Regexp* re, int parent_arg, int pre_arg, int* child_args, int nchild_args) { int prec = parent_arg; switch (re->op()) { case kRegexpNoMatch: // There's no simple symbol for "no match", but // [^0-Runemax] excludes everything. t_->append("[^\\x00-\\x{10ffff}]"); break; case kRegexpEmptyMatch: // Append (?:) to make empty string visible, // unless this is already being parenthesized. if (prec < PrecEmpty) t_->append("(?:)"); break; case kRegexpLiteral: AppendLiteral(t_, re->rune(), re->parse_flags() & Regexp::FoldCase); break; case kRegexpLiteralString: for (int i = 0; i < re->nrunes(); i++) AppendLiteral(t_, re->runes()[i], re->parse_flags() & Regexp::FoldCase); if (prec < PrecConcat) t_->append(")"); break; case kRegexpConcat: if (prec < PrecConcat) t_->append(")"); break; case kRegexpAlternate: // Clumsy but workable: the children all appended | // at the end of their strings, so just remove the last one. if ((*t_)[t_->size()-1] == '|') t_->erase(t_->size()-1); else LOG(DFATAL) << "Bad final char: " << t_; if (prec < PrecAlternate) t_->append(")"); break; case kRegexpStar: t_->append("*"); if (re->parse_flags() & Regexp::NonGreedy) t_->append("?"); if (prec < PrecUnary) t_->append(")"); break; case kRegexpPlus: t_->append("+"); if (re->parse_flags() & Regexp::NonGreedy) t_->append("?"); if (prec < PrecUnary) t_->append(")"); break; case kRegexpQuest: t_->append("?"); if (re->parse_flags() & Regexp::NonGreedy) t_->append("?"); if (prec < PrecUnary) t_->append(")"); break; case kRegexpRepeat: if (re->max() == -1) t_->append(StringPrintf("{%d,}", re->min())); else if (re->min() == re->max()) t_->append(StringPrintf("{%d}", re->min())); else t_->append(StringPrintf("{%d,%d}", re->min(), re->max())); if (re->parse_flags() & Regexp::NonGreedy) t_->append("?"); if (prec < PrecUnary) t_->append(")"); break; case kRegexpAnyChar: t_->append("."); break; case kRegexpAnyByte: t_->append("\\C"); break; case kRegexpBeginLine: t_->append("^"); break; case kRegexpEndLine: t_->append("$"); break; case kRegexpBeginText: t_->append("(?-m:^)"); break; case kRegexpEndText: if (re->parse_flags() & Regexp::WasDollar) t_->append("(?-m:$)"); else t_->append("\\z"); break; case kRegexpWordBoundary: t_->append("\\b"); break; case kRegexpNoWordBoundary: t_->append("\\B"); break; case kRegexpCharClass: { if (re->cc()->size() == 0) { t_->append("[^\\x00-\\x{10ffff}]"); break; } t_->append("["); // Heuristic: show class as negated if it contains the // non-character 0xFFFE. CharClass* cc = re->cc(); if (cc->Contains(0xFFFE)) { cc = cc->Negate(); t_->append("^"); } for (CharClass::iterator i = cc->begin(); i != cc->end(); ++i) AppendCCRange(t_, i->lo, i->hi); if (cc != re->cc()) cc->Delete(); t_->append("]"); break; } case kRegexpCapture: t_->append(")"); break; case kRegexpHaveMatch: // There's no syntax accepted by the parser to generate // this node (it is generated by RE2::Set) so make something // up that is readable but won't compile. t_->append("(?HaveMatch:%d)", re->match_id()); break; } // If the parent is an alternation, append the | for it. if (prec == PrecAlternate) t_->append("|"); return 0; } // Appends a rune for use in a character class to the string t. static void AppendCCChar(string* t, Rune r) { if (0x20 <= r && r <= 0x7E) { if (strchr("[]^-\\", r)) t->append("\\"); t->append(1, r); return; } switch (r) { default: break; case '\r': t->append("\\r"); return; case '\t': t->append("\\t"); return; case '\n': t->append("\\n"); return; case '\f': t->append("\\f"); return; } if (r < 0x100) { StringAppendF(t, "\\x%02x", static_cast(r)); return; } StringAppendF(t, "\\x{%x}", static_cast(r)); } static void AppendCCRange(string* t, Rune lo, Rune hi) { if (lo > hi) return; AppendCCChar(t, lo); if (lo < hi) { t->append("-"); AppendCCChar(t, hi); } } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/unicode_casefold.cc000066400000000000000000000247551266464252400250200ustar00rootroot00000000000000 // GENERATED BY make_unicode_casefold.py; DO NOT EDIT. // make_unicode_casefold.py >unicode_casefold.cc #include "re2/unicode_casefold.h" namespace re2 { // 1034 groups, 2089 pairs, 289 ranges const CaseFold unicode_casefold[] = { { 65, 90, 32 }, { 97, 106, -32 }, { 107, 107, 8383 }, { 108, 114, -32 }, { 115, 115, 268 }, { 116, 122, -32 }, { 181, 181, 743 }, { 192, 214, 32 }, { 216, 222, 32 }, { 223, 223, 7615 }, { 224, 228, -32 }, { 229, 229, 8262 }, { 230, 246, -32 }, { 248, 254, -32 }, { 255, 255, 121 }, { 256, 303, EvenOdd }, { 306, 311, EvenOdd }, { 313, 328, OddEven }, { 330, 375, EvenOdd }, { 376, 376, -121 }, { 377, 382, OddEven }, { 383, 383, -300 }, { 384, 384, 195 }, { 385, 385, 210 }, { 386, 389, EvenOdd }, { 390, 390, 206 }, { 391, 392, OddEven }, { 393, 394, 205 }, { 395, 396, OddEven }, { 398, 398, 79 }, { 399, 399, 202 }, { 400, 400, 203 }, { 401, 402, OddEven }, { 403, 403, 205 }, { 404, 404, 207 }, { 405, 405, 97 }, { 406, 406, 211 }, { 407, 407, 209 }, { 408, 409, EvenOdd }, { 410, 410, 163 }, { 412, 412, 211 }, { 413, 413, 213 }, { 414, 414, 130 }, { 415, 415, 214 }, { 416, 421, EvenOdd }, { 422, 422, 218 }, { 423, 424, OddEven }, { 425, 425, 218 }, { 428, 429, EvenOdd }, { 430, 430, 218 }, { 431, 432, OddEven }, { 433, 434, 217 }, { 435, 438, OddEven }, { 439, 439, 219 }, { 440, 441, EvenOdd }, { 444, 445, EvenOdd }, { 447, 447, 56 }, { 452, 452, EvenOdd }, { 453, 453, OddEven }, { 454, 454, -2 }, { 455, 455, OddEven }, { 456, 456, EvenOdd }, { 457, 457, -2 }, { 458, 458, EvenOdd }, { 459, 459, OddEven }, { 460, 460, -2 }, { 461, 476, OddEven }, { 477, 477, -79 }, { 478, 495, EvenOdd }, { 497, 497, OddEven }, { 498, 498, EvenOdd }, { 499, 499, -2 }, { 500, 501, EvenOdd }, { 502, 502, -97 }, { 503, 503, -56 }, { 504, 543, EvenOdd }, { 544, 544, -130 }, { 546, 563, EvenOdd }, { 570, 570, 10795 }, { 571, 572, OddEven }, { 573, 573, -163 }, { 574, 574, 10792 }, { 575, 576, 10815 }, { 577, 578, OddEven }, { 579, 579, -195 }, { 580, 580, 69 }, { 581, 581, 71 }, { 582, 591, EvenOdd }, { 592, 592, 10783 }, { 593, 593, 10780 }, { 594, 594, 10782 }, { 595, 595, -210 }, { 596, 596, -206 }, { 598, 599, -205 }, { 601, 601, -202 }, { 603, 603, -203 }, { 608, 608, -205 }, { 611, 611, -207 }, { 613, 613, 42280 }, { 614, 614, 42308 }, { 616, 616, -209 }, { 617, 617, -211 }, { 619, 619, 10743 }, { 623, 623, -211 }, { 625, 625, 10749 }, { 626, 626, -213 }, { 629, 629, -214 }, { 637, 637, 10727 }, { 640, 640, -218 }, { 643, 643, -218 }, { 648, 648, -218 }, { 649, 649, -69 }, { 650, 651, -217 }, { 652, 652, -71 }, { 658, 658, -219 }, { 837, 837, 84 }, { 880, 883, EvenOdd }, { 886, 887, EvenOdd }, { 891, 893, 130 }, { 902, 902, 38 }, { 904, 906, 37 }, { 908, 908, 64 }, { 910, 911, 63 }, { 913, 929, 32 }, { 931, 931, 31 }, { 932, 939, 32 }, { 940, 940, -38 }, { 941, 943, -37 }, { 945, 945, -32 }, { 946, 946, 30 }, { 947, 948, -32 }, { 949, 949, 64 }, { 950, 951, -32 }, { 952, 952, 25 }, { 953, 953, 7173 }, { 954, 954, 54 }, { 955, 955, -32 }, { 956, 956, -775 }, { 957, 959, -32 }, { 960, 960, 22 }, { 961, 961, 48 }, { 962, 962, EvenOdd }, { 963, 965, -32 }, { 966, 966, 15 }, { 967, 968, -32 }, { 969, 969, 7517 }, { 970, 971, -32 }, { 972, 972, -64 }, { 973, 974, -63 }, { 975, 975, 8 }, { 976, 976, -62 }, { 977, 977, 35 }, { 981, 981, -47 }, { 982, 982, -54 }, { 983, 983, -8 }, { 984, 1007, EvenOdd }, { 1008, 1008, -86 }, { 1009, 1009, -80 }, { 1010, 1010, 7 }, { 1012, 1012, -92 }, { 1013, 1013, -96 }, { 1015, 1016, OddEven }, { 1017, 1017, -7 }, { 1018, 1019, EvenOdd }, { 1021, 1023, -130 }, { 1024, 1039, 80 }, { 1040, 1071, 32 }, { 1072, 1103, -32 }, { 1104, 1119, -80 }, { 1120, 1153, EvenOdd }, { 1162, 1215, EvenOdd }, { 1216, 1216, 15 }, { 1217, 1230, OddEven }, { 1231, 1231, -15 }, { 1232, 1319, EvenOdd }, { 1329, 1366, 48 }, { 1377, 1414, -48 }, { 4256, 4293, 7264 }, { 4295, 4295, 7264 }, { 4301, 4301, 7264 }, { 7545, 7545, 35332 }, { 7549, 7549, 3814 }, { 7680, 7776, EvenOdd }, { 7777, 7777, 58 }, { 7778, 7829, EvenOdd }, { 7835, 7835, -59 }, { 7838, 7838, -7615 }, { 7840, 7935, EvenOdd }, { 7936, 7943, 8 }, { 7944, 7951, -8 }, { 7952, 7957, 8 }, { 7960, 7965, -8 }, { 7968, 7975, 8 }, { 7976, 7983, -8 }, { 7984, 7991, 8 }, { 7992, 7999, -8 }, { 8000, 8005, 8 }, { 8008, 8013, -8 }, { 8017, 8017, 8 }, { 8019, 8019, 8 }, { 8021, 8021, 8 }, { 8023, 8023, 8 }, { 8025, 8025, -8 }, { 8027, 8027, -8 }, { 8029, 8029, -8 }, { 8031, 8031, -8 }, { 8032, 8039, 8 }, { 8040, 8047, -8 }, { 8048, 8049, 74 }, { 8050, 8053, 86 }, { 8054, 8055, 100 }, { 8056, 8057, 128 }, { 8058, 8059, 112 }, { 8060, 8061, 126 }, { 8064, 8071, 8 }, { 8072, 8079, -8 }, { 8080, 8087, 8 }, { 8088, 8095, -8 }, { 8096, 8103, 8 }, { 8104, 8111, -8 }, { 8112, 8113, 8 }, { 8115, 8115, 9 }, { 8120, 8121, -8 }, { 8122, 8123, -74 }, { 8124, 8124, -9 }, { 8126, 8126, -7289 }, { 8131, 8131, 9 }, { 8136, 8139, -86 }, { 8140, 8140, -9 }, { 8144, 8145, 8 }, { 8152, 8153, -8 }, { 8154, 8155, -100 }, { 8160, 8161, 8 }, { 8165, 8165, 7 }, { 8168, 8169, -8 }, { 8170, 8171, -112 }, { 8172, 8172, -7 }, { 8179, 8179, 9 }, { 8184, 8185, -128 }, { 8186, 8187, -126 }, { 8188, 8188, -9 }, { 8486, 8486, -7549 }, { 8490, 8490, -8415 }, { 8491, 8491, -8294 }, { 8498, 8498, 28 }, { 8526, 8526, -28 }, { 8544, 8559, 16 }, { 8560, 8575, -16 }, { 8579, 8580, OddEven }, { 9398, 9423, 26 }, { 9424, 9449, -26 }, { 11264, 11310, 48 }, { 11312, 11358, -48 }, { 11360, 11361, EvenOdd }, { 11362, 11362, -10743 }, { 11363, 11363, -3814 }, { 11364, 11364, -10727 }, { 11365, 11365, -10795 }, { 11366, 11366, -10792 }, { 11367, 11372, OddEven }, { 11373, 11373, -10780 }, { 11374, 11374, -10749 }, { 11375, 11375, -10783 }, { 11376, 11376, -10782 }, { 11378, 11379, EvenOdd }, { 11381, 11382, OddEven }, { 11390, 11391, -10815 }, { 11392, 11491, EvenOdd }, { 11499, 11502, OddEven }, { 11506, 11507, EvenOdd }, { 11520, 11557, -7264 }, { 11559, 11559, -7264 }, { 11565, 11565, -7264 }, { 42560, 42605, EvenOdd }, { 42624, 42647, EvenOdd }, { 42786, 42799, EvenOdd }, { 42802, 42863, EvenOdd }, { 42873, 42876, OddEven }, { 42877, 42877, -35332 }, { 42878, 42887, EvenOdd }, { 42891, 42892, OddEven }, { 42893, 42893, -42280 }, { 42896, 42899, EvenOdd }, { 42912, 42921, EvenOdd }, { 42922, 42922, -42308 }, { 65313, 65338, 32 }, { 65345, 65370, -32 }, { 66560, 66599, 40 }, { 66600, 66639, -40 }, }; const int num_unicode_casefold = 289; // 1034 groups, 1055 pairs, 167 ranges const CaseFold unicode_tolower[] = { { 65, 90, 32 }, { 181, 181, 775 }, { 192, 214, 32 }, { 216, 222, 32 }, { 256, 302, EvenOddSkip }, { 306, 310, EvenOddSkip }, { 313, 327, OddEvenSkip }, { 330, 374, EvenOddSkip }, { 376, 376, -121 }, { 377, 381, OddEvenSkip }, { 383, 383, -268 }, { 385, 385, 210 }, { 386, 388, EvenOddSkip }, { 390, 390, 206 }, { 391, 391, OddEven }, { 393, 394, 205 }, { 395, 395, OddEven }, { 398, 398, 79 }, { 399, 399, 202 }, { 400, 400, 203 }, { 401, 401, OddEven }, { 403, 403, 205 }, { 404, 404, 207 }, { 406, 406, 211 }, { 407, 407, 209 }, { 408, 408, EvenOdd }, { 412, 412, 211 }, { 413, 413, 213 }, { 415, 415, 214 }, { 416, 420, EvenOddSkip }, { 422, 422, 218 }, { 423, 423, OddEven }, { 425, 425, 218 }, { 428, 428, EvenOdd }, { 430, 430, 218 }, { 431, 431, OddEven }, { 433, 434, 217 }, { 435, 437, OddEvenSkip }, { 439, 439, 219 }, { 440, 440, EvenOdd }, { 444, 444, EvenOdd }, { 452, 452, 2 }, { 453, 453, OddEven }, { 455, 455, 2 }, { 456, 456, EvenOdd }, { 458, 458, 2 }, { 459, 475, OddEvenSkip }, { 478, 494, EvenOddSkip }, { 497, 497, 2 }, { 498, 500, EvenOddSkip }, { 502, 502, -97 }, { 503, 503, -56 }, { 504, 542, EvenOddSkip }, { 544, 544, -130 }, { 546, 562, EvenOddSkip }, { 570, 570, 10795 }, { 571, 571, OddEven }, { 573, 573, -163 }, { 574, 574, 10792 }, { 577, 577, OddEven }, { 579, 579, -195 }, { 580, 580, 69 }, { 581, 581, 71 }, { 582, 590, EvenOddSkip }, { 837, 837, 116 }, { 880, 882, EvenOddSkip }, { 886, 886, EvenOdd }, { 902, 902, 38 }, { 904, 906, 37 }, { 908, 908, 64 }, { 910, 911, 63 }, { 913, 929, 32 }, { 931, 939, 32 }, { 962, 962, EvenOdd }, { 975, 975, 8 }, { 976, 976, -30 }, { 977, 977, -25 }, { 981, 981, -15 }, { 982, 982, -22 }, { 984, 1006, EvenOddSkip }, { 1008, 1008, -54 }, { 1009, 1009, -48 }, { 1012, 1012, -60 }, { 1013, 1013, -64 }, { 1015, 1015, OddEven }, { 1017, 1017, -7 }, { 1018, 1018, EvenOdd }, { 1021, 1023, -130 }, { 1024, 1039, 80 }, { 1040, 1071, 32 }, { 1120, 1152, EvenOddSkip }, { 1162, 1214, EvenOddSkip }, { 1216, 1216, 15 }, { 1217, 1229, OddEvenSkip }, { 1232, 1318, EvenOddSkip }, { 1329, 1366, 48 }, { 4256, 4293, 7264 }, { 4295, 4295, 7264 }, { 4301, 4301, 7264 }, { 7680, 7828, EvenOddSkip }, { 7835, 7835, -58 }, { 7838, 7838, -7615 }, { 7840, 7934, EvenOddSkip }, { 7944, 7951, -8 }, { 7960, 7965, -8 }, { 7976, 7983, -8 }, { 7992, 7999, -8 }, { 8008, 8013, -8 }, { 8025, 8025, -8 }, { 8027, 8027, -8 }, { 8029, 8029, -8 }, { 8031, 8031, -8 }, { 8040, 8047, -8 }, { 8072, 8079, -8 }, { 8088, 8095, -8 }, { 8104, 8111, -8 }, { 8120, 8121, -8 }, { 8122, 8123, -74 }, { 8124, 8124, -9 }, { 8126, 8126, -7173 }, { 8136, 8139, -86 }, { 8140, 8140, -9 }, { 8152, 8153, -8 }, { 8154, 8155, -100 }, { 8168, 8169, -8 }, { 8170, 8171, -112 }, { 8172, 8172, -7 }, { 8184, 8185, -128 }, { 8186, 8187, -126 }, { 8188, 8188, -9 }, { 8486, 8486, -7517 }, { 8490, 8490, -8383 }, { 8491, 8491, -8262 }, { 8498, 8498, 28 }, { 8544, 8559, 16 }, { 8579, 8579, OddEven }, { 9398, 9423, 26 }, { 11264, 11310, 48 }, { 11360, 11360, EvenOdd }, { 11362, 11362, -10743 }, { 11363, 11363, -3814 }, { 11364, 11364, -10727 }, { 11367, 11371, OddEvenSkip }, { 11373, 11373, -10780 }, { 11374, 11374, -10749 }, { 11375, 11375, -10783 }, { 11376, 11376, -10782 }, { 11378, 11378, EvenOdd }, { 11381, 11381, OddEven }, { 11390, 11391, -10815 }, { 11392, 11490, EvenOddSkip }, { 11499, 11501, OddEvenSkip }, { 11506, 11506, EvenOdd }, { 42560, 42604, EvenOddSkip }, { 42624, 42646, EvenOddSkip }, { 42786, 42798, EvenOddSkip }, { 42802, 42862, EvenOddSkip }, { 42873, 42875, OddEvenSkip }, { 42877, 42877, -35332 }, { 42878, 42886, EvenOddSkip }, { 42891, 42891, OddEven }, { 42893, 42893, -42280 }, { 42896, 42898, EvenOddSkip }, { 42912, 42920, EvenOddSkip }, { 42922, 42922, -42308 }, { 65313, 65338, 32 }, { 66560, 66599, 40 }, }; const int num_unicode_tolower = 167; } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/unicode_casefold.h000066400000000000000000000047261266464252400246560ustar00rootroot00000000000000// Copyright 2008 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Unicode case folding tables. // The Unicode case folding tables encode the mapping from one Unicode point // to the next largest Unicode point with equivalent folding. The largest // point wraps back to the first. For example, the tables map: // // 'A' -> 'a' // 'a' -> 'A' // // 'K' -> 'k' // 'k' -> 'K' (Kelvin symbol) // 'K' -> 'K' // // Like everything Unicode, these tables are big. If we represent the table // as a sorted list of uint32 pairs, it has 2049 entries and is 16 kB. // Most table entries look like the ones around them: // 'A' maps to 'A'+32, 'B' maps to 'B'+32, etc. // Instead of listing all the pairs explicitly, we make a list of ranges // and deltas, so that the table entries for 'A' through 'Z' can be represented // as a single entry { 'A', 'Z', +32 }. // // In addition to blocks that map to each other (A-Z mapping to a-z) // there are blocks of pairs that individually map to each other // (for example, 0100<->0101, 0102<->0103, 0104<->0105, ...). // For those, the special delta value EvenOdd marks even/odd pairs // (if even, add 1; if odd, subtract 1), and OddEven marks odd/even pairs. // // In this form, the table has 274 entries, about 3kB. If we were to split // the table into one for 16-bit codes and an overflow table for larger ones, // we could get it down to about 1.5kB, but that's not worth the complexity. // // The grouped form also allows for efficient fold range calculations // rather than looping one character at a time. #ifndef RE2_UNICODE_CASEFOLD_H__ #define RE2_UNICODE_CASEFOLD_H__ #include "util/util.h" namespace re2 { enum { EvenOdd = 1, OddEven = -1, EvenOddSkip = 1<<30, OddEvenSkip, }; struct CaseFold { Rune lo; Rune hi; int32 delta; }; extern const CaseFold unicode_casefold[]; extern const int num_unicode_casefold; extern const CaseFold unicode_tolower[]; extern const int num_unicode_tolower; // Returns the CaseFold* in the tables that contains rune. // If rune is not in the tables, returns the first CaseFold* after rune. // If rune is larger than any value in the tables, returns NULL. extern const CaseFold* LookupCaseFold(const CaseFold*, int, Rune rune); // Returns the result of applying the fold f to the rune r. extern Rune ApplyFold(const CaseFold *f, Rune r); } // namespace re2 #endif // RE2_UNICODE_CASEFOLD_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/unicode_groups.cc000066400000000000000000002734171266464252400245600ustar00rootroot00000000000000 // GENERATED BY make_unicode_groups.py; DO NOT EDIT. // make_unicode_groups.py >unicode_groups.cc #include "re2/unicode_groups.h" namespace re2 { static const URange16 Ps_range16[] = { { 40, 40 }, { 91, 91 }, { 123, 123 }, { 3898, 3898 }, { 3900, 3900 }, { 5787, 5787 }, { 8218, 8218 }, { 8222, 8222 }, { 8261, 8261 }, { 8317, 8317 }, { 8333, 8333 }, { 8968, 8968 }, { 8970, 8970 }, { 9001, 9001 }, { 10088, 10088 }, { 10090, 10090 }, { 10092, 10092 }, { 10094, 10094 }, { 10096, 10096 }, { 10098, 10098 }, { 10100, 10100 }, { 10181, 10181 }, { 10214, 10214 }, { 10216, 10216 }, { 10218, 10218 }, { 10220, 10220 }, { 10222, 10222 }, { 10627, 10627 }, { 10629, 10629 }, { 10631, 10631 }, { 10633, 10633 }, { 10635, 10635 }, { 10637, 10637 }, { 10639, 10639 }, { 10641, 10641 }, { 10643, 10643 }, { 10645, 10645 }, { 10647, 10647 }, { 10712, 10712 }, { 10714, 10714 }, { 10748, 10748 }, { 11810, 11810 }, { 11812, 11812 }, { 11814, 11814 }, { 11816, 11816 }, { 12296, 12296 }, { 12298, 12298 }, { 12300, 12300 }, { 12302, 12302 }, { 12304, 12304 }, { 12308, 12308 }, { 12310, 12310 }, { 12312, 12312 }, { 12314, 12314 }, { 12317, 12317 }, { 64830, 64830 }, { 65047, 65047 }, { 65077, 65077 }, { 65079, 65079 }, { 65081, 65081 }, { 65083, 65083 }, { 65085, 65085 }, { 65087, 65087 }, { 65089, 65089 }, { 65091, 65091 }, { 65095, 65095 }, { 65113, 65113 }, { 65115, 65115 }, { 65117, 65117 }, { 65288, 65288 }, { 65339, 65339 }, { 65371, 65371 }, { 65375, 65375 }, { 65378, 65378 }, }; static const URange16 Nl_range16[] = { { 5870, 5872 }, { 8544, 8578 }, { 8581, 8584 }, { 12295, 12295 }, { 12321, 12329 }, { 12344, 12346 }, { 42726, 42735 }, }; static const URange32 Nl_range32[] = { { 65856, 65908 }, { 66369, 66369 }, { 66378, 66378 }, { 66513, 66517 }, { 74752, 74850 }, }; static const URange16 No_range16[] = { { 178, 179 }, { 185, 185 }, { 188, 190 }, { 2548, 2553 }, { 2930, 2935 }, { 3056, 3058 }, { 3192, 3198 }, { 3440, 3445 }, { 3882, 3891 }, { 4969, 4988 }, { 6128, 6137 }, { 6618, 6618 }, { 8304, 8304 }, { 8308, 8313 }, { 8320, 8329 }, { 8528, 8543 }, { 8585, 8585 }, { 9312, 9371 }, { 9450, 9471 }, { 10102, 10131 }, { 11517, 11517 }, { 12690, 12693 }, { 12832, 12841 }, { 12872, 12879 }, { 12881, 12895 }, { 12928, 12937 }, { 12977, 12991 }, { 43056, 43061 }, }; static const URange32 No_range32[] = { { 65799, 65843 }, { 65909, 65912 }, { 65930, 65930 }, { 66336, 66339 }, { 67672, 67679 }, { 67862, 67867 }, { 68160, 68167 }, { 68221, 68222 }, { 68440, 68447 }, { 68472, 68479 }, { 69216, 69246 }, { 69714, 69733 }, { 119648, 119665 }, { 127232, 127242 }, }; static const URange16 Lo_range16[] = { { 170, 170 }, { 186, 186 }, { 443, 443 }, { 448, 451 }, { 660, 660 }, { 1488, 1514 }, { 1520, 1522 }, { 1568, 1599 }, { 1601, 1610 }, { 1646, 1647 }, { 1649, 1747 }, { 1749, 1749 }, { 1774, 1775 }, { 1786, 1788 }, { 1791, 1791 }, { 1808, 1808 }, { 1810, 1839 }, { 1869, 1957 }, { 1969, 1969 }, { 1994, 2026 }, { 2048, 2069 }, { 2112, 2136 }, { 2208, 2208 }, { 2210, 2220 }, { 2308, 2361 }, { 2365, 2365 }, { 2384, 2384 }, { 2392, 2401 }, { 2418, 2423 }, { 2425, 2431 }, { 2437, 2444 }, { 2447, 2448 }, { 2451, 2472 }, { 2474, 2480 }, { 2482, 2482 }, { 2486, 2489 }, { 2493, 2493 }, { 2510, 2510 }, { 2524, 2525 }, { 2527, 2529 }, { 2544, 2545 }, { 2565, 2570 }, { 2575, 2576 }, { 2579, 2600 }, { 2602, 2608 }, { 2610, 2611 }, { 2613, 2614 }, { 2616, 2617 }, { 2649, 2652 }, { 2654, 2654 }, { 2674, 2676 }, { 2693, 2701 }, { 2703, 2705 }, { 2707, 2728 }, { 2730, 2736 }, { 2738, 2739 }, { 2741, 2745 }, { 2749, 2749 }, { 2768, 2768 }, { 2784, 2785 }, { 2821, 2828 }, { 2831, 2832 }, { 2835, 2856 }, { 2858, 2864 }, { 2866, 2867 }, { 2869, 2873 }, { 2877, 2877 }, { 2908, 2909 }, { 2911, 2913 }, { 2929, 2929 }, { 2947, 2947 }, { 2949, 2954 }, { 2958, 2960 }, { 2962, 2965 }, { 2969, 2970 }, { 2972, 2972 }, { 2974, 2975 }, { 2979, 2980 }, { 2984, 2986 }, { 2990, 3001 }, { 3024, 3024 }, { 3077, 3084 }, { 3086, 3088 }, { 3090, 3112 }, { 3114, 3123 }, { 3125, 3129 }, { 3133, 3133 }, { 3160, 3161 }, { 3168, 3169 }, { 3205, 3212 }, { 3214, 3216 }, { 3218, 3240 }, { 3242, 3251 }, { 3253, 3257 }, { 3261, 3261 }, { 3294, 3294 }, { 3296, 3297 }, { 3313, 3314 }, { 3333, 3340 }, { 3342, 3344 }, { 3346, 3386 }, { 3389, 3389 }, { 3406, 3406 }, { 3424, 3425 }, { 3450, 3455 }, { 3461, 3478 }, { 3482, 3505 }, { 3507, 3515 }, { 3517, 3517 }, { 3520, 3526 }, { 3585, 3632 }, { 3634, 3635 }, { 3648, 3653 }, { 3713, 3714 }, { 3716, 3716 }, { 3719, 3720 }, { 3722, 3722 }, { 3725, 3725 }, { 3732, 3735 }, { 3737, 3743 }, { 3745, 3747 }, { 3749, 3749 }, { 3751, 3751 }, { 3754, 3755 }, { 3757, 3760 }, { 3762, 3763 }, { 3773, 3773 }, { 3776, 3780 }, { 3804, 3807 }, { 3840, 3840 }, { 3904, 3911 }, { 3913, 3948 }, { 3976, 3980 }, { 4096, 4138 }, { 4159, 4159 }, { 4176, 4181 }, { 4186, 4189 }, { 4193, 4193 }, { 4197, 4198 }, { 4206, 4208 }, { 4213, 4225 }, { 4238, 4238 }, { 4304, 4346 }, { 4349, 4680 }, { 4682, 4685 }, { 4688, 4694 }, { 4696, 4696 }, { 4698, 4701 }, { 4704, 4744 }, { 4746, 4749 }, { 4752, 4784 }, { 4786, 4789 }, { 4792, 4798 }, { 4800, 4800 }, { 4802, 4805 }, { 4808, 4822 }, { 4824, 4880 }, { 4882, 4885 }, { 4888, 4954 }, { 4992, 5007 }, { 5024, 5108 }, { 5121, 5740 }, { 5743, 5759 }, { 5761, 5786 }, { 5792, 5866 }, { 5888, 5900 }, { 5902, 5905 }, { 5920, 5937 }, { 5952, 5969 }, { 5984, 5996 }, { 5998, 6000 }, { 6016, 6067 }, { 6108, 6108 }, { 6176, 6210 }, { 6212, 6263 }, { 6272, 6312 }, { 6314, 6314 }, { 6320, 6389 }, { 6400, 6428 }, { 6480, 6509 }, { 6512, 6516 }, { 6528, 6571 }, { 6593, 6599 }, { 6656, 6678 }, { 6688, 6740 }, { 6917, 6963 }, { 6981, 6987 }, { 7043, 7072 }, { 7086, 7087 }, { 7098, 7141 }, { 7168, 7203 }, { 7245, 7247 }, { 7258, 7287 }, { 7401, 7404 }, { 7406, 7409 }, { 7413, 7414 }, { 8501, 8504 }, { 11568, 11623 }, { 11648, 11670 }, { 11680, 11686 }, { 11688, 11694 }, { 11696, 11702 }, { 11704, 11710 }, { 11712, 11718 }, { 11720, 11726 }, { 11728, 11734 }, { 11736, 11742 }, { 12294, 12294 }, { 12348, 12348 }, { 12353, 12438 }, { 12447, 12447 }, { 12449, 12538 }, { 12543, 12543 }, { 12549, 12589 }, { 12593, 12686 }, { 12704, 12730 }, { 12784, 12799 }, { 13312, 19893 }, { 19968, 40908 }, { 40960, 40980 }, { 40982, 42124 }, { 42192, 42231 }, { 42240, 42507 }, { 42512, 42527 }, { 42538, 42539 }, { 42606, 42606 }, { 42656, 42725 }, { 43003, 43009 }, { 43011, 43013 }, { 43015, 43018 }, { 43020, 43042 }, { 43072, 43123 }, { 43138, 43187 }, { 43250, 43255 }, { 43259, 43259 }, { 43274, 43301 }, { 43312, 43334 }, { 43360, 43388 }, { 43396, 43442 }, { 43520, 43560 }, { 43584, 43586 }, { 43588, 43595 }, { 43616, 43631 }, { 43633, 43638 }, { 43642, 43642 }, { 43648, 43695 }, { 43697, 43697 }, { 43701, 43702 }, { 43705, 43709 }, { 43712, 43712 }, { 43714, 43714 }, { 43739, 43740 }, { 43744, 43754 }, { 43762, 43762 }, { 43777, 43782 }, { 43785, 43790 }, { 43793, 43798 }, { 43808, 43814 }, { 43816, 43822 }, { 43968, 44002 }, { 44032, 55203 }, { 55216, 55238 }, { 55243, 55291 }, { 63744, 64109 }, { 64112, 64217 }, { 64285, 64285 }, { 64287, 64296 }, { 64298, 64310 }, { 64312, 64316 }, { 64318, 64318 }, { 64320, 64321 }, { 64323, 64324 }, { 64326, 64433 }, { 64467, 64829 }, { 64848, 64911 }, { 64914, 64967 }, { 65008, 65019 }, { 65136, 65140 }, { 65142, 65276 }, { 65382, 65391 }, { 65393, 65437 }, { 65440, 65470 }, { 65474, 65479 }, { 65482, 65487 }, { 65490, 65495 }, { 65498, 65500 }, }; static const URange32 Lo_range32[] = { { 65536, 65547 }, { 65549, 65574 }, { 65576, 65594 }, { 65596, 65597 }, { 65599, 65613 }, { 65616, 65629 }, { 65664, 65786 }, { 66176, 66204 }, { 66208, 66256 }, { 66304, 66334 }, { 66352, 66368 }, { 66370, 66377 }, { 66432, 66461 }, { 66464, 66499 }, { 66504, 66511 }, { 66640, 66717 }, { 67584, 67589 }, { 67592, 67592 }, { 67594, 67637 }, { 67639, 67640 }, { 67644, 67644 }, { 67647, 67669 }, { 67840, 67861 }, { 67872, 67897 }, { 67968, 68023 }, { 68030, 68031 }, { 68096, 68096 }, { 68112, 68115 }, { 68117, 68119 }, { 68121, 68147 }, { 68192, 68220 }, { 68352, 68405 }, { 68416, 68437 }, { 68448, 68466 }, { 68608, 68680 }, { 69635, 69687 }, { 69763, 69807 }, { 69840, 69864 }, { 69891, 69926 }, { 70019, 70066 }, { 70081, 70084 }, { 71296, 71338 }, { 73728, 74606 }, { 77824, 78894 }, { 92160, 92728 }, { 93952, 94020 }, { 94032, 94032 }, { 110592, 110593 }, { 126464, 126467 }, { 126469, 126495 }, { 126497, 126498 }, { 126500, 126500 }, { 126503, 126503 }, { 126505, 126514 }, { 126516, 126519 }, { 126521, 126521 }, { 126523, 126523 }, { 126530, 126530 }, { 126535, 126535 }, { 126537, 126537 }, { 126539, 126539 }, { 126541, 126543 }, { 126545, 126546 }, { 126548, 126548 }, { 126551, 126551 }, { 126553, 126553 }, { 126555, 126555 }, { 126557, 126557 }, { 126559, 126559 }, { 126561, 126562 }, { 126564, 126564 }, { 126567, 126570 }, { 126572, 126578 }, { 126580, 126583 }, { 126585, 126588 }, { 126590, 126590 }, { 126592, 126601 }, { 126603, 126619 }, { 126625, 126627 }, { 126629, 126633 }, { 126635, 126651 }, { 131072, 173782 }, { 173824, 177972 }, { 177984, 178205 }, { 194560, 195101 }, }; static const URange16 Ll_range16[] = { { 97, 122 }, { 181, 181 }, { 223, 246 }, { 248, 255 }, { 257, 257 }, { 259, 259 }, { 261, 261 }, { 263, 263 }, { 265, 265 }, { 267, 267 }, { 269, 269 }, { 271, 271 }, { 273, 273 }, { 275, 275 }, { 277, 277 }, { 279, 279 }, { 281, 281 }, { 283, 283 }, { 285, 285 }, { 287, 287 }, { 289, 289 }, { 291, 291 }, { 293, 293 }, { 295, 295 }, { 297, 297 }, { 299, 299 }, { 301, 301 }, { 303, 303 }, { 305, 305 }, { 307, 307 }, { 309, 309 }, { 311, 312 }, { 314, 314 }, { 316, 316 }, { 318, 318 }, { 320, 320 }, { 322, 322 }, { 324, 324 }, { 326, 326 }, { 328, 329 }, { 331, 331 }, { 333, 333 }, { 335, 335 }, { 337, 337 }, { 339, 339 }, { 341, 341 }, { 343, 343 }, { 345, 345 }, { 347, 347 }, { 349, 349 }, { 351, 351 }, { 353, 353 }, { 355, 355 }, { 357, 357 }, { 359, 359 }, { 361, 361 }, { 363, 363 }, { 365, 365 }, { 367, 367 }, { 369, 369 }, { 371, 371 }, { 373, 373 }, { 375, 375 }, { 378, 378 }, { 380, 380 }, { 382, 384 }, { 387, 387 }, { 389, 389 }, { 392, 392 }, { 396, 397 }, { 402, 402 }, { 405, 405 }, { 409, 411 }, { 414, 414 }, { 417, 417 }, { 419, 419 }, { 421, 421 }, { 424, 424 }, { 426, 427 }, { 429, 429 }, { 432, 432 }, { 436, 436 }, { 438, 438 }, { 441, 442 }, { 445, 447 }, { 454, 454 }, { 457, 457 }, { 460, 460 }, { 462, 462 }, { 464, 464 }, { 466, 466 }, { 468, 468 }, { 470, 470 }, { 472, 472 }, { 474, 474 }, { 476, 477 }, { 479, 479 }, { 481, 481 }, { 483, 483 }, { 485, 485 }, { 487, 487 }, { 489, 489 }, { 491, 491 }, { 493, 493 }, { 495, 496 }, { 499, 499 }, { 501, 501 }, { 505, 505 }, { 507, 507 }, { 509, 509 }, { 511, 511 }, { 513, 513 }, { 515, 515 }, { 517, 517 }, { 519, 519 }, { 521, 521 }, { 523, 523 }, { 525, 525 }, { 527, 527 }, { 529, 529 }, { 531, 531 }, { 533, 533 }, { 535, 535 }, { 537, 537 }, { 539, 539 }, { 541, 541 }, { 543, 543 }, { 545, 545 }, { 547, 547 }, { 549, 549 }, { 551, 551 }, { 553, 553 }, { 555, 555 }, { 557, 557 }, { 559, 559 }, { 561, 561 }, { 563, 569 }, { 572, 572 }, { 575, 576 }, { 578, 578 }, { 583, 583 }, { 585, 585 }, { 587, 587 }, { 589, 589 }, { 591, 659 }, { 661, 687 }, { 881, 881 }, { 883, 883 }, { 887, 887 }, { 891, 893 }, { 912, 912 }, { 940, 974 }, { 976, 977 }, { 981, 983 }, { 985, 985 }, { 987, 987 }, { 989, 989 }, { 991, 991 }, { 993, 993 }, { 995, 995 }, { 997, 997 }, { 999, 999 }, { 1001, 1001 }, { 1003, 1003 }, { 1005, 1005 }, { 1007, 1011 }, { 1013, 1013 }, { 1016, 1016 }, { 1019, 1020 }, { 1072, 1119 }, { 1121, 1121 }, { 1123, 1123 }, { 1125, 1125 }, { 1127, 1127 }, { 1129, 1129 }, { 1131, 1131 }, { 1133, 1133 }, { 1135, 1135 }, { 1137, 1137 }, { 1139, 1139 }, { 1141, 1141 }, { 1143, 1143 }, { 1145, 1145 }, { 1147, 1147 }, { 1149, 1149 }, { 1151, 1151 }, { 1153, 1153 }, { 1163, 1163 }, { 1165, 1165 }, { 1167, 1167 }, { 1169, 1169 }, { 1171, 1171 }, { 1173, 1173 }, { 1175, 1175 }, { 1177, 1177 }, { 1179, 1179 }, { 1181, 1181 }, { 1183, 1183 }, { 1185, 1185 }, { 1187, 1187 }, { 1189, 1189 }, { 1191, 1191 }, { 1193, 1193 }, { 1195, 1195 }, { 1197, 1197 }, { 1199, 1199 }, { 1201, 1201 }, { 1203, 1203 }, { 1205, 1205 }, { 1207, 1207 }, { 1209, 1209 }, { 1211, 1211 }, { 1213, 1213 }, { 1215, 1215 }, { 1218, 1218 }, { 1220, 1220 }, { 1222, 1222 }, { 1224, 1224 }, { 1226, 1226 }, { 1228, 1228 }, { 1230, 1231 }, { 1233, 1233 }, { 1235, 1235 }, { 1237, 1237 }, { 1239, 1239 }, { 1241, 1241 }, { 1243, 1243 }, { 1245, 1245 }, { 1247, 1247 }, { 1249, 1249 }, { 1251, 1251 }, { 1253, 1253 }, { 1255, 1255 }, { 1257, 1257 }, { 1259, 1259 }, { 1261, 1261 }, { 1263, 1263 }, { 1265, 1265 }, { 1267, 1267 }, { 1269, 1269 }, { 1271, 1271 }, { 1273, 1273 }, { 1275, 1275 }, { 1277, 1277 }, { 1279, 1279 }, { 1281, 1281 }, { 1283, 1283 }, { 1285, 1285 }, { 1287, 1287 }, { 1289, 1289 }, { 1291, 1291 }, { 1293, 1293 }, { 1295, 1295 }, { 1297, 1297 }, { 1299, 1299 }, { 1301, 1301 }, { 1303, 1303 }, { 1305, 1305 }, { 1307, 1307 }, { 1309, 1309 }, { 1311, 1311 }, { 1313, 1313 }, { 1315, 1315 }, { 1317, 1317 }, { 1319, 1319 }, { 1377, 1415 }, { 7424, 7467 }, { 7531, 7543 }, { 7545, 7578 }, { 7681, 7681 }, { 7683, 7683 }, { 7685, 7685 }, { 7687, 7687 }, { 7689, 7689 }, { 7691, 7691 }, { 7693, 7693 }, { 7695, 7695 }, { 7697, 7697 }, { 7699, 7699 }, { 7701, 7701 }, { 7703, 7703 }, { 7705, 7705 }, { 7707, 7707 }, { 7709, 7709 }, { 7711, 7711 }, { 7713, 7713 }, { 7715, 7715 }, { 7717, 7717 }, { 7719, 7719 }, { 7721, 7721 }, { 7723, 7723 }, { 7725, 7725 }, { 7727, 7727 }, { 7729, 7729 }, { 7731, 7731 }, { 7733, 7733 }, { 7735, 7735 }, { 7737, 7737 }, { 7739, 7739 }, { 7741, 7741 }, { 7743, 7743 }, { 7745, 7745 }, { 7747, 7747 }, { 7749, 7749 }, { 7751, 7751 }, { 7753, 7753 }, { 7755, 7755 }, { 7757, 7757 }, { 7759, 7759 }, { 7761, 7761 }, { 7763, 7763 }, { 7765, 7765 }, { 7767, 7767 }, { 7769, 7769 }, { 7771, 7771 }, { 7773, 7773 }, { 7775, 7775 }, { 7777, 7777 }, { 7779, 7779 }, { 7781, 7781 }, { 7783, 7783 }, { 7785, 7785 }, { 7787, 7787 }, { 7789, 7789 }, { 7791, 7791 }, { 7793, 7793 }, { 7795, 7795 }, { 7797, 7797 }, { 7799, 7799 }, { 7801, 7801 }, { 7803, 7803 }, { 7805, 7805 }, { 7807, 7807 }, { 7809, 7809 }, { 7811, 7811 }, { 7813, 7813 }, { 7815, 7815 }, { 7817, 7817 }, { 7819, 7819 }, { 7821, 7821 }, { 7823, 7823 }, { 7825, 7825 }, { 7827, 7827 }, { 7829, 7837 }, { 7839, 7839 }, { 7841, 7841 }, { 7843, 7843 }, { 7845, 7845 }, { 7847, 7847 }, { 7849, 7849 }, { 7851, 7851 }, { 7853, 7853 }, { 7855, 7855 }, { 7857, 7857 }, { 7859, 7859 }, { 7861, 7861 }, { 7863, 7863 }, { 7865, 7865 }, { 7867, 7867 }, { 7869, 7869 }, { 7871, 7871 }, { 7873, 7873 }, { 7875, 7875 }, { 7877, 7877 }, { 7879, 7879 }, { 7881, 7881 }, { 7883, 7883 }, { 7885, 7885 }, { 7887, 7887 }, { 7889, 7889 }, { 7891, 7891 }, { 7893, 7893 }, { 7895, 7895 }, { 7897, 7897 }, { 7899, 7899 }, { 7901, 7901 }, { 7903, 7903 }, { 7905, 7905 }, { 7907, 7907 }, { 7909, 7909 }, { 7911, 7911 }, { 7913, 7913 }, { 7915, 7915 }, { 7917, 7917 }, { 7919, 7919 }, { 7921, 7921 }, { 7923, 7923 }, { 7925, 7925 }, { 7927, 7927 }, { 7929, 7929 }, { 7931, 7931 }, { 7933, 7933 }, { 7935, 7943 }, { 7952, 7957 }, { 7968, 7975 }, { 7984, 7991 }, { 8000, 8005 }, { 8016, 8023 }, { 8032, 8039 }, { 8048, 8061 }, { 8064, 8071 }, { 8080, 8087 }, { 8096, 8103 }, { 8112, 8116 }, { 8118, 8119 }, { 8126, 8126 }, { 8130, 8132 }, { 8134, 8135 }, { 8144, 8147 }, { 8150, 8151 }, { 8160, 8167 }, { 8178, 8180 }, { 8182, 8183 }, { 8458, 8458 }, { 8462, 8463 }, { 8467, 8467 }, { 8495, 8495 }, { 8500, 8500 }, { 8505, 8505 }, { 8508, 8509 }, { 8518, 8521 }, { 8526, 8526 }, { 8580, 8580 }, { 11312, 11358 }, { 11361, 11361 }, { 11365, 11366 }, { 11368, 11368 }, { 11370, 11370 }, { 11372, 11372 }, { 11377, 11377 }, { 11379, 11380 }, { 11382, 11387 }, { 11393, 11393 }, { 11395, 11395 }, { 11397, 11397 }, { 11399, 11399 }, { 11401, 11401 }, { 11403, 11403 }, { 11405, 11405 }, { 11407, 11407 }, { 11409, 11409 }, { 11411, 11411 }, { 11413, 11413 }, { 11415, 11415 }, { 11417, 11417 }, { 11419, 11419 }, { 11421, 11421 }, { 11423, 11423 }, { 11425, 11425 }, { 11427, 11427 }, { 11429, 11429 }, { 11431, 11431 }, { 11433, 11433 }, { 11435, 11435 }, { 11437, 11437 }, { 11439, 11439 }, { 11441, 11441 }, { 11443, 11443 }, { 11445, 11445 }, { 11447, 11447 }, { 11449, 11449 }, { 11451, 11451 }, { 11453, 11453 }, { 11455, 11455 }, { 11457, 11457 }, { 11459, 11459 }, { 11461, 11461 }, { 11463, 11463 }, { 11465, 11465 }, { 11467, 11467 }, { 11469, 11469 }, { 11471, 11471 }, { 11473, 11473 }, { 11475, 11475 }, { 11477, 11477 }, { 11479, 11479 }, { 11481, 11481 }, { 11483, 11483 }, { 11485, 11485 }, { 11487, 11487 }, { 11489, 11489 }, { 11491, 11492 }, { 11500, 11500 }, { 11502, 11502 }, { 11507, 11507 }, { 11520, 11557 }, { 11559, 11559 }, { 11565, 11565 }, { 42561, 42561 }, { 42563, 42563 }, { 42565, 42565 }, { 42567, 42567 }, { 42569, 42569 }, { 42571, 42571 }, { 42573, 42573 }, { 42575, 42575 }, { 42577, 42577 }, { 42579, 42579 }, { 42581, 42581 }, { 42583, 42583 }, { 42585, 42585 }, { 42587, 42587 }, { 42589, 42589 }, { 42591, 42591 }, { 42593, 42593 }, { 42595, 42595 }, { 42597, 42597 }, { 42599, 42599 }, { 42601, 42601 }, { 42603, 42603 }, { 42605, 42605 }, { 42625, 42625 }, { 42627, 42627 }, { 42629, 42629 }, { 42631, 42631 }, { 42633, 42633 }, { 42635, 42635 }, { 42637, 42637 }, { 42639, 42639 }, { 42641, 42641 }, { 42643, 42643 }, { 42645, 42645 }, { 42647, 42647 }, { 42787, 42787 }, { 42789, 42789 }, { 42791, 42791 }, { 42793, 42793 }, { 42795, 42795 }, { 42797, 42797 }, { 42799, 42801 }, { 42803, 42803 }, { 42805, 42805 }, { 42807, 42807 }, { 42809, 42809 }, { 42811, 42811 }, { 42813, 42813 }, { 42815, 42815 }, { 42817, 42817 }, { 42819, 42819 }, { 42821, 42821 }, { 42823, 42823 }, { 42825, 42825 }, { 42827, 42827 }, { 42829, 42829 }, { 42831, 42831 }, { 42833, 42833 }, { 42835, 42835 }, { 42837, 42837 }, { 42839, 42839 }, { 42841, 42841 }, { 42843, 42843 }, { 42845, 42845 }, { 42847, 42847 }, { 42849, 42849 }, { 42851, 42851 }, { 42853, 42853 }, { 42855, 42855 }, { 42857, 42857 }, { 42859, 42859 }, { 42861, 42861 }, { 42863, 42863 }, { 42865, 42872 }, { 42874, 42874 }, { 42876, 42876 }, { 42879, 42879 }, { 42881, 42881 }, { 42883, 42883 }, { 42885, 42885 }, { 42887, 42887 }, { 42892, 42892 }, { 42894, 42894 }, { 42897, 42897 }, { 42899, 42899 }, { 42913, 42913 }, { 42915, 42915 }, { 42917, 42917 }, { 42919, 42919 }, { 42921, 42921 }, { 43002, 43002 }, { 64256, 64262 }, { 64275, 64279 }, { 65345, 65370 }, }; static const URange32 Ll_range32[] = { { 66600, 66639 }, { 119834, 119859 }, { 119886, 119892 }, { 119894, 119911 }, { 119938, 119963 }, { 119990, 119993 }, { 119995, 119995 }, { 119997, 120003 }, { 120005, 120015 }, { 120042, 120067 }, { 120094, 120119 }, { 120146, 120171 }, { 120198, 120223 }, { 120250, 120275 }, { 120302, 120327 }, { 120354, 120379 }, { 120406, 120431 }, { 120458, 120485 }, { 120514, 120538 }, { 120540, 120545 }, { 120572, 120596 }, { 120598, 120603 }, { 120630, 120654 }, { 120656, 120661 }, { 120688, 120712 }, { 120714, 120719 }, { 120746, 120770 }, { 120772, 120777 }, { 120779, 120779 }, }; static const URange16 Lm_range16[] = { { 688, 705 }, { 710, 721 }, { 736, 740 }, { 748, 748 }, { 750, 750 }, { 884, 884 }, { 890, 890 }, { 1369, 1369 }, { 1600, 1600 }, { 1765, 1766 }, { 2036, 2037 }, { 2042, 2042 }, { 2074, 2074 }, { 2084, 2084 }, { 2088, 2088 }, { 2417, 2417 }, { 3654, 3654 }, { 3782, 3782 }, { 4348, 4348 }, { 6103, 6103 }, { 6211, 6211 }, { 6823, 6823 }, { 7288, 7293 }, { 7468, 7530 }, { 7544, 7544 }, { 7579, 7615 }, { 8305, 8305 }, { 8319, 8319 }, { 8336, 8348 }, { 11388, 11389 }, { 11631, 11631 }, { 11823, 11823 }, { 12293, 12293 }, { 12337, 12341 }, { 12347, 12347 }, { 12445, 12446 }, { 12540, 12542 }, { 40981, 40981 }, { 42232, 42237 }, { 42508, 42508 }, { 42623, 42623 }, { 42775, 42783 }, { 42864, 42864 }, { 42888, 42888 }, { 43000, 43001 }, { 43471, 43471 }, { 43632, 43632 }, { 43741, 43741 }, { 43763, 43764 }, { 65392, 65392 }, { 65438, 65439 }, }; static const URange32 Lm_range32[] = { { 94099, 94111 }, }; static const URange16 Nd_range16[] = { { 48, 57 }, { 1632, 1641 }, { 1776, 1785 }, { 1984, 1993 }, { 2406, 2415 }, { 2534, 2543 }, { 2662, 2671 }, { 2790, 2799 }, { 2918, 2927 }, { 3046, 3055 }, { 3174, 3183 }, { 3302, 3311 }, { 3430, 3439 }, { 3664, 3673 }, { 3792, 3801 }, { 3872, 3881 }, { 4160, 4169 }, { 4240, 4249 }, { 6112, 6121 }, { 6160, 6169 }, { 6470, 6479 }, { 6608, 6617 }, { 6784, 6793 }, { 6800, 6809 }, { 6992, 7001 }, { 7088, 7097 }, { 7232, 7241 }, { 7248, 7257 }, { 42528, 42537 }, { 43216, 43225 }, { 43264, 43273 }, { 43472, 43481 }, { 43600, 43609 }, { 44016, 44025 }, { 65296, 65305 }, }; static const URange32 Nd_range32[] = { { 66720, 66729 }, { 69734, 69743 }, { 69872, 69881 }, { 69942, 69951 }, { 70096, 70105 }, { 71360, 71369 }, { 120782, 120831 }, }; static const URange16 Pc_range16[] = { { 95, 95 }, { 8255, 8256 }, { 8276, 8276 }, { 65075, 65076 }, { 65101, 65103 }, { 65343, 65343 }, }; static const URange16 Lt_range16[] = { { 453, 453 }, { 456, 456 }, { 459, 459 }, { 498, 498 }, { 8072, 8079 }, { 8088, 8095 }, { 8104, 8111 }, { 8124, 8124 }, { 8140, 8140 }, { 8188, 8188 }, }; static const URange16 Lu_range16[] = { { 65, 90 }, { 192, 214 }, { 216, 222 }, { 256, 256 }, { 258, 258 }, { 260, 260 }, { 262, 262 }, { 264, 264 }, { 266, 266 }, { 268, 268 }, { 270, 270 }, { 272, 272 }, { 274, 274 }, { 276, 276 }, { 278, 278 }, { 280, 280 }, { 282, 282 }, { 284, 284 }, { 286, 286 }, { 288, 288 }, { 290, 290 }, { 292, 292 }, { 294, 294 }, { 296, 296 }, { 298, 298 }, { 300, 300 }, { 302, 302 }, { 304, 304 }, { 306, 306 }, { 308, 308 }, { 310, 310 }, { 313, 313 }, { 315, 315 }, { 317, 317 }, { 319, 319 }, { 321, 321 }, { 323, 323 }, { 325, 325 }, { 327, 327 }, { 330, 330 }, { 332, 332 }, { 334, 334 }, { 336, 336 }, { 338, 338 }, { 340, 340 }, { 342, 342 }, { 344, 344 }, { 346, 346 }, { 348, 348 }, { 350, 350 }, { 352, 352 }, { 354, 354 }, { 356, 356 }, { 358, 358 }, { 360, 360 }, { 362, 362 }, { 364, 364 }, { 366, 366 }, { 368, 368 }, { 370, 370 }, { 372, 372 }, { 374, 374 }, { 376, 377 }, { 379, 379 }, { 381, 381 }, { 385, 386 }, { 388, 388 }, { 390, 391 }, { 393, 395 }, { 398, 401 }, { 403, 404 }, { 406, 408 }, { 412, 413 }, { 415, 416 }, { 418, 418 }, { 420, 420 }, { 422, 423 }, { 425, 425 }, { 428, 428 }, { 430, 431 }, { 433, 435 }, { 437, 437 }, { 439, 440 }, { 444, 444 }, { 452, 452 }, { 455, 455 }, { 458, 458 }, { 461, 461 }, { 463, 463 }, { 465, 465 }, { 467, 467 }, { 469, 469 }, { 471, 471 }, { 473, 473 }, { 475, 475 }, { 478, 478 }, { 480, 480 }, { 482, 482 }, { 484, 484 }, { 486, 486 }, { 488, 488 }, { 490, 490 }, { 492, 492 }, { 494, 494 }, { 497, 497 }, { 500, 500 }, { 502, 504 }, { 506, 506 }, { 508, 508 }, { 510, 510 }, { 512, 512 }, { 514, 514 }, { 516, 516 }, { 518, 518 }, { 520, 520 }, { 522, 522 }, { 524, 524 }, { 526, 526 }, { 528, 528 }, { 530, 530 }, { 532, 532 }, { 534, 534 }, { 536, 536 }, { 538, 538 }, { 540, 540 }, { 542, 542 }, { 544, 544 }, { 546, 546 }, { 548, 548 }, { 550, 550 }, { 552, 552 }, { 554, 554 }, { 556, 556 }, { 558, 558 }, { 560, 560 }, { 562, 562 }, { 570, 571 }, { 573, 574 }, { 577, 577 }, { 579, 582 }, { 584, 584 }, { 586, 586 }, { 588, 588 }, { 590, 590 }, { 880, 880 }, { 882, 882 }, { 886, 886 }, { 902, 902 }, { 904, 906 }, { 908, 908 }, { 910, 911 }, { 913, 929 }, { 931, 939 }, { 975, 975 }, { 978, 980 }, { 984, 984 }, { 986, 986 }, { 988, 988 }, { 990, 990 }, { 992, 992 }, { 994, 994 }, { 996, 996 }, { 998, 998 }, { 1000, 1000 }, { 1002, 1002 }, { 1004, 1004 }, { 1006, 1006 }, { 1012, 1012 }, { 1015, 1015 }, { 1017, 1018 }, { 1021, 1071 }, { 1120, 1120 }, { 1122, 1122 }, { 1124, 1124 }, { 1126, 1126 }, { 1128, 1128 }, { 1130, 1130 }, { 1132, 1132 }, { 1134, 1134 }, { 1136, 1136 }, { 1138, 1138 }, { 1140, 1140 }, { 1142, 1142 }, { 1144, 1144 }, { 1146, 1146 }, { 1148, 1148 }, { 1150, 1150 }, { 1152, 1152 }, { 1162, 1162 }, { 1164, 1164 }, { 1166, 1166 }, { 1168, 1168 }, { 1170, 1170 }, { 1172, 1172 }, { 1174, 1174 }, { 1176, 1176 }, { 1178, 1178 }, { 1180, 1180 }, { 1182, 1182 }, { 1184, 1184 }, { 1186, 1186 }, { 1188, 1188 }, { 1190, 1190 }, { 1192, 1192 }, { 1194, 1194 }, { 1196, 1196 }, { 1198, 1198 }, { 1200, 1200 }, { 1202, 1202 }, { 1204, 1204 }, { 1206, 1206 }, { 1208, 1208 }, { 1210, 1210 }, { 1212, 1212 }, { 1214, 1214 }, { 1216, 1217 }, { 1219, 1219 }, { 1221, 1221 }, { 1223, 1223 }, { 1225, 1225 }, { 1227, 1227 }, { 1229, 1229 }, { 1232, 1232 }, { 1234, 1234 }, { 1236, 1236 }, { 1238, 1238 }, { 1240, 1240 }, { 1242, 1242 }, { 1244, 1244 }, { 1246, 1246 }, { 1248, 1248 }, { 1250, 1250 }, { 1252, 1252 }, { 1254, 1254 }, { 1256, 1256 }, { 1258, 1258 }, { 1260, 1260 }, { 1262, 1262 }, { 1264, 1264 }, { 1266, 1266 }, { 1268, 1268 }, { 1270, 1270 }, { 1272, 1272 }, { 1274, 1274 }, { 1276, 1276 }, { 1278, 1278 }, { 1280, 1280 }, { 1282, 1282 }, { 1284, 1284 }, { 1286, 1286 }, { 1288, 1288 }, { 1290, 1290 }, { 1292, 1292 }, { 1294, 1294 }, { 1296, 1296 }, { 1298, 1298 }, { 1300, 1300 }, { 1302, 1302 }, { 1304, 1304 }, { 1306, 1306 }, { 1308, 1308 }, { 1310, 1310 }, { 1312, 1312 }, { 1314, 1314 }, { 1316, 1316 }, { 1318, 1318 }, { 1329, 1366 }, { 4256, 4293 }, { 4295, 4295 }, { 4301, 4301 }, { 7680, 7680 }, { 7682, 7682 }, { 7684, 7684 }, { 7686, 7686 }, { 7688, 7688 }, { 7690, 7690 }, { 7692, 7692 }, { 7694, 7694 }, { 7696, 7696 }, { 7698, 7698 }, { 7700, 7700 }, { 7702, 7702 }, { 7704, 7704 }, { 7706, 7706 }, { 7708, 7708 }, { 7710, 7710 }, { 7712, 7712 }, { 7714, 7714 }, { 7716, 7716 }, { 7718, 7718 }, { 7720, 7720 }, { 7722, 7722 }, { 7724, 7724 }, { 7726, 7726 }, { 7728, 7728 }, { 7730, 7730 }, { 7732, 7732 }, { 7734, 7734 }, { 7736, 7736 }, { 7738, 7738 }, { 7740, 7740 }, { 7742, 7742 }, { 7744, 7744 }, { 7746, 7746 }, { 7748, 7748 }, { 7750, 7750 }, { 7752, 7752 }, { 7754, 7754 }, { 7756, 7756 }, { 7758, 7758 }, { 7760, 7760 }, { 7762, 7762 }, { 7764, 7764 }, { 7766, 7766 }, { 7768, 7768 }, { 7770, 7770 }, { 7772, 7772 }, { 7774, 7774 }, { 7776, 7776 }, { 7778, 7778 }, { 7780, 7780 }, { 7782, 7782 }, { 7784, 7784 }, { 7786, 7786 }, { 7788, 7788 }, { 7790, 7790 }, { 7792, 7792 }, { 7794, 7794 }, { 7796, 7796 }, { 7798, 7798 }, { 7800, 7800 }, { 7802, 7802 }, { 7804, 7804 }, { 7806, 7806 }, { 7808, 7808 }, { 7810, 7810 }, { 7812, 7812 }, { 7814, 7814 }, { 7816, 7816 }, { 7818, 7818 }, { 7820, 7820 }, { 7822, 7822 }, { 7824, 7824 }, { 7826, 7826 }, { 7828, 7828 }, { 7838, 7838 }, { 7840, 7840 }, { 7842, 7842 }, { 7844, 7844 }, { 7846, 7846 }, { 7848, 7848 }, { 7850, 7850 }, { 7852, 7852 }, { 7854, 7854 }, { 7856, 7856 }, { 7858, 7858 }, { 7860, 7860 }, { 7862, 7862 }, { 7864, 7864 }, { 7866, 7866 }, { 7868, 7868 }, { 7870, 7870 }, { 7872, 7872 }, { 7874, 7874 }, { 7876, 7876 }, { 7878, 7878 }, { 7880, 7880 }, { 7882, 7882 }, { 7884, 7884 }, { 7886, 7886 }, { 7888, 7888 }, { 7890, 7890 }, { 7892, 7892 }, { 7894, 7894 }, { 7896, 7896 }, { 7898, 7898 }, { 7900, 7900 }, { 7902, 7902 }, { 7904, 7904 }, { 7906, 7906 }, { 7908, 7908 }, { 7910, 7910 }, { 7912, 7912 }, { 7914, 7914 }, { 7916, 7916 }, { 7918, 7918 }, { 7920, 7920 }, { 7922, 7922 }, { 7924, 7924 }, { 7926, 7926 }, { 7928, 7928 }, { 7930, 7930 }, { 7932, 7932 }, { 7934, 7934 }, { 7944, 7951 }, { 7960, 7965 }, { 7976, 7983 }, { 7992, 7999 }, { 8008, 8013 }, { 8025, 8025 }, { 8027, 8027 }, { 8029, 8029 }, { 8031, 8031 }, { 8040, 8047 }, { 8120, 8123 }, { 8136, 8139 }, { 8152, 8155 }, { 8168, 8172 }, { 8184, 8187 }, { 8450, 8450 }, { 8455, 8455 }, { 8459, 8461 }, { 8464, 8466 }, { 8469, 8469 }, { 8473, 8477 }, { 8484, 8484 }, { 8486, 8486 }, { 8488, 8488 }, { 8490, 8493 }, { 8496, 8499 }, { 8510, 8511 }, { 8517, 8517 }, { 8579, 8579 }, { 11264, 11310 }, { 11360, 11360 }, { 11362, 11364 }, { 11367, 11367 }, { 11369, 11369 }, { 11371, 11371 }, { 11373, 11376 }, { 11378, 11378 }, { 11381, 11381 }, { 11390, 11392 }, { 11394, 11394 }, { 11396, 11396 }, { 11398, 11398 }, { 11400, 11400 }, { 11402, 11402 }, { 11404, 11404 }, { 11406, 11406 }, { 11408, 11408 }, { 11410, 11410 }, { 11412, 11412 }, { 11414, 11414 }, { 11416, 11416 }, { 11418, 11418 }, { 11420, 11420 }, { 11422, 11422 }, { 11424, 11424 }, { 11426, 11426 }, { 11428, 11428 }, { 11430, 11430 }, { 11432, 11432 }, { 11434, 11434 }, { 11436, 11436 }, { 11438, 11438 }, { 11440, 11440 }, { 11442, 11442 }, { 11444, 11444 }, { 11446, 11446 }, { 11448, 11448 }, { 11450, 11450 }, { 11452, 11452 }, { 11454, 11454 }, { 11456, 11456 }, { 11458, 11458 }, { 11460, 11460 }, { 11462, 11462 }, { 11464, 11464 }, { 11466, 11466 }, { 11468, 11468 }, { 11470, 11470 }, { 11472, 11472 }, { 11474, 11474 }, { 11476, 11476 }, { 11478, 11478 }, { 11480, 11480 }, { 11482, 11482 }, { 11484, 11484 }, { 11486, 11486 }, { 11488, 11488 }, { 11490, 11490 }, { 11499, 11499 }, { 11501, 11501 }, { 11506, 11506 }, { 42560, 42560 }, { 42562, 42562 }, { 42564, 42564 }, { 42566, 42566 }, { 42568, 42568 }, { 42570, 42570 }, { 42572, 42572 }, { 42574, 42574 }, { 42576, 42576 }, { 42578, 42578 }, { 42580, 42580 }, { 42582, 42582 }, { 42584, 42584 }, { 42586, 42586 }, { 42588, 42588 }, { 42590, 42590 }, { 42592, 42592 }, { 42594, 42594 }, { 42596, 42596 }, { 42598, 42598 }, { 42600, 42600 }, { 42602, 42602 }, { 42604, 42604 }, { 42624, 42624 }, { 42626, 42626 }, { 42628, 42628 }, { 42630, 42630 }, { 42632, 42632 }, { 42634, 42634 }, { 42636, 42636 }, { 42638, 42638 }, { 42640, 42640 }, { 42642, 42642 }, { 42644, 42644 }, { 42646, 42646 }, { 42786, 42786 }, { 42788, 42788 }, { 42790, 42790 }, { 42792, 42792 }, { 42794, 42794 }, { 42796, 42796 }, { 42798, 42798 }, { 42802, 42802 }, { 42804, 42804 }, { 42806, 42806 }, { 42808, 42808 }, { 42810, 42810 }, { 42812, 42812 }, { 42814, 42814 }, { 42816, 42816 }, { 42818, 42818 }, { 42820, 42820 }, { 42822, 42822 }, { 42824, 42824 }, { 42826, 42826 }, { 42828, 42828 }, { 42830, 42830 }, { 42832, 42832 }, { 42834, 42834 }, { 42836, 42836 }, { 42838, 42838 }, { 42840, 42840 }, { 42842, 42842 }, { 42844, 42844 }, { 42846, 42846 }, { 42848, 42848 }, { 42850, 42850 }, { 42852, 42852 }, { 42854, 42854 }, { 42856, 42856 }, { 42858, 42858 }, { 42860, 42860 }, { 42862, 42862 }, { 42873, 42873 }, { 42875, 42875 }, { 42877, 42878 }, { 42880, 42880 }, { 42882, 42882 }, { 42884, 42884 }, { 42886, 42886 }, { 42891, 42891 }, { 42893, 42893 }, { 42896, 42896 }, { 42898, 42898 }, { 42912, 42912 }, { 42914, 42914 }, { 42916, 42916 }, { 42918, 42918 }, { 42920, 42920 }, { 42922, 42922 }, { 65313, 65338 }, }; static const URange32 Lu_range32[] = { { 66560, 66599 }, { 119808, 119833 }, { 119860, 119885 }, { 119912, 119937 }, { 119964, 119964 }, { 119966, 119967 }, { 119970, 119970 }, { 119973, 119974 }, { 119977, 119980 }, { 119982, 119989 }, { 120016, 120041 }, { 120068, 120069 }, { 120071, 120074 }, { 120077, 120084 }, { 120086, 120092 }, { 120120, 120121 }, { 120123, 120126 }, { 120128, 120132 }, { 120134, 120134 }, { 120138, 120144 }, { 120172, 120197 }, { 120224, 120249 }, { 120276, 120301 }, { 120328, 120353 }, { 120380, 120405 }, { 120432, 120457 }, { 120488, 120512 }, { 120546, 120570 }, { 120604, 120628 }, { 120662, 120686 }, { 120720, 120744 }, { 120778, 120778 }, }; static const URange16 Pf_range16[] = { { 187, 187 }, { 8217, 8217 }, { 8221, 8221 }, { 8250, 8250 }, { 11779, 11779 }, { 11781, 11781 }, { 11786, 11786 }, { 11789, 11789 }, { 11805, 11805 }, { 11809, 11809 }, }; static const URange16 Pd_range16[] = { { 45, 45 }, { 1418, 1418 }, { 1470, 1470 }, { 5120, 5120 }, { 6150, 6150 }, { 8208, 8213 }, { 11799, 11799 }, { 11802, 11802 }, { 11834, 11835 }, { 12316, 12316 }, { 12336, 12336 }, { 12448, 12448 }, { 65073, 65074 }, { 65112, 65112 }, { 65123, 65123 }, { 65293, 65293 }, }; static const URange16 Pe_range16[] = { { 41, 41 }, { 93, 93 }, { 125, 125 }, { 3899, 3899 }, { 3901, 3901 }, { 5788, 5788 }, { 8262, 8262 }, { 8318, 8318 }, { 8334, 8334 }, { 8969, 8969 }, { 8971, 8971 }, { 9002, 9002 }, { 10089, 10089 }, { 10091, 10091 }, { 10093, 10093 }, { 10095, 10095 }, { 10097, 10097 }, { 10099, 10099 }, { 10101, 10101 }, { 10182, 10182 }, { 10215, 10215 }, { 10217, 10217 }, { 10219, 10219 }, { 10221, 10221 }, { 10223, 10223 }, { 10628, 10628 }, { 10630, 10630 }, { 10632, 10632 }, { 10634, 10634 }, { 10636, 10636 }, { 10638, 10638 }, { 10640, 10640 }, { 10642, 10642 }, { 10644, 10644 }, { 10646, 10646 }, { 10648, 10648 }, { 10713, 10713 }, { 10715, 10715 }, { 10749, 10749 }, { 11811, 11811 }, { 11813, 11813 }, { 11815, 11815 }, { 11817, 11817 }, { 12297, 12297 }, { 12299, 12299 }, { 12301, 12301 }, { 12303, 12303 }, { 12305, 12305 }, { 12309, 12309 }, { 12311, 12311 }, { 12313, 12313 }, { 12315, 12315 }, { 12318, 12319 }, { 64831, 64831 }, { 65048, 65048 }, { 65078, 65078 }, { 65080, 65080 }, { 65082, 65082 }, { 65084, 65084 }, { 65086, 65086 }, { 65088, 65088 }, { 65090, 65090 }, { 65092, 65092 }, { 65096, 65096 }, { 65114, 65114 }, { 65116, 65116 }, { 65118, 65118 }, { 65289, 65289 }, { 65341, 65341 }, { 65373, 65373 }, { 65376, 65376 }, { 65379, 65379 }, }; static const URange16 Pi_range16[] = { { 171, 171 }, { 8216, 8216 }, { 8219, 8220 }, { 8223, 8223 }, { 8249, 8249 }, { 11778, 11778 }, { 11780, 11780 }, { 11785, 11785 }, { 11788, 11788 }, { 11804, 11804 }, { 11808, 11808 }, }; static const URange16 Po_range16[] = { { 33, 35 }, { 37, 39 }, { 42, 42 }, { 44, 44 }, { 46, 47 }, { 58, 59 }, { 63, 64 }, { 92, 92 }, { 161, 161 }, { 167, 167 }, { 182, 183 }, { 191, 191 }, { 894, 894 }, { 903, 903 }, { 1370, 1375 }, { 1417, 1417 }, { 1472, 1472 }, { 1475, 1475 }, { 1478, 1478 }, { 1523, 1524 }, { 1545, 1546 }, { 1548, 1549 }, { 1563, 1563 }, { 1566, 1567 }, { 1642, 1645 }, { 1748, 1748 }, { 1792, 1805 }, { 2039, 2041 }, { 2096, 2110 }, { 2142, 2142 }, { 2404, 2405 }, { 2416, 2416 }, { 2800, 2800 }, { 3572, 3572 }, { 3663, 3663 }, { 3674, 3675 }, { 3844, 3858 }, { 3860, 3860 }, { 3973, 3973 }, { 4048, 4052 }, { 4057, 4058 }, { 4170, 4175 }, { 4347, 4347 }, { 4960, 4968 }, { 5741, 5742 }, { 5867, 5869 }, { 5941, 5942 }, { 6100, 6102 }, { 6104, 6106 }, { 6144, 6149 }, { 6151, 6154 }, { 6468, 6469 }, { 6686, 6687 }, { 6816, 6822 }, { 6824, 6829 }, { 7002, 7008 }, { 7164, 7167 }, { 7227, 7231 }, { 7294, 7295 }, { 7360, 7367 }, { 7379, 7379 }, { 8214, 8215 }, { 8224, 8231 }, { 8240, 8248 }, { 8251, 8254 }, { 8257, 8259 }, { 8263, 8273 }, { 8275, 8275 }, { 8277, 8286 }, { 11513, 11516 }, { 11518, 11519 }, { 11632, 11632 }, { 11776, 11777 }, { 11782, 11784 }, { 11787, 11787 }, { 11790, 11798 }, { 11800, 11801 }, { 11803, 11803 }, { 11806, 11807 }, { 11818, 11822 }, { 11824, 11833 }, { 12289, 12291 }, { 12349, 12349 }, { 12539, 12539 }, { 42238, 42239 }, { 42509, 42511 }, { 42611, 42611 }, { 42622, 42622 }, { 42738, 42743 }, { 43124, 43127 }, { 43214, 43215 }, { 43256, 43258 }, { 43310, 43311 }, { 43359, 43359 }, { 43457, 43469 }, { 43486, 43487 }, { 43612, 43615 }, { 43742, 43743 }, { 43760, 43761 }, { 44011, 44011 }, { 65040, 65046 }, { 65049, 65049 }, { 65072, 65072 }, { 65093, 65094 }, { 65097, 65100 }, { 65104, 65106 }, { 65108, 65111 }, { 65119, 65121 }, { 65128, 65128 }, { 65130, 65131 }, { 65281, 65283 }, { 65285, 65287 }, { 65290, 65290 }, { 65292, 65292 }, { 65294, 65295 }, { 65306, 65307 }, { 65311, 65312 }, { 65340, 65340 }, { 65377, 65377 }, { 65380, 65381 }, }; static const URange32 Po_range32[] = { { 65792, 65794 }, { 66463, 66463 }, { 66512, 66512 }, { 67671, 67671 }, { 67871, 67871 }, { 67903, 67903 }, { 68176, 68184 }, { 68223, 68223 }, { 68409, 68415 }, { 69703, 69709 }, { 69819, 69820 }, { 69822, 69825 }, { 69952, 69955 }, { 70085, 70088 }, { 74864, 74867 }, }; static const URange16 Me_range16[] = { { 1160, 1161 }, { 8413, 8416 }, { 8418, 8420 }, { 42608, 42610 }, }; static const URange16 C_range16[] = { { 0, 31 }, { 127, 159 }, { 173, 173 }, { 1536, 1540 }, { 1564, 1564 }, { 1757, 1757 }, { 1807, 1807 }, { 6158, 6158 }, { 8203, 8207 }, { 8234, 8238 }, { 8288, 8292 }, { 8294, 8303 }, { 55296, 63743 }, { 65279, 65279 }, { 65529, 65531 }, }; static const URange32 C_range32[] = { { 69821, 69821 }, { 119155, 119162 }, { 917505, 917505 }, { 917536, 917631 }, { 983040, 1048573 }, { 1048576, 1114109 }, }; static const URange16 Mc_range16[] = { { 2307, 2307 }, { 2363, 2363 }, { 2366, 2368 }, { 2377, 2380 }, { 2382, 2383 }, { 2434, 2435 }, { 2494, 2496 }, { 2503, 2504 }, { 2507, 2508 }, { 2519, 2519 }, { 2563, 2563 }, { 2622, 2624 }, { 2691, 2691 }, { 2750, 2752 }, { 2761, 2761 }, { 2763, 2764 }, { 2818, 2819 }, { 2878, 2878 }, { 2880, 2880 }, { 2887, 2888 }, { 2891, 2892 }, { 2903, 2903 }, { 3006, 3007 }, { 3009, 3010 }, { 3014, 3016 }, { 3018, 3020 }, { 3031, 3031 }, { 3073, 3075 }, { 3137, 3140 }, { 3202, 3203 }, { 3262, 3262 }, { 3264, 3268 }, { 3271, 3272 }, { 3274, 3275 }, { 3285, 3286 }, { 3330, 3331 }, { 3390, 3392 }, { 3398, 3400 }, { 3402, 3404 }, { 3415, 3415 }, { 3458, 3459 }, { 3535, 3537 }, { 3544, 3551 }, { 3570, 3571 }, { 3902, 3903 }, { 3967, 3967 }, { 4139, 4140 }, { 4145, 4145 }, { 4152, 4152 }, { 4155, 4156 }, { 4182, 4183 }, { 4194, 4196 }, { 4199, 4205 }, { 4227, 4228 }, { 4231, 4236 }, { 4239, 4239 }, { 4250, 4252 }, { 6070, 6070 }, { 6078, 6085 }, { 6087, 6088 }, { 6435, 6438 }, { 6441, 6443 }, { 6448, 6449 }, { 6451, 6456 }, { 6576, 6592 }, { 6600, 6601 }, { 6681, 6682 }, { 6741, 6741 }, { 6743, 6743 }, { 6753, 6753 }, { 6755, 6756 }, { 6765, 6770 }, { 6916, 6916 }, { 6965, 6965 }, { 6971, 6971 }, { 6973, 6977 }, { 6979, 6980 }, { 7042, 7042 }, { 7073, 7073 }, { 7078, 7079 }, { 7082, 7082 }, { 7084, 7085 }, { 7143, 7143 }, { 7146, 7148 }, { 7150, 7150 }, { 7154, 7155 }, { 7204, 7211 }, { 7220, 7221 }, { 7393, 7393 }, { 7410, 7411 }, { 12334, 12335 }, { 43043, 43044 }, { 43047, 43047 }, { 43136, 43137 }, { 43188, 43203 }, { 43346, 43347 }, { 43395, 43395 }, { 43444, 43445 }, { 43450, 43451 }, { 43453, 43456 }, { 43567, 43568 }, { 43571, 43572 }, { 43597, 43597 }, { 43643, 43643 }, { 43755, 43755 }, { 43758, 43759 }, { 43765, 43765 }, { 44003, 44004 }, { 44006, 44007 }, { 44009, 44010 }, { 44012, 44012 }, }; static const URange32 Mc_range32[] = { { 69632, 69632 }, { 69634, 69634 }, { 69762, 69762 }, { 69808, 69810 }, { 69815, 69816 }, { 69932, 69932 }, { 70018, 70018 }, { 70067, 70069 }, { 70079, 70080 }, { 71340, 71340 }, { 71342, 71343 }, { 71350, 71350 }, { 94033, 94078 }, { 119141, 119142 }, { 119149, 119154 }, }; static const URange16 Mn_range16[] = { { 768, 879 }, { 1155, 1159 }, { 1425, 1469 }, { 1471, 1471 }, { 1473, 1474 }, { 1476, 1477 }, { 1479, 1479 }, { 1552, 1562 }, { 1611, 1631 }, { 1648, 1648 }, { 1750, 1756 }, { 1759, 1764 }, { 1767, 1768 }, { 1770, 1773 }, { 1809, 1809 }, { 1840, 1866 }, { 1958, 1968 }, { 2027, 2035 }, { 2070, 2073 }, { 2075, 2083 }, { 2085, 2087 }, { 2089, 2093 }, { 2137, 2139 }, { 2276, 2302 }, { 2304, 2306 }, { 2362, 2362 }, { 2364, 2364 }, { 2369, 2376 }, { 2381, 2381 }, { 2385, 2391 }, { 2402, 2403 }, { 2433, 2433 }, { 2492, 2492 }, { 2497, 2500 }, { 2509, 2509 }, { 2530, 2531 }, { 2561, 2562 }, { 2620, 2620 }, { 2625, 2626 }, { 2631, 2632 }, { 2635, 2637 }, { 2641, 2641 }, { 2672, 2673 }, { 2677, 2677 }, { 2689, 2690 }, { 2748, 2748 }, { 2753, 2757 }, { 2759, 2760 }, { 2765, 2765 }, { 2786, 2787 }, { 2817, 2817 }, { 2876, 2876 }, { 2879, 2879 }, { 2881, 2884 }, { 2893, 2893 }, { 2902, 2902 }, { 2914, 2915 }, { 2946, 2946 }, { 3008, 3008 }, { 3021, 3021 }, { 3134, 3136 }, { 3142, 3144 }, { 3146, 3149 }, { 3157, 3158 }, { 3170, 3171 }, { 3260, 3260 }, { 3263, 3263 }, { 3270, 3270 }, { 3276, 3277 }, { 3298, 3299 }, { 3393, 3396 }, { 3405, 3405 }, { 3426, 3427 }, { 3530, 3530 }, { 3538, 3540 }, { 3542, 3542 }, { 3633, 3633 }, { 3636, 3642 }, { 3655, 3662 }, { 3761, 3761 }, { 3764, 3769 }, { 3771, 3772 }, { 3784, 3789 }, { 3864, 3865 }, { 3893, 3893 }, { 3895, 3895 }, { 3897, 3897 }, { 3953, 3966 }, { 3968, 3972 }, { 3974, 3975 }, { 3981, 3991 }, { 3993, 4028 }, { 4038, 4038 }, { 4141, 4144 }, { 4146, 4151 }, { 4153, 4154 }, { 4157, 4158 }, { 4184, 4185 }, { 4190, 4192 }, { 4209, 4212 }, { 4226, 4226 }, { 4229, 4230 }, { 4237, 4237 }, { 4253, 4253 }, { 4957, 4959 }, { 5906, 5908 }, { 5938, 5940 }, { 5970, 5971 }, { 6002, 6003 }, { 6068, 6069 }, { 6071, 6077 }, { 6086, 6086 }, { 6089, 6099 }, { 6109, 6109 }, { 6155, 6157 }, { 6313, 6313 }, { 6432, 6434 }, { 6439, 6440 }, { 6450, 6450 }, { 6457, 6459 }, { 6679, 6680 }, { 6683, 6683 }, { 6742, 6742 }, { 6744, 6750 }, { 6752, 6752 }, { 6754, 6754 }, { 6757, 6764 }, { 6771, 6780 }, { 6783, 6783 }, { 6912, 6915 }, { 6964, 6964 }, { 6966, 6970 }, { 6972, 6972 }, { 6978, 6978 }, { 7019, 7027 }, { 7040, 7041 }, { 7074, 7077 }, { 7080, 7081 }, { 7083, 7083 }, { 7142, 7142 }, { 7144, 7145 }, { 7149, 7149 }, { 7151, 7153 }, { 7212, 7219 }, { 7222, 7223 }, { 7376, 7378 }, { 7380, 7392 }, { 7394, 7400 }, { 7405, 7405 }, { 7412, 7412 }, { 7616, 7654 }, { 7676, 7679 }, { 8400, 8412 }, { 8417, 8417 }, { 8421, 8432 }, { 11503, 11505 }, { 11647, 11647 }, { 11744, 11775 }, { 12330, 12333 }, { 12441, 12442 }, { 42607, 42607 }, { 42612, 42621 }, { 42655, 42655 }, { 42736, 42737 }, { 43010, 43010 }, { 43014, 43014 }, { 43019, 43019 }, { 43045, 43046 }, { 43204, 43204 }, { 43232, 43249 }, { 43302, 43309 }, { 43335, 43345 }, { 43392, 43394 }, { 43443, 43443 }, { 43446, 43449 }, { 43452, 43452 }, { 43561, 43566 }, { 43569, 43570 }, { 43573, 43574 }, { 43587, 43587 }, { 43596, 43596 }, { 43696, 43696 }, { 43698, 43700 }, { 43703, 43704 }, { 43710, 43711 }, { 43713, 43713 }, { 43756, 43757 }, { 43766, 43766 }, { 44005, 44005 }, { 44008, 44008 }, { 44013, 44013 }, { 64286, 64286 }, { 65024, 65039 }, { 65056, 65062 }, }; static const URange32 Mn_range32[] = { { 66045, 66045 }, { 68097, 68099 }, { 68101, 68102 }, { 68108, 68111 }, { 68152, 68154 }, { 68159, 68159 }, { 69633, 69633 }, { 69688, 69702 }, { 69760, 69761 }, { 69811, 69814 }, { 69817, 69818 }, { 69888, 69890 }, { 69927, 69931 }, { 69933, 69940 }, { 70016, 70017 }, { 70070, 70078 }, { 71339, 71339 }, { 71341, 71341 }, { 71344, 71349 }, { 71351, 71351 }, { 94095, 94098 }, { 119143, 119145 }, { 119163, 119170 }, { 119173, 119179 }, { 119210, 119213 }, { 119362, 119364 }, { 917760, 917999 }, }; static const URange16 M_range16[] = { { 768, 879 }, { 1155, 1161 }, { 1425, 1469 }, { 1471, 1471 }, { 1473, 1474 }, { 1476, 1477 }, { 1479, 1479 }, { 1552, 1562 }, { 1611, 1631 }, { 1648, 1648 }, { 1750, 1756 }, { 1759, 1764 }, { 1767, 1768 }, { 1770, 1773 }, { 1809, 1809 }, { 1840, 1866 }, { 1958, 1968 }, { 2027, 2035 }, { 2070, 2073 }, { 2075, 2083 }, { 2085, 2087 }, { 2089, 2093 }, { 2137, 2139 }, { 2276, 2302 }, { 2304, 2307 }, { 2362, 2364 }, { 2366, 2383 }, { 2385, 2391 }, { 2402, 2403 }, { 2433, 2435 }, { 2492, 2492 }, { 2494, 2500 }, { 2503, 2504 }, { 2507, 2509 }, { 2519, 2519 }, { 2530, 2531 }, { 2561, 2563 }, { 2620, 2620 }, { 2622, 2626 }, { 2631, 2632 }, { 2635, 2637 }, { 2641, 2641 }, { 2672, 2673 }, { 2677, 2677 }, { 2689, 2691 }, { 2748, 2748 }, { 2750, 2757 }, { 2759, 2761 }, { 2763, 2765 }, { 2786, 2787 }, { 2817, 2819 }, { 2876, 2876 }, { 2878, 2884 }, { 2887, 2888 }, { 2891, 2893 }, { 2902, 2903 }, { 2914, 2915 }, { 2946, 2946 }, { 3006, 3010 }, { 3014, 3016 }, { 3018, 3021 }, { 3031, 3031 }, { 3073, 3075 }, { 3134, 3140 }, { 3142, 3144 }, { 3146, 3149 }, { 3157, 3158 }, { 3170, 3171 }, { 3202, 3203 }, { 3260, 3260 }, { 3262, 3268 }, { 3270, 3272 }, { 3274, 3277 }, { 3285, 3286 }, { 3298, 3299 }, { 3330, 3331 }, { 3390, 3396 }, { 3398, 3400 }, { 3402, 3405 }, { 3415, 3415 }, { 3426, 3427 }, { 3458, 3459 }, { 3530, 3530 }, { 3535, 3540 }, { 3542, 3542 }, { 3544, 3551 }, { 3570, 3571 }, { 3633, 3633 }, { 3636, 3642 }, { 3655, 3662 }, { 3761, 3761 }, { 3764, 3769 }, { 3771, 3772 }, { 3784, 3789 }, { 3864, 3865 }, { 3893, 3893 }, { 3895, 3895 }, { 3897, 3897 }, { 3902, 3903 }, { 3953, 3972 }, { 3974, 3975 }, { 3981, 3991 }, { 3993, 4028 }, { 4038, 4038 }, { 4139, 4158 }, { 4182, 4185 }, { 4190, 4192 }, { 4194, 4196 }, { 4199, 4205 }, { 4209, 4212 }, { 4226, 4237 }, { 4239, 4239 }, { 4250, 4253 }, { 4957, 4959 }, { 5906, 5908 }, { 5938, 5940 }, { 5970, 5971 }, { 6002, 6003 }, { 6068, 6099 }, { 6109, 6109 }, { 6155, 6157 }, { 6313, 6313 }, { 6432, 6443 }, { 6448, 6459 }, { 6576, 6592 }, { 6600, 6601 }, { 6679, 6683 }, { 6741, 6750 }, { 6752, 6780 }, { 6783, 6783 }, { 6912, 6916 }, { 6964, 6980 }, { 7019, 7027 }, { 7040, 7042 }, { 7073, 7085 }, { 7142, 7155 }, { 7204, 7223 }, { 7376, 7378 }, { 7380, 7400 }, { 7405, 7405 }, { 7410, 7412 }, { 7616, 7654 }, { 7676, 7679 }, { 8400, 8432 }, { 11503, 11505 }, { 11647, 11647 }, { 11744, 11775 }, { 12330, 12335 }, { 12441, 12442 }, { 42607, 42610 }, { 42612, 42621 }, { 42655, 42655 }, { 42736, 42737 }, { 43010, 43010 }, { 43014, 43014 }, { 43019, 43019 }, { 43043, 43047 }, { 43136, 43137 }, { 43188, 43204 }, { 43232, 43249 }, { 43302, 43309 }, { 43335, 43347 }, { 43392, 43395 }, { 43443, 43456 }, { 43561, 43574 }, { 43587, 43587 }, { 43596, 43597 }, { 43643, 43643 }, { 43696, 43696 }, { 43698, 43700 }, { 43703, 43704 }, { 43710, 43711 }, { 43713, 43713 }, { 43755, 43759 }, { 43765, 43766 }, { 44003, 44010 }, { 44012, 44013 }, { 64286, 64286 }, { 65024, 65039 }, { 65056, 65062 }, }; static const URange32 M_range32[] = { { 66045, 66045 }, { 68097, 68099 }, { 68101, 68102 }, { 68108, 68111 }, { 68152, 68154 }, { 68159, 68159 }, { 69632, 69634 }, { 69688, 69702 }, { 69760, 69762 }, { 69808, 69818 }, { 69888, 69890 }, { 69927, 69940 }, { 70016, 70018 }, { 70067, 70080 }, { 71339, 71351 }, { 94033, 94078 }, { 94095, 94098 }, { 119141, 119145 }, { 119149, 119154 }, { 119163, 119170 }, { 119173, 119179 }, { 119210, 119213 }, { 119362, 119364 }, { 917760, 917999 }, }; static const URange16 L_range16[] = { { 65, 90 }, { 97, 122 }, { 170, 170 }, { 181, 181 }, { 186, 186 }, { 192, 214 }, { 216, 246 }, { 248, 705 }, { 710, 721 }, { 736, 740 }, { 748, 748 }, { 750, 750 }, { 880, 884 }, { 886, 887 }, { 890, 893 }, { 902, 902 }, { 904, 906 }, { 908, 908 }, { 910, 929 }, { 931, 1013 }, { 1015, 1153 }, { 1162, 1319 }, { 1329, 1366 }, { 1369, 1369 }, { 1377, 1415 }, { 1488, 1514 }, { 1520, 1522 }, { 1568, 1610 }, { 1646, 1647 }, { 1649, 1747 }, { 1749, 1749 }, { 1765, 1766 }, { 1774, 1775 }, { 1786, 1788 }, { 1791, 1791 }, { 1808, 1808 }, { 1810, 1839 }, { 1869, 1957 }, { 1969, 1969 }, { 1994, 2026 }, { 2036, 2037 }, { 2042, 2042 }, { 2048, 2069 }, { 2074, 2074 }, { 2084, 2084 }, { 2088, 2088 }, { 2112, 2136 }, { 2208, 2208 }, { 2210, 2220 }, { 2308, 2361 }, { 2365, 2365 }, { 2384, 2384 }, { 2392, 2401 }, { 2417, 2423 }, { 2425, 2431 }, { 2437, 2444 }, { 2447, 2448 }, { 2451, 2472 }, { 2474, 2480 }, { 2482, 2482 }, { 2486, 2489 }, { 2493, 2493 }, { 2510, 2510 }, { 2524, 2525 }, { 2527, 2529 }, { 2544, 2545 }, { 2565, 2570 }, { 2575, 2576 }, { 2579, 2600 }, { 2602, 2608 }, { 2610, 2611 }, { 2613, 2614 }, { 2616, 2617 }, { 2649, 2652 }, { 2654, 2654 }, { 2674, 2676 }, { 2693, 2701 }, { 2703, 2705 }, { 2707, 2728 }, { 2730, 2736 }, { 2738, 2739 }, { 2741, 2745 }, { 2749, 2749 }, { 2768, 2768 }, { 2784, 2785 }, { 2821, 2828 }, { 2831, 2832 }, { 2835, 2856 }, { 2858, 2864 }, { 2866, 2867 }, { 2869, 2873 }, { 2877, 2877 }, { 2908, 2909 }, { 2911, 2913 }, { 2929, 2929 }, { 2947, 2947 }, { 2949, 2954 }, { 2958, 2960 }, { 2962, 2965 }, { 2969, 2970 }, { 2972, 2972 }, { 2974, 2975 }, { 2979, 2980 }, { 2984, 2986 }, { 2990, 3001 }, { 3024, 3024 }, { 3077, 3084 }, { 3086, 3088 }, { 3090, 3112 }, { 3114, 3123 }, { 3125, 3129 }, { 3133, 3133 }, { 3160, 3161 }, { 3168, 3169 }, { 3205, 3212 }, { 3214, 3216 }, { 3218, 3240 }, { 3242, 3251 }, { 3253, 3257 }, { 3261, 3261 }, { 3294, 3294 }, { 3296, 3297 }, { 3313, 3314 }, { 3333, 3340 }, { 3342, 3344 }, { 3346, 3386 }, { 3389, 3389 }, { 3406, 3406 }, { 3424, 3425 }, { 3450, 3455 }, { 3461, 3478 }, { 3482, 3505 }, { 3507, 3515 }, { 3517, 3517 }, { 3520, 3526 }, { 3585, 3632 }, { 3634, 3635 }, { 3648, 3654 }, { 3713, 3714 }, { 3716, 3716 }, { 3719, 3720 }, { 3722, 3722 }, { 3725, 3725 }, { 3732, 3735 }, { 3737, 3743 }, { 3745, 3747 }, { 3749, 3749 }, { 3751, 3751 }, { 3754, 3755 }, { 3757, 3760 }, { 3762, 3763 }, { 3773, 3773 }, { 3776, 3780 }, { 3782, 3782 }, { 3804, 3807 }, { 3840, 3840 }, { 3904, 3911 }, { 3913, 3948 }, { 3976, 3980 }, { 4096, 4138 }, { 4159, 4159 }, { 4176, 4181 }, { 4186, 4189 }, { 4193, 4193 }, { 4197, 4198 }, { 4206, 4208 }, { 4213, 4225 }, { 4238, 4238 }, { 4256, 4293 }, { 4295, 4295 }, { 4301, 4301 }, { 4304, 4346 }, { 4348, 4680 }, { 4682, 4685 }, { 4688, 4694 }, { 4696, 4696 }, { 4698, 4701 }, { 4704, 4744 }, { 4746, 4749 }, { 4752, 4784 }, { 4786, 4789 }, { 4792, 4798 }, { 4800, 4800 }, { 4802, 4805 }, { 4808, 4822 }, { 4824, 4880 }, { 4882, 4885 }, { 4888, 4954 }, { 4992, 5007 }, { 5024, 5108 }, { 5121, 5740 }, { 5743, 5759 }, { 5761, 5786 }, { 5792, 5866 }, { 5888, 5900 }, { 5902, 5905 }, { 5920, 5937 }, { 5952, 5969 }, { 5984, 5996 }, { 5998, 6000 }, { 6016, 6067 }, { 6103, 6103 }, { 6108, 6108 }, { 6176, 6263 }, { 6272, 6312 }, { 6314, 6314 }, { 6320, 6389 }, { 6400, 6428 }, { 6480, 6509 }, { 6512, 6516 }, { 6528, 6571 }, { 6593, 6599 }, { 6656, 6678 }, { 6688, 6740 }, { 6823, 6823 }, { 6917, 6963 }, { 6981, 6987 }, { 7043, 7072 }, { 7086, 7087 }, { 7098, 7141 }, { 7168, 7203 }, { 7245, 7247 }, { 7258, 7293 }, { 7401, 7404 }, { 7406, 7409 }, { 7413, 7414 }, { 7424, 7615 }, { 7680, 7957 }, { 7960, 7965 }, { 7968, 8005 }, { 8008, 8013 }, { 8016, 8023 }, { 8025, 8025 }, { 8027, 8027 }, { 8029, 8029 }, { 8031, 8061 }, { 8064, 8116 }, { 8118, 8124 }, { 8126, 8126 }, { 8130, 8132 }, { 8134, 8140 }, { 8144, 8147 }, { 8150, 8155 }, { 8160, 8172 }, { 8178, 8180 }, { 8182, 8188 }, { 8305, 8305 }, { 8319, 8319 }, { 8336, 8348 }, { 8450, 8450 }, { 8455, 8455 }, { 8458, 8467 }, { 8469, 8469 }, { 8473, 8477 }, { 8484, 8484 }, { 8486, 8486 }, { 8488, 8488 }, { 8490, 8493 }, { 8495, 8505 }, { 8508, 8511 }, { 8517, 8521 }, { 8526, 8526 }, { 8579, 8580 }, { 11264, 11310 }, { 11312, 11358 }, { 11360, 11492 }, { 11499, 11502 }, { 11506, 11507 }, { 11520, 11557 }, { 11559, 11559 }, { 11565, 11565 }, { 11568, 11623 }, { 11631, 11631 }, { 11648, 11670 }, { 11680, 11686 }, { 11688, 11694 }, { 11696, 11702 }, { 11704, 11710 }, { 11712, 11718 }, { 11720, 11726 }, { 11728, 11734 }, { 11736, 11742 }, { 11823, 11823 }, { 12293, 12294 }, { 12337, 12341 }, { 12347, 12348 }, { 12353, 12438 }, { 12445, 12447 }, { 12449, 12538 }, { 12540, 12543 }, { 12549, 12589 }, { 12593, 12686 }, { 12704, 12730 }, { 12784, 12799 }, { 13312, 19893 }, { 19968, 40908 }, { 40960, 42124 }, { 42192, 42237 }, { 42240, 42508 }, { 42512, 42527 }, { 42538, 42539 }, { 42560, 42606 }, { 42623, 42647 }, { 42656, 42725 }, { 42775, 42783 }, { 42786, 42888 }, { 42891, 42894 }, { 42896, 42899 }, { 42912, 42922 }, { 43000, 43009 }, { 43011, 43013 }, { 43015, 43018 }, { 43020, 43042 }, { 43072, 43123 }, { 43138, 43187 }, { 43250, 43255 }, { 43259, 43259 }, { 43274, 43301 }, { 43312, 43334 }, { 43360, 43388 }, { 43396, 43442 }, { 43471, 43471 }, { 43520, 43560 }, { 43584, 43586 }, { 43588, 43595 }, { 43616, 43638 }, { 43642, 43642 }, { 43648, 43695 }, { 43697, 43697 }, { 43701, 43702 }, { 43705, 43709 }, { 43712, 43712 }, { 43714, 43714 }, { 43739, 43741 }, { 43744, 43754 }, { 43762, 43764 }, { 43777, 43782 }, { 43785, 43790 }, { 43793, 43798 }, { 43808, 43814 }, { 43816, 43822 }, { 43968, 44002 }, { 44032, 55203 }, { 55216, 55238 }, { 55243, 55291 }, { 63744, 64109 }, { 64112, 64217 }, { 64256, 64262 }, { 64275, 64279 }, { 64285, 64285 }, { 64287, 64296 }, { 64298, 64310 }, { 64312, 64316 }, { 64318, 64318 }, { 64320, 64321 }, { 64323, 64324 }, { 64326, 64433 }, { 64467, 64829 }, { 64848, 64911 }, { 64914, 64967 }, { 65008, 65019 }, { 65136, 65140 }, { 65142, 65276 }, { 65313, 65338 }, { 65345, 65370 }, { 65382, 65470 }, { 65474, 65479 }, { 65482, 65487 }, { 65490, 65495 }, { 65498, 65500 }, }; static const URange32 L_range32[] = { { 65536, 65547 }, { 65549, 65574 }, { 65576, 65594 }, { 65596, 65597 }, { 65599, 65613 }, { 65616, 65629 }, { 65664, 65786 }, { 66176, 66204 }, { 66208, 66256 }, { 66304, 66334 }, { 66352, 66368 }, { 66370, 66377 }, { 66432, 66461 }, { 66464, 66499 }, { 66504, 66511 }, { 66560, 66717 }, { 67584, 67589 }, { 67592, 67592 }, { 67594, 67637 }, { 67639, 67640 }, { 67644, 67644 }, { 67647, 67669 }, { 67840, 67861 }, { 67872, 67897 }, { 67968, 68023 }, { 68030, 68031 }, { 68096, 68096 }, { 68112, 68115 }, { 68117, 68119 }, { 68121, 68147 }, { 68192, 68220 }, { 68352, 68405 }, { 68416, 68437 }, { 68448, 68466 }, { 68608, 68680 }, { 69635, 69687 }, { 69763, 69807 }, { 69840, 69864 }, { 69891, 69926 }, { 70019, 70066 }, { 70081, 70084 }, { 71296, 71338 }, { 73728, 74606 }, { 77824, 78894 }, { 92160, 92728 }, { 93952, 94020 }, { 94032, 94032 }, { 94099, 94111 }, { 110592, 110593 }, { 119808, 119892 }, { 119894, 119964 }, { 119966, 119967 }, { 119970, 119970 }, { 119973, 119974 }, { 119977, 119980 }, { 119982, 119993 }, { 119995, 119995 }, { 119997, 120003 }, { 120005, 120069 }, { 120071, 120074 }, { 120077, 120084 }, { 120086, 120092 }, { 120094, 120121 }, { 120123, 120126 }, { 120128, 120132 }, { 120134, 120134 }, { 120138, 120144 }, { 120146, 120485 }, { 120488, 120512 }, { 120514, 120538 }, { 120540, 120570 }, { 120572, 120596 }, { 120598, 120628 }, { 120630, 120654 }, { 120656, 120686 }, { 120688, 120712 }, { 120714, 120744 }, { 120746, 120770 }, { 120772, 120779 }, { 126464, 126467 }, { 126469, 126495 }, { 126497, 126498 }, { 126500, 126500 }, { 126503, 126503 }, { 126505, 126514 }, { 126516, 126519 }, { 126521, 126521 }, { 126523, 126523 }, { 126530, 126530 }, { 126535, 126535 }, { 126537, 126537 }, { 126539, 126539 }, { 126541, 126543 }, { 126545, 126546 }, { 126548, 126548 }, { 126551, 126551 }, { 126553, 126553 }, { 126555, 126555 }, { 126557, 126557 }, { 126559, 126559 }, { 126561, 126562 }, { 126564, 126564 }, { 126567, 126570 }, { 126572, 126578 }, { 126580, 126583 }, { 126585, 126588 }, { 126590, 126590 }, { 126592, 126601 }, { 126603, 126619 }, { 126625, 126627 }, { 126629, 126633 }, { 126635, 126651 }, { 131072, 173782 }, { 173824, 177972 }, { 177984, 178205 }, { 194560, 195101 }, }; static const URange16 N_range16[] = { { 48, 57 }, { 178, 179 }, { 185, 185 }, { 188, 190 }, { 1632, 1641 }, { 1776, 1785 }, { 1984, 1993 }, { 2406, 2415 }, { 2534, 2543 }, { 2548, 2553 }, { 2662, 2671 }, { 2790, 2799 }, { 2918, 2927 }, { 2930, 2935 }, { 3046, 3058 }, { 3174, 3183 }, { 3192, 3198 }, { 3302, 3311 }, { 3430, 3445 }, { 3664, 3673 }, { 3792, 3801 }, { 3872, 3891 }, { 4160, 4169 }, { 4240, 4249 }, { 4969, 4988 }, { 5870, 5872 }, { 6112, 6121 }, { 6128, 6137 }, { 6160, 6169 }, { 6470, 6479 }, { 6608, 6618 }, { 6784, 6793 }, { 6800, 6809 }, { 6992, 7001 }, { 7088, 7097 }, { 7232, 7241 }, { 7248, 7257 }, { 8304, 8304 }, { 8308, 8313 }, { 8320, 8329 }, { 8528, 8578 }, { 8581, 8585 }, { 9312, 9371 }, { 9450, 9471 }, { 10102, 10131 }, { 11517, 11517 }, { 12295, 12295 }, { 12321, 12329 }, { 12344, 12346 }, { 12690, 12693 }, { 12832, 12841 }, { 12872, 12879 }, { 12881, 12895 }, { 12928, 12937 }, { 12977, 12991 }, { 42528, 42537 }, { 42726, 42735 }, { 43056, 43061 }, { 43216, 43225 }, { 43264, 43273 }, { 43472, 43481 }, { 43600, 43609 }, { 44016, 44025 }, { 65296, 65305 }, }; static const URange32 N_range32[] = { { 65799, 65843 }, { 65856, 65912 }, { 65930, 65930 }, { 66336, 66339 }, { 66369, 66369 }, { 66378, 66378 }, { 66513, 66517 }, { 66720, 66729 }, { 67672, 67679 }, { 67862, 67867 }, { 68160, 68167 }, { 68221, 68222 }, { 68440, 68447 }, { 68472, 68479 }, { 69216, 69246 }, { 69714, 69743 }, { 69872, 69881 }, { 69942, 69951 }, { 70096, 70105 }, { 71360, 71369 }, { 74752, 74850 }, { 119648, 119665 }, { 120782, 120831 }, { 127232, 127242 }, }; static const URange16 Sk_range16[] = { { 94, 94 }, { 96, 96 }, { 168, 168 }, { 175, 175 }, { 180, 180 }, { 184, 184 }, { 706, 709 }, { 722, 735 }, { 741, 747 }, { 749, 749 }, { 751, 767 }, { 885, 885 }, { 900, 901 }, { 8125, 8125 }, { 8127, 8129 }, { 8141, 8143 }, { 8157, 8159 }, { 8173, 8175 }, { 8189, 8190 }, { 12443, 12444 }, { 42752, 42774 }, { 42784, 42785 }, { 42889, 42890 }, { 64434, 64449 }, { 65342, 65342 }, { 65344, 65344 }, { 65507, 65507 }, }; static const URange16 P_range16[] = { { 33, 35 }, { 37, 42 }, { 44, 47 }, { 58, 59 }, { 63, 64 }, { 91, 93 }, { 95, 95 }, { 123, 123 }, { 125, 125 }, { 161, 161 }, { 167, 167 }, { 171, 171 }, { 182, 183 }, { 187, 187 }, { 191, 191 }, { 894, 894 }, { 903, 903 }, { 1370, 1375 }, { 1417, 1418 }, { 1470, 1470 }, { 1472, 1472 }, { 1475, 1475 }, { 1478, 1478 }, { 1523, 1524 }, { 1545, 1546 }, { 1548, 1549 }, { 1563, 1563 }, { 1566, 1567 }, { 1642, 1645 }, { 1748, 1748 }, { 1792, 1805 }, { 2039, 2041 }, { 2096, 2110 }, { 2142, 2142 }, { 2404, 2405 }, { 2416, 2416 }, { 2800, 2800 }, { 3572, 3572 }, { 3663, 3663 }, { 3674, 3675 }, { 3844, 3858 }, { 3860, 3860 }, { 3898, 3901 }, { 3973, 3973 }, { 4048, 4052 }, { 4057, 4058 }, { 4170, 4175 }, { 4347, 4347 }, { 4960, 4968 }, { 5120, 5120 }, { 5741, 5742 }, { 5787, 5788 }, { 5867, 5869 }, { 5941, 5942 }, { 6100, 6102 }, { 6104, 6106 }, { 6144, 6154 }, { 6468, 6469 }, { 6686, 6687 }, { 6816, 6822 }, { 6824, 6829 }, { 7002, 7008 }, { 7164, 7167 }, { 7227, 7231 }, { 7294, 7295 }, { 7360, 7367 }, { 7379, 7379 }, { 8208, 8231 }, { 8240, 8259 }, { 8261, 8273 }, { 8275, 8286 }, { 8317, 8318 }, { 8333, 8334 }, { 8968, 8971 }, { 9001, 9002 }, { 10088, 10101 }, { 10181, 10182 }, { 10214, 10223 }, { 10627, 10648 }, { 10712, 10715 }, { 10748, 10749 }, { 11513, 11516 }, { 11518, 11519 }, { 11632, 11632 }, { 11776, 11822 }, { 11824, 11835 }, { 12289, 12291 }, { 12296, 12305 }, { 12308, 12319 }, { 12336, 12336 }, { 12349, 12349 }, { 12448, 12448 }, { 12539, 12539 }, { 42238, 42239 }, { 42509, 42511 }, { 42611, 42611 }, { 42622, 42622 }, { 42738, 42743 }, { 43124, 43127 }, { 43214, 43215 }, { 43256, 43258 }, { 43310, 43311 }, { 43359, 43359 }, { 43457, 43469 }, { 43486, 43487 }, { 43612, 43615 }, { 43742, 43743 }, { 43760, 43761 }, { 44011, 44011 }, { 64830, 64831 }, { 65040, 65049 }, { 65072, 65106 }, { 65108, 65121 }, { 65123, 65123 }, { 65128, 65128 }, { 65130, 65131 }, { 65281, 65283 }, { 65285, 65290 }, { 65292, 65295 }, { 65306, 65307 }, { 65311, 65312 }, { 65339, 65341 }, { 65343, 65343 }, { 65371, 65371 }, { 65373, 65373 }, { 65375, 65381 }, }; static const URange32 P_range32[] = { { 65792, 65794 }, { 66463, 66463 }, { 66512, 66512 }, { 67671, 67671 }, { 67871, 67871 }, { 67903, 67903 }, { 68176, 68184 }, { 68223, 68223 }, { 68409, 68415 }, { 69703, 69709 }, { 69819, 69820 }, { 69822, 69825 }, { 69952, 69955 }, { 70085, 70088 }, { 74864, 74867 }, }; static const URange16 S_range16[] = { { 36, 36 }, { 43, 43 }, { 60, 62 }, { 94, 94 }, { 96, 96 }, { 124, 124 }, { 126, 126 }, { 162, 166 }, { 168, 169 }, { 172, 172 }, { 174, 177 }, { 180, 180 }, { 184, 184 }, { 215, 215 }, { 247, 247 }, { 706, 709 }, { 722, 735 }, { 741, 747 }, { 749, 749 }, { 751, 767 }, { 885, 885 }, { 900, 901 }, { 1014, 1014 }, { 1154, 1154 }, { 1423, 1423 }, { 1542, 1544 }, { 1547, 1547 }, { 1550, 1551 }, { 1758, 1758 }, { 1769, 1769 }, { 1789, 1790 }, { 2038, 2038 }, { 2546, 2547 }, { 2554, 2555 }, { 2801, 2801 }, { 2928, 2928 }, { 3059, 3066 }, { 3199, 3199 }, { 3449, 3449 }, { 3647, 3647 }, { 3841, 3843 }, { 3859, 3859 }, { 3861, 3863 }, { 3866, 3871 }, { 3892, 3892 }, { 3894, 3894 }, { 3896, 3896 }, { 4030, 4037 }, { 4039, 4044 }, { 4046, 4047 }, { 4053, 4056 }, { 4254, 4255 }, { 5008, 5017 }, { 6107, 6107 }, { 6464, 6464 }, { 6622, 6655 }, { 7009, 7018 }, { 7028, 7036 }, { 8125, 8125 }, { 8127, 8129 }, { 8141, 8143 }, { 8157, 8159 }, { 8173, 8175 }, { 8189, 8190 }, { 8260, 8260 }, { 8274, 8274 }, { 8314, 8316 }, { 8330, 8332 }, { 8352, 8378 }, { 8448, 8449 }, { 8451, 8454 }, { 8456, 8457 }, { 8468, 8468 }, { 8470, 8472 }, { 8478, 8483 }, { 8485, 8485 }, { 8487, 8487 }, { 8489, 8489 }, { 8494, 8494 }, { 8506, 8507 }, { 8512, 8516 }, { 8522, 8525 }, { 8527, 8527 }, { 8592, 8967 }, { 8972, 9000 }, { 9003, 9203 }, { 9216, 9254 }, { 9280, 9290 }, { 9372, 9449 }, { 9472, 9983 }, { 9985, 10087 }, { 10132, 10180 }, { 10183, 10213 }, { 10224, 10626 }, { 10649, 10711 }, { 10716, 10747 }, { 10750, 11084 }, { 11088, 11097 }, { 11493, 11498 }, { 11904, 11929 }, { 11931, 12019 }, { 12032, 12245 }, { 12272, 12283 }, { 12292, 12292 }, { 12306, 12307 }, { 12320, 12320 }, { 12342, 12343 }, { 12350, 12351 }, { 12443, 12444 }, { 12688, 12689 }, { 12694, 12703 }, { 12736, 12771 }, { 12800, 12830 }, { 12842, 12871 }, { 12880, 12880 }, { 12896, 12927 }, { 12938, 12976 }, { 12992, 13054 }, { 13056, 13311 }, { 19904, 19967 }, { 42128, 42182 }, { 42752, 42774 }, { 42784, 42785 }, { 42889, 42890 }, { 43048, 43051 }, { 43062, 43065 }, { 43639, 43641 }, { 64297, 64297 }, { 64434, 64449 }, { 65020, 65021 }, { 65122, 65122 }, { 65124, 65126 }, { 65129, 65129 }, { 65284, 65284 }, { 65291, 65291 }, { 65308, 65310 }, { 65342, 65342 }, { 65344, 65344 }, { 65372, 65372 }, { 65374, 65374 }, { 65504, 65510 }, { 65512, 65518 }, { 65532, 65533 }, }; static const URange32 S_range32[] = { { 65847, 65855 }, { 65913, 65929 }, { 65936, 65947 }, { 66000, 66044 }, { 118784, 119029 }, { 119040, 119078 }, { 119081, 119140 }, { 119146, 119148 }, { 119171, 119172 }, { 119180, 119209 }, { 119214, 119261 }, { 119296, 119361 }, { 119365, 119365 }, { 119552, 119638 }, { 120513, 120513 }, { 120539, 120539 }, { 120571, 120571 }, { 120597, 120597 }, { 120629, 120629 }, { 120655, 120655 }, { 120687, 120687 }, { 120713, 120713 }, { 120745, 120745 }, { 120771, 120771 }, { 126704, 126705 }, { 126976, 127019 }, { 127024, 127123 }, { 127136, 127150 }, { 127153, 127166 }, { 127169, 127183 }, { 127185, 127199 }, { 127248, 127278 }, { 127280, 127339 }, { 127344, 127386 }, { 127462, 127490 }, { 127504, 127546 }, { 127552, 127560 }, { 127568, 127569 }, { 127744, 127776 }, { 127792, 127797 }, { 127799, 127868 }, { 127872, 127891 }, { 127904, 127940 }, { 127942, 127946 }, { 127968, 127984 }, { 128000, 128062 }, { 128064, 128064 }, { 128066, 128247 }, { 128249, 128252 }, { 128256, 128317 }, { 128320, 128323 }, { 128336, 128359 }, { 128507, 128576 }, { 128581, 128591 }, { 128640, 128709 }, { 128768, 128883 }, }; static const URange16 So_range16[] = { { 166, 166 }, { 169, 169 }, { 174, 174 }, { 176, 176 }, { 1154, 1154 }, { 1550, 1551 }, { 1758, 1758 }, { 1769, 1769 }, { 1789, 1790 }, { 2038, 2038 }, { 2554, 2554 }, { 2928, 2928 }, { 3059, 3064 }, { 3066, 3066 }, { 3199, 3199 }, { 3449, 3449 }, { 3841, 3843 }, { 3859, 3859 }, { 3861, 3863 }, { 3866, 3871 }, { 3892, 3892 }, { 3894, 3894 }, { 3896, 3896 }, { 4030, 4037 }, { 4039, 4044 }, { 4046, 4047 }, { 4053, 4056 }, { 4254, 4255 }, { 5008, 5017 }, { 6464, 6464 }, { 6622, 6655 }, { 7009, 7018 }, { 7028, 7036 }, { 8448, 8449 }, { 8451, 8454 }, { 8456, 8457 }, { 8468, 8468 }, { 8470, 8471 }, { 8478, 8483 }, { 8485, 8485 }, { 8487, 8487 }, { 8489, 8489 }, { 8494, 8494 }, { 8506, 8507 }, { 8522, 8522 }, { 8524, 8525 }, { 8527, 8527 }, { 8597, 8601 }, { 8604, 8607 }, { 8609, 8610 }, { 8612, 8613 }, { 8615, 8621 }, { 8623, 8653 }, { 8656, 8657 }, { 8659, 8659 }, { 8661, 8691 }, { 8960, 8967 }, { 8972, 8991 }, { 8994, 9000 }, { 9003, 9083 }, { 9085, 9114 }, { 9140, 9179 }, { 9186, 9203 }, { 9216, 9254 }, { 9280, 9290 }, { 9372, 9449 }, { 9472, 9654 }, { 9656, 9664 }, { 9666, 9719 }, { 9728, 9838 }, { 9840, 9983 }, { 9985, 10087 }, { 10132, 10175 }, { 10240, 10495 }, { 11008, 11055 }, { 11077, 11078 }, { 11088, 11097 }, { 11493, 11498 }, { 11904, 11929 }, { 11931, 12019 }, { 12032, 12245 }, { 12272, 12283 }, { 12292, 12292 }, { 12306, 12307 }, { 12320, 12320 }, { 12342, 12343 }, { 12350, 12351 }, { 12688, 12689 }, { 12694, 12703 }, { 12736, 12771 }, { 12800, 12830 }, { 12842, 12871 }, { 12880, 12880 }, { 12896, 12927 }, { 12938, 12976 }, { 12992, 13054 }, { 13056, 13311 }, { 19904, 19967 }, { 42128, 42182 }, { 43048, 43051 }, { 43062, 43063 }, { 43065, 43065 }, { 43639, 43641 }, { 65021, 65021 }, { 65508, 65508 }, { 65512, 65512 }, { 65517, 65518 }, { 65532, 65533 }, }; static const URange32 So_range32[] = { { 65847, 65855 }, { 65913, 65929 }, { 65936, 65947 }, { 66000, 66044 }, { 118784, 119029 }, { 119040, 119078 }, { 119081, 119140 }, { 119146, 119148 }, { 119171, 119172 }, { 119180, 119209 }, { 119214, 119261 }, { 119296, 119361 }, { 119365, 119365 }, { 119552, 119638 }, { 126976, 127019 }, { 127024, 127123 }, { 127136, 127150 }, { 127153, 127166 }, { 127169, 127183 }, { 127185, 127199 }, { 127248, 127278 }, { 127280, 127339 }, { 127344, 127386 }, { 127462, 127490 }, { 127504, 127546 }, { 127552, 127560 }, { 127568, 127569 }, { 127744, 127776 }, { 127792, 127797 }, { 127799, 127868 }, { 127872, 127891 }, { 127904, 127940 }, { 127942, 127946 }, { 127968, 127984 }, { 128000, 128062 }, { 128064, 128064 }, { 128066, 128247 }, { 128249, 128252 }, { 128256, 128317 }, { 128320, 128323 }, { 128336, 128359 }, { 128507, 128576 }, { 128581, 128591 }, { 128640, 128709 }, { 128768, 128883 }, }; static const URange16 Sm_range16[] = { { 43, 43 }, { 60, 62 }, { 124, 124 }, { 126, 126 }, { 172, 172 }, { 177, 177 }, { 215, 215 }, { 247, 247 }, { 1014, 1014 }, { 1542, 1544 }, { 8260, 8260 }, { 8274, 8274 }, { 8314, 8316 }, { 8330, 8332 }, { 8472, 8472 }, { 8512, 8516 }, { 8523, 8523 }, { 8592, 8596 }, { 8602, 8603 }, { 8608, 8608 }, { 8611, 8611 }, { 8614, 8614 }, { 8622, 8622 }, { 8654, 8655 }, { 8658, 8658 }, { 8660, 8660 }, { 8692, 8959 }, { 8992, 8993 }, { 9084, 9084 }, { 9115, 9139 }, { 9180, 9185 }, { 9655, 9655 }, { 9665, 9665 }, { 9720, 9727 }, { 9839, 9839 }, { 10176, 10180 }, { 10183, 10213 }, { 10224, 10239 }, { 10496, 10626 }, { 10649, 10711 }, { 10716, 10747 }, { 10750, 11007 }, { 11056, 11076 }, { 11079, 11084 }, { 64297, 64297 }, { 65122, 65122 }, { 65124, 65126 }, { 65291, 65291 }, { 65308, 65310 }, { 65372, 65372 }, { 65374, 65374 }, { 65506, 65506 }, { 65513, 65516 }, }; static const URange32 Sm_range32[] = { { 120513, 120513 }, { 120539, 120539 }, { 120571, 120571 }, { 120597, 120597 }, { 120629, 120629 }, { 120655, 120655 }, { 120687, 120687 }, { 120713, 120713 }, { 120745, 120745 }, { 120771, 120771 }, { 126704, 126705 }, }; static const URange16 Sc_range16[] = { { 36, 36 }, { 162, 165 }, { 1423, 1423 }, { 1547, 1547 }, { 2546, 2547 }, { 2555, 2555 }, { 2801, 2801 }, { 3065, 3065 }, { 3647, 3647 }, { 6107, 6107 }, { 8352, 8378 }, { 43064, 43064 }, { 65020, 65020 }, { 65129, 65129 }, { 65284, 65284 }, { 65504, 65505 }, { 65509, 65510 }, }; static const URange16 Z_range16[] = { { 32, 32 }, { 160, 160 }, { 5760, 5760 }, { 8192, 8202 }, { 8232, 8233 }, { 8239, 8239 }, { 8287, 8287 }, { 12288, 12288 }, }; static const URange16 Zl_range16[] = { { 8232, 8232 }, }; static const URange16 Co_range16[] = { { 57344, 63743 }, }; static const URange32 Co_range32[] = { { 983040, 1048573 }, { 1048576, 1114109 }, }; static const URange16 Cc_range16[] = { { 0, 31 }, { 127, 159 }, }; static const URange16 Cf_range16[] = { { 173, 173 }, { 1536, 1540 }, { 1564, 1564 }, { 1757, 1757 }, { 1807, 1807 }, { 6158, 6158 }, { 8203, 8207 }, { 8234, 8238 }, { 8288, 8292 }, { 8294, 8303 }, { 65279, 65279 }, { 65529, 65531 }, }; static const URange32 Cf_range32[] = { { 69821, 69821 }, { 119155, 119162 }, { 917505, 917505 }, { 917536, 917631 }, }; static const URange16 Cs_range16[] = { { 55296, 57343 }, }; static const URange16 Zp_range16[] = { { 8233, 8233 }, }; static const URange16 Zs_range16[] = { { 32, 32 }, { 160, 160 }, { 5760, 5760 }, { 8192, 8202 }, { 8239, 8239 }, { 8287, 8287 }, { 12288, 12288 }, }; static const URange16 Thaana_range16[] = { { 1920, 1969 }, }; static const URange16 Telugu_range16[] = { { 3073, 3075 }, { 3077, 3084 }, { 3086, 3088 }, { 3090, 3112 }, { 3114, 3123 }, { 3125, 3129 }, { 3133, 3140 }, { 3142, 3144 }, { 3146, 3149 }, { 3157, 3158 }, { 3160, 3161 }, { 3168, 3171 }, { 3174, 3183 }, { 3192, 3199 }, }; static const URange16 Cyrillic_range16[] = { { 1024, 1156 }, { 1159, 1319 }, { 7467, 7467 }, { 7544, 7544 }, { 11744, 11775 }, { 42560, 42647 }, { 42655, 42655 }, }; static const URange16 Hangul_range16[] = { { 4352, 4607 }, { 12334, 12335 }, { 12593, 12686 }, { 12800, 12830 }, { 12896, 12926 }, { 43360, 43388 }, { 44032, 55203 }, { 55216, 55238 }, { 55243, 55291 }, { 65440, 65470 }, { 65474, 65479 }, { 65482, 65487 }, { 65490, 65495 }, { 65498, 65500 }, }; static const URange32 Old_South_Arabian_range32[] = { { 68192, 68223 }, }; static const URange16 Ethiopic_range16[] = { { 4608, 4680 }, { 4682, 4685 }, { 4688, 4694 }, { 4696, 4696 }, { 4698, 4701 }, { 4704, 4744 }, { 4746, 4749 }, { 4752, 4784 }, { 4786, 4789 }, { 4792, 4798 }, { 4800, 4800 }, { 4802, 4805 }, { 4808, 4822 }, { 4824, 4880 }, { 4882, 4885 }, { 4888, 4954 }, { 4957, 4988 }, { 4992, 5017 }, { 11648, 11670 }, { 11680, 11686 }, { 11688, 11694 }, { 11696, 11702 }, { 11704, 11710 }, { 11712, 11718 }, { 11720, 11726 }, { 11728, 11734 }, { 11736, 11742 }, { 43777, 43782 }, { 43785, 43790 }, { 43793, 43798 }, { 43808, 43814 }, { 43816, 43822 }, }; static const URange16 Inherited_range16[] = { { 768, 879 }, { 1157, 1158 }, { 1611, 1621 }, { 1648, 1648 }, { 2385, 2386 }, { 7376, 7378 }, { 7380, 7392 }, { 7394, 7400 }, { 7405, 7405 }, { 7412, 7412 }, { 7616, 7654 }, { 7676, 7679 }, { 8204, 8205 }, { 8400, 8432 }, { 12330, 12333 }, { 12441, 12442 }, { 65024, 65039 }, { 65056, 65062 }, }; static const URange32 Inherited_range32[] = { { 66045, 66045 }, { 119143, 119145 }, { 119163, 119170 }, { 119173, 119179 }, { 119210, 119213 }, { 917760, 917999 }, }; static const URange32 Meroitic_Cursive_range32[] = { { 68000, 68023 }, { 68030, 68031 }, }; static const URange16 Han_range16[] = { { 11904, 11929 }, { 11931, 12019 }, { 12032, 12245 }, { 12293, 12293 }, { 12295, 12295 }, { 12321, 12329 }, { 12344, 12347 }, { 13312, 19893 }, { 19968, 40908 }, { 63744, 64109 }, { 64112, 64217 }, }; static const URange32 Han_range32[] = { { 131072, 173782 }, { 173824, 177972 }, { 177984, 178205 }, { 194560, 195101 }, }; static const URange16 Armenian_range16[] = { { 1329, 1366 }, { 1369, 1375 }, { 1377, 1415 }, { 1418, 1418 }, { 1423, 1423 }, { 64275, 64279 }, }; static const URange16 Tamil_range16[] = { { 2946, 2947 }, { 2949, 2954 }, { 2958, 2960 }, { 2962, 2965 }, { 2969, 2970 }, { 2972, 2972 }, { 2974, 2975 }, { 2979, 2980 }, { 2984, 2986 }, { 2990, 3001 }, { 3006, 3010 }, { 3014, 3016 }, { 3018, 3021 }, { 3024, 3024 }, { 3031, 3031 }, { 3046, 3066 }, }; static const URange16 Bopomofo_range16[] = { { 746, 747 }, { 12549, 12589 }, { 12704, 12730 }, }; static const URange16 Sundanese_range16[] = { { 7040, 7103 }, { 7360, 7367 }, }; static const URange16 Tagalog_range16[] = { { 5888, 5900 }, { 5902, 5908 }, }; static const URange16 Malayalam_range16[] = { { 3330, 3331 }, { 3333, 3340 }, { 3342, 3344 }, { 3346, 3386 }, { 3389, 3396 }, { 3398, 3400 }, { 3402, 3406 }, { 3415, 3415 }, { 3424, 3427 }, { 3430, 3445 }, { 3449, 3455 }, }; static const URange32 Carian_range32[] = { { 66208, 66256 }, }; static const URange16 Hiragana_range16[] = { { 12353, 12438 }, { 12445, 12447 }, }; static const URange32 Hiragana_range32[] = { { 110593, 110593 }, { 127488, 127488 }, }; static const URange16 Tagbanwa_range16[] = { { 5984, 5996 }, { 5998, 6000 }, { 6002, 6003 }, }; static const URange16 Meetei_Mayek_range16[] = { { 43744, 43766 }, { 43968, 44013 }, { 44016, 44025 }, }; static const URange16 Tai_Le_range16[] = { { 6480, 6509 }, { 6512, 6516 }, }; static const URange16 Kayah_Li_range16[] = { { 43264, 43311 }, }; static const URange16 Buginese_range16[] = { { 6656, 6683 }, { 6686, 6687 }, }; static const URange32 Kharoshthi_range32[] = { { 68096, 68099 }, { 68101, 68102 }, { 68108, 68115 }, { 68117, 68119 }, { 68121, 68147 }, { 68152, 68154 }, { 68159, 68167 }, { 68176, 68184 }, }; static const URange16 Tai_Tham_range16[] = { { 6688, 6750 }, { 6752, 6780 }, { 6783, 6793 }, { 6800, 6809 }, { 6816, 6829 }, }; static const URange32 Old_Italic_range32[] = { { 66304, 66334 }, { 66336, 66339 }, }; static const URange32 Old_Persian_range32[] = { { 66464, 66499 }, { 66504, 66517 }, }; static const URange16 Latin_range16[] = { { 65, 90 }, { 97, 122 }, { 170, 170 }, { 186, 186 }, { 192, 214 }, { 216, 246 }, { 248, 696 }, { 736, 740 }, { 7424, 7461 }, { 7468, 7516 }, { 7522, 7525 }, { 7531, 7543 }, { 7545, 7614 }, { 7680, 7935 }, { 8305, 8305 }, { 8319, 8319 }, { 8336, 8348 }, { 8490, 8491 }, { 8498, 8498 }, { 8526, 8526 }, { 8544, 8584 }, { 11360, 11391 }, { 42786, 42887 }, { 42891, 42894 }, { 42896, 42899 }, { 42912, 42922 }, { 43000, 43007 }, { 64256, 64262 }, { 65313, 65338 }, { 65345, 65370 }, }; static const URange16 Saurashtra_range16[] = { { 43136, 43204 }, { 43214, 43225 }, }; static const URange32 Shavian_range32[] = { { 66640, 66687 }, }; static const URange16 Georgian_range16[] = { { 4256, 4293 }, { 4295, 4295 }, { 4301, 4301 }, { 4304, 4346 }, { 4348, 4351 }, { 11520, 11557 }, { 11559, 11559 }, { 11565, 11565 }, }; static const URange16 Batak_range16[] = { { 7104, 7155 }, { 7164, 7167 }, }; static const URange16 Devanagari_range16[] = { { 2304, 2384 }, { 2387, 2403 }, { 2406, 2423 }, { 2425, 2431 }, { 43232, 43259 }, }; static const URange16 Thai_range16[] = { { 3585, 3642 }, { 3648, 3675 }, }; static const URange16 Tibetan_range16[] = { { 3840, 3911 }, { 3913, 3948 }, { 3953, 3991 }, { 3993, 4028 }, { 4030, 4044 }, { 4046, 4052 }, { 4057, 4058 }, }; static const URange16 Tifinagh_range16[] = { { 11568, 11623 }, { 11631, 11632 }, { 11647, 11647 }, }; static const URange32 Ugaritic_range32[] = { { 66432, 66461 }, { 66463, 66463 }, }; static const URange16 Braille_range16[] = { { 10240, 10495 }, }; static const URange16 Greek_range16[] = { { 880, 883 }, { 885, 887 }, { 890, 893 }, { 900, 900 }, { 902, 902 }, { 904, 906 }, { 908, 908 }, { 910, 929 }, { 931, 993 }, { 1008, 1023 }, { 7462, 7466 }, { 7517, 7521 }, { 7526, 7530 }, { 7615, 7615 }, { 7936, 7957 }, { 7960, 7965 }, { 7968, 8005 }, { 8008, 8013 }, { 8016, 8023 }, { 8025, 8025 }, { 8027, 8027 }, { 8029, 8029 }, { 8031, 8061 }, { 8064, 8116 }, { 8118, 8132 }, { 8134, 8147 }, { 8150, 8155 }, { 8157, 8175 }, { 8178, 8180 }, { 8182, 8190 }, { 8486, 8486 }, }; static const URange32 Greek_range32[] = { { 65856, 65930 }, { 119296, 119365 }, }; static const URange32 Lycian_range32[] = { { 66176, 66204 }, }; static const URange16 Tai_Viet_range16[] = { { 43648, 43714 }, { 43739, 43743 }, }; static const URange16 Vai_range16[] = { { 42240, 42539 }, }; static const URange16 Ogham_range16[] = { { 5760, 5788 }, }; static const URange32 Inscriptional_Parthian_range32[] = { { 68416, 68437 }, { 68440, 68447 }, }; static const URange16 Cham_range16[] = { { 43520, 43574 }, { 43584, 43597 }, { 43600, 43609 }, { 43612, 43615 }, }; static const URange16 Syriac_range16[] = { { 1792, 1805 }, { 1807, 1866 }, { 1869, 1871 }, }; static const URange16 Runic_range16[] = { { 5792, 5866 }, { 5870, 5872 }, }; static const URange32 Gothic_range32[] = { { 66352, 66378 }, }; static const URange16 Katakana_range16[] = { { 12449, 12538 }, { 12541, 12543 }, { 12784, 12799 }, { 13008, 13054 }, { 13056, 13143 }, { 65382, 65391 }, { 65393, 65437 }, }; static const URange32 Katakana_range32[] = { { 110592, 110592 }, }; static const URange32 Osmanya_range32[] = { { 66688, 66717 }, { 66720, 66729 }, }; static const URange16 New_Tai_Lue_range16[] = { { 6528, 6571 }, { 6576, 6601 }, { 6608, 6618 }, { 6622, 6623 }, }; static const URange16 Ol_Chiki_range16[] = { { 7248, 7295 }, }; static const URange16 Limbu_range16[] = { { 6400, 6428 }, { 6432, 6443 }, { 6448, 6459 }, { 6464, 6464 }, { 6468, 6479 }, }; static const URange16 Cherokee_range16[] = { { 5024, 5108 }, }; static const URange32 Miao_range32[] = { { 93952, 94020 }, { 94032, 94078 }, { 94095, 94111 }, }; static const URange16 Oriya_range16[] = { { 2817, 2819 }, { 2821, 2828 }, { 2831, 2832 }, { 2835, 2856 }, { 2858, 2864 }, { 2866, 2867 }, { 2869, 2873 }, { 2876, 2884 }, { 2887, 2888 }, { 2891, 2893 }, { 2902, 2903 }, { 2908, 2909 }, { 2911, 2915 }, { 2918, 2935 }, }; static const URange32 Sharada_range32[] = { { 70016, 70088 }, { 70096, 70105 }, }; static const URange16 Gujarati_range16[] = { { 2689, 2691 }, { 2693, 2701 }, { 2703, 2705 }, { 2707, 2728 }, { 2730, 2736 }, { 2738, 2739 }, { 2741, 2745 }, { 2748, 2757 }, { 2759, 2761 }, { 2763, 2765 }, { 2768, 2768 }, { 2784, 2787 }, { 2790, 2801 }, }; static const URange32 Inscriptional_Pahlavi_range32[] = { { 68448, 68466 }, { 68472, 68479 }, }; static const URange16 Khmer_range16[] = { { 6016, 6109 }, { 6112, 6121 }, { 6128, 6137 }, { 6624, 6655 }, }; static const URange32 Cuneiform_range32[] = { { 73728, 74606 }, { 74752, 74850 }, { 74864, 74867 }, }; static const URange16 Mandaic_range16[] = { { 2112, 2139 }, { 2142, 2142 }, }; static const URange16 Syloti_Nagri_range16[] = { { 43008, 43051 }, }; static const URange16 Nko_range16[] = { { 1984, 2042 }, }; static const URange16 Canadian_Aboriginal_range16[] = { { 5120, 5759 }, { 6320, 6389 }, }; static const URange32 Meroitic_Hieroglyphs_range32[] = { { 67968, 67999 }, }; static const URange32 Phoenician_range32[] = { { 67840, 67867 }, { 67871, 67871 }, }; static const URange16 Bengali_range16[] = { { 2433, 2435 }, { 2437, 2444 }, { 2447, 2448 }, { 2451, 2472 }, { 2474, 2480 }, { 2482, 2482 }, { 2486, 2489 }, { 2492, 2500 }, { 2503, 2504 }, { 2507, 2510 }, { 2519, 2519 }, { 2524, 2525 }, { 2527, 2531 }, { 2534, 2555 }, }; static const URange32 Kaithi_range32[] = { { 69760, 69825 }, }; static const URange16 Glagolitic_range16[] = { { 11264, 11310 }, { 11312, 11358 }, }; static const URange32 Imperial_Aramaic_range32[] = { { 67648, 67669 }, { 67671, 67679 }, }; static const URange32 Sora_Sompeng_range32[] = { { 69840, 69864 }, { 69872, 69881 }, }; static const URange16 Gurmukhi_range16[] = { { 2561, 2563 }, { 2565, 2570 }, { 2575, 2576 }, { 2579, 2600 }, { 2602, 2608 }, { 2610, 2611 }, { 2613, 2614 }, { 2616, 2617 }, { 2620, 2620 }, { 2622, 2626 }, { 2631, 2632 }, { 2635, 2637 }, { 2641, 2641 }, { 2649, 2652 }, { 2654, 2654 }, { 2662, 2677 }, }; static const URange16 Javanese_range16[] = { { 43392, 43469 }, { 43472, 43481 }, { 43486, 43487 }, }; static const URange16 Phags_Pa_range16[] = { { 43072, 43127 }, }; static const URange32 Cypriot_range32[] = { { 67584, 67589 }, { 67592, 67592 }, { 67594, 67637 }, { 67639, 67640 }, { 67644, 67644 }, { 67647, 67647 }, }; static const URange16 Kannada_range16[] = { { 3202, 3203 }, { 3205, 3212 }, { 3214, 3216 }, { 3218, 3240 }, { 3242, 3251 }, { 3253, 3257 }, { 3260, 3268 }, { 3270, 3272 }, { 3274, 3277 }, { 3285, 3286 }, { 3294, 3294 }, { 3296, 3299 }, { 3302, 3311 }, { 3313, 3314 }, }; static const URange16 Mongolian_range16[] = { { 6144, 6145 }, { 6148, 6148 }, { 6150, 6158 }, { 6160, 6169 }, { 6176, 6263 }, { 6272, 6314 }, }; static const URange16 Sinhala_range16[] = { { 3458, 3459 }, { 3461, 3478 }, { 3482, 3505 }, { 3507, 3515 }, { 3517, 3517 }, { 3520, 3526 }, { 3530, 3530 }, { 3535, 3540 }, { 3542, 3542 }, { 3544, 3551 }, { 3570, 3572 }, }; static const URange32 Brahmi_range32[] = { { 69632, 69709 }, { 69714, 69743 }, }; static const URange32 Deseret_range32[] = { { 66560, 66639 }, }; static const URange16 Rejang_range16[] = { { 43312, 43347 }, { 43359, 43359 }, }; static const URange16 Yi_range16[] = { { 40960, 42124 }, { 42128, 42182 }, }; static const URange16 Balinese_range16[] = { { 6912, 6987 }, { 6992, 7036 }, }; static const URange16 Lao_range16[] = { { 3713, 3714 }, { 3716, 3716 }, { 3719, 3720 }, { 3722, 3722 }, { 3725, 3725 }, { 3732, 3735 }, { 3737, 3743 }, { 3745, 3747 }, { 3749, 3749 }, { 3751, 3751 }, { 3754, 3755 }, { 3757, 3769 }, { 3771, 3773 }, { 3776, 3780 }, { 3782, 3782 }, { 3784, 3789 }, { 3792, 3801 }, { 3804, 3807 }, }; static const URange16 Hanunoo_range16[] = { { 5920, 5940 }, }; static const URange32 Linear_B_range32[] = { { 65536, 65547 }, { 65549, 65574 }, { 65576, 65594 }, { 65596, 65597 }, { 65599, 65613 }, { 65616, 65629 }, { 65664, 65786 }, }; static const URange32 Old_Turkic_range32[] = { { 68608, 68680 }, }; static const URange16 Lepcha_range16[] = { { 7168, 7223 }, { 7227, 7241 }, { 7245, 7247 }, }; static const URange32 Lydian_range32[] = { { 67872, 67897 }, { 67903, 67903 }, }; static const URange32 Egyptian_Hieroglyphs_range32[] = { { 77824, 78894 }, }; static const URange16 Samaritan_range16[] = { { 2048, 2093 }, { 2096, 2110 }, }; static const URange16 Lisu_range16[] = { { 42192, 42239 }, }; static const URange16 Buhid_range16[] = { { 5952, 5971 }, }; static const URange16 Common_range16[] = { { 0, 64 }, { 91, 96 }, { 123, 169 }, { 171, 185 }, { 187, 191 }, { 215, 215 }, { 247, 247 }, { 697, 735 }, { 741, 745 }, { 748, 767 }, { 884, 884 }, { 894, 894 }, { 901, 901 }, { 903, 903 }, { 1417, 1417 }, { 1548, 1548 }, { 1563, 1563 }, { 1567, 1567 }, { 1600, 1600 }, { 1632, 1641 }, { 1757, 1757 }, { 2404, 2405 }, { 3647, 3647 }, { 4053, 4056 }, { 4347, 4347 }, { 5867, 5869 }, { 5941, 5942 }, { 6146, 6147 }, { 6149, 6149 }, { 7379, 7379 }, { 7393, 7393 }, { 7401, 7404 }, { 7406, 7411 }, { 7413, 7414 }, { 8192, 8203 }, { 8206, 8292 }, { 8294, 8304 }, { 8308, 8318 }, { 8320, 8334 }, { 8352, 8378 }, { 8448, 8485 }, { 8487, 8489 }, { 8492, 8497 }, { 8499, 8525 }, { 8527, 8543 }, { 8585, 8585 }, { 8592, 9203 }, { 9216, 9254 }, { 9280, 9290 }, { 9312, 9983 }, { 9985, 10239 }, { 10496, 11084 }, { 11088, 11097 }, { 11776, 11835 }, { 12272, 12283 }, { 12288, 12292 }, { 12294, 12294 }, { 12296, 12320 }, { 12336, 12343 }, { 12348, 12351 }, { 12443, 12444 }, { 12448, 12448 }, { 12539, 12540 }, { 12688, 12703 }, { 12736, 12771 }, { 12832, 12895 }, { 12927, 13007 }, { 13144, 13311 }, { 19904, 19967 }, { 42752, 42785 }, { 42888, 42890 }, { 43056, 43065 }, { 43471, 43471 }, { 64830, 64831 }, { 65021, 65021 }, { 65040, 65049 }, { 65072, 65106 }, { 65108, 65126 }, { 65128, 65131 }, { 65279, 65279 }, { 65281, 65312 }, { 65339, 65344 }, { 65371, 65381 }, { 65392, 65392 }, { 65438, 65439 }, { 65504, 65510 }, { 65512, 65518 }, { 65529, 65533 }, }; static const URange32 Common_range32[] = { { 65792, 65794 }, { 65799, 65843 }, { 65847, 65855 }, { 65936, 65947 }, { 66000, 66044 }, { 118784, 119029 }, { 119040, 119078 }, { 119081, 119142 }, { 119146, 119162 }, { 119171, 119172 }, { 119180, 119209 }, { 119214, 119261 }, { 119552, 119638 }, { 119648, 119665 }, { 119808, 119892 }, { 119894, 119964 }, { 119966, 119967 }, { 119970, 119970 }, { 119973, 119974 }, { 119977, 119980 }, { 119982, 119993 }, { 119995, 119995 }, { 119997, 120003 }, { 120005, 120069 }, { 120071, 120074 }, { 120077, 120084 }, { 120086, 120092 }, { 120094, 120121 }, { 120123, 120126 }, { 120128, 120132 }, { 120134, 120134 }, { 120138, 120144 }, { 120146, 120485 }, { 120488, 120779 }, { 120782, 120831 }, { 126976, 127019 }, { 127024, 127123 }, { 127136, 127150 }, { 127153, 127166 }, { 127169, 127183 }, { 127185, 127199 }, { 127232, 127242 }, { 127248, 127278 }, { 127280, 127339 }, { 127344, 127386 }, { 127462, 127487 }, { 127489, 127490 }, { 127504, 127546 }, { 127552, 127560 }, { 127568, 127569 }, { 127744, 127776 }, { 127792, 127797 }, { 127799, 127868 }, { 127872, 127891 }, { 127904, 127940 }, { 127942, 127946 }, { 127968, 127984 }, { 128000, 128062 }, { 128064, 128064 }, { 128066, 128247 }, { 128249, 128252 }, { 128256, 128317 }, { 128320, 128323 }, { 128336, 128359 }, { 128507, 128576 }, { 128581, 128591 }, { 128640, 128709 }, { 128768, 128883 }, { 917505, 917505 }, { 917536, 917631 }, }; static const URange16 Coptic_range16[] = { { 994, 1007 }, { 11392, 11507 }, { 11513, 11519 }, }; static const URange32 Chakma_range32[] = { { 69888, 69940 }, { 69942, 69955 }, }; static const URange16 Arabic_range16[] = { { 1536, 1540 }, { 1542, 1547 }, { 1549, 1562 }, { 1564, 1564 }, { 1566, 1566 }, { 1568, 1599 }, { 1601, 1610 }, { 1622, 1631 }, { 1642, 1647 }, { 1649, 1756 }, { 1758, 1791 }, { 1872, 1919 }, { 2208, 2208 }, { 2210, 2220 }, { 2276, 2302 }, { 64336, 64449 }, { 64467, 64829 }, { 64848, 64911 }, { 64914, 64967 }, { 65008, 65020 }, { 65136, 65140 }, { 65142, 65276 }, }; static const URange32 Arabic_range32[] = { { 69216, 69246 }, { 126464, 126467 }, { 126469, 126495 }, { 126497, 126498 }, { 126500, 126500 }, { 126503, 126503 }, { 126505, 126514 }, { 126516, 126519 }, { 126521, 126521 }, { 126523, 126523 }, { 126530, 126530 }, { 126535, 126535 }, { 126537, 126537 }, { 126539, 126539 }, { 126541, 126543 }, { 126545, 126546 }, { 126548, 126548 }, { 126551, 126551 }, { 126553, 126553 }, { 126555, 126555 }, { 126557, 126557 }, { 126559, 126559 }, { 126561, 126562 }, { 126564, 126564 }, { 126567, 126570 }, { 126572, 126578 }, { 126580, 126583 }, { 126585, 126588 }, { 126590, 126590 }, { 126592, 126601 }, { 126603, 126619 }, { 126625, 126627 }, { 126629, 126633 }, { 126635, 126651 }, { 126704, 126705 }, }; static const URange16 Bamum_range16[] = { { 42656, 42743 }, }; static const URange32 Bamum_range32[] = { { 92160, 92728 }, }; static const URange16 Myanmar_range16[] = { { 4096, 4255 }, { 43616, 43643 }, }; static const URange32 Avestan_range32[] = { { 68352, 68405 }, { 68409, 68415 }, }; static const URange16 Hebrew_range16[] = { { 1425, 1479 }, { 1488, 1514 }, { 1520, 1524 }, { 64285, 64310 }, { 64312, 64316 }, { 64318, 64318 }, { 64320, 64321 }, { 64323, 64324 }, { 64326, 64335 }, }; static const URange32 Takri_range32[] = { { 71296, 71351 }, { 71360, 71369 }, }; // 3867 16-bit ranges, 723 32-bit ranges const UGroup unicode_groups[] = { { "Arabic", +1, Arabic_range16, 22, Arabic_range32, 35 }, { "Armenian", +1, Armenian_range16, 6, 0, 0 }, { "Avestan", +1, 0, 0, Avestan_range32, 2 }, { "Balinese", +1, Balinese_range16, 2, 0, 0 }, { "Bamum", +1, Bamum_range16, 1, Bamum_range32, 1 }, { "Batak", +1, Batak_range16, 2, 0, 0 }, { "Bengali", +1, Bengali_range16, 14, 0, 0 }, { "Bopomofo", +1, Bopomofo_range16, 3, 0, 0 }, { "Brahmi", +1, 0, 0, Brahmi_range32, 2 }, { "Braille", +1, Braille_range16, 1, 0, 0 }, { "Buginese", +1, Buginese_range16, 2, 0, 0 }, { "Buhid", +1, Buhid_range16, 1, 0, 0 }, { "C", +1, C_range16, 15, C_range32, 6 }, { "Canadian_Aboriginal", +1, Canadian_Aboriginal_range16, 2, 0, 0 }, { "Carian", +1, 0, 0, Carian_range32, 1 }, { "Cc", +1, Cc_range16, 2, 0, 0 }, { "Cf", +1, Cf_range16, 12, Cf_range32, 4 }, { "Chakma", +1, 0, 0, Chakma_range32, 2 }, { "Cham", +1, Cham_range16, 4, 0, 0 }, { "Cherokee", +1, Cherokee_range16, 1, 0, 0 }, { "Co", +1, Co_range16, 1, Co_range32, 2 }, { "Common", +1, Common_range16, 88, Common_range32, 70 }, { "Coptic", +1, Coptic_range16, 3, 0, 0 }, { "Cs", +1, Cs_range16, 1, 0, 0 }, { "Cuneiform", +1, 0, 0, Cuneiform_range32, 3 }, { "Cypriot", +1, 0, 0, Cypriot_range32, 6 }, { "Cyrillic", +1, Cyrillic_range16, 7, 0, 0 }, { "Deseret", +1, 0, 0, Deseret_range32, 1 }, { "Devanagari", +1, Devanagari_range16, 5, 0, 0 }, { "Egyptian_Hieroglyphs", +1, 0, 0, Egyptian_Hieroglyphs_range32, 1 }, { "Ethiopic", +1, Ethiopic_range16, 32, 0, 0 }, { "Georgian", +1, Georgian_range16, 8, 0, 0 }, { "Glagolitic", +1, Glagolitic_range16, 2, 0, 0 }, { "Gothic", +1, 0, 0, Gothic_range32, 1 }, { "Greek", +1, Greek_range16, 31, Greek_range32, 2 }, { "Gujarati", +1, Gujarati_range16, 13, 0, 0 }, { "Gurmukhi", +1, Gurmukhi_range16, 16, 0, 0 }, { "Han", +1, Han_range16, 11, Han_range32, 4 }, { "Hangul", +1, Hangul_range16, 14, 0, 0 }, { "Hanunoo", +1, Hanunoo_range16, 1, 0, 0 }, { "Hebrew", +1, Hebrew_range16, 9, 0, 0 }, { "Hiragana", +1, Hiragana_range16, 2, Hiragana_range32, 2 }, { "Imperial_Aramaic", +1, 0, 0, Imperial_Aramaic_range32, 2 }, { "Inherited", +1, Inherited_range16, 18, Inherited_range32, 6 }, { "Inscriptional_Pahlavi", +1, 0, 0, Inscriptional_Pahlavi_range32, 2 }, { "Inscriptional_Parthian", +1, 0, 0, Inscriptional_Parthian_range32, 2 }, { "Javanese", +1, Javanese_range16, 3, 0, 0 }, { "Kaithi", +1, 0, 0, Kaithi_range32, 1 }, { "Kannada", +1, Kannada_range16, 14, 0, 0 }, { "Katakana", +1, Katakana_range16, 7, Katakana_range32, 1 }, { "Kayah_Li", +1, Kayah_Li_range16, 1, 0, 0 }, { "Kharoshthi", +1, 0, 0, Kharoshthi_range32, 8 }, { "Khmer", +1, Khmer_range16, 4, 0, 0 }, { "L", +1, L_range16, 370, L_range32, 116 }, { "Lao", +1, Lao_range16, 18, 0, 0 }, { "Latin", +1, Latin_range16, 30, 0, 0 }, { "Lepcha", +1, Lepcha_range16, 3, 0, 0 }, { "Limbu", +1, Limbu_range16, 5, 0, 0 }, { "Linear_B", +1, 0, 0, Linear_B_range32, 7 }, { "Lisu", +1, Lisu_range16, 1, 0, 0 }, { "Ll", +1, Ll_range16, 582, Ll_range32, 29 }, { "Lm", +1, Lm_range16, 51, Lm_range32, 1 }, { "Lo", +1, Lo_range16, 286, Lo_range32, 85 }, { "Lt", +1, Lt_range16, 10, 0, 0 }, { "Lu", +1, Lu_range16, 576, Lu_range32, 32 }, { "Lycian", +1, 0, 0, Lycian_range32, 1 }, { "Lydian", +1, 0, 0, Lydian_range32, 2 }, { "M", +1, M_range16, 180, M_range32, 24 }, { "Malayalam", +1, Malayalam_range16, 11, 0, 0 }, { "Mandaic", +1, Mandaic_range16, 2, 0, 0 }, { "Mc", +1, Mc_range16, 111, Mc_range32, 15 }, { "Me", +1, Me_range16, 4, 0, 0 }, { "Meetei_Mayek", +1, Meetei_Mayek_range16, 3, 0, 0 }, { "Meroitic_Cursive", +1, 0, 0, Meroitic_Cursive_range32, 2 }, { "Meroitic_Hieroglyphs", +1, 0, 0, Meroitic_Hieroglyphs_range32, 1 }, { "Miao", +1, 0, 0, Miao_range32, 3 }, { "Mn", +1, Mn_range16, 194, Mn_range32, 27 }, { "Mongolian", +1, Mongolian_range16, 6, 0, 0 }, { "Myanmar", +1, Myanmar_range16, 2, 0, 0 }, { "N", +1, N_range16, 64, N_range32, 24 }, { "Nd", +1, Nd_range16, 35, Nd_range32, 7 }, { "New_Tai_Lue", +1, New_Tai_Lue_range16, 4, 0, 0 }, { "Nko", +1, Nko_range16, 1, 0, 0 }, { "Nl", +1, Nl_range16, 7, Nl_range32, 5 }, { "No", +1, No_range16, 28, No_range32, 14 }, { "Ogham", +1, Ogham_range16, 1, 0, 0 }, { "Ol_Chiki", +1, Ol_Chiki_range16, 1, 0, 0 }, { "Old_Italic", +1, 0, 0, Old_Italic_range32, 2 }, { "Old_Persian", +1, 0, 0, Old_Persian_range32, 2 }, { "Old_South_Arabian", +1, 0, 0, Old_South_Arabian_range32, 1 }, { "Old_Turkic", +1, 0, 0, Old_Turkic_range32, 1 }, { "Oriya", +1, Oriya_range16, 14, 0, 0 }, { "Osmanya", +1, 0, 0, Osmanya_range32, 2 }, { "P", +1, P_range16, 126, P_range32, 15 }, { "Pc", +1, Pc_range16, 6, 0, 0 }, { "Pd", +1, Pd_range16, 16, 0, 0 }, { "Pe", +1, Pe_range16, 72, 0, 0 }, { "Pf", +1, Pf_range16, 10, 0, 0 }, { "Phags_Pa", +1, Phags_Pa_range16, 1, 0, 0 }, { "Phoenician", +1, 0, 0, Phoenician_range32, 2 }, { "Pi", +1, Pi_range16, 11, 0, 0 }, { "Po", +1, Po_range16, 120, Po_range32, 15 }, { "Ps", +1, Ps_range16, 74, 0, 0 }, { "Rejang", +1, Rejang_range16, 2, 0, 0 }, { "Runic", +1, Runic_range16, 2, 0, 0 }, { "S", +1, S_range16, 143, S_range32, 56 }, { "Samaritan", +1, Samaritan_range16, 2, 0, 0 }, { "Saurashtra", +1, Saurashtra_range16, 2, 0, 0 }, { "Sc", +1, Sc_range16, 17, 0, 0 }, { "Sharada", +1, 0, 0, Sharada_range32, 2 }, { "Shavian", +1, 0, 0, Shavian_range32, 1 }, { "Sinhala", +1, Sinhala_range16, 11, 0, 0 }, { "Sk", +1, Sk_range16, 27, 0, 0 }, { "Sm", +1, Sm_range16, 53, Sm_range32, 11 }, { "So", +1, So_range16, 108, So_range32, 45 }, { "Sora_Sompeng", +1, 0, 0, Sora_Sompeng_range32, 2 }, { "Sundanese", +1, Sundanese_range16, 2, 0, 0 }, { "Syloti_Nagri", +1, Syloti_Nagri_range16, 1, 0, 0 }, { "Syriac", +1, Syriac_range16, 3, 0, 0 }, { "Tagalog", +1, Tagalog_range16, 2, 0, 0 }, { "Tagbanwa", +1, Tagbanwa_range16, 3, 0, 0 }, { "Tai_Le", +1, Tai_Le_range16, 2, 0, 0 }, { "Tai_Tham", +1, Tai_Tham_range16, 5, 0, 0 }, { "Tai_Viet", +1, Tai_Viet_range16, 2, 0, 0 }, { "Takri", +1, 0, 0, Takri_range32, 2 }, { "Tamil", +1, Tamil_range16, 16, 0, 0 }, { "Telugu", +1, Telugu_range16, 14, 0, 0 }, { "Thaana", +1, Thaana_range16, 1, 0, 0 }, { "Thai", +1, Thai_range16, 2, 0, 0 }, { "Tibetan", +1, Tibetan_range16, 7, 0, 0 }, { "Tifinagh", +1, Tifinagh_range16, 3, 0, 0 }, { "Ugaritic", +1, 0, 0, Ugaritic_range32, 2 }, { "Vai", +1, Vai_range16, 1, 0, 0 }, { "Yi", +1, Yi_range16, 2, 0, 0 }, { "Z", +1, Z_range16, 8, 0, 0 }, { "Zl", +1, Zl_range16, 1, 0, 0 }, { "Zp", +1, Zp_range16, 1, 0, 0 }, { "Zs", +1, Zs_range16, 7, 0, 0 }, }; const int num_unicode_groups = 138; } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/unicode_groups.h000066400000000000000000000027401266464252400244070ustar00rootroot00000000000000// Copyright 2008 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Unicode character groups. // The codes get split into ranges of 16-bit codes // and ranges of 32-bit codes. It would be simpler // to use only 32-bit ranges, but these tables are large // enough to warrant extra care. // // Using just 32-bit ranges gives 27 kB of data. // Adding 16-bit ranges gives 18 kB of data. // Adding an extra table of 16-bit singletons would reduce // to 16.5 kB of data but make the data harder to use; // we don't bother. #ifndef RE2_UNICODE_GROUPS_H__ #define RE2_UNICODE_GROUPS_H__ #include "util/util.h" namespace re2 { struct URange16 { uint16 lo; uint16 hi; }; struct URange32 { Rune lo; Rune hi; }; struct UGroup { const char *name; int sign; // +1 for [abc], -1 for [^abc] const URange16 *r16; int nr16; const URange32 *r32; int nr32; }; // Named by property or script name (e.g., "Nd", "N", "Han"). // Negated groups are not included. extern const UGroup unicode_groups[]; extern const int num_unicode_groups; // Named by POSIX name (e.g., "[:alpha:]", "[:^lower:]"). // Negated groups are included. extern const UGroup posix_groups[]; extern const int num_posix_groups; // Named by Perl name (e.g., "\\d", "\\D"). // Negated groups are included. extern const UGroup perl_groups[]; extern const int num_perl_groups; } // namespace re2 #endif // RE2_UNICODE_GROUPS_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/util/000077500000000000000000000000001266464252400221635ustar00rootroot00000000000000openalpr_2.2.4.orig/src/openalpr/support/re2/util/atomicops.h000066400000000000000000000105051266464252400243330ustar00rootroot00000000000000// Copyright 2006-2008 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #ifndef RE2_UTIL_ATOMICOPS_H__ #define RE2_UTIL_ATOMICOPS_H__ // The memory ordering constraints resemble the ones in C11. // RELAXED - no memory ordering, just an atomic operation. // CONSUME - data-dependent ordering. // ACQUIRE - prevents memory accesses from hoisting above the operation. // RELEASE - prevents memory accesses from sinking below the operation. #ifndef __has_builtin #define __has_builtin(x) 0 #endif #if !defined(OS_NACL) && (__has_builtin(__atomic_load_n) || (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__ >= 40801)) #define ATOMIC_LOAD_RELAXED(x, p) do { (x) = __atomic_load_n((p), __ATOMIC_RELAXED); } while (0) #define ATOMIC_LOAD_CONSUME(x, p) do { (x) = __atomic_load_n((p), __ATOMIC_CONSUME); } while (0) #define ATOMIC_LOAD_ACQUIRE(x, p) do { (x) = __atomic_load_n((p), __ATOMIC_ACQUIRE); } while (0) #define ATOMIC_STORE_RELAXED(p, v) __atomic_store_n((p), (v), __ATOMIC_RELAXED) #define ATOMIC_STORE_RELEASE(p, v) __atomic_store_n((p), (v), __ATOMIC_RELEASE) #else // old compiler #define ATOMIC_LOAD_RELAXED(x, p) do { (x) = *(p); } while (0) #define ATOMIC_LOAD_CONSUME(x, p) do { (x) = *(p); MaybeReadMemoryBarrier(); } while (0) #define ATOMIC_LOAD_ACQUIRE(x, p) do { (x) = *(p); ReadMemoryBarrier(); } while (0) #define ATOMIC_STORE_RELAXED(p, v) do { *(p) = (v); } while (0) #define ATOMIC_STORE_RELEASE(p, v) do { WriteMemoryBarrier(); *(p) = (v); } while (0) // WriteMemoryBarrier(), ReadMemoryBarrier() and MaybeReadMemoryBarrier() // are an implementation detail and must not be used in the rest of the code. #if defined(__i386__) static inline void WriteMemoryBarrier() { int x; __asm__ __volatile__("xchgl (%0),%0" // The lock prefix is implicit for xchg. :: "r" (&x)); } #elif defined(__x86_64__) // 64-bit implementations of memory barrier can be simpler, because // "sfence" is guaranteed to exist. static inline void WriteMemoryBarrier() { __asm__ __volatile__("sfence" : : : "memory"); } #elif defined(__ppc__) || defined(__powerpc64__) static inline void WriteMemoryBarrier() { __asm__ __volatile__("eieio" : : : "memory"); } #elif defined(__alpha__) static inline void WriteMemoryBarrier() { __asm__ __volatile__("wmb" : : : "memory"); } #elif defined(__aarch64__) static inline void WriteMemoryBarrier() { __asm__ __volatile__("dmb st" : : : "memory"); } #elif defined(__arm__) && defined(__linux__) // Linux on ARM puts a suitable memory barrier at a magic address for us to call. static inline void WriteMemoryBarrier() { ((void(*)(void))0xffff0fa0)(); } #elif defined(__windows__) // Windows inline void WriteMemoryBarrier() { LONG x; ::InterlockedExchange(&x, 0); } #elif defined(OS_NACL) // Native Client inline void WriteMemoryBarrier() { __sync_synchronize(); } #elif defined(__mips__) inline void WriteMemoryBarrier() { __asm__ __volatile__("sync" : : : "memory"); } #else #include "util/mutex.h" static inline void WriteMemoryBarrier() { // Slight overkill, but good enough: // any mutex implementation must have // a read barrier after the lock operation and // a write barrier before the unlock operation. // // It may be worthwhile to write architecture-specific // barriers for the common platforms, as above, but // this is a correct fallback. re2::Mutex mu; re2::MutexLock l(&mu); } #endif // Alpha has very weak memory ordering. If relying on WriteBarriers, one must // use read barriers for the readers too. #if defined(__alpha__) static inline void MaybeReadMemoryBarrier() { __asm__ __volatile__("mb" : : : "memory"); } #else static inline void MaybeReadMemoryBarrier() {} #endif // __alpha__ // Read barrier for various targets. #if defined(__aarch64__) static inline void ReadMemoryBarrier() { __asm__ __volatile__("dmb ld" : : : "memory"); } #elif defined(__alpha__) static inline void ReadMemoryBarrier() { __asm__ __volatile__("mb" : : : "memory"); } #elif defined(__mips__) inline void ReadMemoryBarrier() { __asm__ __volatile__("sync" : : : "memory"); } #else static inline void ReadMemoryBarrier() {} #endif #endif // old compiler #ifndef NO_THREAD_SAFETY_ANALYSIS #define NO_THREAD_SAFETY_ANALYSIS #endif #endif // RE2_UTIL_ATOMICOPS_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/util/benchmark.h000066400000000000000000000023571266464252400242750ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #ifndef RE2_UTIL_BENCHMARK_H__ #define RE2_UTIL_BENCHMARK_H__ namespace testing { struct Benchmark { const char* name; void (*fn)(int); void (*fnr)(int, int); int lo; int hi; int threadlo; int threadhi; void Register(); Benchmark(const char* name, void (*f)(int)) { Clear(name); fn = f; Register(); } Benchmark(const char* name, void (*f)(int, int), int l, int h) { Clear(name); fnr = f; lo = l; hi = h; Register(); } void Clear(const char* n) { name = n; fn = 0; fnr = 0; lo = 0; hi = 0; threadlo = 0; threadhi = 0; } Benchmark* ThreadRange(int lo, int hi) { threadlo = lo; threadhi = hi; return this; } }; } // namespace testing void SetBenchmarkBytesProcessed(long long); void StopBenchmarkTiming(); void StartBenchmarkTiming(); void BenchmarkMemoryUsage(); void SetBenchmarkItemsProcessed(int); int NumCPUs(); #define BENCHMARK(f) \ ::testing::Benchmark* _benchmark_##f = (new ::testing::Benchmark(#f, f)) #define BENCHMARK_RANGE(f, lo, hi) \ ::testing::Benchmark* _benchmark_##f = \ (new ::testing::Benchmark(#f, f, lo, hi)) #endif // RE2_UTIL_BENCHMARK_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/util/flags.h000066400000000000000000000017341266464252400234350ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Simplified version of Google's command line flags. // Does not support parsing the command line. // If you want to do that, see // https://gflags.github.io/gflags/ #ifndef RE2_UTIL_FLAGS_H__ #define RE2_UTIL_FLAGS_H__ #define DEFINE_flag(type, name, deflt, desc) \ namespace re2 { type FLAGS_##name = deflt; } #define DECLARE_flag(type, name) \ namespace re2 { extern type FLAGS_##name; } #define DEFINE_bool(name, deflt, desc) DEFINE_flag(bool, name, deflt, desc) #define DEFINE_int32(name, deflt, desc) DEFINE_flag(int32, name, deflt, desc) #define DEFINE_string(name, deflt, desc) DEFINE_flag(string, name, deflt, desc) #define DECLARE_bool(name) DECLARE_flag(bool, name) #define DECLARE_int32(name) DECLARE_flag(int32, name) #define DECLARE_string(name) DECLARE_flag(string, name) #endif // RE2_UTIL_FLAGS_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/util/hash.cc000066400000000000000000000205021266464252400234140ustar00rootroot00000000000000// Modified by Russ Cox to add "namespace re2". // Also threw away all but hashword and hashword2. // http://burtleburtle.net/bob/c/lookup3.c /* ------------------------------------------------------------------------------- lookup3.c, by Bob Jenkins, May 2006, Public Domain. These are functions for producing 32-bit hashes for hash table lookup. hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() are externally useful functions. Routines to test the hash are included if SELF_TEST is defined. You can use this free for any purpose. It's in the public domain. It has no warranty. You probably want to use hashlittle(). hashlittle() and hashbig() hash byte arrays. hashlittle() is is faster than hashbig() on little-endian machines. Intel and AMD are little-endian machines. On second thought, you probably want hashlittle2(), which is identical to hashlittle() except it returns two 32-bit hashes for the price of one. You could implement hashbig2() if you wanted but I haven't bothered here. If you want to find a hash of, say, exactly 7 integers, do a = i1; b = i2; c = i3; mix(a,b,c); a += i4; b += i5; c += i6; mix(a,b,c); a += i7; final(a,b,c); then use c as the hash value. If you have a variable length array of 4-byte integers to hash, use hashword(). If you have a byte array (like a character string), use hashlittle(). If you have several byte arrays, or a mix of things, see the comments above hashlittle(). Why is this so big? I read 12 bytes at a time into 3 4-byte integers, then mix those integers. This is fast (you can do a lot more thorough mixing with 12*3 instructions on 3 integers than you can with 3 instructions on 1 byte), but shoehorning those bytes into integers efficiently is messy. ------------------------------------------------------------------------------- */ #include "re2/util/util.h" #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) /* ------------------------------------------------------------------------------- mix -- mix 3 32-bit values reversibly. This is reversible, so any information in (a,b,c) before mix() is still in (a,b,c) after mix(). If four pairs of (a,b,c) inputs are run through mix(), or through mix() in reverse, there are at least 32 bits of the output that are sometimes the same for one pair and different for another pair. This was tested for: * pairs that differed by one bit, by two bits, in any combination of top bits of (a,b,c), or in any combination of bottom bits of (a,b,c). * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly produced by subtraction) look like a single 1-bit difference. * the base values were pseudorandom, all zero but one bit set, or all zero plus a counter that starts at zero. Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that satisfy this are 4 6 8 16 19 4 9 15 3 18 27 15 14 9 3 7 17 3 Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing for "differ" defined as + with a one-bit base and a two-bit delta. I used http://burtleburtle.net/bob/hash/avalanche.html to choose the operations, constants, and arrangements of the variables. This does not achieve avalanche. There are input bits of (a,b,c) that fail to affect some output bits of (a,b,c), especially of a. The most thoroughly mixed value is c, but it doesn't really even achieve avalanche in c. This allows some parallelism. Read-after-writes are good at doubling the number of bits affected, so the goal of mixing pulls in the opposite direction as the goal of parallelism. I did what I could. Rotates seem to cost as much as shifts on every machine I could lay my hands on, and rotates are much kinder to the top and bottom bits, so I used rotates. ------------------------------------------------------------------------------- */ #define mix(a,b,c) \ { \ a -= c; a ^= rot(c, 4); c += b; \ b -= a; b ^= rot(a, 6); a += c; \ c -= b; c ^= rot(b, 8); b += a; \ a -= c; a ^= rot(c,16); c += b; \ b -= a; b ^= rot(a,19); a += c; \ c -= b; c ^= rot(b, 4); b += a; \ } /* ------------------------------------------------------------------------------- final -- final mixing of 3 32-bit values (a,b,c) into c Pairs of (a,b,c) values differing in only a few bits will usually produce values of c that look totally different. This was tested for * pairs that differed by one bit, by two bits, in any combination of top bits of (a,b,c), or in any combination of bottom bits of (a,b,c). * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly produced by subtraction) look like a single 1-bit difference. * the base values were pseudorandom, all zero but one bit set, or all zero plus a counter that starts at zero. These constants passed: 14 11 25 16 4 14 24 12 14 25 16 4 14 24 and these came close: 4 8 15 26 3 22 24 10 8 15 26 3 22 24 11 8 15 26 3 22 24 ------------------------------------------------------------------------------- */ #define final(a,b,c) \ { \ c ^= b; c -= rot(b,14); \ a ^= c; a -= rot(c,11); \ b ^= a; b -= rot(a,25); \ c ^= b; c -= rot(b,16); \ a ^= c; a -= rot(c,4); \ b ^= a; b -= rot(a,14); \ c ^= b; c -= rot(b,24); \ } namespace re2 { /* -------------------------------------------------------------------- This works on all machines. To be useful, it requires -- that the key be an array of uint32_t's, and -- that the length be the number of uint32_t's in the key The function hashword() is identical to hashlittle() on little-endian machines, and identical to hashbig() on big-endian machines, except that the length has to be measured in uint32_ts rather than in bytes. hashlittle() is more complicated than hashword() only because hashlittle() has to dance around fitting the key bytes into registers. -------------------------------------------------------------------- */ uint32 hashword( const uint32 *k, /* the key, an array of uint32_t values */ size_t length, /* the length of the key, in uint32_ts */ uint32 initval) /* the previous hash, or an arbitrary value */ { uint32_t a,b,c; /* Set up the internal state */ a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; /*------------------------------------------------- handle most of the key */ while (length > 3) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 3; k += 3; } /*------------------------------------------- handle the last 3 uint32_t's */ switch(length) /* all the case statements fall through */ { case 3 : c+=k[2]; case 2 : b+=k[1]; case 1 : a+=k[0]; final(a,b,c); case 0: /* case 0: nothing left to add */ break; } /*------------------------------------------------------ report the result */ return c; } /* -------------------------------------------------------------------- hashword2() -- same as hashword(), but take two seeds and return two 32-bit values. pc and pb must both be nonnull, and *pc and *pb must both be initialized with seeds. If you pass in (*pb)==0, the output (*pc) will be the same as the return value from hashword(). -------------------------------------------------------------------- */ void hashword2 ( const uint32 *k, /* the key, an array of uint32_t values */ size_t length, /* the length of the key, in uint32_ts */ uint32 *pc, /* IN: seed OUT: primary hash value */ uint32 *pb) /* IN: more seed OUT: secondary hash value */ { uint32_t a,b,c; /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; c += *pb; /*------------------------------------------------- handle most of the key */ while (length > 3) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 3; k += 3; } /*------------------------------------------- handle the last 3 uint32_t's */ switch(length) /* all the case statements fall through */ { case 3 : c+=k[2]; case 2 : b+=k[1]; case 1 : a+=k[0]; final(a,b,c); case 0: /* case 0: nothing left to add */ break; } /*------------------------------------------------------ report the result */ *pc=c; *pb=b; } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/util/logging.h000066400000000000000000000043641266464252400237710ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Simplified version of Google's logging. #ifndef RE2_UTIL_LOGGING_H__ #define RE2_UTIL_LOGGING_H__ #include /* for fwrite */ #include // Debug-only checking. #define DCHECK(condition) assert(condition) #define DCHECK_EQ(val1, val2) assert((val1) == (val2)) #define DCHECK_NE(val1, val2) assert((val1) != (val2)) #define DCHECK_LE(val1, val2) assert((val1) <= (val2)) #define DCHECK_LT(val1, val2) assert((val1) < (val2)) #define DCHECK_GE(val1, val2) assert((val1) >= (val2)) #define DCHECK_GT(val1, val2) assert((val1) > (val2)) // Always-on checking #define CHECK(x) if(x){}else LogMessageFatal(__FILE__, __LINE__).stream() << "Check failed: " #x #define CHECK_LT(x, y) CHECK((x) < (y)) #define CHECK_GT(x, y) CHECK((x) > (y)) #define CHECK_LE(x, y) CHECK((x) <= (y)) #define CHECK_GE(x, y) CHECK((x) >= (y)) #define CHECK_EQ(x, y) CHECK((x) == (y)) #define CHECK_NE(x, y) CHECK((x) != (y)) #define LOG_INFO LogMessage(__FILE__, __LINE__) #define LOG_ERROR LOG_INFO #define LOG_WARNING LOG_INFO #define LOG_FATAL LogMessageFatal(__FILE__, __LINE__) #define LOG_QFATAL LOG_FATAL #define VLOG(x) if((x)>0){}else LOG_INFO.stream() #ifdef NDEBUG #define DEBUG_MODE 0 #define LOG_DFATAL LOG_ERROR #else #define DEBUG_MODE 1 #define LOG_DFATAL LOG_FATAL #endif #define LOG(severity) LOG_ ## severity.stream() class LogMessage { public: LogMessage(const char* file, int line) : flushed_(false) { stream() << file << ":" << line << ": "; } void Flush() { stream() << "\n"; string s = str_.str(); size_t n = s.size(); if (fwrite(s.data(), 1, n, stderr) < n) {} // shut up gcc flushed_ = true; } ~LogMessage() { if (!flushed_) { Flush(); } } ostream& stream() { return str_; } private: bool flushed_; std::ostringstream str_; DISALLOW_COPY_AND_ASSIGN(LogMessage); }; class LogMessageFatal : public LogMessage { public: LogMessageFatal(const char* file, int line) : LogMessage(file, line) { } ~LogMessageFatal() { Flush(); abort(); } private: DISALLOW_COPY_AND_ASSIGN(LogMessageFatal); }; #endif // RE2_UTIL_LOGGING_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/util/mutex.h000066400000000000000000000165521266464252400235070ustar00rootroot00000000000000// Copyright 2007 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* * A simple mutex wrapper, supporting locks and read-write locks. * You should assume the locks are *not* re-entrant. */ #ifndef RE2_UTIL_MUTEX_H_ #define RE2_UTIL_MUTEX_H_ #include namespace re2 { #ifndef WIN32 #define HAVE_PTHREAD 1 #define HAVE_RWLOCK 1 #endif #if defined(NO_THREADS) typedef int MutexType; // to keep a lock-count #elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK) // Needed for pthread_rwlock_*. If it causes problems, you could take it // out, but then you'd have to unset HAVE_RWLOCK (at least on linux -- it // *does* cause problems for FreeBSD, or MacOSX, but isn't needed // for locking there.) # ifdef __linux__ # undef _XOPEN_SOURCE # define _XOPEN_SOURCE 500 // may be needed to get the rwlock calls # endif # include typedef pthread_rwlock_t MutexType; #elif defined(HAVE_PTHREAD) # include typedef pthread_mutex_t MutexType; #elif defined(_WIN32) # define WIN32_LEAN_AND_MEAN // We only need minimal includes # ifdef GMUTEX_TRYLOCK // We need Windows NT or later for TryEnterCriticalSection(). If you // don't need that functionality, you can remove these _WIN32_WINNT // lines, and change TryLock() to assert(0) or something. # ifndef _WIN32_WINNT # define _WIN32_WINNT 0x0400 # endif # endif # include typedef CRITICAL_SECTION MutexType; #else # error Need to implement mutex.h for your architecture, or #define NO_THREADS #endif class Mutex { public: // Create a Mutex that is not held by anybody. inline Mutex(); // Destructor inline ~Mutex(); inline void Lock(); // Block if needed until free then acquire exclusively inline void Unlock(); // Release a lock acquired via Lock() inline bool TryLock(); // If free, Lock() and return true, else return false // Note that on systems that don't support read-write locks, these may // be implemented as synonyms to Lock() and Unlock(). So you can use // these for efficiency, but don't use them anyplace where being able // to do shared reads is necessary to avoid deadlock. inline void ReaderLock(); // Block until free or shared then acquire a share inline void ReaderUnlock(); // Release a read share of this Mutex inline void WriterLock() { Lock(); } // Acquire an exclusive lock inline void WriterUnlock() { Unlock(); } // Release a lock from WriterLock() inline void AssertHeld() { } private: MutexType mutex_; // Catch the error of writing Mutex when intending MutexLock. Mutex(Mutex *ignored); // Disallow "evil" constructors Mutex(const Mutex&); void operator=(const Mutex&); }; // Now the implementation of Mutex for various systems #if defined(NO_THREADS) // When we don't have threads, we can be either reading or writing, // but not both. We can have lots of readers at once (in no-threads // mode, that's most likely to happen in recursive function calls), // but only one writer. We represent this by having mutex_ be -1 when // writing and a number > 0 when reading (and 0 when no lock is held). // // In debug mode, we assert these invariants, while in non-debug mode // we do nothing, for efficiency. That's why everything is in an // assert. #include Mutex::Mutex() : mutex_(0) { } Mutex::~Mutex() { assert(mutex_ == 0); } void Mutex::Lock() { assert(--mutex_ == -1); } void Mutex::Unlock() { assert(mutex_++ == -1); } bool Mutex::TryLock() { if (mutex_) return false; Lock(); return true; } void Mutex::ReaderLock() { assert(++mutex_ > 0); } void Mutex::ReaderUnlock() { assert(mutex_-- > 0); } #elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK) #define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0) Mutex::Mutex() { SAFE_PTHREAD(pthread_rwlock_init(&mutex_, NULL)); } Mutex::~Mutex() { SAFE_PTHREAD(pthread_rwlock_destroy(&mutex_)); } void Mutex::Lock() { SAFE_PTHREAD(pthread_rwlock_wrlock(&mutex_)); } void Mutex::Unlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&mutex_)); } bool Mutex::TryLock() { return pthread_rwlock_trywrlock(&mutex_) == 0; } void Mutex::ReaderLock() { SAFE_PTHREAD(pthread_rwlock_rdlock(&mutex_)); } void Mutex::ReaderUnlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&mutex_)); } #undef SAFE_PTHREAD #elif defined(HAVE_PTHREAD) #define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0) Mutex::Mutex() { SAFE_PTHREAD(pthread_mutex_init(&mutex_, NULL)); } Mutex::~Mutex() { SAFE_PTHREAD(pthread_mutex_destroy(&mutex_)); } void Mutex::Lock() { SAFE_PTHREAD(pthread_mutex_lock(&mutex_)); } void Mutex::Unlock() { SAFE_PTHREAD(pthread_mutex_unlock(&mutex_)); } bool Mutex::TryLock() { return pthread_mutex_trylock(&mutex_) == 0; } void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks void Mutex::ReaderUnlock() { Unlock(); } #undef SAFE_PTHREAD #elif defined(_WIN32) Mutex::Mutex() { InitializeCriticalSection(&mutex_); } Mutex::~Mutex() { DeleteCriticalSection(&mutex_); } void Mutex::Lock() { EnterCriticalSection(&mutex_); } void Mutex::Unlock() { LeaveCriticalSection(&mutex_); } bool Mutex::TryLock() { return TryEnterCriticalSection(&mutex_) != 0; } void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks void Mutex::ReaderUnlock() { Unlock(); } #endif // -------------------------------------------------------------------------- // Some helper classes // MutexLock(mu) acquires mu when constructed and releases it when destroyed. class MutexLock { public: explicit MutexLock(Mutex *mu) : mu_(mu) { mu_->Lock(); } ~MutexLock() { mu_->Unlock(); } private: Mutex * const mu_; // Disallow "evil" constructors MutexLock(const MutexLock&); void operator=(const MutexLock&); }; // ReaderMutexLock and WriterMutexLock do the same, for rwlocks class ReaderMutexLock { public: explicit ReaderMutexLock(Mutex *mu) : mu_(mu) { mu_->ReaderLock(); } ~ReaderMutexLock() { mu_->ReaderUnlock(); } private: Mutex * const mu_; // Disallow "evil" constructors ReaderMutexLock(const ReaderMutexLock&); void operator=(const ReaderMutexLock&); }; class WriterMutexLock { public: explicit WriterMutexLock(Mutex *mu) : mu_(mu) { mu_->WriterLock(); } ~WriterMutexLock() { mu_->WriterUnlock(); } private: Mutex * const mu_; // Disallow "evil" constructors WriterMutexLock(const WriterMutexLock&); void operator=(const WriterMutexLock&); }; // Catch bug where variable name is omitted, e.g. MutexLock (&mu); #define MutexLock(x) COMPILE_ASSERT(0, mutex_lock_decl_missing_var_name) #define ReaderMutexLock(x) COMPILE_ASSERT(0, rmutex_lock_decl_missing_var_name) #define WriterMutexLock(x) COMPILE_ASSERT(0, wmutex_lock_decl_missing_var_name) // Provide safe way to declare and use global, linker-initialized mutex. Sigh. #ifdef HAVE_PTHREAD #define GLOBAL_MUTEX(name) \ static pthread_mutex_t (name) = PTHREAD_MUTEX_INITIALIZER #define GLOBAL_MUTEX_LOCK(name) \ pthread_mutex_lock(&(name)) #define GLOBAL_MUTEX_UNLOCK(name) \ pthread_mutex_unlock(&(name)) #else #define GLOBAL_MUTEX(name) \ static Mutex name #define GLOBAL_MUTEX_LOCK(name) \ name.Lock() #define GLOBAL_MUTEX_UNLOCK(name) \ name.Unlock() #endif } // namespace re2 #endif /* #define RE2_UTIL_MUTEX_H_ */ openalpr_2.2.4.orig/src/openalpr/support/re2/util/pcre.h000066400000000000000000000655501266464252400233000ustar00rootroot00000000000000// Copyright 2003-2010 Google Inc. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // This is a variant of PCRE's pcrecpp.h, originally written at Google. // The main changes are the addition of the HitLimit method and // compilation as PCRE in namespace re2. // C++ interface to the pcre regular-expression library. PCRE supports // Perl-style regular expressions (with extensions like \d, \w, \s, // ...). // // ----------------------------------------------------------------------- // REGEXP SYNTAX: // // This module uses the pcre library and hence supports its syntax // for regular expressions: // // http://www.google.com/search?q=pcre // // The syntax is pretty similar to Perl's. For those not familiar // with Perl's regular expressions, here are some examples of the most // commonly used extensions: // // "hello (\\w+) world" -- \w matches a "word" character // "version (\\d+)" -- \d matches a digit // "hello\\s+world" -- \s matches any whitespace character // "\\b(\\w+)\\b" -- \b matches empty string at a word boundary // "(?i)hello" -- (?i) turns on case-insensitive matching // "/\\*(.*?)\\*/" -- .*? matches . minimum no. of times possible // // ----------------------------------------------------------------------- // MATCHING INTERFACE: // // The "FullMatch" operation checks that supplied text matches a // supplied pattern exactly. // // Example: successful match // CHECK(PCRE::FullMatch("hello", "h.*o")); // // Example: unsuccessful match (requires full match): // CHECK(!PCRE::FullMatch("hello", "e")); // // ----------------------------------------------------------------------- // UTF-8 AND THE MATCHING INTERFACE: // // By default, pattern and text are plain text, one byte per character. // The UTF8 flag, passed to the constructor, causes both pattern // and string to be treated as UTF-8 text, still a byte stream but // potentially multiple bytes per character. In practice, the text // is likelier to be UTF-8 than the pattern, but the match returned // may depend on the UTF8 flag, so always use it when matching // UTF8 text. E.g., "." will match one byte normally but with UTF8 // set may match up to three bytes of a multi-byte character. // // Example: // PCRE re(utf8_pattern, PCRE::UTF8); // CHECK(PCRE::FullMatch(utf8_string, re)); // // ----------------------------------------------------------------------- // MATCHING WITH SUB-STRING EXTRACTION: // // You can supply extra pointer arguments to extract matched subpieces. // // Example: extracts "ruby" into "s" and 1234 into "i" // int i; // string s; // CHECK(PCRE::FullMatch("ruby:1234", "(\\w+):(\\d+)", &s, &i)); // // Example: fails because string cannot be stored in integer // CHECK(!PCRE::FullMatch("ruby", "(.*)", &i)); // // Example: fails because there aren't enough sub-patterns: // CHECK(!PCRE::FullMatch("ruby:1234", "\\w+:\\d+", &s)); // // Example: does not try to extract any extra sub-patterns // CHECK(PCRE::FullMatch("ruby:1234", "(\\w+):(\\d+)", &s)); // // Example: does not try to extract into NULL // CHECK(PCRE::FullMatch("ruby:1234", "(\\w+):(\\d+)", NULL, &i)); // // Example: integer overflow causes failure // CHECK(!PCRE::FullMatch("ruby:1234567891234", "\\w+:(\\d+)", &i)); // // ----------------------------------------------------------------------- // PARTIAL MATCHES // // You can use the "PartialMatch" operation when you want the pattern // to match any substring of the text. // // Example: simple search for a string: // CHECK(PCRE::PartialMatch("hello", "ell")); // // Example: find first number in a string // int number; // CHECK(PCRE::PartialMatch("x*100 + 20", "(\\d+)", &number)); // CHECK_EQ(number, 100); // // ----------------------------------------------------------------------- // PPCRE-COMPILED PCREGULAR EXPPCRESSIONS // // PCRE makes it easy to use any string as a regular expression, without // requiring a separate compilation step. // // If speed is of the essence, you can create a pre-compiled "PCRE" // object from the pattern and use it multiple times. If you do so, // you can typically parse text faster than with sscanf. // // Example: precompile pattern for faster matching: // PCRE pattern("h.*o"); // while (ReadLine(&str)) { // if (PCRE::FullMatch(str, pattern)) ...; // } // // ----------------------------------------------------------------------- // SCANNING TEXT INCPCREMENTALLY // // The "Consume" operation may be useful if you want to repeatedly // match regular expressions at the front of a string and skip over // them as they match. This requires use of the "StringPiece" type, // which represents a sub-range of a real string. // // Example: read lines of the form "var = value" from a string. // string contents = ...; // Fill string somehow // StringPiece input(contents); // Wrap a StringPiece around it // // string var; // int value; // while (PCRE::Consume(&input, "(\\w+) = (\\d+)\n", &var, &value)) { // ...; // } // // Each successful call to "Consume" will set "var/value", and also // advance "input" so it points past the matched text. Note that if the // regular expression matches an empty string, input will advance // by 0 bytes. If the regular expression being used might match // an empty string, the loop body must check for this case and either // advance the string or break out of the loop. // // The "FindAndConsume" operation is similar to "Consume" but does not // anchor your match at the beginning of the string. For example, you // could extract all words from a string by repeatedly calling // PCRE::FindAndConsume(&input, "(\\w+)", &word) // // ----------------------------------------------------------------------- // PARSING HEX/OCTAL/C-RADIX NUMBERS // // By default, if you pass a pointer to a numeric value, the // corresponding text is interpreted as a base-10 number. You can // instead wrap the pointer with a call to one of the operators Hex(), // Octal(), or CRadix() to interpret the text in another base. The // CRadix operator interprets C-style "0" (base-8) and "0x" (base-16) // prefixes, but defaults to base-10. // // Example: // int a, b, c, d; // CHECK(PCRE::FullMatch("100 40 0100 0x40", "(.*) (.*) (.*) (.*)", // Octal(&a), Hex(&b), CRadix(&c), CRadix(&d)); // will leave 64 in a, b, c, and d. #include "util/util.h" #include "re2/stringpiece.h" #ifdef USEPCRE #include namespace re2 { const bool UsingPCRE = true; } // namespace re2 #else namespace re2 { const bool UsingPCRE = false; struct pcre; struct pcre_extra { int flags, match_limit, match_limit_recursion; }; #define pcre_free(x) {} #define PCRE_EXTRA_MATCH_LIMIT 0 #define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0 #define PCRE_ANCHORED 0 #define PCRE_NOTEMPTY 0 #define PCRE_ERROR_NOMATCH 1 #define PCRE_ERROR_MATCHLIMIT 2 #define PCRE_ERROR_RECURSIONLIMIT 3 #define PCRE_INFO_CAPTURECOUNT 0 #define pcre_compile(a,b,c,d,e) ({ (void)(a); (void)(b); *(c)=""; *(d)=0; (void)(e); ((pcre*)0); }) #define pcre_exec(a, b, c, d, e, f, g, h) ({ (void)(a); (void)(b); (void)(c); (void)(d); (void)(e); (void)(f); (void)(g); (void)(h); 0; }) #define pcre_fullinfo(a, b, c, d) ({ (void)(a); (void)(b); (void)(c); *(d) = 0; 0; }) } // namespace re2 #endif namespace re2 { class PCRE_Options; // Interface for regular expression matching. Also corresponds to a // pre-compiled regular expression. An "PCRE" object is safe for // concurrent use by multiple threads. class PCRE { public: // We convert user-passed pointers into special Arg objects class Arg; // Marks end of arg list. // ONLY USE IN OPTIONAL ARG DEFAULTS. // DO NOT PASS EXPLICITLY. static Arg no_more_args; // Options are same value as those in pcre. We provide them here // to avoid users needing to include pcre.h and also to isolate // users from pcre should we change the underlying library. // Only those needed by Google programs are exposed here to // avoid collision with options employed internally by regexp.cc // Note that some options have equivalents that can be specified in // the regexp itself. For example, prefixing your regexp with // "(?s)" has the same effect as the PCRE_DOTALL option. enum Option { None = 0x0000, UTF8 = 0x0800, // == PCRE_UTF8 EnabledCompileOptions = UTF8, EnabledExecOptions = 0x0000, // TODO: use to replace anchor flag }; // We provide implicit conversions from strings so that users can // pass in a string or a "const char*" wherever an "PCRE" is expected. PCRE(const char* pattern); PCRE(const char* pattern, Option option); PCRE(const string& pattern); PCRE(const string& pattern, Option option); PCRE(const char *pattern, const PCRE_Options& re_option); PCRE(const string& pattern, const PCRE_Options& re_option); ~PCRE(); // The string specification for this PCRE. E.g. // PCRE re("ab*c?d+"); // re.pattern(); // "ab*c?d+" const string& pattern() const { return pattern_; } // If PCRE could not be created properly, returns an error string. // Else returns the empty string. const string& error() const { return *error_; } // Whether the PCRE has hit a match limit during execution. // Not thread safe. Intended only for testing. // If hitting match limits is a problem, // you should be using PCRE2 (re2/re2.h) // instead of checking this flag. bool HitLimit(); void ClearHitLimit(); /***** The useful part: the matching interface *****/ // Matches "text" against "pattern". If pointer arguments are // supplied, copies matched sub-patterns into them. // // You can pass in a "const char*" or a "string" for "text". // You can pass in a "const char*" or a "string" or a "PCRE" for "pattern". // // The provided pointer arguments can be pointers to any scalar numeric // type, or one of: // string (matched piece is copied to string) // StringPiece (StringPiece is mutated to point to matched piece) // T (where "bool T::ParseFrom(const char*, int)" exists) // (void*)NULL (the corresponding matched sub-pattern is not copied) // // Returns true iff all of the following conditions are satisfied: // a. "text" matches "pattern" exactly // b. The number of matched sub-patterns is >= number of supplied pointers // c. The "i"th argument has a suitable type for holding the // string captured as the "i"th sub-pattern. If you pass in // NULL for the "i"th argument, or pass fewer arguments than // number of sub-patterns, "i"th captured sub-pattern is // ignored. // // CAVEAT: An optional sub-pattern that does not exist in the // matched string is assigned the empty string. Therefore, the // following will return false (because the empty string is not a // valid number): // int number; // PCRE::FullMatch("abc", "[a-z]+(\\d+)?", &number); struct FullMatchFunctor { bool operator ()(const StringPiece& text, const PCRE& re, // 3..16 args const Arg& ptr1 = no_more_args, const Arg& ptr2 = no_more_args, const Arg& ptr3 = no_more_args, const Arg& ptr4 = no_more_args, const Arg& ptr5 = no_more_args, const Arg& ptr6 = no_more_args, const Arg& ptr7 = no_more_args, const Arg& ptr8 = no_more_args, const Arg& ptr9 = no_more_args, const Arg& ptr10 = no_more_args, const Arg& ptr11 = no_more_args, const Arg& ptr12 = no_more_args, const Arg& ptr13 = no_more_args, const Arg& ptr14 = no_more_args, const Arg& ptr15 = no_more_args, const Arg& ptr16 = no_more_args) const; }; static const FullMatchFunctor FullMatch; // Exactly like FullMatch(), except that "pattern" is allowed to match // a substring of "text". struct PartialMatchFunctor { bool operator ()(const StringPiece& text, const PCRE& re, // 3..16 args const Arg& ptr1 = no_more_args, const Arg& ptr2 = no_more_args, const Arg& ptr3 = no_more_args, const Arg& ptr4 = no_more_args, const Arg& ptr5 = no_more_args, const Arg& ptr6 = no_more_args, const Arg& ptr7 = no_more_args, const Arg& ptr8 = no_more_args, const Arg& ptr9 = no_more_args, const Arg& ptr10 = no_more_args, const Arg& ptr11 = no_more_args, const Arg& ptr12 = no_more_args, const Arg& ptr13 = no_more_args, const Arg& ptr14 = no_more_args, const Arg& ptr15 = no_more_args, const Arg& ptr16 = no_more_args) const; }; static const PartialMatchFunctor PartialMatch; // Like FullMatch() and PartialMatch(), except that pattern has to // match a prefix of "text", and "input" is advanced past the matched // text. Note: "input" is modified iff this routine returns true. struct ConsumeFunctor { bool operator ()(StringPiece* input, const PCRE& pattern, // 3..16 args const Arg& ptr1 = no_more_args, const Arg& ptr2 = no_more_args, const Arg& ptr3 = no_more_args, const Arg& ptr4 = no_more_args, const Arg& ptr5 = no_more_args, const Arg& ptr6 = no_more_args, const Arg& ptr7 = no_more_args, const Arg& ptr8 = no_more_args, const Arg& ptr9 = no_more_args, const Arg& ptr10 = no_more_args, const Arg& ptr11 = no_more_args, const Arg& ptr12 = no_more_args, const Arg& ptr13 = no_more_args, const Arg& ptr14 = no_more_args, const Arg& ptr15 = no_more_args, const Arg& ptr16 = no_more_args) const; }; static const ConsumeFunctor Consume; // Like Consume(..), but does not anchor the match at the beginning of the // string. That is, "pattern" need not start its match at the beginning of // "input". For example, "FindAndConsume(s, "(\\w+)", &word)" finds the next // word in "s" and stores it in "word". struct FindAndConsumeFunctor { bool operator ()(StringPiece* input, const PCRE& pattern, const Arg& ptr1 = no_more_args, const Arg& ptr2 = no_more_args, const Arg& ptr3 = no_more_args, const Arg& ptr4 = no_more_args, const Arg& ptr5 = no_more_args, const Arg& ptr6 = no_more_args, const Arg& ptr7 = no_more_args, const Arg& ptr8 = no_more_args, const Arg& ptr9 = no_more_args, const Arg& ptr10 = no_more_args, const Arg& ptr11 = no_more_args, const Arg& ptr12 = no_more_args, const Arg& ptr13 = no_more_args, const Arg& ptr14 = no_more_args, const Arg& ptr15 = no_more_args, const Arg& ptr16 = no_more_args) const; }; static const FindAndConsumeFunctor FindAndConsume; // Replace the first match of "pattern" in "str" with "rewrite". // Within "rewrite", backslash-escaped digits (\1 to \9) can be // used to insert text matching corresponding parenthesized group // from the pattern. \0 in "rewrite" refers to the entire matching // text. E.g., // // string s = "yabba dabba doo"; // CHECK(PCRE::Replace(&s, "b+", "d")); // // will leave "s" containing "yada dabba doo" // // Returns true if the pattern matches and a replacement occurs, // false otherwise. static bool Replace(string *str, const PCRE& pattern, const StringPiece& rewrite); // Like Replace(), except replaces all occurrences of the pattern in // the string with the rewrite. Replacements are not subject to // re-matching. E.g., // // string s = "yabba dabba doo"; // CHECK(PCRE::GlobalReplace(&s, "b+", "d")); // // will leave "s" containing "yada dada doo" // // Returns the number of replacements made. static int GlobalReplace(string *str, const PCRE& pattern, const StringPiece& rewrite); // Like Replace, except that if the pattern matches, "rewrite" // is copied into "out" with substitutions. The non-matching // portions of "text" are ignored. // // Returns true iff a match occurred and the extraction happened // successfully; if no match occurs, the string is left unaffected. static bool Extract(const StringPiece &text, const PCRE& pattern, const StringPiece &rewrite, string *out); // Check that the given @p rewrite string is suitable for use with // this PCRE. It checks that: // * The PCRE has enough parenthesized subexpressions to satisfy all // of the \N tokens in @p rewrite, and // * The @p rewrite string doesn't have any syntax errors // ('\' followed by anything besides [0-9] and '\'). // Making this test will guarantee that "replace" and "extract" // operations won't LOG(ERROR) or fail because of a bad rewrite // string. // @param rewrite The proposed rewrite string. // @param error An error message is recorded here, iff we return false. // Otherwise, it is unchanged. // @return true, iff @p rewrite is suitable for use with the PCRE. bool CheckRewriteString(const StringPiece& rewrite, string* error) const; // Returns a copy of 'unquoted' with all potentially meaningful // regexp characters backslash-escaped. The returned string, used // as a regular expression, will exactly match the original string. // For example, // 1.5-2.0? // becomes: // 1\.5\-2\.0\? static string QuoteMeta(const StringPiece& unquoted); /***** Generic matching interface (not so nice to use) *****/ // Type of match (TODO: Should be restructured as an Option) enum Anchor { UNANCHORED, // No anchoring ANCHOR_START, // Anchor at start only ANCHOR_BOTH, // Anchor at start and end }; // General matching routine. Stores the length of the match in // "*consumed" if successful. bool DoMatch(const StringPiece& text, Anchor anchor, int* consumed, const Arg* const* args, int n) const; // Return the number of capturing subpatterns, or -1 if the // regexp wasn't valid on construction. int NumberOfCapturingGroups() const; private: void Init(const char* pattern, Option option, int match_limit, int stack_limit, bool report_errors); // Match against "text", filling in "vec" (up to "vecsize" * 2/3) with // pairs of integers for the beginning and end positions of matched // text. The first pair corresponds to the entire matched text; // subsequent pairs correspond, in order, to parentheses-captured // matches. Returns the number of pairs (one more than the number of // the last subpattern with a match) if matching was successful // and zero if the match failed. // I.e. for PCRE("(foo)|(bar)|(baz)") it will return 2, 3, and 4 when matching // against "foo", "bar", and "baz" respectively. // When matching PCRE("(foo)|hello") against "hello", it will return 1. // But the values for all subpattern are filled in into "vec". int TryMatch(const StringPiece& text, int startpos, Anchor anchor, bool empty_ok, int *vec, int vecsize) const; // Append the "rewrite" string, with backslash subsitutions from "text" // and "vec", to string "out". bool Rewrite(string *out, const StringPiece &rewrite, const StringPiece &text, int *vec, int veclen) const; // internal implementation for DoMatch bool DoMatchImpl(const StringPiece& text, Anchor anchor, int* consumed, const Arg* const args[], int n, int* vec, int vecsize) const; // Compile the regexp for the specified anchoring mode pcre* Compile(Anchor anchor); string pattern_; Option options_; pcre* re_full_; // For full matches pcre* re_partial_; // For partial matches const string* error_; // Error indicator (or empty string) bool report_errors_; // Silences error logging if false int match_limit_; // Limit on execution resources int stack_limit_; // Limit on stack resources (bytes) mutable int32_t hit_limit_; // Hit limit during execution (bool)? DISALLOW_COPY_AND_ASSIGN(PCRE); }; // PCRE_Options allow you to set the PCRE::Options, plus any pcre // "extra" options. The only extras are match_limit, which limits // the CPU time of a match, and stack_limit, which limits the // stack usage. Setting a limit to <= 0 lets PCRE pick a sensible default // that should not cause too many problems in production code. // If PCRE hits a limit during a match, it may return a false negative, // but (hopefully) it won't crash. // // NOTE: If you are handling regular expressions specified by // (external or internal) users, rather than hard-coded ones, // you should be using PCRE2, which uses an alternate implementation // that avoids these issues. See http://go/re2quick. class PCRE_Options { public: // constructor PCRE_Options() : option_(PCRE::None), match_limit_(0), stack_limit_(0), report_errors_(true) {} // accessors PCRE::Option option() const { return option_; } void set_option(PCRE::Option option) { option_ = option; } int match_limit() const { return match_limit_; } void set_match_limit(int match_limit) { match_limit_ = match_limit; } int stack_limit() const { return stack_limit_; } void set_stack_limit(int stack_limit) { stack_limit_ = stack_limit; } // If the regular expression is malformed, an error message will be printed // iff report_errors() is true. Default: true. bool report_errors() const { return report_errors_; } void set_report_errors(bool report_errors) { report_errors_ = report_errors; } private: PCRE::Option option_; int match_limit_; int stack_limit_; bool report_errors_; }; /***** Implementation details *****/ // Hex/Octal/Binary? // Special class for parsing into objects that define a ParseFrom() method template class _PCRE_MatchObject { public: static inline bool Parse(const char* str, int n, void* dest) { if (dest == NULL) return true; T* object = reinterpret_cast(dest); return object->ParseFrom(str, n); } }; class PCRE::Arg { public: // Empty constructor so we can declare arrays of PCRE::Arg Arg(); // Constructor specially designed for NULL arguments Arg(void*); typedef bool (*Parser)(const char* str, int n, void* dest); // Type-specific parsers #define MAKE_PARSER(type,name) \ Arg(type* p) : arg_(p), parser_(name) { } \ Arg(type* p, Parser parser) : arg_(p), parser_(parser) { } \ MAKE_PARSER(char, parse_char); MAKE_PARSER(unsigned char, parse_uchar); MAKE_PARSER(short, parse_short); MAKE_PARSER(unsigned short, parse_ushort); MAKE_PARSER(int, parse_int); MAKE_PARSER(unsigned int, parse_uint); MAKE_PARSER(long, parse_long); MAKE_PARSER(unsigned long, parse_ulong); MAKE_PARSER(long long, parse_longlong); MAKE_PARSER(unsigned long long, parse_ulonglong); MAKE_PARSER(float, parse_float); MAKE_PARSER(double, parse_double); MAKE_PARSER(string, parse_string); MAKE_PARSER(StringPiece, parse_stringpiece); #undef MAKE_PARSER // Generic constructor template Arg(T*, Parser parser); // Generic constructor template template Arg(T* p) : arg_(p), parser_(_PCRE_MatchObject::Parse) { } // Parse the data bool Parse(const char* str, int n) const; private: void* arg_; Parser parser_; static bool parse_null (const char* str, int n, void* dest); static bool parse_char (const char* str, int n, void* dest); static bool parse_uchar (const char* str, int n, void* dest); static bool parse_float (const char* str, int n, void* dest); static bool parse_double (const char* str, int n, void* dest); static bool parse_string (const char* str, int n, void* dest); static bool parse_stringpiece (const char* str, int n, void* dest); #define DECLARE_INTEGER_PARSER(name) \ private: \ static bool parse_ ## name(const char* str, int n, void* dest); \ static bool parse_ ## name ## _radix( \ const char* str, int n, void* dest, int radix); \ public: \ static bool parse_ ## name ## _hex(const char* str, int n, void* dest); \ static bool parse_ ## name ## _octal(const char* str, int n, void* dest); \ static bool parse_ ## name ## _cradix(const char* str, int n, void* dest) DECLARE_INTEGER_PARSER(short); DECLARE_INTEGER_PARSER(ushort); DECLARE_INTEGER_PARSER(int); DECLARE_INTEGER_PARSER(uint); DECLARE_INTEGER_PARSER(long); DECLARE_INTEGER_PARSER(ulong); DECLARE_INTEGER_PARSER(longlong); DECLARE_INTEGER_PARSER(ulonglong); #undef DECLARE_INTEGER_PARSER }; inline PCRE::Arg::Arg() : arg_(NULL), parser_(parse_null) { } inline PCRE::Arg::Arg(void* p) : arg_(p), parser_(parse_null) { } inline bool PCRE::Arg::Parse(const char* str, int n) const { return (*parser_)(str, n, arg_); } // This part of the parser, appropriate only for ints, deals with bases #define MAKE_INTEGER_PARSER(type, name) \ inline PCRE::Arg Hex(type* ptr) { \ return PCRE::Arg(ptr, PCRE::Arg::parse_ ## name ## _hex); } \ inline PCRE::Arg Octal(type* ptr) { \ return PCRE::Arg(ptr, PCRE::Arg::parse_ ## name ## _octal); } \ inline PCRE::Arg CRadix(type* ptr) { \ return PCRE::Arg(ptr, PCRE::Arg::parse_ ## name ## _cradix); } MAKE_INTEGER_PARSER(short, short); MAKE_INTEGER_PARSER(unsigned short, ushort); MAKE_INTEGER_PARSER(int, int); MAKE_INTEGER_PARSER(unsigned int, uint); MAKE_INTEGER_PARSER(long, long); MAKE_INTEGER_PARSER(unsigned long, ulong); MAKE_INTEGER_PARSER(long long, longlong); MAKE_INTEGER_PARSER(unsigned long long, ulonglong); #undef MAKE_INTEGER_PARSER } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/util/random.h000066400000000000000000000011561266464252400236170ustar00rootroot00000000000000// Copyright 2005-2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Modified from Google perftools's tcmalloc_unittest.cc. #ifndef RE2_UTIL_RANDOM_H__ #define RE2_UTIL_RANDOM_H__ #include "util/util.h" namespace re2 { // ACM minimal standard random number generator. (re-entrant.) class ACMRandom { public: ACMRandom(int32 seed) : seed_(seed) {} int32 Next(); int32 Uniform(int32); void Reset(int32 seed) { seed_ = seed; } private: int32 seed_; }; } // namespace re2 #endif // RE2_UTIL_RANDOM_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/util/rune.cc000066400000000000000000000114161266464252400234460ustar00rootroot00000000000000/* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "re2/util/utf.h" namespace re2 { enum { Bit1 = 7, Bitx = 6, Bit2 = 5, Bit3 = 4, Bit4 = 3, Bit5 = 2, T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */ T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */ Rune4 = (1<<(Bit4+3*Bitx))-1, /* 0001 1111 1111 1111 1111 1111 */ Maskx = (1< T1 */ c = *(unsigned char*)str; if(c < Tx) { *rune = c; return 1; } /* * two character sequence * 0080-07FF => T2 Tx */ c1 = *(unsigned char*)(str+1) ^ Tx; if(c1 & Testx) goto bad; if(c < T3) { if(c < T2) goto bad; l = ((c << Bitx) | c1) & Rune2; if(l <= Rune1) goto bad; *rune = l; return 2; } /* * three character sequence * 0800-FFFF => T3 Tx Tx */ c2 = *(unsigned char*)(str+2) ^ Tx; if(c2 & Testx) goto bad; if(c < T4) { l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; if(l <= Rune2) goto bad; *rune = l; return 3; } /* * four character sequence (21-bit value) * 10000-1FFFFF => T4 Tx Tx Tx */ c3 = *(unsigned char*)(str+3) ^ Tx; if (c3 & Testx) goto bad; if (c < T5) { l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4; if (l <= Rune3) goto bad; *rune = l; return 4; } /* * Support for 5-byte or longer UTF-8 would go here, but * since we don't have that, we'll just fall through to bad. */ /* * bad decoding */ bad: *rune = Bad; return 1; } int runetochar(char *str, const Rune *rune) { /* Runes are signed, so convert to unsigned for range check. */ unsigned long c; /* * one character sequence * 00000-0007F => 00-7F */ c = *rune; if(c <= Rune1) { str[0] = c; return 1; } /* * two character sequence * 0080-07FF => T2 Tx */ if(c <= Rune2) { str[0] = T2 | (c >> 1*Bitx); str[1] = Tx | (c & Maskx); return 2; } /* * If the Rune is out of range, convert it to the error rune. * Do this test here because the error rune encodes to three bytes. * Doing it earlier would duplicate work, since an out of range * Rune wouldn't have fit in one or two bytes. */ if (c > Runemax) c = Runeerror; /* * three character sequence * 0800-FFFF => T3 Tx Tx */ if (c <= Rune3) { str[0] = T3 | (c >> 2*Bitx); str[1] = Tx | ((c >> 1*Bitx) & Maskx); str[2] = Tx | (c & Maskx); return 3; } /* * four character sequence (21-bit value) * 10000-1FFFFF => T4 Tx Tx Tx */ str[0] = T4 | (c >> 3*Bitx); str[1] = Tx | ((c >> 2*Bitx) & Maskx); str[2] = Tx | ((c >> 1*Bitx) & Maskx); str[3] = Tx | (c & Maskx); return 4; } int runelen(Rune rune) { char str[10]; return runetochar(str, &rune); } int fullrune(const char *str, int n) { if (n > 0) { int c = *(unsigned char*)str; if (c < Tx) return 1; if (n > 1) { if (c < T3) return 1; if (n > 2) { if (c < T4 || n > 3) return 1; } } } return 0; } int utflen(const char *s) { int c; long n; Rune rune; n = 0; for(;;) { c = *(unsigned char*)s; if(c < Runeself) { if(c == 0) return n; s++; } else s += chartorune(&rune, s); n++; } return 0; } char* utfrune(const char *s, Rune c) { long c1; Rune r; int n; if(c < Runesync) /* not part of utf sequence */ return strchr((char*)s, c); for(;;) { c1 = *(unsigned char*)s; if(c1 < Runeself) { /* one byte rune */ if(c1 == 0) return 0; if(c1 == c) return (char*)s; s++; continue; } n = chartorune(&r, s); if(r == c) return (char*)s; s += n; } return 0; } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/util/sparse_array.h000066400000000000000000000336061266464252400250370ustar00rootroot00000000000000// Copyright 2006 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // DESCRIPTION // // SparseArray(m) is a map from integers in [0, m) to T values. // It requires (sizeof(T)+sizeof(int))*m memory, but it provides // fast iteration through the elements in the array and fast clearing // of the array. The array has a concept of certain elements being // uninitialized (having no value). // // Insertion and deletion are constant time operations. // // Allocating the array is a constant time operation // when memory allocation is a constant time operation. // // Clearing the array is a constant time operation (unusual!). // // Iterating through the array is an O(n) operation, where n // is the number of items in the array (not O(m)). // // The array iterator visits entries in the order they were first // inserted into the array. It is safe to add items to the array while // using an iterator: the iterator will visit indices added to the array // during the iteration, but will not re-visit indices whose values // change after visiting. Thus SparseArray can be a convenient // implementation of a work queue. // // The SparseArray implementation is NOT thread-safe. It is up to the // caller to make sure only one thread is accessing the array. (Typically // these arrays are temporary values and used in situations where speed is // important.) // // The SparseArray interface does not present all the usual STL bells and // whistles. // // Implemented with reference to Briggs & Torczon, An Efficient // Representation for Sparse Sets, ACM Letters on Programming Languages // and Systems, Volume 2, Issue 1-4 (March-Dec. 1993), pp. 59-69. // // Briggs & Torczon popularized this technique, but it had been known // long before their paper. They point out that Aho, Hopcroft, and // Ullman's 1974 Design and Analysis of Computer Algorithms and Bentley's // 1986 Programming Pearls both hint at the technique in exercises to the // reader (in Aho & Hopcroft, exercise 2.12; in Bentley, column 1 // exercise 8). // // Briggs & Torczon describe a sparse set implementation. I have // trivially generalized it to create a sparse array (actually the original // target of the AHU and Bentley exercises). // IMPLEMENTATION // // SparseArray uses a vector dense_ and an array sparse_to_dense_, both of // size max_size_. At any point, the number of elements in the sparse array is // size_. // // The vector dense_ contains the size_ elements in the sparse array (with // their indices), // in the order that the elements were first inserted. This array is dense: // the size_ pairs are dense_[0] through dense_[size_-1]. // // The array sparse_to_dense_ maps from indices in [0,m) to indices in // [0,size_). // For indices present in the array, dense_[sparse_to_dense_[i]].index_ == i. // For indices not present in the array, sparse_to_dense_ can contain // any value at all, perhaps outside the range [0, size_) but perhaps not. // // The lax requirement on sparse_to_dense_ values makes clearing // the array very easy: set size_ to 0. Lookups are slightly more // complicated. An index i has a value in the array if and only if: // sparse_to_dense_[i] is in [0, size_) AND // dense_[sparse_to_dense_[i]].index_ == i. // If both these properties hold, only then it is safe to refer to // dense_[sparse_to_dense_[i]].value_ // as the value associated with index i. // // To insert a new entry, set sparse_to_dense_[i] to size_, // initialize dense_[size_], and then increment size_. // // Deletion of specific values from the array is implemented by // swapping dense_[size_-1] and the dense_ being deleted and then // updating the appropriate sparse_to_dense_ entries. // // To make the sparse array as efficient as possible for non-primitive types, // elements may or may not be destroyed when they are deleted from the sparse // array through a call to erase(), erase_existing() or resize(). They // immediately become inaccessible, but they are only guaranteed to be // destroyed when the SparseArray destructor is called. #ifndef RE2_UTIL_SPARSE_ARRAY_H__ #define RE2_UTIL_SPARSE_ARRAY_H__ #include "re2/util/util.h" namespace re2 { template class SparseArray { public: SparseArray(); SparseArray(int max_size); ~SparseArray(); // IndexValue pairs: exposed in SparseArray::iterator. class IndexValue; typedef IndexValue value_type; typedef typename vector::iterator iterator; typedef typename vector::const_iterator const_iterator; inline const IndexValue& iv(int i) const; // Return the number of entries in the array. int size() const { return size_; } // Iterate over the array. iterator begin() { return dense_.begin(); } iterator end() { return dense_.begin() + size_; } const_iterator begin() const { return dense_.begin(); } const_iterator end() const { return dense_.begin() + size_; } // Change the maximum size of the array. // Invalidates all iterators. void resize(int max_size); // Return the maximum size of the array. // Indices can be in the range [0, max_size). int max_size() const { return max_size_; } // Clear the array. void clear() { size_ = 0; } // Check whether index i is in the array. inline bool has_index(int i) const; // Comparison function for sorting. // Can sort the sparse array so that future iterations // will visit indices in increasing order using // sort(arr.begin(), arr.end(), arr.less); static bool less(const IndexValue& a, const IndexValue& b); public: // Set the value at index i to v. inline iterator set(int i, Value v); pair insert(const value_type& new_value); // Returns the value at index i // or defaultv if index i is not initialized in the array. inline Value get(int i, Value defaultv) const; iterator find(int i); const_iterator find(int i) const; // Change the value at index i to v. // Fast but unsafe: only use if has_index(i) is true. inline iterator set_existing(int i, Value v); // Set the value at the new index i to v. // Fast but unsafe: only use if has_index(i) is false. inline iterator set_new(int i, Value v); // Get the value at index i from the array.. // Fast but unsafe: only use if has_index(i) is true. inline Value get_existing(int i) const; // Erasing items from the array during iteration is in general // NOT safe. There is one special case, which is that the current // index-value pair can be erased as long as the iterator is then // checked for being at the end before being incremented. // For example: // // for (i = m.begin(); i != m.end(); ++i) { // if (ShouldErase(i->index(), i->value())) { // m.erase(i->index()); // --i; // } // } // // Except in the specific case just described, elements must // not be erased from the array (including clearing the array) // while iterators are walking over the array. Otherwise, // the iterators could walk past the end of the array. // Erases the element at index i from the array. inline void erase(int i); // Erases the element at index i from the array. // Fast but unsafe: only use if has_index(i) is true. inline void erase_existing(int i); private: // Add the index i to the array. // Only use if has_index(i) is known to be false. // Since it doesn't set the value associated with i, // this function is private, only intended as a helper // for other methods. inline void create_index(int i); // In debug mode, verify that some invariant properties of the class // are being maintained. This is called at the end of the constructor // and at the beginning and end of all public non-const member functions. inline void DebugCheckInvariants() const; int size_; int max_size_; int* sparse_to_dense_; vector dense_; bool valgrind_; DISALLOW_COPY_AND_ASSIGN(SparseArray); }; template SparseArray::SparseArray() : size_(0), max_size_(0), sparse_to_dense_(NULL), dense_(), valgrind_(RunningOnValgrind()) {} // IndexValue pairs: exposed in SparseArray::iterator. template class SparseArray::IndexValue { friend class SparseArray; public: typedef int first_type; typedef Value second_type; IndexValue() {} IndexValue(int index, const Value& value) : second(value), index_(index) {} int index() const { return index_; } Value value() const { return second; } // Provide the data in the 'second' member so that the utilities // in map-util work. Value second; private: int index_; }; template const typename SparseArray::IndexValue& SparseArray::iv(int i) const { DCHECK_GE(i, 0); DCHECK_LT(i, size_); return dense_[i]; } // Change the maximum size of the array. // Invalidates all iterators. template void SparseArray::resize(int new_max_size) { DebugCheckInvariants(); if (new_max_size > max_size_) { int* a = new int[new_max_size]; if (sparse_to_dense_) { memmove(a, sparse_to_dense_, max_size_*sizeof a[0]); // Don't need to zero the memory but appease Valgrind. if (valgrind_) { for (int i = max_size_; i < new_max_size; i++) a[i] = 0xababababU; } delete[] sparse_to_dense_; } sparse_to_dense_ = a; dense_.resize(new_max_size); } max_size_ = new_max_size; if (size_ > max_size_) size_ = max_size_; DebugCheckInvariants(); } // Check whether index i is in the array. template bool SparseArray::has_index(int i) const { DCHECK_GE(i, 0); DCHECK_LT(i, max_size_); if (static_cast(i) >= static_cast(max_size_)) { return false; } // Unsigned comparison avoids checking sparse_to_dense_[i] < 0. return (uint)sparse_to_dense_[i] < (uint)size_ && dense_[sparse_to_dense_[i]].index_ == i; } // Set the value at index i to v. template typename SparseArray::iterator SparseArray::set(int i, Value v) { DebugCheckInvariants(); if (static_cast(i) >= static_cast(max_size_)) { // Semantically, end() would be better here, but we already know // the user did something stupid, so begin() insulates them from // dereferencing an invalid pointer. return begin(); } if (!has_index(i)) create_index(i); return set_existing(i, v); } template pair::iterator, bool> SparseArray::insert( const value_type& new_value) { DebugCheckInvariants(); pair::iterator, bool> p; if (has_index(new_value.index_)) { p = make_pair(dense_.begin() + sparse_to_dense_[new_value.index_], false); } else { p = make_pair(set_new(new_value.index_, new_value.second), true); } DebugCheckInvariants(); return p; } template Value SparseArray::get(int i, Value defaultv) const { if (!has_index(i)) return defaultv; return get_existing(i); } template typename SparseArray::iterator SparseArray::find(int i) { if (has_index(i)) return dense_.begin() + sparse_to_dense_[i]; return end(); } template typename SparseArray::const_iterator SparseArray::find(int i) const { if (has_index(i)) { return dense_.begin() + sparse_to_dense_[i]; } return end(); } template typename SparseArray::iterator SparseArray::set_existing(int i, Value v) { DebugCheckInvariants(); DCHECK(has_index(i)); dense_[sparse_to_dense_[i]].second = v; DebugCheckInvariants(); return dense_.begin() + sparse_to_dense_[i]; } template typename SparseArray::iterator SparseArray::set_new(int i, Value v) { DebugCheckInvariants(); if (static_cast(i) >= static_cast(max_size_)) { // Semantically, end() would be better here, but we already know // the user did something stupid, so begin() insulates them from // dereferencing an invalid pointer. return begin(); } DCHECK(!has_index(i)); create_index(i); return set_existing(i, v); } template Value SparseArray::get_existing(int i) const { DCHECK(has_index(i)); return dense_[sparse_to_dense_[i]].second; } template void SparseArray::erase(int i) { DebugCheckInvariants(); if (has_index(i)) erase_existing(i); DebugCheckInvariants(); } template void SparseArray::erase_existing(int i) { DebugCheckInvariants(); DCHECK(has_index(i)); int di = sparse_to_dense_[i]; if (di < size_ - 1) { dense_[di] = dense_[size_ - 1]; sparse_to_dense_[dense_[di].index_] = di; } size_--; DebugCheckInvariants(); } template void SparseArray::create_index(int i) { DCHECK(!has_index(i)); DCHECK_LT(size_, max_size_); sparse_to_dense_[i] = size_; dense_[size_].index_ = i; size_++; } template SparseArray::SparseArray(int max_size) { max_size_ = max_size; sparse_to_dense_ = new int[max_size]; valgrind_ = RunningOnValgrind(); dense_.resize(max_size); // Don't need to zero the new memory, but appease Valgrind. if (valgrind_) { for (int i = 0; i < max_size; i++) { sparse_to_dense_[i] = 0xababababU; dense_[i].index_ = 0xababababU; } } size_ = 0; DebugCheckInvariants(); } template SparseArray::~SparseArray() { DebugCheckInvariants(); delete[] sparse_to_dense_; } template void SparseArray::DebugCheckInvariants() const { DCHECK_LE(0, size_); DCHECK_LE(size_, max_size_); DCHECK(size_ == 0 || sparse_to_dense_ != NULL); } // Comparison function for sorting. template bool SparseArray::less(const IndexValue& a, const IndexValue& b) { return a.index_ < b.index_; } } // namespace re2 #endif // RE2_UTIL_SPARSE_ARRAY_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/util/sparse_set.h000066400000000000000000000124171266464252400245110ustar00rootroot00000000000000// Copyright 2006 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // DESCRIPTION // // SparseSet(m) is a set of integers in [0, m). // It requires sizeof(int)*m memory, but it provides // fast iteration through the elements in the set and fast clearing // of the set. // // Insertion and deletion are constant time operations. // // Allocating the set is a constant time operation // when memory allocation is a constant time operation. // // Clearing the set is a constant time operation (unusual!). // // Iterating through the set is an O(n) operation, where n // is the number of items in the set (not O(m)). // // The set iterator visits entries in the order they were first // inserted into the array. It is safe to add items to the set while // using an iterator: the iterator will visit indices added to the set // during the iteration, but will not re-visit indices whose values // change after visiting. Thus SparseSet can be a convenient // implementation of a work queue. // // The SparseSet implementation is NOT thread-safe. It is up to the // caller to make sure only one thread is accessing the set. (Typically // these sets are temporary values and used in situations where speed is // important.) // // The SparseSet interface does not present all the usual STL bells and // whistles. // // Implemented with reference to Briggs & Torczon, An Efficient // Representation for Sparse Sets, ACM Letters on Programming Languages // and Systems, Volume 2, Issue 1-4 (March-Dec. 1993), pp. 59-69. // // For a generalization to sparse array, see sparse_array.h. // IMPLEMENTATION // // See sparse_array.h for implementation details #ifndef RE2_UTIL_SPARSE_SET_H__ #define RE2_UTIL_SPARSE_SET_H__ #include "re2/util/util.h" namespace re2 { static bool InitMemory() { #ifdef MEMORY_SANITIZER return true; #else return RunningOnValgrind(); #endif } class SparseSet { public: SparseSet() : size_(0), max_size_(0), sparse_to_dense_(NULL), dense_(NULL), init_memory_(InitMemory()) {} SparseSet(int max_size) { max_size_ = max_size; sparse_to_dense_ = new int[max_size]; dense_ = new int[max_size]; init_memory_ = InitMemory(); // Don't need to zero the memory, but do so anyway // to appease Valgrind. if (init_memory_) { for (int i = 0; i < max_size; i++) { dense_[i] = 0xababababU; sparse_to_dense_[i] = 0xababababU; } } size_ = 0; } ~SparseSet() { delete[] sparse_to_dense_; delete[] dense_; } typedef int* iterator; typedef const int* const_iterator; int size() const { return size_; } iterator begin() { return dense_; } iterator end() { return dense_ + size_; } const_iterator begin() const { return dense_; } const_iterator end() const { return dense_ + size_; } // Change the maximum size of the array. // Invalidates all iterators. void resize(int new_max_size) { if (size_ > new_max_size) size_ = new_max_size; if (new_max_size > max_size_) { int* a = new int[new_max_size]; if (sparse_to_dense_) { memmove(a, sparse_to_dense_, max_size_*sizeof a[0]); if (init_memory_) { for (int i = max_size_; i < new_max_size; i++) a[i] = 0xababababU; } delete[] sparse_to_dense_; } sparse_to_dense_ = a; a = new int[new_max_size]; if (dense_) { memmove(a, dense_, size_*sizeof a[0]); if (init_memory_) { for (int i = size_; i < new_max_size; i++) a[i] = 0xababababU; } delete[] dense_; } dense_ = a; } max_size_ = new_max_size; } // Return the maximum size of the array. // Indices can be in the range [0, max_size). int max_size() const { return max_size_; } // Clear the array. void clear() { size_ = 0; } // Check whether i is in the array. bool contains(int i) const { DCHECK_GE(i, 0); DCHECK_LT(i, max_size_); if (static_cast(i) >= static_cast(max_size_)) { return false; } // Unsigned comparison avoids checking sparse_to_dense_[i] < 0. return (uint)sparse_to_dense_[i] < (uint)size_ && dense_[sparse_to_dense_[i]] == i; } // Adds i to the set. void insert(int i) { if (!contains(i)) insert_new(i); } // Set the value at the new index i to v. // Fast but unsafe: only use if contains(i) is false. void insert_new(int i) { if (static_cast(i) >= static_cast(max_size_)) { // Semantically, end() would be better here, but we already know // the user did something stupid, so begin() insulates them from // dereferencing an invalid pointer. return; } DCHECK(!contains(i)); DCHECK_LT(size_, max_size_); sparse_to_dense_[i] = size_; dense_[size_] = i; size_++; } // Comparison function for sorting. // Can sort the sparse array so that future iterations // will visit indices in increasing order using // sort(arr.begin(), arr.end(), arr.less); static bool less(int a, int b) { return a < b; } private: int size_; int max_size_; int* sparse_to_dense_; int* dense_; bool init_memory_; DISALLOW_COPY_AND_ASSIGN(SparseSet); }; } // namespace re2 #endif // RE2_UTIL_SPARSE_SET_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/util/stringprintf.cc000066400000000000000000000041711266464252400252260ustar00rootroot00000000000000// Copyright 2002 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "re2/util/util.h" namespace re2 { static void StringAppendV(string* dst, const char* format, va_list ap) { // First try with a small fixed size buffer char space[1024]; // It's possible for methods that use a va_list to invalidate // the data in it upon use. The fix is to make a copy // of the structure before using it and use that copy instead. va_list backup_ap; #if defined(WIN32) backup_ap = ap; #else va_copy(backup_ap, ap); #endif int result = vsnprintf(space, sizeof(space), format, backup_ap); va_end(backup_ap); if ((result >= 0) && (static_cast(result) < sizeof(space))) { // It fit dst->append(space, result); return; } // Repeatedly increase buffer size until it fits int length = sizeof(space); while (true) { if (result < 0) { // Older behavior: just try doubling the buffer size length *= 2; } else { // We need exactly "result+1" characters length = result+1; } char* buf = new char[length]; // Restore the va_list before we use it again #if defined(WIN32) va_list backup_ap = ap; #else va_copy(backup_ap, ap); #endif #ifdef WIN32 result = vsnprintf_s(buf, length, length, format, backup_ap); #else result = vsnprintf(buf, length, format, backup_ap); #endif va_end(backup_ap); if ((result >= 0) && (result < length)) { // It fit dst->append(buf, result); delete[] buf; return; } delete[] buf; } } string StringPrintf(const char* format, ...) { va_list ap; va_start(ap, format); string result; StringAppendV(&result, format, ap); va_end(ap); return result; } void SStringPrintf(string* dst, const char* format, ...) { va_list ap; va_start(ap, format); dst->clear(); StringAppendV(dst, format, ap); va_end(ap); } void StringAppendF(string* dst, const char* format, ...) { va_list ap; va_start(ap, format); StringAppendV(dst, format, ap); va_end(ap); } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/util/strutil.cc000066400000000000000000000066631266464252400242130ustar00rootroot00000000000000// Copyright 1999-2005 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include #include "re2/util/util.h" #include "re2/stringpiece.h" namespace re2 { // ---------------------------------------------------------------------- // CEscapeString() // Copies 'src' to 'dest', escaping dangerous characters using // C-style escape sequences. 'src' and 'dest' should not overlap. // Returns the number of bytes written to 'dest' (not including the \0) // or -1 if there was insufficient space. // ---------------------------------------------------------------------- int CEscapeString(const char* src, int src_len, char* dest, int dest_len) { const char* src_end = src + src_len; int used = 0; for (; src < src_end; src++) { if (dest_len - used < 2) // space for two-character escape return -1; unsigned char c = *src; switch (c) { case '\n': dest[used++] = '\\'; dest[used++] = 'n'; break; case '\r': dest[used++] = '\\'; dest[used++] = 'r'; break; case '\t': dest[used++] = '\\'; dest[used++] = 't'; break; case '\"': dest[used++] = '\\'; dest[used++] = '\"'; break; case '\'': dest[used++] = '\\'; dest[used++] = '\''; break; case '\\': dest[used++] = '\\'; dest[used++] = '\\'; break; default: // Note that if we emit \xNN and the src character after that is a hex // digit then that digit must be escaped too to prevent it being // interpreted as part of the character code by C. if (c < ' ' || c > '~') { if (dest_len - used < 5) // space for four-character escape + \0 return -1; #ifdef WIN32 sprintf_s(dest + used, dest_len, "\\%03o", c); #else sprintf(dest + used, "\\%03o", c); #endif used += 4; } else { dest[used++] = c; break; } } } if (dest_len - used < 1) // make sure that there is room for \0 return -1; dest[used] = '\0'; // doesn't count towards return value though return used; } // ---------------------------------------------------------------------- // CEscape() // Copies 'src' to result, escaping dangerous characters using // C-style escape sequences. 'src' and 'dest' should not overlap. // ---------------------------------------------------------------------- string CEscape(const StringPiece& src) { const int dest_length = src.size() * 4 + 1; // Maximum possible expansion char* dest = new char[dest_length]; const int len = CEscapeString(src.data(), src.size(), dest, dest_length); string s = string(dest, len); delete[] dest; return s; } string PrefixSuccessor(const StringPiece& prefix) { // We can increment the last character in the string and be done // unless that character is 255, in which case we have to erase the // last character and increment the previous character, unless that // is 255, etc. If the string is empty or consists entirely of // 255's, we just return the empty string. bool done = false; string limit(prefix.data(), prefix.size()); int index = limit.length() - 1; while (!done && index >= 0) { if ((limit[index]&255) == 255) { limit.erase(index); index--; } else { limit[index]++; done = true; } } if (!done) { return ""; } else { return limit; } } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/util/test.h000066400000000000000000000021701266464252400233130ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #ifndef RE2_UTIL_TEST_H__ #define RE2_UTIL_TEST_H__ #include "util/util.h" #include "util/flags.h" #define TEST(x, y) \ void x##y(void); \ TestRegisterer r##x##y(x##y, # x "." # y); \ void x##y(void) void RegisterTest(void (*)(void), const char*); class TestRegisterer { public: TestRegisterer(void (*fn)(void), const char *s) { RegisterTest(fn, s); } }; // TODO(rsc): Do a better job. #define EXPECT_EQ CHECK_EQ #define EXPECT_TRUE CHECK #define EXPECT_LT CHECK_LT #define EXPECT_GT CHECK_GT #define EXPECT_LE CHECK_LE #define EXPECT_GE CHECK_GE #define EXPECT_FALSE(x) CHECK(!(x)) const bool UsingMallocCounter = false; namespace testing { class MallocCounter { public: MallocCounter(int x) { } static const int THIS_THREAD_ONLY = 0; long long HeapGrowth() { return 0; } long long PeakHeapGrowth() { return 0; } void Reset() { } }; } // namespace testing namespace re2 { int64 VirtualProcessSize(); } // namespace re2 #endif // RE2_UTIL_TEST_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/util/thread.h000066400000000000000000000007421266464252400236060ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #ifndef RE2_UTIL_THREAD_H__ #define RE2_UTIL_THREAD_H__ #include class Thread { public: Thread(); virtual ~Thread(); void Start(); void Join(); void SetJoinable(bool); virtual void Run() = 0; private: pthread_t pid_; bool running_; bool joinable_; }; #endif // RE2_UTIL_THREAD_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/util/utf.h000066400000000000000000000027721266464252400231420ustar00rootroot00000000000000/* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. * * This file and rune.cc have been converted to compile as C++ code * in name space re2. */ #ifndef RE2_UTIL_UTF_H__ #define RE2_UTIL_UTF_H__ #include namespace re2 { typedef signed int Rune; /* Code-point values in Unicode 4.0 are 21 bits wide.*/ enum { UTFmax = 4, /* maximum bytes per rune */ Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ Runeself = 0x80, /* rune and UTF sequences are the same (<) */ Runeerror = 0xFFFD, /* decoding error in UTF */ Runemax = 0x10FFFF, /* maximum rune value */ }; int runetochar(char* s, const Rune* r); int chartorune(Rune* r, const char* s); int fullrune(const char* s, int n); int utflen(const char* s); char* utfrune(const char*, Rune); } // namespace re2 #endif // RE2_UTIL_UTF_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/util/util.h000066400000000000000000000073451266464252400233220ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #ifndef RE2_UTIL_UTIL_H__ #define RE2_UTIL_UTIL_H__ // C #include #include #include #include // For size_t #include #include #include // For clock_gettime, CLOCK_REALTIME #include // For isdigit, isalpha #if !defined(_WIN32) #include // For gettimeofday #endif // C++ #include #include #include #include #include #include #include #include #include #include // Use std names. using std::set; using std::pair; using std::vector; using std::string; using std::min; using std::max; using std::ostream; using std::map; using std::stack; using std::sort; using std::swap; using std::make_pair; #if defined(__GNUC__) && !defined(USE_CXX0X) && !defined(_LIBCPP_ABI_VERSION) #include using std::tr1::unordered_set; #else #include #if defined(_WIN32) using std::tr1::unordered_set; #else using std::unordered_set; #endif #endif #ifdef _WIN32 #define snprintf _snprintf_s #define sprintf sprintf_s #define stricmp _stricmp #define strtof strtod /* not really correct but best we can do */ #define strtoll _strtoi64 #define strtoull _strtoui64 #define vsnprintf vsnprintf_s #pragma warning(disable: 4018) // signed/unsigned mismatch #pragma warning(disable: 4244) // possible data loss in int conversion #pragma warning(disable: 4800) // conversion from int to bool #endif namespace re2 { typedef int8_t int8; typedef uint8_t uint8; typedef int16_t int16; typedef uint16_t uint16; typedef int32_t int32; typedef uint32_t uint32; typedef int64_t int64; typedef uint64_t uint64; typedef unsigned long ulong; typedef unsigned int uint; typedef unsigned short ushort; // Prevent the compiler from complaining about or optimizing away variables // that appear unused. #undef ATTRIBUTE_UNUSED #if defined(__GNUC__) #define ATTRIBUTE_UNUSED __attribute__ ((unused)) #else #define ATTRIBUTE_UNUSED #endif // COMPILE_ASSERT causes a compile error about msg if expr is not true. #if __cplusplus >= 201103L #define COMPILE_ASSERT(expr, msg) static_assert(expr, #msg) #else template struct CompileAssert {}; #define COMPILE_ASSERT(expr, msg) \ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] ATTRIBUTE_UNUSED #endif // DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. // It goes in the private: declarations in a class. #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ void operator=(const TypeName&) #define arraysize(array) (int)(sizeof(array)/sizeof((array)[0])) class StringPiece; string CEscape(const StringPiece& src); int CEscapeString(const char* src, int src_len, char* dest, int dest_len); extern string StringPrintf(const char* format, ...); extern void SStringPrintf(string* dst, const char* format, ...); extern void StringAppendF(string* dst, const char* format, ...); extern string PrefixSuccessor(const StringPiece& prefix); uint32 hashword(const uint32*, size_t, uint32); void hashword2(const uint32*, size_t, uint32*, uint32*); static inline uint32 Hash32StringWithSeed(const char* s, int len, uint32 seed) { return hashword((uint32*)s, len/4, seed); } static inline uint64 Hash64StringWithSeed(const char* s, int len, uint32 seed) { uint32 x, y; x = seed; y = 0; hashword2((uint32*)s, len/4, &x, &y); return ((uint64)x << 32) | y; } int RunningOnValgrind(); } // namespace re2 #include "re2/util/logging.h" #include "re2/util/mutex.h" #include "re2/util/utf.h" #endif // RE2_UTIL_UTIL_H__ openalpr_2.2.4.orig/src/openalpr/support/re2/util/valgrind.cc000066400000000000000000000007701266464252400243040ustar00rootroot00000000000000// Copyright 2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "re2/util/util.h" #ifndef _WIN32 #include "re2/util/valgrind.h" #endif namespace re2 { #ifndef __has_feature #define __has_feature(x) 0 #endif int RunningOnValgrind() { #if __has_feature(memory_sanitizer) return true; #elif defined(RUNNING_ON_VALGRIND) return RUNNING_ON_VALGRIND; #else return 0; #endif } } // namespace re2 openalpr_2.2.4.orig/src/openalpr/support/re2/util/valgrind.h000066400000000000000000010123671266464252400241540ustar00rootroot00000000000000/* -*- c -*- ---------------------------------------------------------------- Notice that the following BSD-style license applies to this one file (valgrind.h) only. The rest of Valgrind is licensed under the terms of the GNU General Public License, version 2, unless otherwise indicated. See the COPYING file in the source distribution for details. ---------------------------------------------------------------- This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2000-2009 Julian Seward. 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. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 4. 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. ---------------------------------------------------------------- Notice that the above BSD-style license applies to this one file (valgrind.h) only. The entire rest of Valgrind is licensed under the terms of the GNU General Public License, version 2. See the COPYING file in the source distribution for details. ---------------------------------------------------------------- */ /* This file is for inclusion into client (your!) code. You can use these macros to manipulate and query Valgrind's execution inside your own programs. The resulting executables will still run without Valgrind, just a little bit more slowly than they otherwise would, but otherwise unchanged. When not running on valgrind, each client request consumes very few (eg. 7) instructions, so the resulting performance loss is negligible unless you plan to execute client requests millions of times per second. Nevertheless, if that is still a problem, you can compile with the NVALGRIND symbol defined (gcc -DNVALGRIND) so that client requests are not even compiled in. */ #ifndef __VALGRIND_H #define __VALGRIND_H #include /* Nb: this file might be included in a file compiled with -ansi. So we can't use C++ style "//" comments nor the "asm" keyword (instead use "__asm__"). */ /* Derive some tags indicating what the target platform is. Note that in this file we're using the compiler's CPP symbols for identifying architectures, which are different to the ones we use within the rest of Valgrind. Note, __powerpc__ is active for both 32 and 64-bit PPC, whereas __powerpc64__ is only active for the latter (on Linux, that is). Misc note: how to find out what's predefined in gcc by default: gcc -Wp,-dM somefile.c */ #undef PLAT_ppc64_aix5 #undef PLAT_ppc32_aix5 #undef PLAT_x86_darwin #undef PLAT_amd64_darwin #undef PLAT_x86_linux #undef PLAT_amd64_linux #undef PLAT_ppc32_linux #undef PLAT_ppc64_linux #undef PLAT_arm_linux #if defined(_AIX) && defined(__64BIT__) # define PLAT_ppc64_aix5 1 #elif defined(_AIX) && !defined(__64BIT__) # define PLAT_ppc32_aix5 1 #elif defined(__APPLE__) && defined(__i386__) # define PLAT_x86_darwin 1 #elif defined(__APPLE__) && defined(__x86_64__) # define PLAT_amd64_darwin 1 #elif defined(__linux__) && defined(__i386__) # define PLAT_x86_linux 1 #elif defined(__linux__) && defined(__x86_64__) # define PLAT_amd64_linux 1 #elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__) # define PLAT_ppc32_linux 1 #elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) # define PLAT_ppc64_linux 1 #elif defined(__linux__) && defined(__arm__) # define PLAT_arm_linux 1 #else /* If we're not compiling for our target platform, don't generate any inline asms. */ # if !defined(NVALGRIND) # define NVALGRIND 1 # endif #endif /* ------------------------------------------------------------------ */ /* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ /* in here of use to end-users -- skip to the next section. */ /* ------------------------------------------------------------------ */ #if defined(NVALGRIND) /* Define NVALGRIND to completely remove the Valgrind magic sequence from the compiled code (analogous to NDEBUG's effects on assert()) */ #define VALGRIND_DO_CLIENT_REQUEST( \ _zzq_rlval, _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ { \ (_zzq_rlval) = (_zzq_default); \ } #else /* ! NVALGRIND */ /* The following defines the magic code sequences which the JITter spots and handles magically. Don't look too closely at them as they will rot your brain. The assembly code sequences for all architectures is in this one file. This is because this file must be stand-alone, and we don't want to have multiple files. For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default value gets put in the return slot, so that everything works when this is executed not under Valgrind. Args are passed in a memory block, and so there's no intrinsic limit to the number that could be passed, but it's currently five. The macro args are: _zzq_rlval result lvalue _zzq_default default value (result returned when running on real CPU) _zzq_request request code _zzq_arg1..5 request params The other two macros are used to support function wrapping, and are a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the guest's NRADDR pseudo-register and whatever other information is needed to safely run the call original from the wrapper: on ppc64-linux, the R2 value at the divert point is also needed. This information is abstracted into a user-visible type, OrigFn. VALGRIND_CALL_NOREDIR_* behaves the same as the following on the guest, but guarantees that the branch instruction will not be redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a complete inline asm, since it needs to be combined with more magic inline asm stuff to be useful. */ /* ------------------------- x86-{linux,darwin} ---------------- */ #if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "roll $3, %%edi ; roll $13, %%edi\n\t" \ "roll $29, %%edi ; roll $19, %%edi\n\t" #define VALGRIND_DO_CLIENT_REQUEST( \ _zzq_rlval, _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ { volatile unsigned int _zzq_args[6]; \ volatile unsigned int _zzq_result; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %EDX = client_request ( %EAX ) */ \ "xchgl %%ebx,%%ebx" \ : "=d" (_zzq_result) \ : "a" (&_zzq_args[0]), "0" (_zzq_default) \ : "cc", "memory" \ ); \ _zzq_rlval = _zzq_result; \ } #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %EAX = guest_NRADDR */ \ "xchgl %%ecx,%%ecx" \ : "=a" (__addr) \ : \ : "cc", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_EAX \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%EAX */ \ "xchgl %%edx,%%edx\n\t" #endif /* PLAT_x86_linux || PLAT_x86_darwin */ /* ------------------------ amd64-{linux,darwin} --------------- */ #if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) typedef struct { unsigned long long int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" #define VALGRIND_DO_CLIENT_REQUEST( \ _zzq_rlval, _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ { volatile unsigned long long int _zzq_args[6]; \ volatile unsigned long long int _zzq_result; \ _zzq_args[0] = (unsigned long long int)(_zzq_request); \ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %RDX = client_request ( %RAX ) */ \ "xchgq %%rbx,%%rbx" \ : "=d" (_zzq_result) \ : "a" (&_zzq_args[0]), "0" (_zzq_default) \ : "cc", "memory" \ ); \ _zzq_rlval = _zzq_result; \ } #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned long long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %RAX = guest_NRADDR */ \ "xchgq %%rcx,%%rcx" \ : "=a" (__addr) \ : \ : "cc", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_RAX \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%RAX */ \ "xchgq %%rdx,%%rdx\n\t" #endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ /* ------------------------ ppc32-linux ------------------------ */ #if defined(PLAT_ppc32_linux) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \ "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t" #define VALGRIND_DO_CLIENT_REQUEST( \ _zzq_rlval, _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ { unsigned int _zzq_args[6]; \ unsigned int _zzq_result; \ unsigned int* _zzq_ptr; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ _zzq_ptr = _zzq_args; \ __asm__ volatile("mr 3,%1\n\t" /*default*/ \ "mr 4,%2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1\n\t" \ "mr %0,3" /*result*/ \ : "=b" (_zzq_result) \ : "b" (_zzq_default), "b" (_zzq_ptr) \ : "cc", "memory", "r3", "r4"); \ _zzq_rlval = _zzq_result; \ } #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R11 */ \ "or 3,3,3\n\t" #endif /* PLAT_ppc32_linux */ /* ------------------------ ppc64-linux ------------------------ */ #if defined(PLAT_ppc64_linux) typedef struct { unsigned long long int nraddr; /* where's the code? */ unsigned long long int r2; /* what tocptr do we need? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ "rotldi 0,0,61 ; rotldi 0,0,51\n\t" #define VALGRIND_DO_CLIENT_REQUEST( \ _zzq_rlval, _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ { unsigned long long int _zzq_args[6]; \ register unsigned long long int _zzq_result __asm__("r3"); \ register unsigned long long int* _zzq_ptr __asm__("r4"); \ _zzq_args[0] = (unsigned long long int)(_zzq_request); \ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ _zzq_ptr = _zzq_args; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1" \ : "=r" (_zzq_result) \ : "0" (_zzq_default), "r" (_zzq_ptr) \ : "cc", "memory"); \ _zzq_rlval = _zzq_result; \ } #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ register unsigned long long int __addr __asm__("r3"); \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2" \ : "=r" (__addr) \ : \ : "cc", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR_GPR2 */ \ "or 4,4,4" \ : "=r" (__addr) \ : \ : "cc", "memory" \ ); \ _zzq_orig->r2 = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R11 */ \ "or 3,3,3\n\t" #endif /* PLAT_ppc64_linux */ /* ------------------------- arm-linux ------------------------- */ #if defined(PLAT_arm_linux) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \ "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t" #define VALGRIND_DO_CLIENT_REQUEST( \ _zzq_rlval, _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ { volatile unsigned int _zzq_args[6]; \ volatile unsigned int _zzq_result; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ __asm__ volatile("mov r3, %1\n\t" /*default*/ \ "mov r4, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* R3 = client_request ( R4 ) */ \ "orr r10, r10, r10\n\t" \ "mov %0, r3" /*result*/ \ : "=r" (_zzq_result) \ : "r" (_zzq_default), "r" (&_zzq_args[0]) \ : "cc","memory", "r3", "r4"); \ _zzq_rlval = _zzq_result; \ } #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* R3 = guest_NRADDR */ \ "orr r11, r11, r11\n\t" \ "mov %0, r3" \ : "=r" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R4 */ \ "orr r12, r12, r12\n\t" #endif /* PLAT_arm_linux */ /* ------------------------ ppc32-aix5 ------------------------- */ #if defined(PLAT_ppc32_aix5) typedef struct { unsigned int nraddr; /* where's the code? */ unsigned int r2; /* what tocptr do we need? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \ "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t" #define VALGRIND_DO_CLIENT_REQUEST( \ _zzq_rlval, _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ { unsigned int _zzq_args[7]; \ register unsigned int _zzq_result; \ register unsigned int* _zzq_ptr; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ _zzq_args[6] = (unsigned int)(_zzq_default); \ _zzq_ptr = _zzq_args; \ __asm__ volatile("mr 4,%1\n\t" \ "lwz 3, 24(4)\n\t" \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1\n\t" \ "mr %0,3" \ : "=b" (_zzq_result) \ : "b" (_zzq_ptr) \ : "r3", "r4", "cc", "memory"); \ _zzq_rlval = _zzq_result; \ } #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ register unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "r3", "cc", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR_GPR2 */ \ "or 4,4,4\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "r3", "cc", "memory" \ ); \ _zzq_orig->r2 = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R11 */ \ "or 3,3,3\n\t" #endif /* PLAT_ppc32_aix5 */ /* ------------------------ ppc64-aix5 ------------------------- */ #if defined(PLAT_ppc64_aix5) typedef struct { unsigned long long int nraddr; /* where's the code? */ unsigned long long int r2; /* what tocptr do we need? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ "rotldi 0,0,61 ; rotldi 0,0,51\n\t" #define VALGRIND_DO_CLIENT_REQUEST( \ _zzq_rlval, _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ { unsigned long long int _zzq_args[7]; \ register unsigned long long int _zzq_result; \ register unsigned long long int* _zzq_ptr; \ _zzq_args[0] = (unsigned int long long)(_zzq_request); \ _zzq_args[1] = (unsigned int long long)(_zzq_arg1); \ _zzq_args[2] = (unsigned int long long)(_zzq_arg2); \ _zzq_args[3] = (unsigned int long long)(_zzq_arg3); \ _zzq_args[4] = (unsigned int long long)(_zzq_arg4); \ _zzq_args[5] = (unsigned int long long)(_zzq_arg5); \ _zzq_args[6] = (unsigned int long long)(_zzq_default); \ _zzq_ptr = _zzq_args; \ __asm__ volatile("mr 4,%1\n\t" \ "ld 3, 48(4)\n\t" \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1\n\t" \ "mr %0,3" \ : "=b" (_zzq_result) \ : "b" (_zzq_ptr) \ : "r3", "r4", "cc", "memory"); \ _zzq_rlval = _zzq_result; \ } #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ register unsigned long long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "r3", "cc", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR_GPR2 */ \ "or 4,4,4\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "r3", "cc", "memory" \ ); \ _zzq_orig->r2 = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R11 */ \ "or 3,3,3\n\t" #endif /* PLAT_ppc64_aix5 */ /* Insert assembly code for other platforms here... */ #endif /* NVALGRIND */ /* ------------------------------------------------------------------ */ /* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ /* ugly. It's the least-worst tradeoff I can think of. */ /* ------------------------------------------------------------------ */ /* This section defines magic (a.k.a appalling-hack) macros for doing guaranteed-no-redirection macros, so as to get from function wrappers to the functions they are wrapping. The whole point is to construct standard call sequences, but to do the call itself with a special no-redirect call pseudo-instruction that the JIT understands and handles specially. This section is long and repetitious, and I can't see a way to make it shorter. The naming scheme is as follows: CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} 'W' stands for "word" and 'v' for "void". Hence there are different macros for calling arity 0, 1, 2, 3, 4, etc, functions, and for each, the possibility of returning a word-typed result, or no result. */ /* Use these to write the name of your wrapper. NOTE: duplicates VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. */ /* Use an extra level of macroisation so as to ensure the soname/fnname args are fully macro-expanded before pasting them together. */ #define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd #define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ VG_CONCAT4(_vgwZU_,soname,_,fnname) #define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ VG_CONCAT4(_vgwZZ_,soname,_,fnname) /* Use this macro from within a wrapper function to collect the context (address and possibly other info) of the original function. Once you have that you can then use it in one of the CALL_FN_ macros. The type of the argument _lval is OrigFn. */ #define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) /* Derivatives of the main macros below, for calling functions returning void. */ #define CALL_FN_v_v(fnptr) \ do { volatile unsigned long _junk; \ CALL_FN_W_v(_junk,fnptr); } while (0) #define CALL_FN_v_W(fnptr, arg1) \ do { volatile unsigned long _junk; \ CALL_FN_W_W(_junk,fnptr,arg1); } while (0) #define CALL_FN_v_WW(fnptr, arg1,arg2) \ do { volatile unsigned long _junk; \ CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) #define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ do { volatile unsigned long _junk; \ CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) #define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \ do { volatile unsigned long _junk; \ CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0) #define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \ do { volatile unsigned long _junk; \ CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0) #define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \ do { volatile unsigned long _junk; \ CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0) #define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \ do { volatile unsigned long _junk; \ CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0) /* ------------------------- x86-{linux,darwin} ---------------- */ #if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) /* These regs are trashed by the hidden call. No need to mention eax as gcc can already see that, plus causes gcc to bomb. */ #define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" /* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ "addl $4, %%esp\n" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ "addl $8, %%esp\n" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ "addl $12, %%esp\n" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ "addl $16, %%esp\n" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ "addl $20, %%esp\n" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ "addl $24, %%esp\n" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ "addl $28, %%esp\n" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ "addl $32, %%esp\n" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ "addl $36, %%esp\n" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ "pushl 40(%%eax)\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ "addl $40, %%esp\n" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ "pushl 44(%%eax)\n\t" \ "pushl 40(%%eax)\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ "addl $44, %%esp\n" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ "pushl 48(%%eax)\n\t" \ "pushl 44(%%eax)\n\t" \ "pushl 40(%%eax)\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ "addl $48, %%esp\n" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_x86_linux || PLAT_x86_darwin */ /* ------------------------ amd64-{linux,darwin} --------------- */ #if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) /* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ "rdi", "r8", "r9", "r10", "r11" /* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned long) == 8. */ /* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ macros. In order not to trash the stack redzone, we need to drop %rsp by 128 before the hidden call, and restore afterwards. The nastyness is that it is only by luck that the stack still appears to be unwindable during the hidden call - since then the behaviour of any routine using this macro does not match what the CFI data says. Sigh. Why is this important? Imagine that a wrapper has a stack allocated local, and passes to the hidden call, a pointer to it. Because gcc does not know about the hidden call, it may allocate that local in the redzone. Unfortunately the hidden call may then trash it before it comes to use it. So we must step clear of the redzone, for the duration of the hidden call, to make it safe. Probably the same problem afflicts the other redzone-style ABIs too (ppc64-linux, ppc32-aix5, ppc64-aix5); but for those, the stack is self describing (none of this CFI nonsense) so at least messing with the stack pointer doesn't give a danger of non-unwindable stack. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ "addq $128,%%rsp\n\t" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ "addq $128,%%rsp\n\t" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ "addq $128,%%rsp\n\t" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ "addq $128,%%rsp\n\t" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ "addq $128,%%rsp\n\t" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ "addq $128,%%rsp\n\t" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ "addq $128,%%rsp\n\t" \ VALGRIND_CALL_NOREDIR_RAX \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ "addq $8, %%rsp\n" \ "addq $128,%%rsp\n\t" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ "addq $16, %%rsp\n" \ "addq $128,%%rsp\n\t" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ "addq $24, %%rsp\n" \ "addq $128,%%rsp\n\t" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "pushq 80(%%rax)\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ "addq $32, %%rsp\n" \ "addq $128,%%rsp\n\t" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "pushq 88(%%rax)\n\t" \ "pushq 80(%%rax)\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ "addq $40, %%rsp\n" \ "addq $128,%%rsp\n\t" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ "subq $128,%%rsp\n\t" \ "pushq 96(%%rax)\n\t" \ "pushq 88(%%rax)\n\t" \ "pushq 80(%%rax)\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ "addq $48, %%rsp\n" \ "addq $128,%%rsp\n\t" \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ /* ------------------------ ppc32-linux ------------------------ */ #if defined(PLAT_ppc32_linux) /* This is useful for finding out about the on-stack stuff: extern int f9 ( int,int,int,int,int,int,int,int,int ); extern int f10 ( int,int,int,int,int,int,int,int,int,int ); extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); int g9 ( void ) { return f9(11,22,33,44,55,66,77,88,99); } int g10 ( void ) { return f10(11,22,33,44,55,66,77,88,99,110); } int g11 ( void ) { return f11(11,22,33,44,55,66,77,88,99,110,121); } int g12 ( void ) { return f12(11,22,33,44,55,66,77,88,99,110,121,132); } */ /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* These CALL_FN_ macros assume that on ppc32-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "addi 1,1,-16\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "addi 1,1,16\n\t" \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "addi 1,1,-16\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,12(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "addi 1,1,16\n\t" \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "addi 1,1,-32\n\t" \ /* arg11 */ \ "lwz 3,44(11)\n\t" \ "stw 3,16(1)\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,12(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "addi 1,1,32\n\t" \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ _argvec[12] = (unsigned long)arg12; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "addi 1,1,-32\n\t" \ /* arg12 */ \ "lwz 3,48(11)\n\t" \ "stw 3,20(1)\n\t" \ /* arg11 */ \ "lwz 3,44(11)\n\t" \ "stw 3,16(1)\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,12(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "addi 1,1,32\n\t" \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc32_linux */ /* ------------------------ ppc64-linux ------------------------ */ #if defined(PLAT_ppc64_linux) /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned long) == 8. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+0]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)" /* restore tocptr */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+1]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)" /* restore tocptr */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+2]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)" /* restore tocptr */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+3]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)" /* restore tocptr */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+4]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)" /* restore tocptr */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+5]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)" /* restore tocptr */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+6]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)" /* restore tocptr */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+7]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)" /* restore tocptr */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+8]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)" /* restore tocptr */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+9]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ "addi 1,1,128" /* restore frame */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+10]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ "addi 1,1,128" /* restore frame */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+11]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg11 */ \ "ld 3,88(11)\n\t" \ "std 3,128(1)\n\t" \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ "addi 1,1,144" /* restore frame */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+12]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ _argvec[2+12] = (unsigned long)arg12; \ __asm__ volatile( \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg12 */ \ "ld 3,96(11)\n\t" \ "std 3,136(1)\n\t" \ /* arg11 */ \ "ld 3,88(11)\n\t" \ "std 3,128(1)\n\t" \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ "addi 1,1,144" /* restore frame */ \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc64_linux */ /* ------------------------- arm-linux ------------------------- */ #if defined(PLAT_arm_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4","r14" /* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ "ldr r0, [%1, #4] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ "ldr r0, [%1, #20] \n\t" \ "push {r0} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "add sp, sp, #4 \n\t" \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "push {r0, r1} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "add sp, sp, #8 \n\t" \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "push {r0, r1, r2} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "add sp, sp, #12 \n\t" \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "push {r0, r1, r2, r3} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "add sp, sp, #16 \n\t" \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "add sp, sp, #20 \n\t" \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ "ldr r0, [%1, #40] \n\t" \ "push {r0} \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "add sp, sp, #24 \n\t" \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ "ldr r0, [%1, #40] \n\t" \ "ldr r1, [%1, #44] \n\t" \ "push {r0, r1} \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "add sp, sp, #28 \n\t" \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory",__CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ "ldr r0, [%1, #40] \n\t" \ "ldr r1, [%1, #44] \n\t" \ "ldr r2, [%1, #48] \n\t" \ "push {r0, r1, r2} \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ "add sp, sp, #32 \n\t" \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_arm_linux */ /* ------------------------ ppc32-aix5 ------------------------- */ #if defined(PLAT_ppc32_aix5) /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* Expand the stack frame, copying enough info that unwinding still works. Trashes r3. */ #define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \ "addi 1,1,-" #_n_fr "\n\t" \ "lwz 3," #_n_fr "(1)\n\t" \ "stw 3,0(1)\n\t" #define VG_CONTRACT_FRAME_BY(_n_fr) \ "addi 1,1," #_n_fr "\n\t" /* These CALL_FN_ macros assume that on ppc32-aix5, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+0]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+1]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+2]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+3]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+4]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+5]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+6]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+7]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+8]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+9]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ VG_EXPAND_FRAME_BY_trashes_r3(64) \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,56(1)\n\t" \ /* args1-8 */ \ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(64) \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+10]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ VG_EXPAND_FRAME_BY_trashes_r3(64) \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,60(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,56(1)\n\t" \ /* args1-8 */ \ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(64) \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+11]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ VG_EXPAND_FRAME_BY_trashes_r3(72) \ /* arg11 */ \ "lwz 3,44(11)\n\t" \ "stw 3,64(1)\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,60(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,56(1)\n\t" \ /* args1-8 */ \ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(72) \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+12]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ _argvec[2+12] = (unsigned long)arg12; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "stw 2,-8(11)\n\t" /* save tocptr */ \ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ VG_EXPAND_FRAME_BY_trashes_r3(72) \ /* arg12 */ \ "lwz 3,48(11)\n\t" \ "stw 3,68(1)\n\t" \ /* arg11 */ \ "lwz 3,44(11)\n\t" \ "stw 3,64(1)\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,60(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,56(1)\n\t" \ /* args1-8 */ \ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ "lwz 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "lwz 2,-8(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(72) \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc32_aix5 */ /* ------------------------ ppc64-aix5 ------------------------- */ #if defined(PLAT_ppc64_aix5) /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* Expand the stack frame, copying enough info that unwinding still works. Trashes r3. */ #define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \ "addi 1,1,-" #_n_fr "\n\t" \ "ld 3," #_n_fr "(1)\n\t" \ "std 3,0(1)\n\t" #define VG_CONTRACT_FRAME_BY(_n_fr) \ "addi 1,1," #_n_fr "\n\t" /* These CALL_FN_ macros assume that on ppc64-aix5, sizeof(unsigned long) == 8. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+0]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+1]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+2]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+3]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+4]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+5]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+6]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+7]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+8]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+9]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ VG_EXPAND_FRAME_BY_trashes_r3(128) \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(128) \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+10]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ VG_EXPAND_FRAME_BY_trashes_r3(128) \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(128) \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+11]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ VG_EXPAND_FRAME_BY_trashes_r3(144) \ /* arg11 */ \ "ld 3,88(11)\n\t" \ "std 3,128(1)\n\t" \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(144) \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+12]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ _argvec[2+12] = (unsigned long)arg12; \ __asm__ volatile( \ "mr 11,%1\n\t" \ VG_EXPAND_FRAME_BY_trashes_r3(512) \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ VG_EXPAND_FRAME_BY_trashes_r3(144) \ /* arg12 */ \ "ld 3,96(11)\n\t" \ "std 3,136(1)\n\t" \ /* arg11 */ \ "ld 3,88(11)\n\t" \ "std 3,128(1)\n\t" \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VG_CONTRACT_FRAME_BY(144) \ VG_CONTRACT_FRAME_BY(512) \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc64_aix5 */ /* ------------------------------------------------------------------ */ /* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ /* */ /* ------------------------------------------------------------------ */ /* Some request codes. There are many more of these, but most are not exposed to end-user view. These are the public ones, all of the form 0x1000 + small_number. Core ones are in the range 0x00000000--0x0000ffff. The non-public ones start at 0x2000. */ /* These macros are used by tools -- they must be public, but don't embed them into other programs. */ #define VG_USERREQ_TOOL_BASE(a,b) \ ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) #define VG_IS_TOOL_USERREQ(a, b, v) \ (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) /* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! This enum comprises an ABI exported by Valgrind to programs which use client requests. DO NOT CHANGE THE ORDER OF THESE ENTRIES, NOR DELETE ANY -- add new ones at the end. */ typedef enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, /* These allow any function to be called from the simulated CPU but run on the real CPU. Nb: the first arg passed to the function is always the ThreadId of the running thread! So CLIENT_CALL0 actually requires a 1 arg function, etc. */ VG_USERREQ__CLIENT_CALL0 = 0x1101, VG_USERREQ__CLIENT_CALL1 = 0x1102, VG_USERREQ__CLIENT_CALL2 = 0x1103, VG_USERREQ__CLIENT_CALL3 = 0x1104, /* Can be useful in regression testing suites -- eg. can send Valgrind's output to /dev/null and still count errors. */ VG_USERREQ__COUNT_ERRORS = 0x1201, /* These are useful and can be interpreted by any tool that tracks malloc() et al, by using vg_replace_malloc.c. */ VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, VG_USERREQ__FREELIKE_BLOCK = 0x1302, /* Memory pool support. */ VG_USERREQ__CREATE_MEMPOOL = 0x1303, VG_USERREQ__DESTROY_MEMPOOL = 0x1304, VG_USERREQ__MEMPOOL_ALLOC = 0x1305, VG_USERREQ__MEMPOOL_FREE = 0x1306, VG_USERREQ__MEMPOOL_TRIM = 0x1307, VG_USERREQ__MOVE_MEMPOOL = 0x1308, VG_USERREQ__MEMPOOL_CHANGE = 0x1309, VG_USERREQ__MEMPOOL_EXISTS = 0x130a, /* Allow printfs to valgrind log. */ /* The first two pass the va_list argument by value, which assumes it is the same size as or smaller than a UWord, which generally isn't the case. Hence are deprecated. The second two pass the vargs by reference and so are immune to this problem. */ /* both :: char* fmt, va_list vargs (DEPRECATED) */ VG_USERREQ__PRINTF = 0x1401, VG_USERREQ__PRINTF_BACKTRACE = 0x1402, /* both :: char* fmt, va_list* vargs */ VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403, VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404, /* Stack support. */ VG_USERREQ__STACK_REGISTER = 0x1501, VG_USERREQ__STACK_DEREGISTER = 0x1502, VG_USERREQ__STACK_CHANGE = 0x1503, /* Wine support */ VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601 } Vg_ClientRequest; #if !defined(__GNUC__) # define __extension__ /* */ #endif /* Returns the number of Valgrinds this code is running under. That is, 0 if running natively, 1 if running under Valgrind, 2 if running under Valgrind which is running under another Valgrind, etc. */ #define RUNNING_ON_VALGRIND __extension__ \ ({unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* if not */, \ VG_USERREQ__RUNNING_ON_VALGRIND, \ 0, 0, 0, 0, 0); \ _qzz_res; \ }) /* Discard translation of code in the range [_qzz_addr .. _qzz_addr + _qzz_len - 1]. Useful if you are debugging a JITter or some such, since it provides a way to make sure valgrind will retranslate the invalidated area. Returns no value. */ #define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__DISCARD_TRANSLATIONS, \ _qzz_addr, _qzz_len, 0, 0, 0); \ } /* These requests are for getting Valgrind itself to print something. Possibly with a backtrace. This is a really ugly hack. The return value is the number of characters printed, excluding the "**** " part at the start and the backtrace (if present). */ #if defined(NVALGRIND) # define VALGRIND_PRINTF(...) # define VALGRIND_PRINTF_BACKTRACE(...) #else /* NVALGRIND */ /* Modern GCC will optimize the static routine out if unused, and unused attribute will shut down warnings about it. */ static int VALGRIND_PRINTF(const char *format, ...) __attribute__((format(__printf__, 1, 2), __unused__)); static int VALGRIND_PRINTF(const char *format, ...) { unsigned long _qzz_res; va_list vargs; va_start(vargs, format); VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, VG_USERREQ__PRINTF_VALIST_BY_REF, (unsigned long)format, (unsigned long)&vargs, 0, 0, 0); va_end(vargs); return (int)_qzz_res; } static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) __attribute__((format(__printf__, 1, 2), __unused__)); static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) { unsigned long _qzz_res; va_list vargs; va_start(vargs, format); VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, (unsigned long)format, (unsigned long)&vargs, 0, 0, 0); va_end(vargs); return (int)_qzz_res; } #endif /* NVALGRIND */ /* These requests allow control to move from the simulated CPU to the real CPU, calling an arbitrary function. Note that the current ThreadId is inserted as the first argument. So this call: VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) requires f to have this signature: Word f(Word tid, Word arg1, Word arg2) where "Word" is a word-sized type. Note that these client requests are not entirely reliable. For example, if you call a function with them that subsequently calls printf(), there's a high chance Valgrind will crash. Generally, your prospects of these working are made higher if the called function does not refer to any global variables, and does not refer to any libc or other functions (printf et al). Any kind of entanglement with libc or dynamic linking is likely to have a bad outcome, for tricky reasons which we've grappled with a lot in the past. */ #define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ __extension__ \ ({unsigned long _qyy_res; \ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ VG_USERREQ__CLIENT_CALL0, \ _qyy_fn, \ 0, 0, 0, 0); \ _qyy_res; \ }) #define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ __extension__ \ ({unsigned long _qyy_res; \ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ VG_USERREQ__CLIENT_CALL1, \ _qyy_fn, \ _qyy_arg1, 0, 0, 0); \ _qyy_res; \ }) #define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ __extension__ \ ({unsigned long _qyy_res; \ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ VG_USERREQ__CLIENT_CALL2, \ _qyy_fn, \ _qyy_arg1, _qyy_arg2, 0, 0); \ _qyy_res; \ }) #define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ __extension__ \ ({unsigned long _qyy_res; \ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ VG_USERREQ__CLIENT_CALL3, \ _qyy_fn, \ _qyy_arg1, _qyy_arg2, \ _qyy_arg3, 0); \ _qyy_res; \ }) /* Counts the number of errors that have been recorded by a tool. Nb: the tool must record the errors with VG_(maybe_record_error)() or VG_(unique_error)() for them to be counted. */ #define VALGRIND_COUNT_ERRORS \ __extension__ \ ({unsigned int _qyy_res; \ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ VG_USERREQ__COUNT_ERRORS, \ 0, 0, 0, 0, 0); \ _qyy_res; \ }) /* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing when heap blocks are allocated in order to give accurate results. This happens automatically for the standard allocator functions such as malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete, delete[], etc. But if your program uses a custom allocator, this doesn't automatically happen, and Valgrind will not do as well. For example, if you allocate superblocks with mmap() and then allocates chunks of the superblocks, all Valgrind's observations will be at the mmap() level and it won't know that the chunks should be considered separate entities. In Memcheck's case, that means you probably won't get heap block overrun detection (because there won't be redzones marked as unaddressable) and you definitely won't get any leak detection. The following client requests allow a custom allocator to be annotated so that it can be handled accurately by Valgrind. VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated by a malloc()-like function. For Memcheck (an illustrative case), this does two things: - It records that the block has been allocated. This means any addresses within the block mentioned in error messages will be identified as belonging to the block. It also means that if the block isn't freed it will be detected by the leak checker. - It marks the block as being addressable and undefined (if 'is_zeroed' is not set), or addressable and defined (if 'is_zeroed' is set). This controls how accesses to the block by the program are handled. 'addr' is the start of the usable block (ie. after any redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator can apply redzones -- these are blocks of padding at the start and end of each block. Adding redzones is recommended as it makes it much more likely Valgrind will spot block overruns. `is_zeroed' indicates if the memory is zeroed (or filled with another predictable value), as is the case for calloc(). VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a heap block -- that will be used by the client program -- is allocated. It's best to put it at the outermost level of the allocator if possible; for example, if you have a function my_alloc() which calls internal_alloc(), and the client request is put inside internal_alloc(), stack traces relating to the heap block will contain entries for both my_alloc() and internal_alloc(), which is probably not what you want. For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out custom blocks from within a heap block, B, that has been allocated with malloc/calloc/new/etc, then block B will be *ignored* during leak-checking -- the custom blocks will take precedence. VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For Memcheck, it does two things: - It records that the block has been deallocated. This assumes that the block was annotated as having been allocated via VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. - It marks the block as being unaddressable. VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a heap block is deallocated. In many cases, these two client requests will not be enough to get your allocator working well with Memcheck. More specifically, if your allocator writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call will be necessary to mark the memory as addressable just before the zeroing occurs, otherwise you'll get a lot of invalid write errors. For example, you'll need to do this if your allocator recycles freed blocks, but it zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK). Alternatively, if your allocator reuses freed blocks for allocator-internal data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary. Really, what's happening is a blurring of the lines between the client program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the memory should be considered unaddressable to the client program, but the allocator knows more than the rest of the client program and so may be able to safely access it. Extra client requests are necessary for Valgrind to understand the distinction between the allocator and the rest of the program. Note: there is currently no VALGRIND_REALLOCLIKE_BLOCK client request; it has to be emulated with MALLOCLIKE/FREELIKE and memory copying. Ignored if addr == 0. */ #define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__MALLOCLIKE_BLOCK, \ addr, sizeB, rzB, is_zeroed, 0); \ } /* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. Ignored if addr == 0. */ #define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__FREELIKE_BLOCK, \ addr, rzB, 0, 0, 0); \ } /* Create a memory pool. */ #define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__CREATE_MEMPOOL, \ pool, rzB, is_zeroed, 0, 0); \ } /* Destroy a memory pool. */ #define VALGRIND_DESTROY_MEMPOOL(pool) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__DESTROY_MEMPOOL, \ pool, 0, 0, 0, 0); \ } /* Associate a piece of memory with a memory pool. */ #define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__MEMPOOL_ALLOC, \ pool, addr, size, 0, 0); \ } /* Disassociate a piece of memory from a memory pool. */ #define VALGRIND_MEMPOOL_FREE(pool, addr) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__MEMPOOL_FREE, \ pool, addr, 0, 0, 0); \ } /* Disassociate any pieces outside a particular range. */ #define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__MEMPOOL_TRIM, \ pool, addr, size, 0, 0); \ } /* Resize and/or move a piece associated with a memory pool. */ #define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__MOVE_MEMPOOL, \ poolA, poolB, 0, 0, 0); \ } /* Resize and/or move a piece associated with a memory pool. */ #define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__MEMPOOL_CHANGE, \ pool, addrA, addrB, size, 0); \ } /* Return 1 if a mempool exists, else 0. */ #define VALGRIND_MEMPOOL_EXISTS(pool) \ __extension__ \ ({unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__MEMPOOL_EXISTS, \ pool, 0, 0, 0, 0); \ _qzz_res; \ }) /* Mark a piece of memory as being a stack. Returns a stack id. */ #define VALGRIND_STACK_REGISTER(start, end) \ __extension__ \ ({unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__STACK_REGISTER, \ start, end, 0, 0, 0); \ _qzz_res; \ }) /* Unmark the piece of memory associated with a stack id as being a stack. */ #define VALGRIND_STACK_DEREGISTER(id) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__STACK_DEREGISTER, \ id, 0, 0, 0, 0); \ } /* Change the start and end address of the stack id. */ #define VALGRIND_STACK_CHANGE(id, start, end) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__STACK_CHANGE, \ id, start, end, 0, 0); \ } /* Load PDB debug info for Wine PE image_map. */ #define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \ {unsigned int _qzz_res; \ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ VG_USERREQ__LOAD_PDB_DEBUGINFO, \ fd, ptr, total_size, delta, 0); \ } #undef PLAT_x86_linux #undef PLAT_amd64_linux #undef PLAT_ppc32_linux #undef PLAT_ppc64_linux #undef PLAT_arm_linux #undef PLAT_ppc32_aix5 #undef PLAT_ppc64_aix5 #endif /* __VALGRIND_H */ openalpr_2.2.4.orig/src/openalpr/support/re2/variadic_function.h000066400000000000000000000404111266464252400250460ustar00rootroot00000000000000// Copyright 2010 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #ifndef RE2_VARIADIC_FUNCTION_H_ #define RE2_VARIADIC_FUNCTION_H_ namespace re2 { template class VariadicFunction2 { public: Result operator()(Param0 p0, Param1 p1) const { return Func(p0, p1, 0, 0); } Result operator()(Param0 p0, Param1 p1, const Arg& a0) const { const Arg* const args[] = { &a0 }; return Func(p0, p1, args, 1); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1) const { const Arg* const args[] = { &a0, &a1 }; return Func(p0, p1, args, 2); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2) const { const Arg* const args[] = { &a0, &a1, &a2 }; return Func(p0, p1, args, 3); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3) const { const Arg* const args[] = { &a0, &a1, &a2, &a3 }; return Func(p0, p1, args, 4); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4 }; return Func(p0, p1, args, 5); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5 }; return Func(p0, p1, args, 6); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6 }; return Func(p0, p1, args, 7); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7 }; return Func(p0, p1, args, 8); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8 }; return Func(p0, p1, args, 9); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9 }; return Func(p0, p1, args, 10); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10 }; return Func(p0, p1, args, 11); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11 }; return Func(p0, p1, args, 12); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12 }; return Func(p0, p1, args, 13); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13 }; return Func(p0, p1, args, 14); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14 }; return Func(p0, p1, args, 15); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15 }; return Func(p0, p1, args, 16); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16 }; return Func(p0, p1, args, 17); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17 }; return Func(p0, p1, args, 18); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18 }; return Func(p0, p1, args, 19); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19 }; return Func(p0, p1, args, 20); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19, const Arg& a20) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20 }; return Func(p0, p1, args, 21); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20, &a21 }; return Func(p0, p1, args, 22); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21, const Arg& a22) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20, &a21, &a22 }; return Func(p0, p1, args, 23); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21, const Arg& a22, const Arg& a23) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20, &a21, &a22, &a23 }; return Func(p0, p1, args, 24); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21, const Arg& a22, const Arg& a23, const Arg& a24) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20, &a21, &a22, &a23, &a24 }; return Func(p0, p1, args, 25); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21, const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20, &a21, &a22, &a23, &a24, &a25 }; return Func(p0, p1, args, 26); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21, const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25, const Arg& a26) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20, &a21, &a22, &a23, &a24, &a25, &a26 }; return Func(p0, p1, args, 27); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21, const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25, const Arg& a26, const Arg& a27) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20, &a21, &a22, &a23, &a24, &a25, &a26, &a27 }; return Func(p0, p1, args, 28); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21, const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25, const Arg& a26, const Arg& a27, const Arg& a28) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20, &a21, &a22, &a23, &a24, &a25, &a26, &a27, &a28 }; return Func(p0, p1, args, 29); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21, const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25, const Arg& a26, const Arg& a27, const Arg& a28, const Arg& a29) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20, &a21, &a22, &a23, &a24, &a25, &a26, &a27, &a28, &a29 }; return Func(p0, p1, args, 30); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21, const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25, const Arg& a26, const Arg& a27, const Arg& a28, const Arg& a29, const Arg& a30) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20, &a21, &a22, &a23, &a24, &a25, &a26, &a27, &a28, &a29, &a30 }; return Func(p0, p1, args, 31); } Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1, const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5, const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9, const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13, const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17, const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21, const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25, const Arg& a26, const Arg& a27, const Arg& a28, const Arg& a29, const Arg& a30, const Arg& a31) const { const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20, &a21, &a22, &a23, &a24, &a25, &a26, &a27, &a28, &a29, &a30, &a31 }; return Func(p0, p1, args, 32); } }; } // namespace re2 #endif // RE2_VARIADIC_FUNCTION_H_ openalpr_2.2.4.orig/src/openalpr/support/re2/walker-inl.h000066400000000000000000000170601266464252400234300ustar00rootroot00000000000000// Copyright 2006 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Helper class for traversing Regexps without recursion. // Clients should declare their own subclasses that override // the PreVisit and PostVisit methods, which are called before // and after visiting the subexpressions. // Not quite the Visitor pattern, because (among other things) // the Visitor pattern is recursive. #ifndef RE2_WALKER_INL_H__ #define RE2_WALKER_INL_H__ #include "re2/regexp.h" namespace re2 { template struct WalkState; template class Regexp::Walker { public: Walker(); virtual ~Walker(); // Virtual method called before visiting re's children. // PreVisit passes ownership of its return value to its caller. // The Arg* that PreVisit returns will be passed to PostVisit as pre_arg // and passed to the child PreVisits and PostVisits as parent_arg. // At the top-most Regexp, parent_arg is arg passed to walk. // If PreVisit sets *stop to true, the walk does not recurse // into the children. Instead it behaves as though the return // value from PreVisit is the return value from PostVisit. // The default PreVisit returns parent_arg. virtual T PreVisit(Regexp* re, T parent_arg, bool* stop); // Virtual method called after visiting re's children. // The pre_arg is the T that PreVisit returned. // The child_args is a vector of the T that the child PostVisits returned. // PostVisit takes ownership of pre_arg. // PostVisit takes ownership of the Ts // in *child_args, but not the vector itself. // PostVisit passes ownership of its return value // to its caller. // The default PostVisit simply returns pre_arg. virtual T PostVisit(Regexp* re, T parent_arg, T pre_arg, T* child_args, int nchild_args); // Virtual method called to copy a T, // when Walk notices that more than one child is the same re. virtual T Copy(T arg); // Virtual method called to do a "quick visit" of the re, // but not its children. Only called once the visit budget // has been used up and we're trying to abort the walk // as quickly as possible. Should return a value that // makes sense for the parent PostVisits still to be run. // This function is (hopefully) only called by // WalkExponential, but must be implemented by all clients, // just in case. virtual T ShortVisit(Regexp* re, T parent_arg) = 0; // Walks over a regular expression. // Top_arg is passed as parent_arg to PreVisit and PostVisit of re. // Returns the T returned by PostVisit on re. T Walk(Regexp* re, T top_arg); // Like Walk, but doesn't use Copy. This can lead to // exponential runtimes on cross-linked Regexps like the // ones generated by Simplify. To help limit this, // at most max_visits nodes will be visited and then // the walk will be cut off early. // If the walk *is* cut off early, ShortVisit(re) // will be called on regexps that cannot be fully // visited rather than calling PreVisit/PostVisit. T WalkExponential(Regexp* re, T top_arg, int max_visits); // Clears the stack. Should never be necessary, since // Walk always enters and exits with an empty stack. // Logs DFATAL if stack is not already clear. void Reset(); // Returns whether walk was cut off. bool stopped_early() { return stopped_early_; } private: // Walk state for the entire traversal. stack >* stack_; bool stopped_early_; int max_visits_; T WalkInternal(Regexp* re, T top_arg, bool use_copy); DISALLOW_COPY_AND_ASSIGN(Walker); }; template T Regexp::Walker::PreVisit(Regexp* re, T parent_arg, bool* stop) { return parent_arg; } template T Regexp::Walker::PostVisit(Regexp* re, T parent_arg, T pre_arg, T* child_args, int nchild_args) { return pre_arg; } template T Regexp::Walker::Copy(T arg) { return arg; } // State about a single level in the traversal. template struct WalkState { WalkState(Regexp* re, T parent) : re(re), n(-1), parent_arg(parent), child_args(NULL) { } Regexp* re; // The regexp int n; // The index of the next child to process; -1 means need to PreVisit T parent_arg; // Accumulated arguments. T pre_arg; T child_arg; // One-element buffer for child_args. T* child_args; }; template Regexp::Walker::Walker() { stack_ = new stack >; stopped_early_ = false; } template Regexp::Walker::~Walker() { Reset(); delete stack_; } // Clears the stack. Should never be necessary, since // Walk always enters and exits with an empty stack. // Logs DFATAL if stack is not already clear. template void Regexp::Walker::Reset() { if (stack_ && stack_->size() > 0) { LOG(DFATAL) << "Stack not empty."; while (stack_->size() > 0) { delete stack_->top().child_args; stack_->pop(); } } } template T Regexp::Walker::WalkInternal(Regexp* re, T top_arg, bool use_copy) { Reset(); if (re == NULL) { LOG(DFATAL) << "Walk NULL"; return top_arg; } stack_->push(WalkState(re, top_arg)); WalkState* s; for (;;) { T t; s = &stack_->top(); Regexp* re = s->re; switch (s->n) { case -1: { if (--max_visits_ < 0) { stopped_early_ = true; t = ShortVisit(re, s->parent_arg); break; } bool stop = false; s->pre_arg = PreVisit(re, s->parent_arg, &stop); if (stop) { t = s->pre_arg; break; } s->n = 0; s->child_args = NULL; if (re->nsub_ == 1) s->child_args = &s->child_arg; else if (re->nsub_ > 1) s->child_args = new T[re->nsub_]; // Fall through. } default: { if (re->nsub_ > 0) { Regexp** sub = re->sub(); if (s->n < re->nsub_) { if (use_copy && s->n > 0 && sub[s->n - 1] == sub[s->n]) { s->child_args[s->n] = Copy(s->child_args[s->n - 1]); s->n++; } else { stack_->push(WalkState(sub[s->n], s->pre_arg)); } continue; } } t = PostVisit(re, s->parent_arg, s->pre_arg, s->child_args, s->n); if (re->nsub_ > 1) delete[] s->child_args; break; } } // We've finished stack_->top(). // Update next guy down. stack_->pop(); if (stack_->size() == 0) return t; s = &stack_->top(); if (s->child_args != NULL) s->child_args[s->n] = t; else s->child_arg = t; s->n++; } } template T Regexp::Walker::Walk(Regexp* re, T top_arg) { // Without the exponential walking behavior, // this budget should be more than enough for any // regexp, and yet not enough to get us in trouble // as far as CPU time. max_visits_ = 1000000; return WalkInternal(re, top_arg, true); } template T Regexp::Walker::WalkExponential(Regexp* re, T top_arg, int max_visits) { max_visits_ = max_visits; return WalkInternal(re, top_arg, false); } } // namespace re2 #endif // RE2_WALKER_INL_H__ openalpr_2.2.4.orig/src/openalpr/support/timing.cpp000066400000000000000000000107351266464252400225170ustar00rootroot00000000000000#include "timing.h" namespace alpr { timespec diff(timespec start, timespec end); #ifdef WINDOWS // Windows timing code LARGE_INTEGER getFILETIMEoffset() { SYSTEMTIME s; FILETIME f; LARGE_INTEGER t; s.wYear = 1970; s.wMonth = 1; s.wDay = 1; s.wHour = 0; s.wMinute = 0; s.wSecond = 0; s.wMilliseconds = 0; SystemTimeToFileTime(&s, &f); t.QuadPart = f.dwHighDateTime; t.QuadPart <<= 32; t.QuadPart |= f.dwLowDateTime; return (t); } int clock_gettime(int X, timespec *tv) { LARGE_INTEGER t; FILETIME f; double microseconds; static LARGE_INTEGER offset; static double frequencyToMicroseconds; static int initialized = 0; static BOOL usePerformanceCounter = 0; if (!initialized) { LARGE_INTEGER performanceFrequency; initialized = 1; usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency); if (usePerformanceCounter) { QueryPerformanceCounter(&offset); frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.; } else { offset = getFILETIMEoffset(); frequencyToMicroseconds = 10.; } } if (usePerformanceCounter) QueryPerformanceCounter(&t); else { GetSystemTimeAsFileTime(&f); t.QuadPart = f.dwHighDateTime; t.QuadPart <<= 32; t.QuadPart |= f.dwLowDateTime; } t.QuadPart -= offset.QuadPart; microseconds = (double)t.QuadPart / frequencyToMicroseconds; t.QuadPart = microseconds; tv->tv_sec = t.QuadPart / 1000000; tv->tv_nsec = (t.QuadPart % 1000000)*1000; return (0); } void getTimeMonotonic(timespec* time) { clock_gettime(0, time); } int64_t getTimeMonotonicMs() { timespec time; getTimeMonotonic(&time); timespec time_start; time_start.tv_sec = 0; time_start.tv_nsec = 0; return diffclock(time_start, time); } double diffclock(timespec time1,timespec time2) { timespec delta = diff(time1,time2); double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_nsec) / 1000000.0); return milliseconds; } timespec diff(timespec start, timespec end) { timespec temp; if ((end.tv_nsec-start.tv_nsec)<0) { temp.tv_sec = end.tv_sec-start.tv_sec-1; temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; } else { temp.tv_sec = end.tv_sec-start.tv_sec; temp.tv_nsec = end.tv_nsec-start.tv_nsec; } return temp; } int64_t getEpochTimeMs() { return std::time(0) * 1000; } #else void _getTime(bool realtime, timespec* time) { #if defined(__APPLE__) && defined(__MACH__) // OS X does not have clock_gettime, use clock_get_time clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); time->tv_sec = mts.tv_sec; time->tv_nsec = mts.tv_nsec; #else if (realtime) clock_gettime(CLOCK_REALTIME, time); else clock_gettime(CLOCK_MONOTONIC, time); #endif } // Returns a monotonic clock time unaffected by time changes (e.g., NTP) // Useful for interval comparisons void getTimeMonotonic(timespec* time) { _getTime(false, time); } int64_t getTimeMonotonicMs() { timespec time; getTimeMonotonic(&time); timespec time_start; time_start.tv_sec = 0; time_start.tv_nsec = 0; return diffclock(time_start, time); } double diffclock(timespec time1,timespec time2) { timespec delta = diff(time1,time2); double milliseconds = (((double) delta.tv_sec) * 1000.0) + (((double) delta.tv_nsec) / 1000000.0); return milliseconds; } timespec diff(timespec start, timespec end) { timespec temp; if ((end.tv_nsec-start.tv_nsec)<0) { temp.tv_sec = end.tv_sec-start.tv_sec-1; temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; } else { temp.tv_sec = end.tv_sec-start.tv_sec; temp.tv_nsec = end.tv_nsec-start.tv_nsec; } return temp; } // Returns wall clock time since Unix epoch (Jan 1, 1970) int64_t getEpochTimeMs() { timespec time; _getTime(true, &time); timespec epoch_start; epoch_start.tv_sec = 0; epoch_start.tv_nsec = 0; return diffclock(epoch_start, time); } #endif } openalpr_2.2.4.orig/src/openalpr/support/timing.h000066400000000000000000000012751266464252400221630ustar00rootroot00000000000000#ifndef OPENALPR_TIMING_H #define OPENALPR_TIMING_H #include #include #include #ifdef WINDOWS // Import windows only stuff #include #if _MSC_VER < 1900 struct timespec { time_t tv_sec; // Seconds - >= 0 long tv_nsec; // Nanoseconds - [0, 999999999] }; #else //#define timespec timeval #endif #else #include #endif // Support for OS X #if defined(__APPLE__) && defined(__MACH__) #include #include #endif namespace alpr { void getTimeMonotonic(timespec* time); int64_t getTimeMonotonicMs(); double diffclock(timespec time1,timespec time2); int64_t getEpochTimeMs(); } #endif openalpr_2.2.4.orig/src/openalpr/support/tinydir.h000066400000000000000000000204651266464252400223600ustar00rootroot00000000000000/* Copyright (c) 2013, Cong Xu 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. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. */ #ifndef TINYDIR_H #define TINYDIR_H #include #include #include #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN #include #pragma warning (disable : 4996) #else #include #include #endif /* types */ #define _TINYDIR_PATH_MAX 4096 #ifdef _MSC_VER /* extra chars for the "\\*" mask */ #define _TINYDIR_PATH_EXTRA 2 #else #define _TINYDIR_PATH_EXTRA 0 #endif #define _TINYDIR_FILENAME_MAX 256 #ifdef _MSC_VER #define strncasecmp _strnicmp #endif #ifdef _MSC_VER #define _TINYDIR_FUNC static __inline #else #define _TINYDIR_FUNC static __inline__ #endif typedef struct { char path[_TINYDIR_PATH_MAX]; char name[_TINYDIR_FILENAME_MAX]; int is_dir; int is_reg; #ifdef _MSC_VER #else struct stat _s; #endif } tinydir_file; typedef struct { char path[_TINYDIR_PATH_MAX]; int has_next; int n_files; tinydir_file *_files; #ifdef _MSC_VER HANDLE _h; WIN32_FIND_DATA _f; #else DIR *_d; struct dirent *_e; #endif } tinydir_dir; /* declarations */ _TINYDIR_FUNC int tinydir_open(tinydir_dir *dir, const char *path); _TINYDIR_FUNC int tinydir_open_sorted(tinydir_dir *dir, const char *path); _TINYDIR_FUNC void tinydir_close(tinydir_dir *dir); _TINYDIR_FUNC int tinydir_next(tinydir_dir *dir); _TINYDIR_FUNC int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file); _TINYDIR_FUNC int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, int i); _TINYDIR_FUNC int tinydir_open_subdir_n(tinydir_dir *dir, int i); _TINYDIR_FUNC int _tinydir_file_cmp(const void *a, const void *b); /* definitions*/ _TINYDIR_FUNC int tinydir_open(tinydir_dir *dir, const char *path) { if (dir == NULL || path == NULL || strlen(path) == 0) { errno = EINVAL; return -1; } if (strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) { errno = ENAMETOOLONG; return -1; } /* initialise dir */ dir->_files = NULL; #ifdef _MSC_VER dir->_h = INVALID_HANDLE_VALUE; #else dir->_d = NULL; #endif tinydir_close(dir); strcpy(dir->path, path); #ifdef _MSC_VER strcat(dir->path, "\\*"); dir->_h = FindFirstFile(dir->path, &dir->_f); dir->path[strlen(dir->path) - 2] = '\0'; if (dir->_h == INVALID_HANDLE_VALUE) #else dir->_d = opendir(path); if (dir->_d == NULL) #endif { errno = ENOENT; goto bail; } /* read first file */ dir->has_next = 1; #ifndef _MSC_VER dir->_e = readdir(dir->_d); if (dir->_e == NULL) { dir->has_next = 0; } #endif return 0; bail: tinydir_close(dir); return -1; } _TINYDIR_FUNC int tinydir_open_sorted(tinydir_dir *dir, const char *path) { if (tinydir_open(dir, path) == -1) { return -1; } dir->n_files = 0; while (dir->has_next) { tinydir_file *p_file; dir->n_files++; dir->_files = (tinydir_file *)realloc(dir->_files, sizeof(tinydir_file)*dir->n_files); if (dir->_files == NULL) { errno = ENOMEM; goto bail; } p_file = &dir->_files[dir->n_files - 1]; if (tinydir_readfile(dir, p_file) == -1) { goto bail; } if (tinydir_next(dir) == -1) { goto bail; } } qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp); return 0; bail: tinydir_close(dir); return -1; } _TINYDIR_FUNC void tinydir_close(tinydir_dir *dir) { if (dir == NULL) { return; } memset(dir->path, 0, sizeof(dir->path)); dir->has_next = 0; dir->n_files = -1; if (dir->_files != NULL) { free(dir->_files); } dir->_files = NULL; #ifdef _MSC_VER if (dir->_h != INVALID_HANDLE_VALUE) { FindClose(dir->_h); } dir->_h = INVALID_HANDLE_VALUE; #else if (dir->_d) { closedir(dir->_d); } dir->_d = NULL; dir->_e = NULL; #endif } _TINYDIR_FUNC int tinydir_next(tinydir_dir *dir) { if (dir == NULL) { errno = EINVAL; return -1; } if (!dir->has_next) { errno = ENOENT; return -1; } #ifdef _MSC_VER if (FindNextFile(dir->_h, &dir->_f) == 0) #else dir->_e = readdir(dir->_d); if (dir->_e == NULL) #endif { dir->has_next = 0; #ifdef _MSC_VER if (GetLastError() != ERROR_SUCCESS && GetLastError() != ERROR_NO_MORE_FILES) { tinydir_close(dir); errno = EIO; return -1; } #endif } return 0; } _TINYDIR_FUNC int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file) { if (dir == NULL || file == NULL) { errno = EINVAL; return -1; } #ifdef _MSC_VER if (dir->_h == INVALID_HANDLE_VALUE) #else if (dir->_e == NULL) #endif { errno = ENOENT; return -1; } if (strlen(dir->path) + strlen( #ifdef _MSC_VER dir->_f.cFileName #else dir->_e->d_name #endif ) + 1 + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) { /* the path for the file will be too long */ errno = ENAMETOOLONG; return -1; } if (strlen( #ifdef _MSC_VER dir->_f.cFileName #else dir->_e->d_name #endif ) >= _TINYDIR_FILENAME_MAX) { errno = ENAMETOOLONG; return -1; } strcpy(file->path, dir->path); strcat(file->path, "/"); strcpy(file->name, #ifdef _MSC_VER dir->_f.cFileName #else dir->_e->d_name #endif ); strcat(file->path, file->name); #ifndef _MSC_VER if (stat(file->path, &file->_s) == -1) { return -1; } #endif file->is_dir = #ifdef _MSC_VER !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); #else S_ISDIR(file->_s.st_mode); #endif file->is_reg = #ifdef _MSC_VER !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || ( !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) && !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && #ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) && #endif #ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) && #endif !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) && !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY)); #else S_ISREG(file->_s.st_mode); #endif return 0; } _TINYDIR_FUNC int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, int i) { if (dir == NULL || file == NULL || i < 0) { errno = EINVAL; return -1; } if (i >= dir->n_files) { errno = ENOENT; return -1; } memcpy(file, &dir->_files[i], sizeof(tinydir_file)); return 0; } _TINYDIR_FUNC int tinydir_open_subdir_n(tinydir_dir *dir, int i) { char path[_TINYDIR_PATH_MAX]; if (dir == NULL || i < 0) { errno = EINVAL; return -1; } if (i >= dir->n_files || !dir->_files[i].is_dir) { errno = ENOENT; return -1; } strcpy(path, dir->_files[i].path); tinydir_close(dir); if (tinydir_open_sorted(dir, path) == -1) { return -1; } return 0; } _TINYDIR_FUNC int _tinydir_file_cmp(const void *a, const void *b) { const tinydir_file *fa = (const tinydir_file *)a; const tinydir_file *fb = (const tinydir_file *)b; if (fa->is_dir != fb->is_dir) { return -(fa->is_dir - fb->is_dir); } return strncasecmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX); } #endifopenalpr_2.2.4.orig/src/openalpr/support/tinythread.cpp000066400000000000000000000204721266464252400234020ustar00rootroot00000000000000/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- Copyright (c) 2010-2012 Marcus Geelnard This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include #include "tinythread.h" #if defined(_TTHREAD_POSIX_) #include #include #elif defined(_TTHREAD_WIN32_) #include #endif namespace tthread { //------------------------------------------------------------------------------ // condition_variable //------------------------------------------------------------------------------ // NOTE 1: The Win32 implementation of the condition_variable class is based on // the corresponding implementation in GLFW, which in turn is based on a // description by Douglas C. Schmidt and Irfan Pyarali: // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html // // NOTE 2: Windows Vista actually has native support for condition variables // (InitializeConditionVariable, WakeConditionVariable, etc), but we want to // be portable with pre-Vista Windows versions, so TinyThread++ does not use // Vista condition variables. //------------------------------------------------------------------------------ #if defined(_TTHREAD_WIN32_) #define _CONDITION_EVENT_ONE 0 #define _CONDITION_EVENT_ALL 1 #endif #if defined(_TTHREAD_WIN32_) condition_variable::condition_variable() : mWaitersCount(0) { mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); InitializeCriticalSection(&mWaitersCountLock); } #endif #if defined(_TTHREAD_WIN32_) condition_variable::~condition_variable() { CloseHandle(mEvents[_CONDITION_EVENT_ONE]); CloseHandle(mEvents[_CONDITION_EVENT_ALL]); DeleteCriticalSection(&mWaitersCountLock); } #endif #if defined(_TTHREAD_WIN32_) void condition_variable::_wait() { // Wait for either event to become signaled due to notify_one() or // notify_all() being called int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE); // Check if we are the last waiter EnterCriticalSection(&mWaitersCountLock); -- mWaitersCount; bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && (mWaitersCount == 0); LeaveCriticalSection(&mWaitersCountLock); // If we are the last waiter to be notified to stop waiting, reset the event if(lastWaiter) ResetEvent(mEvents[_CONDITION_EVENT_ALL]); } #endif #if defined(_TTHREAD_WIN32_) void condition_variable::notify_one() { // Are there any waiters? EnterCriticalSection(&mWaitersCountLock); bool haveWaiters = (mWaitersCount > 0); LeaveCriticalSection(&mWaitersCountLock); // If we have any waiting threads, send them a signal if(haveWaiters) SetEvent(mEvents[_CONDITION_EVENT_ONE]); } #endif #if defined(_TTHREAD_WIN32_) void condition_variable::notify_all() { // Are there any waiters? EnterCriticalSection(&mWaitersCountLock); bool haveWaiters = (mWaitersCount > 0); LeaveCriticalSection(&mWaitersCountLock); // If we have any waiting threads, send them a signal if(haveWaiters) SetEvent(mEvents[_CONDITION_EVENT_ALL]); } #endif //------------------------------------------------------------------------------ // POSIX pthread_t to unique thread::id mapping logic. // Note: Here we use a global thread safe std::map to convert instances of // pthread_t to small thread identifier numbers (unique within one process). // This method should be portable across different POSIX implementations. //------------------------------------------------------------------------------ #if defined(_TTHREAD_POSIX_) static thread::id _pthread_t_to_ID(const pthread_t &aHandle) { static mutex idMapLock; static std::map idMap; static unsigned long int idCount(1); lock_guard guard(idMapLock); if(idMap.find(aHandle) == idMap.end()) idMap[aHandle] = idCount ++; return thread::id(idMap[aHandle]); } #endif // _TTHREAD_POSIX_ //------------------------------------------------------------------------------ // thread //------------------------------------------------------------------------------ /// Information to pass to the new thread (what to run). struct _thread_start_info { void (*mFunction)(void *); ///< Pointer to the function to be executed. void * mArg; ///< Function argument for the thread function. thread * mThread; ///< Pointer to the thread object. }; // Thread wrapper function. #if defined(_TTHREAD_WIN32_) unsigned WINAPI thread::wrapper_function(void * aArg) #elif defined(_TTHREAD_POSIX_) void * thread::wrapper_function(void * aArg) #endif { // Get thread startup information _thread_start_info * ti = (_thread_start_info *) aArg; try { // Call the actual client thread function ti->mFunction(ti->mArg); } catch(...) { // Uncaught exceptions will terminate the application (default behavior // according to C++11) std::terminate(); } // The thread is no longer executing lock_guard guard(ti->mThread->mDataMutex); ti->mThread->mNotAThread = true; // The thread is responsible for freeing the startup information delete ti; return 0; } thread::thread(void (*aFunction)(void *), void * aArg) { // Serialize access to this thread structure lock_guard guard(mDataMutex); // Fill out the thread startup information (passed to the thread wrapper, // which will eventually free it) _thread_start_info * ti = new _thread_start_info; ti->mFunction = aFunction; ti->mArg = aArg; ti->mThread = this; // The thread is now alive mNotAThread = false; // Create the thread #if defined(_TTHREAD_WIN32_) mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID); #elif defined(_TTHREAD_POSIX_) if(pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0) mHandle = 0; #endif // Did we fail to create the thread? if(!mHandle) { mNotAThread = true; delete ti; } } thread::~thread() { if(joinable()) std::terminate(); } void thread::join() { if(joinable()) { #if defined(_TTHREAD_WIN32_) WaitForSingleObject(mHandle, INFINITE); CloseHandle(mHandle); #elif defined(_TTHREAD_POSIX_) pthread_join(mHandle, NULL); #endif } } bool thread::joinable() const { mDataMutex.lock(); bool result = !mNotAThread; mDataMutex.unlock(); return result; } void thread::detach() { mDataMutex.lock(); if(!mNotAThread) { #if defined(_TTHREAD_WIN32_) CloseHandle(mHandle); #elif defined(_TTHREAD_POSIX_) pthread_detach(mHandle); #endif mNotAThread = true; } mDataMutex.unlock(); } thread::id thread::get_id() const { if(!joinable()) return id(); #if defined(_TTHREAD_WIN32_) return id((unsigned long int) mWin32ThreadID); #elif defined(_TTHREAD_POSIX_) return _pthread_t_to_ID(mHandle); #endif } unsigned thread::hardware_concurrency() { #if defined(_TTHREAD_WIN32_) SYSTEM_INFO si; GetSystemInfo(&si); return (int) si.dwNumberOfProcessors; #elif defined(_SC_NPROCESSORS_ONLN) return (int) sysconf(_SC_NPROCESSORS_ONLN); #elif defined(_SC_NPROC_ONLN) return (int) sysconf(_SC_NPROC_ONLN); #else // The standard requires this function to return zero if the number of // hardware cores could not be determined. return 0; #endif } //------------------------------------------------------------------------------ // this_thread //------------------------------------------------------------------------------ thread::id this_thread::get_id() { #if defined(_TTHREAD_WIN32_) return thread::id((unsigned long int) GetCurrentThreadId()); #elif defined(_TTHREAD_POSIX_) return _pthread_t_to_ID(pthread_self()); #endif } } openalpr_2.2.4.orig/src/openalpr/support/tinythread.h000066400000000000000000000513441266464252400230510ustar00rootroot00000000000000/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- Copyright (c) 2010-2012 Marcus Geelnard This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef _TINYTHREAD_H_ #define _TINYTHREAD_H_ /// @file /// @mainpage TinyThread++ API Reference /// /// @section intro_sec Introduction /// TinyThread++ is a minimal, portable implementation of basic threading /// classes for C++. /// /// They closely mimic the functionality and naming of the C++11 standard, and /// should be easily replaceable with the corresponding std:: variants. /// /// @section port_sec Portability /// The Win32 variant uses the native Win32 API for implementing the thread /// classes, while for other systems, the POSIX threads API (pthread) is used. /// /// @section class_sec Classes /// In order to mimic the threading API of the C++11 standard, subsets of /// several classes are provided. The fundamental classes are: /// @li tthread::thread /// @li tthread::mutex /// @li tthread::recursive_mutex /// @li tthread::condition_variable /// @li tthread::lock_guard /// @li tthread::fast_mutex /// /// @section misc_sec Miscellaneous /// The following special keywords are available: #thread_local. /// /// For more detailed information (including additional classes), browse the /// different sections of this documentation. A good place to start is: /// tinythread.h. // Which platform are we on? #if !defined(_TTHREAD_PLATFORM_DEFINED_) #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) #define _TTHREAD_WIN32_ #else #define _TTHREAD_POSIX_ #endif #define _TTHREAD_PLATFORM_DEFINED_ #endif // Platform specific includes #if defined(_TTHREAD_WIN32_) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #define __UNDEF_LEAN_AND_MEAN #endif #include #ifdef __UNDEF_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN #undef __UNDEF_LEAN_AND_MEAN #endif #else #include #include #include #include #endif // Generic includes #include /// TinyThread++ version (major number). #define TINYTHREAD_VERSION_MAJOR 1 /// TinyThread++ version (minor number). #define TINYTHREAD_VERSION_MINOR 1 /// TinyThread++ version (full version). #define TINYTHREAD_VERSION (TINYTHREAD_VERSION_MAJOR * 100 + TINYTHREAD_VERSION_MINOR) // Do we have a fully featured C++11 compiler? #if (__cplusplus > 199711L) || (defined(__STDCXX_VERSION__) && (__STDCXX_VERSION__ >= 201001L)) #define _TTHREAD_CPP11_ #endif // ...at least partial C++11? #if defined(_TTHREAD_CPP11_) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__) #define _TTHREAD_CPP11_PARTIAL_ #endif // Macro for disabling assignments of objects. #ifdef _TTHREAD_CPP11_PARTIAL_ #define _TTHREAD_DISABLE_ASSIGNMENT(name) \ name(const name&) = delete; \ name& operator=(const name&) = delete; #else #define _TTHREAD_DISABLE_ASSIGNMENT(name) \ name(const name&); \ name& operator=(const name&); #endif /// @def thread_local /// Thread local storage keyword. /// A variable that is declared with the @c thread_local keyword makes the /// value of the variable local to each thread (known as thread-local storage, /// or TLS). Example usage: /// @code /// // This variable is local to each thread. /// thread_local int variable; /// @endcode /// @note The @c thread_local keyword is a macro that maps to the corresponding /// compiler directive (e.g. @c __declspec(thread)). While the C++11 standard /// allows for non-trivial types (e.g. classes with constructors and /// destructors) to be declared with the @c thread_local keyword, most pre-C++11 /// compilers only allow for trivial types (e.g. @c int). So, to guarantee /// portable code, only use trivial types for thread local storage. /// @note This directive is currently not supported on Mac OS X (it will give /// a compiler error), since compile-time TLS is not supported in the Mac OS X /// executable format. Also, some older versions of MinGW (before GCC 4.x) do /// not support this directive. /// @hideinitializer #if !defined(_TTHREAD_CPP11_) && !defined(thread_local) #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) #define thread_local __thread #else #define thread_local __declspec(thread) #endif #endif /// Main name space for TinyThread++. /// This namespace is more or less equivalent to the @c std namespace for the /// C++11 thread classes. For instance, the tthread::mutex class corresponds to /// the std::mutex class. namespace tthread { /// Mutex class. /// This is a mutual exclusion object for synchronizing access to shared /// memory areas for several threads. The mutex is non-recursive (i.e. a /// program may deadlock if the thread that owns a mutex object calls lock() /// on that object). /// @see recursive_mutex class mutex { public: /// Constructor. mutex() #if defined(_TTHREAD_WIN32_) : mAlreadyLocked(false) #endif { #if defined(_TTHREAD_WIN32_) InitializeCriticalSection(&mHandle); #else pthread_mutex_init(&mHandle, NULL); #endif } /// Destructor. ~mutex() { #if defined(_TTHREAD_WIN32_) DeleteCriticalSection(&mHandle); #else pthread_mutex_destroy(&mHandle); #endif } /// Lock the mutex. /// The method will block the calling thread until a lock on the mutex can /// be obtained. The mutex remains locked until @c unlock() is called. /// @see lock_guard inline void lock() { #if defined(_TTHREAD_WIN32_) EnterCriticalSection(&mHandle); while(mAlreadyLocked) Sleep(1000); // Simulate deadlock... mAlreadyLocked = true; #else pthread_mutex_lock(&mHandle); #endif } /// Try to lock the mutex. /// The method will try to lock the mutex. If it fails, the function will /// return immediately (non-blocking). /// @return @c true if the lock was acquired, or @c false if the lock could /// not be acquired. inline bool try_lock() { #if defined(_TTHREAD_WIN32_) bool ret = (TryEnterCriticalSection(&mHandle) ? true : false); if(ret && mAlreadyLocked) { LeaveCriticalSection(&mHandle); ret = false; } return ret; #else return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; #endif } /// Unlock the mutex. /// If any threads are waiting for the lock on this mutex, one of them will /// be unblocked. inline void unlock() { #if defined(_TTHREAD_WIN32_) mAlreadyLocked = false; LeaveCriticalSection(&mHandle); #else pthread_mutex_unlock(&mHandle); #endif } _TTHREAD_DISABLE_ASSIGNMENT(mutex) private: #if defined(_TTHREAD_WIN32_) CRITICAL_SECTION mHandle; bool mAlreadyLocked; #else pthread_mutex_t mHandle; #endif friend class condition_variable; }; /// Recursive mutex class. /// This is a mutual exclusion object for synchronizing access to shared /// memory areas for several threads. The mutex is recursive (i.e. a thread /// may lock the mutex several times, as long as it unlocks the mutex the same /// number of times). /// @see mutex class recursive_mutex { public: /// Constructor. recursive_mutex() { #if defined(_TTHREAD_WIN32_) InitializeCriticalSection(&mHandle); #else pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&mHandle, &attr); #endif } /// Destructor. ~recursive_mutex() { #if defined(_TTHREAD_WIN32_) DeleteCriticalSection(&mHandle); #else pthread_mutex_destroy(&mHandle); #endif } /// Lock the mutex. /// The method will block the calling thread until a lock on the mutex can /// be obtained. The mutex remains locked until @c unlock() is called. /// @see lock_guard inline void lock() { #if defined(_TTHREAD_WIN32_) EnterCriticalSection(&mHandle); #else pthread_mutex_lock(&mHandle); #endif } /// Try to lock the mutex. /// The method will try to lock the mutex. If it fails, the function will /// return immediately (non-blocking). /// @return @c true if the lock was acquired, or @c false if the lock could /// not be acquired. inline bool try_lock() { #if defined(_TTHREAD_WIN32_) return TryEnterCriticalSection(&mHandle) ? true : false; #else return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; #endif } /// Unlock the mutex. /// If any threads are waiting for the lock on this mutex, one of them will /// be unblocked. inline void unlock() { #if defined(_TTHREAD_WIN32_) LeaveCriticalSection(&mHandle); #else pthread_mutex_unlock(&mHandle); #endif } _TTHREAD_DISABLE_ASSIGNMENT(recursive_mutex) private: #if defined(_TTHREAD_WIN32_) CRITICAL_SECTION mHandle; #else pthread_mutex_t mHandle; #endif friend class condition_variable; }; /// Lock guard class. /// The constructor locks the mutex, and the destructor unlocks the mutex, so /// the mutex will automatically be unlocked when the lock guard goes out of /// scope. Example usage: /// @code /// mutex m; /// int counter; /// /// void increment() /// { /// lock_guard guard(m); /// ++ counter; /// } /// @endcode template class lock_guard { public: typedef T mutex_type; lock_guard() : mMutex(0) {} /// The constructor locks the mutex. explicit lock_guard(mutex_type &aMutex) { mMutex = &aMutex; mMutex->lock(); } /// The destructor unlocks the mutex. ~lock_guard() { if(mMutex) mMutex->unlock(); } private: mutex_type * mMutex; }; /// Condition variable class. /// This is a signalling object for synchronizing the execution flow for /// several threads. Example usage: /// @code /// // Shared data and associated mutex and condition variable objects /// int count; /// mutex m; /// condition_variable cond; /// /// // Wait for the counter to reach a certain number /// void wait_counter(int targetCount) /// { /// lock_guard guard(m); /// while(count < targetCount) /// cond.wait(m); /// } /// /// // Increment the counter, and notify waiting threads /// void increment() /// { /// lock_guard guard(m); /// ++ count; /// cond.notify_all(); /// } /// @endcode class condition_variable { public: /// Constructor. #if defined(_TTHREAD_WIN32_) condition_variable(); #else condition_variable() { pthread_cond_init(&mHandle, NULL); } #endif /// Destructor. #if defined(_TTHREAD_WIN32_) ~condition_variable(); #else ~condition_variable() { pthread_cond_destroy(&mHandle); } #endif /// Wait for the condition. /// The function will block the calling thread until the condition variable /// is woken by @c notify_one(), @c notify_all() or a spurious wake up. /// @param[in] aMutex A mutex that will be unlocked when the wait operation /// starts, an locked again as soon as the wait operation is finished. template inline void wait(_mutexT &aMutex) { #if defined(_TTHREAD_WIN32_) // Increment number of waiters EnterCriticalSection(&mWaitersCountLock); ++ mWaitersCount; LeaveCriticalSection(&mWaitersCountLock); // Release the mutex while waiting for the condition (will decrease // the number of waiters when done)... aMutex.unlock(); _wait(); aMutex.lock(); #else pthread_cond_wait(&mHandle, &aMutex.mHandle); #endif } /// Notify one thread that is waiting for the condition. /// If at least one thread is blocked waiting for this condition variable, /// one will be woken up. /// @note Only threads that started waiting prior to this call will be /// woken up. #if defined(_TTHREAD_WIN32_) void notify_one(); #else inline void notify_one() { pthread_cond_signal(&mHandle); } #endif /// Notify all threads that are waiting for the condition. /// All threads that are blocked waiting for this condition variable will /// be woken up. /// @note Only threads that started waiting prior to this call will be /// woken up. #if defined(_TTHREAD_WIN32_) void notify_all(); #else inline void notify_all() { pthread_cond_broadcast(&mHandle); } #endif _TTHREAD_DISABLE_ASSIGNMENT(condition_variable) private: #if defined(_TTHREAD_WIN32_) void _wait(); HANDLE mEvents[2]; ///< Signal and broadcast event HANDLEs. unsigned int mWaitersCount; ///< Count of the number of waiters. CRITICAL_SECTION mWaitersCountLock; ///< Serialize access to mWaitersCount. #else pthread_cond_t mHandle; #endif }; /// Thread class. class thread { public: #if defined(_TTHREAD_WIN32_) typedef HANDLE native_handle_type; #else typedef pthread_t native_handle_type; #endif class id; /// Default constructor. /// Construct a @c thread object without an associated thread of execution /// (i.e. non-joinable). thread() : mHandle(0), mNotAThread(true) #if defined(_TTHREAD_WIN32_) , mWin32ThreadID(0) #endif {} /// Thread starting constructor. /// Construct a @c thread object with a new thread of execution. /// @param[in] aFunction A function pointer to a function of type: /// void fun(void * arg) /// @param[in] aArg Argument to the thread function. /// @note This constructor is not fully compatible with the standard C++ /// thread class. It is more similar to the pthread_create() (POSIX) and /// CreateThread() (Windows) functions. thread(void (*aFunction)(void *), void * aArg); /// Destructor. /// @note If the thread is joinable upon destruction, @c std::terminate() /// will be called, which terminates the process. It is always wise to do /// @c join() before deleting a thread object. ~thread(); /// Wait for the thread to finish (join execution flows). /// After calling @c join(), the thread object is no longer associated with /// a thread of execution (i.e. it is not joinable, and you may not join /// with it nor detach from it). void join(); /// Check if the thread is joinable. /// A thread object is joinable if it has an associated thread of execution. bool joinable() const; /// Detach from the thread. /// After calling @c detach(), the thread object is no longer assicated with /// a thread of execution (i.e. it is not joinable). The thread continues /// execution without the calling thread blocking, and when the thread /// ends execution, any owned resources are released. void detach(); /// Return the thread ID of a thread object. id get_id() const; /// Get the native handle for this thread. /// @note Under Windows, this is a @c HANDLE, and under POSIX systems, this /// is a @c pthread_t. inline native_handle_type native_handle() { return mHandle; } /// Determine the number of threads which can possibly execute concurrently. /// This function is useful for determining the optimal number of threads to /// use for a task. /// @return The number of hardware thread contexts in the system. /// @note If this value is not defined, the function returns zero (0). static unsigned hardware_concurrency(); _TTHREAD_DISABLE_ASSIGNMENT(thread) private: native_handle_type mHandle; ///< Thread handle. mutable mutex mDataMutex; ///< Serializer for access to the thread private data. bool mNotAThread; ///< True if this object is not a thread of execution. #if defined(_TTHREAD_WIN32_) unsigned int mWin32ThreadID; ///< Unique thread ID (filled out by _beginthreadex). #endif // This is the internal thread wrapper function. #if defined(_TTHREAD_WIN32_) static unsigned WINAPI wrapper_function(void * aArg); #else static void * wrapper_function(void * aArg); #endif }; /// Thread ID. /// The thread ID is a unique identifier for each thread. /// @see thread::get_id() class thread::id { public: /// Default constructor. /// The default constructed ID is that of thread without a thread of /// execution. id() : mId(0) {}; id(unsigned long int aId) : mId(aId) {}; id(const id& aId) : mId(aId.mId) {}; inline id & operator=(const id &aId) { mId = aId.mId; return *this; } inline friend bool operator==(const id &aId1, const id &aId2) { return (aId1.mId == aId2.mId); } inline friend bool operator!=(const id &aId1, const id &aId2) { return (aId1.mId != aId2.mId); } inline friend bool operator<=(const id &aId1, const id &aId2) { return (aId1.mId <= aId2.mId); } inline friend bool operator<(const id &aId1, const id &aId2) { return (aId1.mId < aId2.mId); } inline friend bool operator>=(const id &aId1, const id &aId2) { return (aId1.mId >= aId2.mId); } inline friend bool operator>(const id &aId1, const id &aId2) { return (aId1.mId > aId2.mId); } inline friend std::ostream& operator <<(std::ostream &os, const id &obj) { os << obj.mId; return os; } private: unsigned long int mId; }; // Related to - minimal to be able to support chrono. typedef long long __intmax_t; /// Minimal implementation of the @c ratio class. This class provides enough /// functionality to implement some basic @c chrono classes. template <__intmax_t N, __intmax_t D = 1> class ratio { public: static double _as_double() { return double(N) / double(D); } }; /// Minimal implementation of the @c chrono namespace. /// The @c chrono namespace provides types for specifying time intervals. namespace chrono { /// Duration template class. This class provides enough functionality to /// implement @c this_thread::sleep_for(). template > class duration { private: _Rep rep_; public: typedef _Rep rep; typedef _Period period; /// Construct a duration object with the given duration. template explicit duration(const _Rep2& r) : rep_(r) {}; /// Return the value of the duration object. rep count() const { return rep_; } }; // Standard duration types. typedef duration<__intmax_t, ratio<1, 1000000000> > nanoseconds; ///< Duration with the unit nanoseconds. typedef duration<__intmax_t, ratio<1, 1000000> > microseconds; ///< Duration with the unit microseconds. typedef duration<__intmax_t, ratio<1, 1000> > milliseconds; ///< Duration with the unit milliseconds. typedef duration<__intmax_t> seconds; ///< Duration with the unit seconds. typedef duration<__intmax_t, ratio<60> > minutes; ///< Duration with the unit minutes. typedef duration<__intmax_t, ratio<3600> > hours; ///< Duration with the unit hours. } /// The namespace @c this_thread provides methods for dealing with the /// calling thread. namespace this_thread { /// Return the thread ID of the calling thread. thread::id get_id(); /// Yield execution to another thread. /// Offers the operating system the opportunity to schedule another thread /// that is ready to run on the current processor. inline void yield() { #if defined(_TTHREAD_WIN32_) Sleep(0); #else sched_yield(); #endif } /// Blocks the calling thread for a period of time. /// @param[in] aTime Minimum time to put the thread to sleep. /// Example usage: /// @code /// // Sleep for 100 milliseconds /// this_thread::sleep_for(chrono::milliseconds(100)); /// @endcode /// @note Supported duration types are: nanoseconds, microseconds, /// milliseconds, seconds, minutes and hours. template void sleep_for(const chrono::duration<_Rep, _Period>& aTime) { #if defined(_TTHREAD_WIN32_) Sleep(int(double(aTime.count()) * (1000.0 * _Period::_as_double()) + 0.5)); #else usleep(int(double(aTime.count()) * (1000000.0 * _Period::_as_double()) + 0.5)); #endif } } } // Define/macro cleanup #undef _TTHREAD_DISABLE_ASSIGNMENT #endif // _TINYTHREAD_H_ openalpr_2.2.4.orig/src/openalpr/support/utf8.cpp000066400000000000000000000021551266464252400221130ustar00rootroot00000000000000#include "utf8.h" #include std::string utf8chr(int cp) { char c[5]={ 0x00,0x00,0x00,0x00,0x00 }; if (cp<=0x7F) { c[0] = cp; } else if(cp<=0x7FF) { c[0] = (cp>>6)+192; c[1] = (cp&63)+128; } else if(0xd800<=cp && cp<=0xdfff) {} //invalid block of utf8 else if(cp<=0xFFFF) { c[0] = (cp>>12)+224; c[1]= ((cp>>6)&63)+128; c[2]=(cp&63)+128; } else if(cp<=0x10FFFF) { c[0] = (cp>>18)+240; c[1] = ((cp>>12)&63)+128; c[2] = ((cp>>6)&63)+128; c[3]=(cp&63)+128; } return std::string(c); } int codepoint(const std::string &u) { int l = u.length(); if (l<1) return -1; unsigned char u0 = u[0]; if (u0>=0 && u0<=127) return u0; if (l<2) return -1; unsigned char u1 = u[1]; if (u0>=192 && u0<=223) return (u0-192)*64 + (u1-128); if (u[0]==0xed && (u[1] & 0xa0) == 0xa0) return -1; //code points, 0xd800 to 0xdfff if (l<3) return -1; unsigned char u2 = u[2]; if (u0>=224 && u0<=239) return (u0-224)*4096 + (u1-128)*64 + (u2-128); if (l<4) return -1; unsigned char u3 = u[3]; if (u0>=240 && u0<=247) return (u0-240)*262144 + (u1-128)*4096 + (u2-128)*64 + (u3-128); return -1; }openalpr_2.2.4.orig/src/openalpr/support/utf8.h000066400000000000000000000031331266464252400215550ustar00rootroot00000000000000// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "utf8/checked.h" #include "utf8/unchecked.h" std::string utf8chr(int cp); int codepoint(const std::string &u); #endif // header guard openalpr_2.2.4.orig/src/openalpr/support/utf8/000077500000000000000000000000001266464252400214045ustar00rootroot00000000000000openalpr_2.2.4.orig/src/openalpr/support/utf8/checked.h000066400000000000000000000276141266464252400231550ustar00rootroot00000000000000// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "core.h" #include namespace utf8 { // Base for the exceptions that may be thrown from the library class exception : public ::std::exception { }; // Exceptions that may be thrown from the library functions. class invalid_code_point : public exception { uint32_t cp; public: invalid_code_point(uint32_t cp) : cp(cp) {} virtual const char* what() const throw() { return "Invalid code point"; } uint32_t code_point() const {return cp;} }; class invalid_utf8 : public exception { uint8_t u8; public: invalid_utf8 (uint8_t u) : u8(u) {} virtual const char* what() const throw() { return "Invalid UTF-8"; } uint8_t utf8_octet() const {return u8;} }; class invalid_utf16 : public exception { uint16_t u16; public: invalid_utf16 (uint16_t u) : u16(u) {} virtual const char* what() const throw() { return "Invalid UTF-16"; } uint16_t utf16_word() const {return u16;} }; class not_enough_room : public exception { public: virtual const char* what() const throw() { return "Not enough space"; } }; /// The library API - functions intended to be called by the users template octet_iterator append(uint32_t cp, octet_iterator result) { if (!utf8::internal::is_code_point_valid(cp)) throw invalid_code_point(cp); if (cp < 0x80) // one octet *(result++) = static_cast(cp); else if (cp < 0x800) { // two octets *(result++) = static_cast((cp >> 6) | 0xc0); *(result++) = static_cast((cp & 0x3f) | 0x80); } else if (cp < 0x10000) { // three octets *(result++) = static_cast((cp >> 12) | 0xe0); *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast((cp & 0x3f) | 0x80); } else { // four octets *(result++) = static_cast((cp >> 18) | 0xf0); *(result++) = static_cast(((cp >> 12) & 0x3f) | 0x80); *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast((cp & 0x3f) | 0x80); } return result; } template output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) { while (start != end) { octet_iterator sequence_start = start; internal::utf_error err_code = utf8::internal::validate_next(start, end); switch (err_code) { case internal::UTF8_OK : for (octet_iterator it = sequence_start; it != start; ++it) *out++ = *it; break; case internal::NOT_ENOUGH_ROOM: throw not_enough_room(); case internal::INVALID_LEAD: out = utf8::append (replacement, out); ++start; break; case internal::INCOMPLETE_SEQUENCE: case internal::OVERLONG_SEQUENCE: case internal::INVALID_CODE_POINT: out = utf8::append (replacement, out); ++start; // just one replacement mark for the sequence while (start != end && utf8::internal::is_trail(*start)) ++start; break; } } return out; } template inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) { static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); return utf8::replace_invalid(start, end, out, replacement_marker); } template uint32_t next(octet_iterator& it, octet_iterator end) { uint32_t cp = 0; internal::utf_error err_code = utf8::internal::validate_next(it, end, cp); switch (err_code) { case internal::UTF8_OK : break; case internal::NOT_ENOUGH_ROOM : throw not_enough_room(); case internal::INVALID_LEAD : case internal::INCOMPLETE_SEQUENCE : case internal::OVERLONG_SEQUENCE : throw invalid_utf8(*it); case internal::INVALID_CODE_POINT : throw invalid_code_point(cp); } return cp; } template uint32_t peek_next(octet_iterator it, octet_iterator end) { return utf8::next(it, end); } template uint32_t prior(octet_iterator& it, octet_iterator start) { // can't do much if it == start if (it == start) throw not_enough_room(); octet_iterator end = it; // Go back until we hit either a lead octet or start while (utf8::internal::is_trail(*(--it))) if (it == start) throw invalid_utf8(*it); // error - no lead byte in the sequence return utf8::peek_next(it, end); } /// Deprecated in versions that include "prior" template uint32_t previous(octet_iterator& it, octet_iterator pass_start) { octet_iterator end = it; while (utf8::internal::is_trail(*(--it))) if (it == pass_start) throw invalid_utf8(*it); // error - no lead byte in the sequence octet_iterator temp = it; return utf8::next(temp, end); } template void advance (octet_iterator& it, distance_type n, octet_iterator end) { for (distance_type i = 0; i < n; ++i) utf8::next(it, end); } template typename std::iterator_traits::difference_type distance (octet_iterator first, octet_iterator last) { typename std::iterator_traits::difference_type dist; for (dist = 0; first < last; ++dist) utf8::next(first, last); return dist; } template octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) { while (start != end) { uint32_t cp = utf8::internal::mask16(*start++); // Take care of surrogate pairs first if (utf8::internal::is_lead_surrogate(cp)) { if (start != end) { uint32_t trail_surrogate = utf8::internal::mask16(*start++); if (utf8::internal::is_trail_surrogate(trail_surrogate)) cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; else throw invalid_utf16(static_cast(trail_surrogate)); } else throw invalid_utf16(static_cast(cp)); } // Lone trail surrogate else if (utf8::internal::is_trail_surrogate(cp)) throw invalid_utf16(static_cast(cp)); result = utf8::append(cp, result); } return result; } template u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) { while (start != end) { uint32_t cp = utf8::next(start, end); if (cp > 0xffff) { //make a surrogate pair *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); } else *result++ = static_cast(cp); } return result; } template octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) { while (start != end) result = utf8::append(*(start++), result); return result; } template u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) { while (start != end) (*result++) = utf8::next(start, end); return result; } // The iterator class template class iterator : public std::iterator { octet_iterator it; octet_iterator range_start; octet_iterator range_end; public: iterator () {} explicit iterator (const octet_iterator& octet_it, const octet_iterator& range_start, const octet_iterator& range_end) : it(octet_it), range_start(range_start), range_end(range_end) { if (it < range_start || it > range_end) throw std::out_of_range("Invalid utf-8 iterator position"); } // the default "big three" are OK octet_iterator base () const { return it; } uint32_t operator * () const { octet_iterator temp = it; return utf8::next(temp, range_end); } bool operator == (const iterator& rhs) const { if (range_start != rhs.range_start || range_end != rhs.range_end) throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); return (it == rhs.it); } bool operator != (const iterator& rhs) const { return !(operator == (rhs)); } iterator& operator ++ () { utf8::next(it, range_end); return *this; } iterator operator ++ (int) { iterator temp = *this; utf8::next(it, range_end); return temp; } iterator& operator -- () { utf8::prior(it, range_start); return *this; } iterator operator -- (int) { iterator temp = *this; utf8::prior(it, range_start); return temp; } }; // class iterator } // namespace utf8 #endif //header guard openalpr_2.2.4.orig/src/openalpr/support/utf8/core.h000066400000000000000000000247111266464252400225120ustar00rootroot00000000000000// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include namespace utf8 { // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers // You may need to change them to match your system. // These typedefs have the same names as ones from cstdint, or boost/cstdint typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; // Helper code - not intended to be directly called by the library users. May be changed at any time namespace internal { // Unicode constants // Leading (high) surrogates: 0xd800 - 0xdbff // Trailing (low) surrogates: 0xdc00 - 0xdfff const uint16_t LEAD_SURROGATE_MIN = 0xd800u; const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; // Maximum valid value for a Unicode code point const uint32_t CODE_POINT_MAX = 0x0010ffffu; template inline uint8_t mask8(octet_type oc) { return static_cast(0xff & oc); } template inline uint16_t mask16(u16_type oc) { return static_cast(0xffff & oc); } template inline bool is_trail(octet_type oc) { return ((utf8::internal::mask8(oc) >> 6) == 0x2); } template inline bool is_lead_surrogate(u16 cp) { return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); } template inline bool is_trail_surrogate(u16 cp) { return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); } template inline bool is_surrogate(u16 cp) { return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); } template inline bool is_code_point_valid(u32 cp) { return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp)); } template inline typename std::iterator_traits::difference_type sequence_length(octet_iterator lead_it) { uint8_t lead = utf8::internal::mask8(*lead_it); if (lead < 0x80) return 1; else if ((lead >> 5) == 0x6) return 2; else if ((lead >> 4) == 0xe) return 3; else if ((lead >> 3) == 0x1e) return 4; else return 0; } template inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) { if (cp < 0x80) { if (length != 1) return true; } else if (cp < 0x800) { if (length != 2) return true; } else if (cp < 0x10000) { if (length != 3) return true; } return false; } enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; /// Helper for get_sequence_x template utf_error increase_safely(octet_iterator& it, octet_iterator end) { if (++it == end) return NOT_ENOUGH_ROOM; if (!utf8::internal::is_trail(*it)) return INCOMPLETE_SEQUENCE; return UTF8_OK; } #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} /// get_sequence_x functions decode utf-8 sequences of the length x template utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); return UTF8_OK; } template utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f); return UTF8_OK; } template utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point += (*it) & 0x3f; return UTF8_OK; } template utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point += (utf8::internal::mask8(*it) << 6) & 0xfff; UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point += (*it) & 0x3f; return UTF8_OK; } #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR template utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) { // Save the original value of it so we can go back in case of failure // Of course, it does not make much sense with i.e. stream iterators octet_iterator original_it = it; uint32_t cp = 0; // Determine the sequence length based on the lead octet typedef typename std::iterator_traits::difference_type octet_difference_type; const octet_difference_type length = utf8::internal::sequence_length(it); // Get trail octets and calculate the code point utf_error err = UTF8_OK; switch (length) { case 0: return INVALID_LEAD; case 1: err = utf8::internal::get_sequence_1(it, end, cp); break; case 2: err = utf8::internal::get_sequence_2(it, end, cp); break; case 3: err = utf8::internal::get_sequence_3(it, end, cp); break; case 4: err = utf8::internal::get_sequence_4(it, end, cp); break; } if (err == UTF8_OK) { // Decoding succeeded. Now, security checks... if (utf8::internal::is_code_point_valid(cp)) { if (!utf8::internal::is_overlong_sequence(cp, length)){ // Passed! Return here. code_point = cp; ++it; return UTF8_OK; } else err = OVERLONG_SEQUENCE; } else err = INVALID_CODE_POINT; } // Failure branch - restore the original value of the iterator it = original_it; return err; } template inline utf_error validate_next(octet_iterator& it, octet_iterator end) { uint32_t ignored; return utf8::internal::validate_next(it, end, ignored); } } // namespace internal /// The library API - functions intended to be called by the users // Byte order mark const uint8_t bom[] = {0xef, 0xbb, 0xbf}; template octet_iterator find_invalid(octet_iterator start, octet_iterator end) { octet_iterator result = start; while (result != end) { utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end); if (err_code != internal::UTF8_OK) return result; } return result; } template inline bool is_valid(octet_iterator start, octet_iterator end) { return (utf8::find_invalid(start, end) == end); } template inline bool starts_with_bom (octet_iterator it, octet_iterator end) { return ( ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) && ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) ); } //Deprecated in release 2.3 template inline bool is_bom (octet_iterator it) { return ( (utf8::internal::mask8(*it++)) == bom[0] && (utf8::internal::mask8(*it++)) == bom[1] && (utf8::internal::mask8(*it)) == bom[2] ); } } // namespace utf8 #endif // header guard openalpr_2.2.4.orig/src/openalpr/support/utf8/unchecked.h000066400000000000000000000213131266464252400235060ustar00rootroot00000000000000// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "core.h" namespace utf8 { namespace unchecked { template octet_iterator append(uint32_t cp, octet_iterator result) { if (cp < 0x80) // one octet *(result++) = static_cast(cp); else if (cp < 0x800) { // two octets *(result++) = static_cast((cp >> 6) | 0xc0); *(result++) = static_cast((cp & 0x3f) | 0x80); } else if (cp < 0x10000) { // three octets *(result++) = static_cast((cp >> 12) | 0xe0); *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast((cp & 0x3f) | 0x80); } else { // four octets *(result++) = static_cast((cp >> 18) | 0xf0); *(result++) = static_cast(((cp >> 12) & 0x3f)| 0x80); *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast((cp & 0x3f) | 0x80); } return result; } template uint32_t next(octet_iterator& it) { uint32_t cp = utf8::internal::mask8(*it); typename std::iterator_traits::difference_type length = utf8::internal::sequence_length(it); switch (length) { case 1: break; case 2: it++; cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); break; case 3: ++it; cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); ++it; cp += (*it) & 0x3f; break; case 4: ++it; cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); ++it; cp += (utf8::internal::mask8(*it) << 6) & 0xfff; ++it; cp += (*it) & 0x3f; break; } ++it; return cp; } template uint32_t peek_next(octet_iterator it) { return utf8::unchecked::next(it); } template uint32_t prior(octet_iterator& it) { while (utf8::internal::is_trail(*(--it))) ; octet_iterator temp = it; return utf8::unchecked::next(temp); } // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous) template inline uint32_t previous(octet_iterator& it) { return utf8::unchecked::prior(it); } template void advance (octet_iterator& it, distance_type n) { for (distance_type i = 0; i < n; ++i) utf8::unchecked::next(it); } template typename std::iterator_traits::difference_type distance (octet_iterator first, octet_iterator last) { typename std::iterator_traits::difference_type dist; for (dist = 0; first < last; ++dist) utf8::unchecked::next(first); return dist; } template octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) { while (start != end) { uint32_t cp = utf8::internal::mask16(*start++); // Take care of surrogate pairs first if (utf8::internal::is_lead_surrogate(cp)) { uint32_t trail_surrogate = utf8::internal::mask16(*start++); cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; } result = utf8::unchecked::append(cp, result); } return result; } template u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) { while (start < end) { uint32_t cp = utf8::unchecked::next(start); if (cp > 0xffff) { //make a surrogate pair *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); } else *result++ = static_cast(cp); } return result; } template octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) { while (start != end) result = utf8::unchecked::append(*(start++), result); return result; } template u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) { while (start < end) (*result++) = utf8::unchecked::next(start); return result; } // The iterator class template class iterator : public std::iterator { octet_iterator it; public: iterator () {} explicit iterator (const octet_iterator& octet_it): it(octet_it) {} // the default "big three" are OK octet_iterator base () const { return it; } uint32_t operator * () const { octet_iterator temp = it; return utf8::unchecked::next(temp); } bool operator == (const iterator& rhs) const { return (it == rhs.it); } bool operator != (const iterator& rhs) const { return !(operator == (rhs)); } iterator& operator ++ () { ::std::advance(it, utf8::internal::sequence_length(it)); return *this; } iterator operator ++ (int) { iterator temp = *this; ::std::advance(it, utf8::internal::sequence_length(it)); return temp; } iterator& operator -- () { utf8::unchecked::prior(it); return *this; } iterator operator -- (int) { iterator temp = *this; utf8::unchecked::prior(it); return temp; } }; // class iterator } // namespace utf8::unchecked } // namespace utf8 #endif // header guard openalpr_2.2.4.orig/src/openalpr/support/version.cpp000066400000000000000000000011341266464252400227060ustar00rootroot00000000000000#include "version.h" #include #include #include int cmpVersion(const char *v1, const char *v2) { int i; int oct_v1[4], oct_v2[4]; for (int i = 0; i < 4; i++) { oct_v1[i] = 0; oct_v2[i] = 0; } sscanf(v1, "%d.%d.%d.%d", &oct_v1[0], &oct_v1[1], &oct_v1[2], &oct_v1[3]); sscanf(v2, "%d.%d.%d.%d", &oct_v2[0], &oct_v2[1], &oct_v2[2], &oct_v2[3]); for (i = 0; i < 4; i++) { if (oct_v1[i] > oct_v2[i]) return 1; else if (oct_v1[i] < oct_v2[i]) return -1; } return 0; }openalpr_2.2.4.orig/src/openalpr/support/version.h000066400000000000000000000004001266464252400223460ustar00rootroot00000000000000#ifndef OPENALPR_VERSION_H #define OPENALPR_VERSION_H #include #include /* * return 1 if v1 > v2 * return 0 if v1 = v2 * return -1 if v1 < v2 */ int cmpVersion(const char *v1, const char *v2); #endif /* OPENALPR_VERSION_H */ openalpr_2.2.4.orig/src/openalpr/support/windows/000077500000000000000000000000001266464252400222105ustar00rootroot00000000000000openalpr_2.2.4.orig/src/openalpr/support/windows/dirent.h000066400000000000000000000562641266464252400236630ustar00rootroot00000000000000/* * dirent.h - dirent API for Microsoft Visual Studio * * Copyright (C) 2006-2012 Toni Ronkko * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * ``Software''), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * * Version 1.13, Dec 12 2012, Toni Ronkko * Use traditional 8+3 file name if the name cannot be represented in the * default ANSI code page. Now compiles again with MSVC 6.0. Thanks to * Konstantin Khomoutov for testing. * * Version 1.12.1, Oct 1 2012, Toni Ronkko * Bug fix: renamed wide-character DIR structure _wDIR to _WDIR (with * capital W) in order to maintain compatibility with MingW. * * Version 1.12, Sep 30 2012, Toni Ronkko * Define PATH_MAX and NAME_MAX. Added wide-character variants _wDIR, * _wdirent, _wopendir(), _wreaddir(), _wclosedir() and _wrewinddir(). * Thanks to Edgar Buerkle and Jan Nijtmans for ideas and code. * * Do not include windows.h. This allows dirent.h to be integrated more * easily into programs using winsock. Thanks to Fernando Azaldegui. * * Version 1.11, Mar 15, 2011, Toni Ronkko * Defined FILE_ATTRIBUTE_DEVICE for MSVC 6.0. * * Version 1.10, Aug 11, 2010, Toni Ronkko * Added d_type and d_namlen fields to dirent structure. The former is * especially useful for determining whether directory entry represents a * file or a directory. For more information, see * http://www.delorie.com/gnu/docs/glibc/libc_270.html * * Improved conformance to the standards. For example, errno is now set * properly on failure and assert() is never used. Thanks to Peter Brockam * for suggestions. * * Fixed a bug in rewinddir(): when using relative directory names, change * of working directory no longer causes rewinddir() to fail. * * Version 1.9, Dec 15, 2009, John Cunningham * Added rewinddir member function * * Version 1.8, Jan 18, 2008, Toni Ronkko * Using FindFirstFileA and WIN32_FIND_DATAA to avoid converting string * between multi-byte and unicode representations. This makes the * code simpler and also allows the code to be compiled under MingW. Thanks * to Azriel Fasten for the suggestion. * * Mar 4, 2007, Toni Ronkko * Bug fix: due to the strncpy_s() function this file only compiled in * Visual Studio 2005. Using the new string functions only when the * compiler version allows. * * Nov 2, 2006, Toni Ronkko * Major update: removed support for Watcom C, MS-DOS and Turbo C to * simplify the file, updated the code to compile cleanly on Visual * Studio 2005 with both unicode and multi-byte character strings, * removed rewinddir() as it had a bug. * * Aug 20, 2006, Toni Ronkko * Removed all remarks about MSVC 1.0, which is antiqued now. Simplified * comments by removing SGML tags. * * May 14 2002, Toni Ronkko * Embedded the function definitions directly to the header so that no * source modules need to be included in the Visual Studio project. Removed * all the dependencies to other projects so that this header file can be * used independently. * * May 28 1998, Toni Ronkko * First version. *****************************************************************************/ #ifndef DIRENT_H #define DIRENT_H #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86) # define _X86_ #endif #include #include #include #include #include #include #include #include #include #include #include /* Indicates that d_type field is available in dirent structure */ #define _DIRENT_HAVE_D_TYPE /* Indicates that d_namlen field is available in dirent structure */ #define _DIRENT_HAVE_D_NAMLEN /* Entries missing from MSVC 6.0 */ #if !defined(FILE_ATTRIBUTE_DEVICE) # define FILE_ATTRIBUTE_DEVICE 0x40 #endif /* File type and permission flags for stat() */ #if !defined(S_IFMT) # define S_IFMT _S_IFMT /* File type mask */ #endif #if !defined(S_IFDIR) # define S_IFDIR _S_IFDIR /* Directory */ #endif #if !defined(S_IFCHR) # define S_IFCHR _S_IFCHR /* Character device */ #endif #if !defined(S_IFFIFO) # define S_IFFIFO _S_IFFIFO /* Pipe */ #endif #if !defined(S_IFREG) # define S_IFREG _S_IFREG /* Regular file */ #endif #if !defined(S_IREAD) # define S_IREAD _S_IREAD /* Read permission */ #endif #if !defined(S_IWRITE) # define S_IWRITE _S_IWRITE /* Write permission */ #endif #if !defined(S_IEXEC) # define S_IEXEC _S_IEXEC /* Execute permission */ #endif #if !defined(S_IFIFO) # define S_IFIFO _S_IFIFO /* Pipe */ #endif #if !defined(S_IFBLK) # define S_IFBLK 0 /* Block device */ #endif #if !defined(S_IFLNK) # define S_IFLNK 0 /* Link */ #endif #if !defined(S_IFSOCK) # define S_IFSOCK 0 /* Socket */ #endif #if defined(_MSC_VER) # define S_IRUSR S_IREAD /* Read user */ # define S_IWUSR S_IWRITE /* Write user */ # define S_IXUSR 0 /* Execute user */ # define S_IRGRP 0 /* Read group */ # define S_IWGRP 0 /* Write group */ # define S_IXGRP 0 /* Execute group */ # define S_IROTH 0 /* Read others */ # define S_IWOTH 0 /* Write others */ # define S_IXOTH 0 /* Execute others */ #endif /* Maximum length of file name */ #if !defined(PATH_MAX) # define PATH_MAX MAX_PATH #endif #if !defined(FILENAME_MAX) # define FILENAME_MAX MAX_PATH #endif #if !defined(NAME_MAX) # define NAME_MAX FILENAME_MAX #endif /* File type flags for d_type */ #define DT_UNKNOWN 0 #define DT_REG S_IFREG #define DT_DIR S_IFDIR #define DT_FIFO S_IFIFO #define DT_SOCK S_IFSOCK #define DT_CHR S_IFCHR #define DT_BLK S_IFBLK /* Macros for converting between st_mode and d_type */ #define IFTODT(mode) ((mode) & S_IFMT) #define DTTOIF(type) (type) /* * File type macros. Note that block devices, sockets and links cannot be * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are * only defined for compatibility. These macros should always return false * on Windows. */ #define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) /* Return the exact length of d_namlen without zero terminator */ #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) /* Return number of bytes needed to store d_namlen */ #define _D_ALLOC_NAMLEN(p) (PATH_MAX + 1) #ifdef __cplusplus extern "C" { #endif /* Wide-character version */ struct _wdirent { long d_ino; /* Always zero */ unsigned short d_reclen; /* Structure size */ size_t d_namlen; /* Length of name without \0 */ int d_type; /* File type */ wchar_t d_name[PATH_MAX + 1]; /* File name */ }; typedef struct _wdirent _wdirent; struct _WDIR { struct _wdirent ent; /* Current directory entry */ WIN32_FIND_DATAW data; /* Private file data */ int cached; /* True if data is valid */ HANDLE handle; /* Win32 search handle */ wchar_t *patt; /* Initial directory name */ }; typedef struct _WDIR _WDIR; static _WDIR *_wopendir (const wchar_t *dirname); static struct _wdirent *_wreaddir (_WDIR *dirp); static int _wclosedir (_WDIR *dirp); static void _wrewinddir (_WDIR* dirp); /* For compatibility with Symbian */ #define wdirent _wdirent #define WDIR _WDIR #define wopendir _wopendir #define wreaddir _wreaddir #define wclosedir _wclosedir #define wrewinddir _wrewinddir /* Multi-byte character versions */ struct dirent { long d_ino; /* Always zero */ unsigned short d_reclen; /* Structure size */ size_t d_namlen; /* Length of name without \0 */ int d_type; /* File type */ char d_name[PATH_MAX + 1]; /* File name */ }; typedef struct dirent dirent; struct DIR { struct dirent ent; struct _WDIR *wdirp; }; typedef struct DIR DIR; static DIR *opendir (const char *dirname); static struct dirent *readdir (DIR *dirp); static int closedir (DIR *dirp); static void rewinddir (DIR* dirp); /* Internal utility functions */ static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); static int dirent_mbstowcs_s( size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, const char *mbstr, size_t count); static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, size_t sizeInBytes, const wchar_t *wcstr, size_t count); static void dirent_set_errno (int error); /* * Open directory stream DIRNAME for read and return a pointer to the * internal working area that is used to retrieve individual directory * entries. */ static _WDIR* _wopendir( const wchar_t *dirname) { _WDIR *dirp = NULL; int error; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { dirent_set_errno (ENOENT); return NULL; } /* Allocate new _WDIR structure */ dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); if (dirp != NULL) { DWORD n; /* Reset _WDIR structure */ dirp->handle = INVALID_HANDLE_VALUE; dirp->patt = NULL; dirp->cached = 0; /* Compute the length of full path plus zero terminator */ n = GetFullPathNameW (dirname, 0, NULL, NULL); /* Allocate room for absolute directory name and search pattern */ dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); if (dirp->patt) { /* * Convert relative directory name to an absolute one. This * allows rewinddir() to function correctly even when current * working directory is changed between opendir() and rewinddir(). */ n = GetFullPathNameW (dirname, n, dirp->patt, NULL); if (n > 0) { wchar_t *p; /* Append search pattern \* to the directory name */ p = dirp->patt + n; if (dirp->patt < p) { switch (p[-1]) { case '\\': case '/': case ':': /* Directory ends in path separator, e.g. c:\temp\ */ /*NOP*/ ; break; default: /* Directory name doesn't end in path separator */ *p++ = '\\'; } } *p++ = '*'; *p = '\0'; /* Open directory stream and retrieve the first entry */ if (dirent_first (dirp)) { /* Directory stream opened successfully */ error = 0; } else { /* Cannot retrieve first entry */ error = 1; dirent_set_errno (ENOENT); } } else { /* Cannot retrieve full path name */ dirent_set_errno (ENOENT); error = 1; } } else { /* Cannot allocate memory for search pattern */ error = 1; } } else { /* Cannot allocate _WDIR structure */ error = 1; } /* Clean up in case of error */ if (error && dirp) { _wclosedir (dirp); dirp = NULL; } return dirp; } /* * Read next directory entry. The directory entry is returned in dirent * structure in the d_name field. Individual directory entries returned by * this function include regular files, sub-directories, pseudo-directories * "." and ".." as well as volume labels, hidden files and system files. */ static struct _wdirent* _wreaddir( _WDIR *dirp) { WIN32_FIND_DATAW *datap; struct _wdirent *entp; /* Read next directory entry */ datap = dirent_next (dirp); if (datap) { size_t n; DWORD attr; /* Pointer to directory entry to return */ entp = &dirp->ent; /* * Copy file name as wide-character string. If the file name is too * long to fit in to the destination buffer, then truncate file name * to PATH_MAX characters and zero-terminate the buffer. */ n = 0; while (n < PATH_MAX && datap->cFileName[n] != 0) { entp->d_name[n] = datap->cFileName[n]; n++; } dirp->ent.d_name[n] = 0; /* Length of file name excluding zero terminator */ entp->d_namlen = n; /* File type */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { entp->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { entp->d_type = DT_DIR; } else { entp->d_type = DT_REG; } /* Reset dummy fields */ entp->d_ino = 0; entp->d_reclen = sizeof (struct _wdirent); } else { /* Last directory entry read */ entp = NULL; } return entp; } /* * Close directory stream opened by opendir() function. This invalidates the * DIR structure as well as any directory entry read previously by * _wreaddir(). */ static int _wclosedir( _WDIR *dirp) { int ok; if (dirp) { /* Release search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose (dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; } /* Release search pattern */ if (dirp->patt) { free (dirp->patt); dirp->patt = NULL; } /* Release directory structure */ free (dirp); ok = /*success*/0; } else { /* Invalid directory stream */ dirent_set_errno (EBADF); ok = /*failure*/-1; } return ok; } /* * Rewind directory stream such that _wreaddir() returns the very first * file name again. */ static void _wrewinddir( _WDIR* dirp) { if (dirp) { /* Release existing search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose (dirp->handle); } /* Open new search handle */ dirent_first (dirp); } } /* Get first directory entry (internal) */ static WIN32_FIND_DATAW* dirent_first( _WDIR *dirp) { WIN32_FIND_DATAW *datap; /* Open directory and retrieve the first entry */ dirp->handle = FindFirstFileW (dirp->patt, &dirp->data); if (dirp->handle != INVALID_HANDLE_VALUE) { /* a directory entry is now waiting in memory */ datap = &dirp->data; dirp->cached = 1; } else { /* Failed to re-open directory: no directory entry in memory */ dirp->cached = 0; datap = NULL; } return datap; } /* Get next directory entry (internal) */ static WIN32_FIND_DATAW* dirent_next( _WDIR *dirp) { WIN32_FIND_DATAW *p; /* Get next directory entry */ if (dirp->cached != 0) { /* A valid directory entry already in memory */ p = &dirp->data; dirp->cached = 0; } else if (dirp->handle != INVALID_HANDLE_VALUE) { /* Get the next directory entry from stream */ if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { /* Got a file */ p = &dirp->data; } else { /* The very last entry has been processed or an error occurred */ FindClose (dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; p = NULL; } } else { /* End of directory stream reached */ p = NULL; } return p; } /* * Open directory stream using plain old C-string. */ static DIR* opendir( const char *dirname) { struct DIR *dirp; int error; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { dirent_set_errno (ENOENT); return NULL; } /* Allocate memory for DIR structure */ dirp = (DIR*) malloc (sizeof (struct DIR)); if (dirp) { wchar_t wname[PATH_MAX + 1]; size_t n; /* Convert directory name to wide-character string */ error = dirent_mbstowcs_s( &n, wname, PATH_MAX + 1, dirname, PATH_MAX); if (!error) { /* Open directory stream using wide-character name */ dirp->wdirp = _wopendir (wname); if (dirp->wdirp) { /* Directory stream opened */ error = 0; } else { /* Failed to open directory stream */ error = 1; } } else { /* * Cannot convert file name to wide-character string. This * occurs if the string contains invalid multi-byte sequences or * the output buffer is too small to contain the resulting * string. */ error = 1; } } else { /* Cannot allocate DIR structure */ error = 1; } /* Clean up in case of error */ if (error && dirp) { free (dirp); dirp = NULL; } return dirp; } /* * Read next directory entry. * * When working with text consoles, please note that file names returned by * readdir() are represented in the default ANSI code page while any output to * console is typically formatted on another code page. Thus, non-ASCII * characters in file names will not usually display correctly on console. The * problem can be fixed in two ways: (1) change the character set of console * to 1252 using chcp utility and use Lucida Console font, or (2) use * _cprintf function when writing to console. The _cprinf() will re-encode * ANSI strings to the console code page so many non-ASCII characters will * display correcly. */ static struct dirent* readdir( DIR *dirp) { WIN32_FIND_DATAW *datap; struct dirent *entp; /* Read next directory entry */ datap = dirent_next (dirp->wdirp); if (datap) { size_t n; int error; /* Attempt to convert file name to multi-byte string */ error = dirent_wcstombs_s( &n, dirp->ent.d_name, MAX_PATH + 1, datap->cFileName, MAX_PATH); /* * If the file name cannot be represented by a multi-byte string, * then attempt to use old 8+3 file name. This allows traditional * Unix-code to access some file names despite of unicode * characters, although file names may seem unfamiliar to the user. * * Be ware that the code below cannot come up with a short file * name unless the file system provides one. At least * VirtualBox shared folders fail to do this. */ if (error && datap->cAlternateFileName[0] != '\0') { error = dirent_wcstombs_s( &n, dirp->ent.d_name, MAX_PATH + 1, datap->cAlternateFileName, sizeof (datap->cAlternateFileName) / sizeof (datap->cAlternateFileName[0])); } if (!error) { DWORD attr; /* Initialize directory entry for return */ entp = &dirp->ent; /* Length of file name excluding zero terminator */ entp->d_namlen = n - 1; /* File attributes */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { entp->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { entp->d_type = DT_DIR; } else { entp->d_type = DT_REG; } /* Reset dummy fields */ entp->d_ino = 0; entp->d_reclen = sizeof (struct dirent); } else { /* * Cannot convert file name to multi-byte string so construct * an errornous directory entry and return that. Note that * we cannot return NULL as that would stop the processing * of directory entries completely. */ entp = &dirp->ent; entp->d_name[0] = '?'; entp->d_name[1] = '\0'; entp->d_namlen = 1; entp->d_type = DT_UNKNOWN; entp->d_ino = 0; entp->d_reclen = 0; } } else { /* No more directory entries */ entp = NULL; } return entp; } /* * Close directory stream. */ static int closedir( DIR *dirp) { int ok; if (dirp) { /* Close wide-character directory stream */ ok = _wclosedir (dirp->wdirp); dirp->wdirp = NULL; /* Release multi-byte character version */ free (dirp); } else { /* Invalid directory stream */ dirent_set_errno (EBADF); ok = /*failure*/-1; } return ok; } /* * Rewind directory stream to beginning. */ static void rewinddir( DIR* dirp) { /* Rewind wide-character string directory stream */ _wrewinddir (dirp->wdirp); } /* Convert multi-byte string to wide character string */ static int dirent_mbstowcs_s( size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, const char *mbstr, size_t count) { int error; #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 or later */ error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); #else /* Older Visual Studio or non-Microsoft compiler */ size_t n; /* Convert to wide-character string */ n = mbstowcs (wcstr, mbstr, count); if (n < sizeInWords) { /* Zero-terminate output buffer */ if (wcstr) { wcstr[n] = 0; } /* Length of resuting multi-byte string WITH zero terminator */ if (pReturnValue) { *pReturnValue = n + 1; } /* Success */ error = 0; } else { /* Could not convert string */ error = 1; } #endif return error; } /* Convert wide-character string to multi-byte string */ static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, size_t sizeInBytes, const wchar_t *wcstr, size_t count) { int error; #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 or later */ error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); #else /* Older Visual Studio or non-Microsoft compiler */ size_t n; /* Convert to multi-byte string */ n = wcstombs (mbstr, wcstr, count); if (n < sizeInBytes) { /* Zero-terminate output buffer */ if (mbstr) { mbstr[n] = '\0'; } /* Length of resulting multi-bytes string WITH zero-terminator */ if (pReturnValue) { *pReturnValue = n + 1; } /* Success */ error = 0; } else { /* Cannot convert string */ error = 1; } #endif return error; } /* Set errno variable */ static void dirent_set_errno( int error) { #if defined(_MSC_VER) /* Microsoft Visual Studio */ _set_errno (error); #else /* Non-Microsoft compiler */ errno = error; #endif } #ifdef __cplusplus } #endif #endif /*DIRENT_H*/ openalpr_2.2.4.orig/src/openalpr/support/windows/unistd_partial.h000066400000000000000000000023021266464252400254000ustar00rootroot00000000000000#ifndef _UNISTD_H #define _UNISTD_H 1 /* This file intended to serve as a drop-in replacement for * unistd.h on Windows * Please add functionality as neeeded */ #include #include //#include /* getopt from: http://www.pwilson.net/sample.html. */ #include /* for getpid() and the exec..() family */ #define srandom srand #define random rand /* Values for the second argument to access. These may be OR'd together. */ #define R_OK 4 /* Test for read permission. */ #define W_OK 2 /* Test for write permission. */ //#define X_OK 1 /* execute permission - unsupported in windows*/ #define F_OK 0 /* Test for existence. */ #define access _access #define ftruncate _chsize #define ssize_t int #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 /* should be in some equivalent to */ //typedef __int8 int8_t; typedef __int16 int16_t; typedef __int32 int32_t; typedef __int64 int64_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; #endif /* unistd.h */ openalpr_2.2.4.orig/src/openalpr/support/windows/utils.h000066400000000000000000000001321266464252400235150ustar00rootroot00000000000000 #include static inline double round(double val) { return floor(val + 0.5); } openalpr_2.2.4.orig/src/openalpr/textdetection/000077500000000000000000000000001266464252400216655ustar00rootroot00000000000000openalpr_2.2.4.orig/src/openalpr/textdetection/characteranalysis.cpp000066400000000000000000000521721266464252400261000ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "characteranalysis.h" #include "linefinder.h" using namespace cv; using namespace std; namespace alpr { bool sort_text_line(TextLine i, TextLine j) { return (i.topLine.p1.y < j.topLine.p1.y); } CharacterAnalysis::CharacterAnalysis(PipelineData* pipeline_data) { this->pipeline_data = pipeline_data; this->config = pipeline_data->config; if (this->config->debugCharAnalysis) cout << "Starting CharacterAnalysis identification" << endl; this->analyze(); } CharacterAnalysis::~CharacterAnalysis() { } void CharacterAnalysis::analyze() { timespec startTime; getTimeMonotonic(&startTime); if (config->always_invert) bitwise_not(pipeline_data->crop_gray, pipeline_data->crop_gray); pipeline_data->clearThresholds(); pipeline_data->thresholds = produceThresholds(pipeline_data->crop_gray, config); timespec contoursStartTime; getTimeMonotonic(&contoursStartTime); pipeline_data->textLines.clear(); for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { TextContours tc(pipeline_data->thresholds[i]); allTextContours.push_back(tc); } if (config->debugTiming) { timespec contoursEndTime; getTimeMonotonic(&contoursEndTime); cout << " -- Character Analysis Find Contours Time: " << diffclock(contoursStartTime, contoursEndTime) << "ms." << endl; } //Mat img_equalized = equalizeBrightness(img_gray); timespec filterStartTime; getTimeMonotonic(&filterStartTime); for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { this->filter(pipeline_data->thresholds[i], allTextContours[i]); if (config->debugCharAnalysis) cout << "Threshold " << i << " had " << allTextContours[i].getGoodIndicesCount() << " good indices." << endl; } if (config->debugTiming) { timespec filterEndTime; getTimeMonotonic(&filterEndTime); cout << " -- Character Analysis Filter Time: " << diffclock(filterStartTime, filterEndTime) << "ms." << endl; } PlateMask plateMask(pipeline_data); plateMask.findOuterBoxMask(allTextContours); pipeline_data->hasPlateBorder = plateMask.hasPlateMask; pipeline_data->plateBorderMask = plateMask.getMask(); if (plateMask.hasPlateMask) { // Filter out bad contours now that we have an outer box mask... for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { filterByOuterMask(allTextContours[i]); } } int bestFitScore = -1; int bestFitIndex = -1; for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { int segmentCount = allTextContours[i].getGoodIndicesCount(); if (segmentCount > bestFitScore) { bestFitScore = segmentCount; bestFitIndex = i; bestThreshold = pipeline_data->thresholds[i]; bestContours = allTextContours[i]; } } if (this->config->debugCharAnalysis) cout << "Best fit score: " << bestFitScore << " Index: " << bestFitIndex << endl; if (bestFitScore <= 1) { pipeline_data->disqualified = true; pipeline_data->disqualify_reason = "Low best fit score in characteranalysis"; return; } //getColorMask(img, allContours, allHierarchy, charSegments); if (this->config->debugCharAnalysis) { Mat img_contours = bestContours.drawDebugImage(bestThreshold); displayImage(config, "Matching Contours", img_contours); } if (config->auto_invert) pipeline_data->plate_inverted = isPlateInverted(); else pipeline_data->plate_inverted = config->always_invert; if (config->debugGeneral) cout << "Plate inverted: " << pipeline_data->plate_inverted << endl; // Invert multiline plates and redo the thresholds before finding the second line if (config->multiline && config->auto_invert && pipeline_data->plate_inverted) { bitwise_not(pipeline_data->crop_gray, pipeline_data->crop_gray); pipeline_data->thresholds = produceThresholds(pipeline_data->crop_gray, pipeline_data->config); } LineFinder lf(pipeline_data); vector > linePolygons = lf.findLines(pipeline_data->crop_gray, bestContours); vector tempTextLines; for (unsigned int i = 0; i < linePolygons.size(); i++) { vector linePolygon = linePolygons[i]; LineSegment topLine = LineSegment(linePolygon[0].x, linePolygon[0].y, linePolygon[1].x, linePolygon[1].y); LineSegment bottomLine = LineSegment(linePolygon[3].x, linePolygon[3].y, linePolygon[2].x, linePolygon[2].y); vector textArea = getCharArea(topLine, bottomLine); TextLine textLine(textArea, linePolygon, pipeline_data->crop_gray.size()); tempTextLines.push_back(textLine); } filterBetweenLines(bestThreshold, bestContours, tempTextLines); // Sort the lines from top to bottom. std::sort(tempTextLines.begin(), tempTextLines.end(), sort_text_line); // Now that we've filtered a few more contours, re-do the text area. for (unsigned int i = 0; i < tempTextLines.size(); i++) { vector updatedTextArea = getCharArea(tempTextLines[i].topLine, tempTextLines[i].bottomLine); vector linePolygon = tempTextLines[i].linePolygon; if (updatedTextArea.size() > 0 && linePolygon.size() > 0) { pipeline_data->textLines.push_back(TextLine(updatedTextArea, linePolygon, pipeline_data->crop_gray.size())); } } if (pipeline_data->textLines.size() > 0) { int confidenceDrainers = 0; int charSegmentCount = this->bestContours.getGoodIndicesCount(); if (charSegmentCount == 1) confidenceDrainers += 91; else if (charSegmentCount < 5) confidenceDrainers += (5 - charSegmentCount) * 10; // Use the angle for the first line -- assume they'll always be parallel for multi-line plates int absangle = abs(pipeline_data->textLines[0].topLine.angle); if (absangle > config->maxPlateAngleDegrees) confidenceDrainers += 91; else if (absangle > 1) confidenceDrainers += (config->maxPlateAngleDegrees - absangle) ; // If a multiline plate has only one line, disqualify if (pipeline_data->isMultiline && pipeline_data->textLines.size() < 2) { if (config->debugCharAnalysis) std::cout << "Did not detect multiple lines on multi-line plate" << std::endl; confidenceDrainers += 95; } if (confidenceDrainers >= 90) { pipeline_data->disqualified = true; pipeline_data->disqualify_reason = "Low confidence in characteranalysis"; } else { float confidence = 100 - confidenceDrainers; pipeline_data->confidence_weights.setScore("CHARACTER_ANALYSIS_SCORE", confidence, 1.0); } } else { pipeline_data->disqualified = true; pipeline_data->disqualify_reason = "No text lines found in characteranalysis"; } if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << "Character Analysis Time: " << diffclock(startTime, endTime) << "ms." << endl; } // Draw debug dashboard if (this->pipeline_data->config->debugCharAnalysis && pipeline_data->textLines.size() > 0) { vector tempDash; for (unsigned int z = 0; z < pipeline_data->thresholds.size(); z++) { Mat tmp(pipeline_data->thresholds[z].size(), pipeline_data->thresholds[z].type()); pipeline_data->thresholds[z].copyTo(tmp); cvtColor(tmp, tmp, CV_GRAY2BGR); tempDash.push_back(tmp); } Mat bestVal(this->bestThreshold.size(), this->bestThreshold.type()); this->bestThreshold.copyTo(bestVal); cvtColor(bestVal, bestVal, CV_GRAY2BGR); for (unsigned int z = 0; z < this->bestContours.size(); z++) { Scalar dcolor(255,0,0); if (this->bestContours.goodIndices[z]) dcolor = Scalar(0,255,0); drawContours(bestVal, this->bestContours.contours, z, dcolor, 1); } tempDash.push_back(bestVal); displayImage(config, "Character Region Step 1 Thresholds", drawImageDashboard(tempDash, bestVal.type(), 3)); } } Mat CharacterAnalysis::getCharacterMask() { Mat charMask = Mat::zeros(bestThreshold.size(), CV_8U); for (unsigned int i = 0; i < bestContours.size(); i++) { if (bestContours.goodIndices[i] == false) continue; drawContours(charMask, bestContours.contours, i, // draw this contour cv::Scalar(255,255,255), // in CV_FILLED, 8, bestContours.hierarchy, 1 ); } return charMask; } void CharacterAnalysis::filter(Mat img, TextContours& textContours) { int STARTING_MIN_HEIGHT = round (((float) img.rows) * config->charAnalysisMinPercent); int STARTING_MAX_HEIGHT = round (((float) img.rows) * (config->charAnalysisMinPercent + config->charAnalysisHeightRange)); int HEIGHT_STEP = round (((float) img.rows) * config->charAnalysisHeightStepSize); int NUM_STEPS = config->charAnalysisNumSteps; int bestFitScore = -1; vector bestIndices; for (int i = 0; i < NUM_STEPS; i++) { //vector goodIndices(contours.size()); for (unsigned int z = 0; z < textContours.size(); z++) textContours.goodIndices[z] = true; this->filterByBoxSize(textContours, STARTING_MIN_HEIGHT + (i * HEIGHT_STEP), STARTING_MAX_HEIGHT + (i * HEIGHT_STEP)); int goodIndices = textContours.getGoodIndicesCount(); if ( goodIndices == 0 || goodIndices <= bestFitScore) // Don't bother doing more filtering if we already lost... continue; this->filterContourHoles(textContours); goodIndices = textContours.getGoodIndicesCount(); if ( goodIndices == 0 || goodIndices <= bestFitScore) // Don't bother doing more filtering if we already lost... continue; int segmentCount = textContours.getGoodIndicesCount(); if (segmentCount > bestFitScore) { bestFitScore = segmentCount; bestIndices = textContours.getIndicesCopy(); } } textContours.setIndices(bestIndices); } // Goes through the contours for the plate and picks out possible char segments based on min/max height void CharacterAnalysis::filterByBoxSize(TextContours& textContours, int minHeightPx, int maxHeightPx) { // For multiline plates, we want to target the biggest line for character analysis, since it should be easier to spot. float larger_char_height_mm = 0; float larger_char_width_mm = 0; for (unsigned int i = 0; i < config->charHeightMM.size(); i++) { if (config->charHeightMM[i] > larger_char_height_mm) { larger_char_height_mm = config->charHeightMM[i]; larger_char_width_mm = config->charWidthMM[i]; } } float idealAspect=larger_char_width_mm / larger_char_height_mm; float aspecttolerance=0.25; for (unsigned int i = 0; i < textContours.size(); i++) { if (textContours.goodIndices[i] == false) continue; textContours.goodIndices[i] = false; // Set it to not included unless it proves valid //Create bounding rect of object Rect mr= boundingRect(textContours.contours[i]); float minWidth = mr.height * 0.2; //Crop image //cout << "Height: " << minHeightPx << " - " << mr.height << " - " << maxHeightPx << " ////// Width: " << mr.width << " - " << minWidth << endl; if(mr.height >= minHeightPx && mr.height <= maxHeightPx && mr.width > minWidth) { float charAspect= (float)mr.width/(float)mr.height; //cout << " -- stage 2 aspect: " << abs(charAspect) << " - " << aspecttolerance << endl; if (abs(charAspect - idealAspect) < aspecttolerance) textContours.goodIndices[i] = true; } } } void CharacterAnalysis::filterContourHoles(TextContours& textContours) { for (unsigned int i = 0; i < textContours.size(); i++) { if (textContours.goodIndices[i] == false) continue; textContours.goodIndices[i] = false; // Set it to not included unless it proves valid int parentIndex = textContours.hierarchy[i][3]; if (parentIndex >= 0 && textContours.goodIndices[parentIndex]) { // this contour is a child of an already identified contour. REMOVE it if (this->config->debugCharAnalysis) { cout << "filterContourHoles: contour index: " << i << endl; } } else { textContours.goodIndices[i] = true; } } } // Goes through the contours for the plate and picks out possible char segments based on min/max height // returns a vector of indices corresponding to valid contours void CharacterAnalysis::filterByParentContour( TextContours& textContours) { vector parentIDs; vector votes; for (unsigned int i = 0; i < textContours.size(); i++) { if (textContours.goodIndices[i] == false) continue; textContours.goodIndices[i] = false; // Set it to not included unless it proves int voteIndex = -1; int parentID = textContours.hierarchy[i][3]; // check if parentID is already in the lsit for (unsigned int j = 0; j < parentIDs.size(); j++) { if (parentIDs[j] == parentID) { voteIndex = j; break; } } if (voteIndex == -1) { parentIDs.push_back(parentID); votes.push_back(1); } else { votes[voteIndex] = votes[voteIndex] + 1; } } // Tally up the votes, pick the winner int totalVotes = 0; int winningParentId = 0; int highestVotes = 0; for (unsigned int i = 0; i < parentIDs.size(); i++) { if (votes[i] > highestVotes) { winningParentId = parentIDs[i]; highestVotes = votes[i]; } totalVotes += votes[i]; } // Now filter out all the contours with a different parent ID (assuming the totalVotes > 2) for (unsigned int i = 0; i < textContours.size(); i++) { if (textContours.goodIndices[i] == false) continue; if (totalVotes <= 2) { textContours.goodIndices[i] = true; } else if (textContours.hierarchy[i][3] == winningParentId) { textContours.goodIndices[i] = true; } } } void CharacterAnalysis::filterBetweenLines(Mat img, TextContours& textContours, vector textLines ) { static float MIN_AREA_PERCENT_WITHIN_LINES = 0.88; static float MAX_DISTANCE_PERCENT_FROM_LINES = 0.15; if (textLines.size() == 0) return; vector validPoints; // Create a white mask for the area inside the polygon Mat outerMask = Mat::zeros(img.size(), CV_8U); for (unsigned int i = 0; i < textLines.size(); i++) fillConvexPoly(outerMask, textLines[i].linePolygon.data(), textLines[i].linePolygon.size(), Scalar(255,255,255)); // For each contour, determine if enough of it is between the lines to qualify for (unsigned int i = 0; i < textContours.size(); i++) { if (textContours.goodIndices[i] == false) continue; float percentInsideMask = getContourAreaPercentInsideMask(outerMask, textContours.contours, textContours.hierarchy, (int) i); if (percentInsideMask < MIN_AREA_PERCENT_WITHIN_LINES) { // Not enough area is inside the lines. if (config->debugCharAnalysis) cout << "Rejecting due to insufficient area" << endl; textContours.goodIndices[i] = false; continue; } // now check to make sure that the top and bottom of the contour are near enough to the lines // First get the high and low point for the contour // Remember that origin is top-left, so the top Y values are actually closer to 0. Rect brect = boundingRect(textContours.contours[i]); int xmiddle = brect.x + (brect.width / 2); Point topMiddle = Point(xmiddle, brect.y); Point botMiddle = Point(xmiddle, brect.y+brect.height); // Get the absolute distance from the top and bottom lines for (unsigned int i = 0; i < textLines.size(); i++) { Point closestTopPoint = textLines[i].topLine.closestPointOnSegmentTo(topMiddle); Point closestBottomPoint = textLines[i].bottomLine.closestPointOnSegmentTo(botMiddle); float absTopDistance = distanceBetweenPoints(closestTopPoint, topMiddle); float absBottomDistance = distanceBetweenPoints(closestBottomPoint, botMiddle); float maxDistance = textLines[i].lineHeight * MAX_DISTANCE_PERCENT_FROM_LINES; if (absTopDistance < maxDistance && absBottomDistance < maxDistance) { // It's ok, leave it as-is. } else { textContours.goodIndices[i] = false; if (config->debugCharAnalysis) cout << "Rejecting due to top/bottom points that are out of range" << endl; } } } } void CharacterAnalysis::filterByOuterMask(TextContours& textContours) { float MINIMUM_PERCENT_LEFT_AFTER_MASK = 0.1; float MINIMUM_PERCENT_OF_CHARS_INSIDE_PLATE_MASK = 0.6; if (this->pipeline_data->hasPlateBorder == false) return; cv::Mat plateMask = pipeline_data->plateBorderMask; Mat tempMaskedContour = Mat::zeros(plateMask.size(), CV_8U); Mat tempFullContour = Mat::zeros(plateMask.size(), CV_8U); int charsInsideMask = 0; int totalChars = 0; vector originalindices; for (unsigned int i = 0; i < textContours.size(); i++) originalindices.push_back(textContours.goodIndices[i]); for (unsigned int i=0; i < textContours.size(); i++) { if (textContours.goodIndices[i] == false) continue; totalChars++; tempFullContour = Mat::zeros(plateMask.size(), CV_8U); drawContours(tempFullContour, textContours.contours, i, Scalar(255,255,255), CV_FILLED, 8, textContours.hierarchy); bitwise_and(tempFullContour, plateMask, tempMaskedContour); textContours.goodIndices[i] = false; float beforeMaskWhiteness = mean(tempFullContour)[0]; float afterMaskWhiteness = mean(tempMaskedContour)[0]; if (afterMaskWhiteness / beforeMaskWhiteness > MINIMUM_PERCENT_LEFT_AFTER_MASK) { charsInsideMask++; textContours.goodIndices[i] = true; } } if (totalChars == 0) { textContours.goodIndices = originalindices; return; } // Check to make sure that this is a valid box. If the box is too small (e.g., 1 char is inside, and 3 are outside) // then don't use this to filter. float percentCharsInsideMask = ((float) charsInsideMask) / ((float) totalChars); if (percentCharsInsideMask < MINIMUM_PERCENT_OF_CHARS_INSIDE_PLATE_MASK) { textContours.goodIndices = originalindices; return; } } bool CharacterAnalysis::isPlateInverted() { Mat charMask = getCharacterMask(); Scalar meanVal = mean(bestThreshold, charMask)[0]; if (this->config->debugCharAnalysis) cout << "CharacterAnalysis, plate inverted: MEAN: " << meanVal << " : " << bestThreshold.type() << endl; if (meanVal[0] < 100) // Half would be 122.5. Give it a little extra oomf before saying it needs inversion. Most states aren't inverted. return true; return false; } vector CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bottomLine) { const int MAX = 100000; const int MIN= -1; int leftX = MAX; int rightX = MIN; for (unsigned int i = 0; i < bestContours.size(); i++) { if (bestContours.goodIndices[i] == false) continue; for (unsigned int z = 0; z < bestContours.contours[i].size(); z++) { if (bestContours.contours[i][z].x < leftX) leftX = bestContours.contours[i][z].x; if (bestContours.contours[i][z].x > rightX) rightX = bestContours.contours[i][z].x; } } vector charArea; if (leftX != MAX && rightX != MIN) { Point tl(leftX, topLine.getPointAt(leftX)); Point tr(rightX, topLine.getPointAt(rightX)); Point br(rightX, bottomLine.getPointAt(rightX)); Point bl(leftX, bottomLine.getPointAt(leftX)); charArea.push_back(tl); charArea.push_back(tr); charArea.push_back(br); charArea.push_back(bl); } return charArea; } } openalpr_2.2.4.orig/src/openalpr/textdetection/characteranalysis.h000066400000000000000000000037771266464252400255540ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_CHARACTERANALYSIS_H #define OPENALPR_CHARACTERANALYSIS_H #include #include "opencv2/imgproc/imgproc.hpp" #include "utility.h" #include "config.h" #include "pipeline_data.h" #include "textcontours.h" #include "platemask.h" #include "linefinder.h" namespace alpr { class CharacterAnalysis { public: CharacterAnalysis(PipelineData* pipeline_data); virtual ~CharacterAnalysis(); cv::Mat bestThreshold; TextContours bestContours; std::vector allTextContours; void analyze(); cv::Mat getCharacterMask(); private: PipelineData* pipeline_data; Config* config; bool isPlateInverted(); void filter(cv::Mat img, TextContours& textContours); void filterByBoxSize(TextContours& textContours, int minHeightPx, int maxHeightPx); void filterByParentContour( TextContours& textContours ); void filterContourHoles(TextContours& textContours); void filterByOuterMask(TextContours& textContours); std::vector getCharArea(LineSegment topLine, LineSegment bottomLine); void filterBetweenLines(cv::Mat img, TextContours& textContours, std::vector textLines ); }; } #endif // OPENALPR_CHARACTERANALYSIS_H openalpr_2.2.4.orig/src/openalpr/textdetection/linefinder.cpp000066400000000000000000000414551266464252400245210ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include "linefinder.h" #include "utility.h" #include "pipeline_data.h" using namespace std; using namespace cv; namespace alpr { LineFinder::LineFinder(PipelineData* pipeline_data) { this->pipeline_data = pipeline_data; } LineFinder::~LineFinder() { } vector > LineFinder::findLines(Mat image, const TextContours contours) { const float MIN_AREA_TO_IGNORE = 0.65; vector > linesFound; cvtColor(image, image, CV_GRAY2BGR); vector charPoints; for (unsigned int i = 0; i < contours.contours.size(); i++) { if (contours.goodIndices[i] == false) continue; charPoints.push_back( CharPointInfo(contours.contours[i], i) ); } vector bestCharArea = getBestLine(contours, charPoints); vector bestLine = extendToEdges(Size(contours.width, contours.height), bestCharArea); if (bestLine.size() > 0) linesFound.push_back(bestLine); if (pipeline_data->isMultiline && bestCharArea.size() > 0) { vector next_best_line = findNextBestLine(Size(contours.width, contours.height), bestCharArea); if (next_best_line.size() > 0) { vector next_best_line_extended = extendToEdges(Size(contours.width, contours.height), next_best_line); linesFound.push_back(next_best_line_extended); } } return linesFound; } std::vector LineFinder::calculateCroppedRegionForHistogram(cv::Size imageSize, std::vector charArea) { LineSegment topLine(charArea[0], charArea[1]); LineSegment new_top; LineSegment new_bottom; if (topLine.angle < 0) { float distance_from_top = topLine.p1.y; new_top = topLine.getParallelLine(distance_from_top); float distance_from_bottom = imageSize.height - topLine.p2.y; new_bottom = topLine.getParallelLine(-1 * distance_from_bottom); } else { float distance_from_top = topLine.p2.y; new_top = topLine.getParallelLine(distance_from_top); float distance_from_bottom = imageSize.height - topLine.p1.y; new_bottom = topLine.getParallelLine(-1 * distance_from_bottom); } vector points; points.push_back(new_top.p1); points.push_back(new_top.p2); points.push_back(new_bottom.p2); points.push_back(new_bottom.p1); return points; } std::vector LineFinder::findNextBestLine(cv::Size imageSize, std::vector bestLine) { // Pull out a crop of the plate around the line we know about, // then do a horizontal histogram on all the thresholds. Find the other line based on that histogram vector histogramArea = calculateCroppedRegionForHistogram(imageSize, bestLine); Size cropped_quad_size(distanceBetweenPoints(histogramArea[0], histogramArea[1]), distanceBetweenPoints(histogramArea[0], histogramArea[3])); Mat mask = Mat::zeros(cropped_quad_size, CV_8U); bitwise_not(mask, mask); vector inputQuad; for (int i = 0; i < histogramArea.size(); i++) inputQuad.push_back(histogramArea[i]); vector outputQuad; outputQuad.push_back(Point2f(0,0)); outputQuad.push_back(Point2f(cropped_quad_size.width,0)); outputQuad.push_back(Point2f(cropped_quad_size.width,cropped_quad_size.height)); outputQuad.push_back(Point2f(0,cropped_quad_size.height)); int pxLeniency = 2; Mat trans_matrix = getPerspectiveTransform(inputQuad, outputQuad); vector orig_best_line; for (int i = 0; i < bestLine.size(); i++) orig_best_line.push_back(bestLine[i]); vector transformed_best_line; perspectiveTransform(orig_best_line, transformed_best_line, trans_matrix); int transformed_best_line_start = round(transformed_best_line[0].y); int transformed_best_line_end = round(transformed_best_line[3].y); int transformed_best_line_width = transformed_best_line_end - transformed_best_line_start; int transformed_best_line_variance = (int) ((float) transformed_best_line_width) * 0.25; float lowest_width_diff = 99999999999; int best_secondline_index = -1; int best_secondline_threshold = -1; int best_secondline_top_pixel_offset_from_bestline_top = 0; int best_secondline_bottom_pixel_offset_from_bestline_top = 0; for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { Mat warpedImage = Mat::zeros(cropped_quad_size, CV_8U); warpPerspective(pipeline_data->thresholds[i], warpedImage, trans_matrix, cropped_quad_size); HistogramHorizontal histogram(warpedImage, mask); vector > histogram_hits = histogram.get1DHits(pxLeniency); // First find the histogram blob for the "best line" that we already found // Do this by comparing the "transformed_best_line" points to the histogram_hits int best_line_index = -1; for (unsigned int hitidx = 0; hitidx < histogram_hits.size(); hitidx++) { pair hit = histogram_hits[hitidx]; if ((hit.first >= transformed_best_line_start - transformed_best_line_variance) && (hit.first <= transformed_best_line_start + transformed_best_line_variance) && (hit.second >= transformed_best_line_end - transformed_best_line_variance) && (hit.second <= transformed_best_line_end + transformed_best_line_variance)) { best_line_index = hitidx; break; } } if (best_line_index < 0) // Best line not found on this threshold... { if (pipeline_data->config->debugCharAnalysis) cout << "Could not find best line for multiline plate" << endl; continue; } if (pipeline_data->config->debugCharAnalysis) cout << "Found a multiline best line " << histogram_hits[best_line_index].first << " -> " << histogram_hits[best_line_index].second << endl; // Now look at all other hits and find one that is above or below our best line and has the correct text height ratio // I'm either looking for a bigger line above or a smaller line below (or vice versa, or two same sized lines depending on the plate config) // Assume maximum of two lines per plate for now // TODO: Use char_whitespace_between_lines_mm to score lines better //float best_line_width = histogram_hits[best_line_index].second - histogram_hits[best_line_index].first; if (pipeline_data->config->debugCharAnalysis) cout << "Ideal calculation: " << pipeline_data->config->charHeightMM[0] << " : " << pipeline_data->config->charHeightMM[1] << " - " << transformed_best_line_width << endl; float ideal_above_size = (pipeline_data->config->charHeightMM[0] / pipeline_data->config->charHeightMM[1]) * transformed_best_line_width; float ideal_below_size = (pipeline_data->config->charHeightMM[1] / pipeline_data->config->charHeightMM[0]) * transformed_best_line_width; float max_deviation_percent = 0.30; for (unsigned int hitidx = 0; hitidx < histogram_hits.size(); hitidx++) { if (hitidx == best_line_index) continue; float hit_width = histogram_hits[hitidx].second - histogram_hits[hitidx].first; float ideal_width; if (histogram_hits[hitidx].second <= histogram_hits[best_line_index].first) ideal_width = ideal_above_size; else if (histogram_hits[hitidx].first >= histogram_hits[best_line_index].second) ideal_width = ideal_below_size; else assert(false); if (pipeline_data->config->debugCharAnalysis) cout << "Hit Width: " << hit_width << " -- ideal width: " << ideal_width << endl; if ((hit_width >= ideal_width * (1-max_deviation_percent)) && (hit_width <= ideal_width * (1+max_deviation_percent))) { float diff_from_ideal = hit_width - ideal_width; if (diff_from_ideal < lowest_width_diff) { lowest_width_diff = diff_from_ideal; best_secondline_index = hitidx; best_secondline_threshold = i; best_secondline_top_pixel_offset_from_bestline_top = (histogram_hits[best_line_index].first - histogram_hits[hitidx].first); best_secondline_bottom_pixel_offset_from_bestline_top = (histogram_hits[best_line_index].first - histogram_hits[hitidx].second); } } } } Mat debugImg(pipeline_data->thresholds[1].size(), pipeline_data->thresholds[1].type()); pipeline_data->thresholds[1].copyTo(debugImg); cvtColor(debugImg, debugImg, CV_GRAY2BGR); LineSegment orig_top_line(bestLine[0], bestLine[1]); LineSegment secondline_top = orig_top_line.getParallelLine(best_secondline_top_pixel_offset_from_bestline_top + 1); LineSegment secondline_bottom = orig_top_line.getParallelLine(best_secondline_bottom_pixel_offset_from_bestline_top - 1); line(debugImg, orig_top_line.p1, orig_top_line.p2, Scalar(0,0,255), 2); line(debugImg, secondline_top.p1, secondline_top.p2, Scalar(255,255,0), 2); line(debugImg, secondline_bottom.p1, secondline_bottom.p2, Scalar(0,255,0), 2); if (pipeline_data->config->debugCharAnalysis) { cout << "Multiline = " << secondline_top.str() << " -- " << secondline_bottom.str() << endl; cout << "Multiline winner is: " << best_secondline_index << " on threshold " << best_secondline_threshold << endl; } vector response; if (best_secondline_index >= 0) { response.push_back(secondline_top.p1); response.push_back(secondline_top.p2); response.push_back(secondline_bottom.p1); response.push_back(secondline_bottom.p2); } return response; } // Returns a polygon "stripe" across the width of the character region. The lines are voted and the polygon starts at 0 and extends to image width vector LineFinder::getBestLine(const TextContours contours, vector charPoints) { vector bestStripe; // Find the best fit line segment that is parallel with the most char segments if (charPoints.size() <= 1) { // Maybe do something about this later, for now let's just ignore return bestStripe; } vector charheights; for (unsigned int i = 0; i < charPoints.size(); i++) charheights.push_back(charPoints[i].boundingBox.height); float medianCharHeight = median(charheights.data(), charheights.size()); vector topLines; vector bottomLines; // Iterate through each possible char and find all possible lines for the top and bottom of each char segment for (unsigned int i = 0; i < charPoints.size() - 1; i++) { for (unsigned int k = i+1; k < charPoints.size(); k++) { int leftCPIndex, rightCPIndex; if (charPoints[i].top.x < charPoints[k].top.x) { leftCPIndex = i; rightCPIndex = k; } else { leftCPIndex = k; rightCPIndex = i; } LineSegment top(charPoints[leftCPIndex].top, charPoints[rightCPIndex].top); LineSegment bottom(charPoints[leftCPIndex].bottom, charPoints[rightCPIndex].bottom); LineSegment parallelBot = top.getParallelLine(medianCharHeight * -1); LineSegment parallelTop = bottom.getParallelLine(medianCharHeight); // Only allow lines that have a sane angle if (abs(top.angle) <= pipeline_data->config->maxPlateAngleDegrees && abs(parallelBot.angle) <= pipeline_data->config->maxPlateAngleDegrees) { topLines.push_back(top); bottomLines.push_back(parallelBot); } // Only allow lines that have a sane angle if (abs(parallelTop.angle) <= pipeline_data->config->maxPlateAngleDegrees && abs(bottom.angle) <= pipeline_data->config->maxPlateAngleDegrees) { topLines.push_back(parallelTop); bottomLines.push_back(bottom); } } } int bestScoreIndex = 0; int bestScore = -1; int bestScoreDistance = -1; // Line segment distance is used as a tie breaker // Now, among all possible lines, find the one that is the best fit for (unsigned int i = 0; i < topLines.size(); i++) { float SCORING_MIN_THRESHOLD = 0.97; float SCORING_MAX_THRESHOLD = 1.03; int curScore = 0; for (unsigned int charidx = 0; charidx < charPoints.size(); charidx++) { float topYPos = topLines[i].getPointAt(charPoints[charidx].top.x); float botYPos = bottomLines[i].getPointAt(charPoints[charidx].bottom.x); float minTop = charPoints[charidx].top.y * SCORING_MIN_THRESHOLD; float maxTop = charPoints[charidx].top.y * SCORING_MAX_THRESHOLD; float minBot = (charPoints[charidx].bottom.y) * SCORING_MIN_THRESHOLD; float maxBot = (charPoints[charidx].bottom.y) * SCORING_MAX_THRESHOLD; if ( (topYPos >= minTop && topYPos <= maxTop) && (botYPos >= minBot && botYPos <= maxBot)) { curScore++; } } // Tie goes to the one with longer line segments if ((curScore > bestScore) || (curScore == bestScore && topLines[i].length > bestScoreDistance)) { bestScore = curScore; bestScoreIndex = i; // Just use x distance for now bestScoreDistance = topLines[i].length; } } if (bestScore < 0) return bestStripe; if (pipeline_data->config->debugCharAnalysis) { cout << "The winning score is: " << bestScore << endl; // Draw the winning line segment Mat tempImg = Mat::zeros(Size(contours.width, contours.height), CV_8U); cvtColor(tempImg, tempImg, CV_GRAY2BGR); cv::line(tempImg, topLines[bestScoreIndex].p1, topLines[bestScoreIndex].p2, Scalar(0, 0, 255), 2); cv::line(tempImg, bottomLines[bestScoreIndex].p1, bottomLines[bestScoreIndex].p2, Scalar(0, 0, 255), 2); displayImage(pipeline_data->config, "Winning lines", tempImg); } bestStripe.push_back(topLines[bestScoreIndex].p1); bestStripe.push_back(topLines[bestScoreIndex].p2); bestStripe.push_back(bottomLines[bestScoreIndex].p2); bestStripe.push_back(bottomLines[bestScoreIndex].p1); return bestStripe; } std::vector LineFinder::extendToEdges(cv::Size imageSize, std::vector charArea) { vector extended; if (charArea.size() < 4) return extended; LineSegment top(charArea[0], charArea[1]); LineSegment bottom(charArea[3], charArea[2]); Point topLeft = Point(0, top.getPointAt(0) ); Point topRight = Point(imageSize.width, top.getPointAt(imageSize.width)); Point bottomRight = Point(imageSize.width, bottom.getPointAt(imageSize.width)); Point bottomLeft = Point(0, bottom.getPointAt(0)); extended.push_back(topLeft); extended.push_back(topRight); extended.push_back(bottomRight); extended.push_back(bottomLeft); return extended; } CharPointInfo::CharPointInfo(vector contour, int index) { this->contourIndex = index; this->boundingBox = cv::boundingRect( Mat(contour) ); int x = boundingBox.x + (boundingBox.width / 2); int y = boundingBox.y; this->top = Point(x, y); x = boundingBox.x + (boundingBox.width / 2); y = boundingBox.y + boundingBox.height; this->bottom = Point(x,y); } }openalpr_2.2.4.orig/src/openalpr/textdetection/linefinder.h000066400000000000000000000044311266464252400241570ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ // This class finds lines of text given an array of contours #ifndef OPENALPR_LINEFINDER_H #define OPENALPR_LINEFINDER_H #include "opencv2/imgproc/imgproc.hpp" #include "textcontours.h" #include "textline.h" #include "pipeline_data.h" #include "segmentation/histogramhorizontal.h" namespace alpr { class CharPointInfo { public: CharPointInfo(std::vector contour, int index); cv::Rect boundingBox; cv::Point top; cv::Point bottom; int contourIndex; }; class LineFinder { public: LineFinder(PipelineData* pipeline_data); virtual ~LineFinder(); std::vector > findLines(cv::Mat image, const TextContours contours); private: PipelineData* pipeline_data; // Returns 4 points, counter clockwise that bound the detected character area std::vector getBestLine(const TextContours contours, std::vector charPoints); // Extends the top and bottom lines to the left and right edge of the image. Returns 4 points, counter clockwise. std::vector extendToEdges(cv::Size imageSize, std::vector charArea); std::vector findNextBestLine(cv::Size imageSize, std::vector bestLine); // Gets a polygon that covers the entire area we wish to run a horizontal histogram over // This needs to be done to handle rotation/skew std::vector calculateCroppedRegionForHistogram(cv::Size imageSize, std::vector charArea); }; } #endif /* OPENALPR_LINEFINDER_H */ openalpr_2.2.4.orig/src/openalpr/textdetection/platemask.cpp000066400000000000000000000153421266464252400243570ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "platemask.h" using namespace std; using namespace cv; namespace alpr { PlateMask::PlateMask(PipelineData* pipeline_data) { this->pipeline_data = pipeline_data; this->hasPlateMask = false; } PlateMask::~PlateMask() { } cv::Mat PlateMask::getMask() { return this->plateMask; } // Tries to find a rectangular area surrounding most of the characters. Not required // but helpful when determining the plate edges void PlateMask::findOuterBoxMask( vector contours ) { double min_parent_area = pipeline_data->config->templateHeightPx * pipeline_data->config->templateWidthPx * 0.10; // Needs to be at least 10% of the plate area to be considered. int winningIndex = -1; int winningParentId = -1; int bestCharCount = 0; double lowestArea = 99999999999999; if (pipeline_data->config->debugCharAnalysis) cout << "CharacterAnalysis::findOuterBoxMask" << endl; for (unsigned int imgIndex = 0; imgIndex < contours.size(); imgIndex++) { //vector charContours = filter(thresholds[imgIndex], allContours[imgIndex], allHierarchy[imgIndex]); int charsRecognized = 0; int parentId = -1; bool hasParent = false; int bestParentId = -1; vector < int > charsRecognizedInContours(contours[imgIndex].goodIndices.size(),0); for (unsigned int i = 0; i < contours[imgIndex].goodIndices.size(); i++) { if (contours[imgIndex].goodIndices[i]) charsRecognized++; if (contours[imgIndex].goodIndices[i] && contours[imgIndex].hierarchy[i][3] != -1) { parentId = contours[imgIndex].hierarchy[i][3]; hasParent = true; charsRecognizedInContours[parentId] ++; } } if (charsRecognized == 0) continue; if (hasParent) { charsRecognized = 0; for(unsigned int i = 0 ; i < contours[imgIndex].goodIndices.size(); i++) { if(charsRecognizedInContours[i] > charsRecognized) { charsRecognized = charsRecognizedInContours[i]; bestParentId = i; } } double boxArea = contourArea(contours[imgIndex].contours[bestParentId]); if (boxArea < min_parent_area) continue; if ((charsRecognized > bestCharCount) || (charsRecognized == bestCharCount && boxArea < lowestArea)) //(boxArea < lowestArea) { bestCharCount = charsRecognized; winningIndex = imgIndex; //winningParentId = parentId; winningParentId = bestParentId; lowestArea = boxArea; } } } if (pipeline_data->config->debugCharAnalysis) cout << "Winning image index (findOuterBoxMask) is: " << winningIndex << endl; if (winningIndex != -1 && bestCharCount >= 3) { Mat mask = Mat::zeros(pipeline_data->thresholds[winningIndex].size(), CV_8U); // get rid of the outline by drawing a 1 pixel width black line drawContours(mask, contours[winningIndex].contours, winningParentId, // draw this contour cv::Scalar(255,255,255), // in CV_FILLED, 8, contours[winningIndex].hierarchy, 0 ); // Morph Open the mask to get rid of any little connectors to non-plate portions int morph_elem = 2; int morph_size = 3; Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); //morphologyEx( mask, mask, MORPH_CLOSE, element ); morphologyEx( mask, mask, MORPH_OPEN, element ); //morph_size = 1; //element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); //dilate(mask, mask, element); // Drawing the edge black effectively erodes the image. This may clip off some extra junk from the edges. // We'll want to do the contour again and find the larges one so that we remove the clipped portion. vector > contoursSecondRound; findContours(mask, contoursSecondRound, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); int biggestContourIndex = -1; double largestArea = 0; for (unsigned int c = 0; c < contoursSecondRound.size(); c++) { double area = contourArea(contoursSecondRound[c]); if (area > largestArea) { biggestContourIndex = c; largestArea = area; } } if (biggestContourIndex != -1) { mask = Mat::zeros(pipeline_data->thresholds[winningIndex].size(), CV_8U); vector smoothedMaskPoints; approxPolyDP(contoursSecondRound[biggestContourIndex], smoothedMaskPoints, 2, true); vector > tempvec; tempvec.push_back(smoothedMaskPoints); //fillPoly(mask, smoothedMaskPoints.data(), smoothedMaskPoints, Scalar(255,255,255)); drawContours(mask, tempvec, 0, // draw this contour cv::Scalar(255,255,255), // in CV_FILLED, 8, contours[winningIndex].hierarchy, 0 ); } if (pipeline_data->config->debugCharAnalysis) { vector debugImgs; Mat debugImgMasked = Mat::zeros(pipeline_data->thresholds[winningIndex].size(), CV_8U); pipeline_data->thresholds[winningIndex].copyTo(debugImgMasked, mask); debugImgs.push_back(mask); debugImgs.push_back(pipeline_data->thresholds[winningIndex]); debugImgs.push_back(debugImgMasked); Mat dashboard = drawImageDashboard(debugImgs, CV_8U, 1); displayImage(pipeline_data->config, "Winning outer box", dashboard); } hasPlateMask = true; this->plateMask = mask; } else { hasPlateMask = false; Mat fullMask = Mat::zeros(pipeline_data->thresholds[0].size(), CV_8U); bitwise_not(fullMask, fullMask); this->plateMask = fullMask; } } } openalpr_2.2.4.orig/src/openalpr/textdetection/platemask.h000066400000000000000000000023611266464252400240210ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_PLATEMASK_H #define OPENALPR_PLATEMASK_H #include "opencv2/imgproc/imgproc.hpp" #include "pipeline_data.h" #include "textcontours.h" namespace alpr { class PlateMask { public: PlateMask(PipelineData* pipeline_data); virtual ~PlateMask(); bool hasPlateMask; cv::Mat getMask(); void findOuterBoxMask(std::vector contours); private: PipelineData* pipeline_data; cv::Mat plateMask; }; } #endif /* OPENALPR_PLATEMASK_H */ openalpr_2.2.4.orig/src/openalpr/textdetection/textcontours.cpp000066400000000000000000000065011266464252400251540ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "textcontours.h" using namespace std; using namespace cv; namespace alpr { TextContours::TextContours() { } TextContours::TextContours(cv::Mat threshold) { load(threshold); } TextContours::~TextContours() { } void TextContours::load(cv::Mat threshold) { Mat tempThreshold(threshold.size(), CV_8U); threshold.copyTo(tempThreshold); findContours(tempThreshold, contours, // a vector of contours hierarchy, CV_RETR_TREE, // retrieve all contours CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours for (unsigned int i = 0; i < contours.size(); i++) goodIndices.push_back(true); this->width = threshold.cols; this->height = threshold.rows; } unsigned int TextContours::size() { return contours.size(); } int TextContours::getGoodIndicesCount() { int count = 0; for (unsigned int i = 0; i < goodIndices.size(); i++) { if (goodIndices[i]) count++; } return count; } std::vector TextContours::getIndicesCopy() { vector copyArray; for (unsigned int i = 0; i < goodIndices.size(); i++) { bool val = goodIndices[i]; copyArray.push_back(goodIndices[i]); } return copyArray; } void TextContours::setIndices(std::vector newIndices) { if (newIndices.size() == goodIndices.size()) { for (unsigned int i = 0; i < newIndices.size(); i++) goodIndices[i] = newIndices[i]; } else { assert("Invalid set operation on indices"); } } Mat TextContours::drawDebugImage() const { Mat img_contours = Mat::zeros(Size(width, height), CV_8U); return drawDebugImage(img_contours); } Mat TextContours::drawDebugImage(Mat baseImage) const { Mat img_contours(baseImage.size(), CV_8U); baseImage.copyTo(img_contours); cvtColor(img_contours, img_contours, CV_GRAY2RGB); vector > allowedContours; for (unsigned int i = 0; i < this->contours.size(); i++) { if (this->goodIndices[i]) allowedContours.push_back(this->contours[i]); } drawContours(img_contours, this->contours, -1, // draw all contours cv::Scalar(255,0,0), // in blue 1); // with a thickness of 1 drawContours(img_contours, allowedContours, -1, // draw all contours cv::Scalar(0,255,0), // in green 1); // with a thickness of 1 return img_contours; } } openalpr_2.2.4.orig/src/openalpr/textdetection/textcontours.h000066400000000000000000000027251266464252400246250ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef TEXTCONTOURS_H #define TEXTCONTOURS_H #include #include "opencv2/imgproc/imgproc.hpp" namespace alpr { class TextContours { public: TextContours(); TextContours(cv::Mat threshold); virtual ~TextContours(); void load(cv::Mat threshold); int width; int height; std::vector goodIndices; std::vector > contours; std::vector hierarchy; unsigned int size(); int getGoodIndicesCount(); std::vector getIndicesCopy(); void setIndices(std::vector newIndices); cv::Mat drawDebugImage() const; cv::Mat drawDebugImage(cv::Mat baseImage) const; private: }; } #endif /* TEXTCONTOURS_H */ openalpr_2.2.4.orig/src/openalpr/textdetection/textline.cpp000066400000000000000000000114251266464252400242300ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "textline.h" using namespace cv; namespace alpr { TextLine::TextLine(std::vector textArea, std::vector linePolygon, cv::Size imgSize) { std::vector textAreaInts, linePolygonInts; for (unsigned int i = 0; i < textArea.size(); i++) textAreaInts.push_back(Point(round(textArea[i].x), round(textArea[i].y))); for (unsigned int i = 0; i < linePolygon.size(); i++) linePolygonInts.push_back(Point(round(linePolygon[i].x), round(linePolygon[i].y))); initialize(textAreaInts, linePolygonInts, imgSize); } TextLine::TextLine(std::vector textArea, std::vector linePolygon, cv::Size imgSize) { initialize(textArea, linePolygon, imgSize); } TextLine::~TextLine() { } void TextLine::initialize(std::vector textArea, std::vector linePolygon, cv::Size imgSize) { if (textArea.size() > 0) { if (this->textArea.size() > 0) this->textArea.clear(); if (this->linePolygon.size() > 0) this->linePolygon.clear(); for (unsigned int i = 0; i < textArea.size(); i++) this->textArea.push_back(textArea[i]); this->topLine = LineSegment(linePolygon[0].x, linePolygon[0].y, linePolygon[1].x, linePolygon[1].y); this->bottomLine = LineSegment(linePolygon[3].x, linePolygon[3].y, linePolygon[2].x, linePolygon[2].y); // Adjust the line polygon so that it always touches the edges // This is needed after applying perspective transforms, so just fix it here if (linePolygon[0].x != 0) { linePolygon[0].x = 0; linePolygon[0].y = topLine.getPointAt(linePolygon[0].x); } if (linePolygon[1].x != imgSize.width) { linePolygon[1].x = imgSize.width; linePolygon[1].y = topLine.getPointAt(linePolygon[1].x); } if (linePolygon[2].x != imgSize.width) { linePolygon[2].x = imgSize.width; linePolygon[2].y = bottomLine.getPointAt(linePolygon[2].x); } if (linePolygon[3].x != 0) { linePolygon[3].x = 0; linePolygon[3].y = bottomLine.getPointAt(linePolygon[3].x); } for (unsigned int i = 0; i < linePolygon.size(); i++) this->linePolygon.push_back(linePolygon[i]); this->charBoxTop = LineSegment(textArea[0].x, textArea[0].y, textArea[1].x, textArea[1].y); this->charBoxBottom = LineSegment(textArea[3].x, textArea[3].y, textArea[2].x, textArea[2].y); this->charBoxLeft = LineSegment(textArea[3].x, textArea[3].y, textArea[0].x, textArea[0].y); this->charBoxRight = LineSegment(textArea[2].x, textArea[2].y, textArea[1].x, textArea[1].y); // Calculate line height float x = ((float) linePolygon[1].x) / 2; Point midpoint = Point(x, bottomLine.getPointAt(x)); Point acrossFromMidpoint = topLine.closestPointOnSegmentTo(midpoint); this->lineHeight = distanceBetweenPoints(midpoint, acrossFromMidpoint); // Subtract a pixel since the height is a little overestimated by the bounding box this->lineHeight = this->lineHeight - 1; this->angle = (topLine.angle + bottomLine.angle) / 2; } } cv::Mat TextLine::drawDebugImage(cv::Mat baseImage) { cv::Mat debugImage(baseImage.size(), baseImage.type()); baseImage.copyTo(debugImage); cv::cvtColor(debugImage, debugImage, CV_GRAY2BGR); fillConvexPoly(debugImage, linePolygon.data(), linePolygon.size(), Scalar(0,0,165)); fillConvexPoly(debugImage, textArea.data(), textArea.size(), Scalar(125,255,0)); line(debugImage, topLine.p1, topLine.p2, Scalar(255,0,0), 1); line(debugImage, bottomLine.p1, bottomLine.p2, Scalar(255,0,0), 1); line(debugImage, charBoxTop.p1, charBoxTop.p2, Scalar(0,125,125), 1); line(debugImage, charBoxLeft.p1, charBoxLeft.p2, Scalar(0,125,125), 1); line(debugImage, charBoxRight.p1, charBoxRight.p2, Scalar(0,125,125), 1); line(debugImage, charBoxBottom.p1, charBoxBottom.p2, Scalar(0,125,125), 1); return debugImage; } }openalpr_2.2.4.orig/src/openalpr/textdetection/textline.h000066400000000000000000000032031266464252400236700ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_TEXTLINE_H #define OPENALPR_TEXTLINE_H #include "utility.h" #include "opencv2/imgproc/imgproc.hpp" namespace alpr { class TextLine { public: TextLine(std::vector textArea, std::vector linePolygon, cv::Size imgSize); TextLine(std::vector textArea, std::vector linePolygon, cv::Size imgSize); virtual ~TextLine(); std::vector linePolygon; std::vector textArea; LineSegment topLine; LineSegment bottomLine; LineSegment charBoxTop; LineSegment charBoxBottom; LineSegment charBoxLeft; LineSegment charBoxRight; float lineHeight; float angle; cv::Mat drawDebugImage(cv::Mat baseImage); private: void initialize(std::vector textArea, std::vector linePolygon, cv::Size imgSize); }; } #endif /* OPENALPR_TEXTLINE_H */ openalpr_2.2.4.orig/src/openalpr/transformation.cpp000066400000000000000000000114621266464252400225600ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "transformation.h" using namespace std; using namespace cv; namespace alpr { Transformation::Transformation(Mat bigImage, Mat smallImage, Rect regionInBigImage) { this->bigImage = bigImage; this->smallImage = smallImage; this->regionInBigImage = regionInBigImage; } Transformation::~Transformation() { } // Re-maps the coordinates from the smallImage to the coordinate space of the bigImage. vector Transformation::transformSmallPointsToBigImage(vector points) { vector floatPoints; for (unsigned int i = 0; i < points.size(); i++) floatPoints.push_back(points[i]); return transformSmallPointsToBigImage(floatPoints); } // Re-maps the coordinates from the smallImage to the coordinate space of the bigImage. vector Transformation::transformSmallPointsToBigImage(vector points) { vector bigPoints; for (unsigned int i = 0; i < points.size(); i++) { float bigX = (points[i].x * ((float) regionInBigImage.width / smallImage.cols)); float bigY = (points[i].y * ((float) regionInBigImage.height / smallImage.rows)); bigX = bigX + regionInBigImage.x; bigY = bigY + regionInBigImage.y; bigPoints.push_back(Point2f(bigX, bigY)); } return bigPoints; } Mat Transformation::getTransformationMatrix(vector corners, Size outputImageSize) { // Corners of the destination image vector quad_pts; quad_pts.push_back(Point2f(0, 0)); quad_pts.push_back(Point2f(outputImageSize.width, 0)); quad_pts.push_back(Point2f(outputImageSize.width, outputImageSize.height)); quad_pts.push_back(Point2f(0, outputImageSize.height)); return getTransformationMatrix(corners, quad_pts); } Mat Transformation::getTransformationMatrix(vector corners, vector outputCorners) { // Get transformation matrix Mat transmtx = getPerspectiveTransform(corners, outputCorners); return transmtx; } Mat Transformation::crop(Size outputImageSize, Mat transformationMatrix) { Mat deskewed(outputImageSize, this->bigImage.type()); // Apply perspective transformation to the image warpPerspective(this->bigImage, deskewed, transformationMatrix, deskewed.size(), INTER_CUBIC); return deskewed; } vector Transformation::remapSmallPointstoCrop(vector smallPoints, cv::Mat transformationMatrix) { vector floatPoints; for (unsigned int i = 0; i < smallPoints.size(); i++) floatPoints.push_back(smallPoints[i]); return remapSmallPointstoCrop(floatPoints, transformationMatrix); } vector Transformation::remapSmallPointstoCrop(vector smallPoints, cv::Mat transformationMatrix) { vector remappedPoints; perspectiveTransform(smallPoints, remappedPoints, transformationMatrix); return remappedPoints; } Size Transformation::getCropSize(vector areaCorners, Size targetSize) { // Figure out the approximate width/height of the license plate region, so we can maintain the aspect ratio. LineSegment leftEdge(round(areaCorners[3].x), round(areaCorners[3].y), round(areaCorners[0].x), round(areaCorners[0].y)); LineSegment rightEdge(round(areaCorners[2].x), round(areaCorners[2].y), round(areaCorners[1].x), round(areaCorners[1].y)); LineSegment topEdge(round(areaCorners[0].x), round(areaCorners[0].y), round(areaCorners[1].x), round(areaCorners[1].y)); LineSegment bottomEdge(round(areaCorners[3].x), round(areaCorners[3].y), round(areaCorners[2].x), round(areaCorners[2].y)); float w = distanceBetweenPoints(leftEdge.midpoint(), rightEdge.midpoint()); float h = distanceBetweenPoints(bottomEdge.midpoint(), topEdge.midpoint()); if (w <= 0 || h <= 0) return Size(0,0); float aspect = w/h; int width = targetSize.width; int height = round(((float) width) / aspect); if (height > targetSize.height) { height = targetSize.height; width = round(((float) height) * aspect); } return Size(width, height); } }openalpr_2.2.4.orig/src/openalpr/transformation.h000066400000000000000000000037231266464252400222260ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_TRANSFORMATION_H #define OPENALPR_TRANSFORMATION_H #include "opencv2/imgproc/imgproc.hpp" #include "utility.h" namespace alpr { class Transformation { public: Transformation(cv::Mat bigImage, cv::Mat smallImage, cv::Rect regionInBigImage); virtual ~Transformation(); std::vector transformSmallPointsToBigImage(std::vector points); std::vector transformSmallPointsToBigImage(std::vector points); cv::Mat getTransformationMatrix(std::vector corners, cv::Size outputImageSize); cv::Mat getTransformationMatrix(std::vector corners, std::vector outputCorners); cv::Mat crop(cv::Size outputImageSize, cv::Mat transformationMatrix); std::vector remapSmallPointstoCrop(std::vector smallPoints, cv::Mat transformationMatrix); std::vector remapSmallPointstoCrop(std::vector smallPoints, cv::Mat transformationMatrix); cv::Size getCropSize(std::vector areaCorners, cv::Size targetSize); private: cv::Mat bigImage; cv::Mat smallImage; cv::Rect regionInBigImage; }; } #endif /* OPENALPR_TRANSFORMATION_H */ openalpr_2.2.4.orig/src/openalpr/utility.cpp000066400000000000000000000411661266464252400212210ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include "utility.h" using namespace cv; using namespace std; namespace alpr { Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY) { Rect expandedRegion = Rect(original); float halfX = round((float) expandXPixels / 2.0); float halfY = round((float) expandYPixels / 2.0); expandedRegion.x = expandedRegion.x - halfX; expandedRegion.width = expandedRegion.width + expandXPixels; expandedRegion.y = expandedRegion.y - halfY; expandedRegion.height = expandedRegion.height + expandYPixels; expandedRegion.x = std::min(std::max(expandedRegion.x, 0), maxX); expandedRegion.y = std::min(std::max(expandedRegion.y, 0), maxY); if (expandedRegion.x + expandedRegion.width > maxX) expandedRegion.width = maxX - expandedRegion.x; if (expandedRegion.y + expandedRegion.height > maxY) expandedRegion.height = maxY - expandedRegion.y; return expandedRegion; } Mat drawImageDashboard(vector images, int imageType, unsigned int numColumns) { unsigned int numRows = ceil((float) images.size() / (float) numColumns); Mat dashboard(Size(images[0].cols * numColumns, images[0].rows * numRows), imageType); for (unsigned int i = 0; i < numColumns * numRows; i++) { if (i < images.size()) images[i].copyTo(dashboard(Rect((i%numColumns) * images[i].cols, floor((float) i/numColumns) * images[i].rows, images[i].cols, images[i].rows))); else { Mat black = Mat::zeros(images[0].size(), imageType); black.copyTo(dashboard(Rect((i%numColumns) * images[0].cols, floor((float) i/numColumns) * images[0].rows, images[0].cols, images[0].rows))); } } return dashboard; } Mat addLabel(Mat input, string label) { const int border_size = 1; const Scalar border_color(0,0,255); const int extraHeight = 20; const Scalar bg(222,222,222); const Scalar fg(0,0,0); Rect destinationRect(border_size, extraHeight, input.cols, input.rows); Mat newImage(Size(input.cols + (border_size), input.rows + extraHeight + (border_size )), input.type()); input.copyTo(newImage(destinationRect)); cout << " Adding label " << label << endl; if (input.type() == CV_8U) cvtColor(newImage, newImage, CV_GRAY2BGR); rectangle(newImage, Point(0,0), Point(input.cols, extraHeight), bg, CV_FILLED); putText(newImage, label, Point(5, extraHeight - 5), CV_FONT_HERSHEY_PLAIN , 0.7, fg); rectangle(newImage, Point(0,0), Point(newImage.cols - 1, newImage.rows -1), border_color, border_size); return newImage; } void drawAndWait(cv::Mat frame) { drawAndWait(&frame); } void drawAndWait(cv::Mat* frame) { cv::imshow("Temp Window", *frame); while (cv::waitKey(50) == -1) { // loop } cv::destroyWindow("Temp Window"); } void displayImage(Config* config, string windowName, cv::Mat frame) { if (config->debugShowImages) { imshow(windowName, frame); cv::waitKey(5); } } vector produceThresholds(const Mat img_gray, Config* config) { const int THRESHOLD_COUNT = 3; //Mat img_equalized = equalizeBrightness(img_gray); timespec startTime; getTimeMonotonic(&startTime); vector thresholds; for (int i = 0; i < THRESHOLD_COUNT; i++) thresholds.push_back(Mat(img_gray.size(), CV_8U)); int i = 0; // Adaptive //adaptiveThreshold(img_gray, thresholds[i++], 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV , 7, 3); //adaptiveThreshold(img_gray, thresholds[i++], 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV , 13, 3); //adaptiveThreshold(img_gray, thresholds[i++], 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV , 17, 3); // Wolf int k = 0, win=18; //NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35)); //bitwise_not(thresholds[i-1], thresholds[i-1]); NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35)); bitwise_not(thresholds[i-1], thresholds[i-1]); k = 1; win = 22; NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35)); bitwise_not(thresholds[i-1], thresholds[i-1]); //NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35)); //bitwise_not(thresholds[i-1], thresholds[i-1]); // Sauvola k = 1; NiblackSauvolaWolfJolion (img_gray, thresholds[i++], SAUVOLA, 12, 12, 0.18 * k); bitwise_not(thresholds[i-1], thresholds[i-1]); //k=2; //NiblackSauvolaWolfJolion (img_gray, thresholds[i++], SAUVOLA, 12, 12, 0.18 * k); //bitwise_not(thresholds[i-1], thresholds[i-1]); if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << " -- Produce Threshold Time: " << diffclock(startTime, endTime) << "ms." << endl; } return thresholds; //threshold(img_equalized, img_threshold, 100, 255, THRESH_BINARY); } double median(int array[], int arraySize) { if (arraySize == 0) { //std::cerr << "Median calculation requested on empty array" << endl; return 0; } std::sort(&array[0], &array[arraySize]); return arraySize % 2 ? array[arraySize / 2] : (array[arraySize / 2 - 1] + array[arraySize / 2]) / 2; } Mat equalizeBrightness(Mat img) { // Divide the image by its morphologically closed counterpart Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(19,19)); Mat closed; morphologyEx(img, closed, MORPH_CLOSE, kernel); img.convertTo(img, CV_32FC1); // divide requires floating-point divide(img, closed, img, 1, CV_32FC1); normalize(img, img, 0, 255, NORM_MINMAX); img.convertTo(img, CV_8U); // convert back to unsigned int return img; } void drawRotatedRect(Mat* img, RotatedRect rect, Scalar color, int thickness) { Point2f rect_points[4]; rect.points( rect_points ); for( int j = 0; j < 4; j++ ) line( *img, rect_points[j], rect_points[(j+1)%4], color, thickness, 8 ); } void fillMask(Mat img, const Mat mask, Scalar color) { for (int row = 0; row < img.rows; row++) { for (int col = 0; col < img.cols; col++) { int m = (int) mask.at(row, col); if (m) { for (int z = 0; z < 3; z++) { int prevVal = img.at(row, col)[z]; img.at(row, col)[z] = ((int) color[z]) | prevVal; } } } } } void drawX(Mat img, Rect rect, Scalar color, int thickness) { Point tl(rect.x, rect.y); Point tr(rect.x + rect.width, rect.y); Point bl(rect.x, rect.y + rect.height); Point br(rect.x + rect.width, rect.y + rect.height); line(img, tl, br, color, thickness); line(img, bl, tr, color, thickness); } double distanceBetweenPoints(Point p1, Point p2) { float asquared = (p2.x - p1.x)*(p2.x - p1.x); float bsquared = (p2.y - p1.y)*(p2.y - p1.y); return sqrt(asquared + bsquared); } float angleBetweenPoints(Point p1, Point p2) { int deltaY = p2.y - p1.y; int deltaX = p2.x - p1.x; return atan2((float) deltaY, (float) deltaX) * (180 / CV_PI); } Size getSizeMaintainingAspect(Mat inputImg, int maxWidth, int maxHeight) { float aspect = ((float) inputImg.cols) / ((float) inputImg.rows); if (maxWidth / aspect > maxHeight) { return Size(maxHeight * aspect, maxHeight); } else { return Size(maxWidth, maxWidth / aspect); } } // Compares two strings and computes the edit distance between them // http://en.wikipedia.org/wiki/Levenshtein_distance // max is the cutoff (i.e., max distance) where we stop trying to find distance int levenshteinDistance (const std::string &s1, const std::string &s2, int max) { const char* word1 = s1.c_str(); int len1 = s1.length(); const char* word2 = s2.c_str(); int len2 = s2.length(); max--; //int matrix[2][len2 + 1]; std::vector > matrix; for (unsigned int i = 0; i < 2; i++) { std::vector top_elem; matrix.push_back(top_elem); for (unsigned int j = 0; j < len2 + 1; j++) matrix[i].push_back(0); } int i; int j; /* Initialize the 0 row of "matrix". 0 1 2 3 */ for (j = 0; j <= len2; j++) { matrix[0][j] = j; } /* Loop over column. */ for (i = 1; i <= len1; i++) { char c1; /* The first value to consider of the ith column. */ int min_j; /* The last value to consider of the ith column. */ int max_j; /* The smallest value of the matrix in the ith column. */ int col_min; /* The next column of the matrix to fill in. */ int next; /* The previously-filled-in column of the matrix. */ int prev; c1 = word1[i-1]; min_j = 1; if (i > max) { min_j = i - max; } max_j = len2; if (len2 > max + i) { max_j = max + i; } col_min = INT_MAX; next = i % 2; if (next == 1) { prev = 0; } else { prev = 1; } matrix[next][0] = i; /* Loop over rows. */ for (j = 1; j <= len2; j++) { if (j < min_j || j > max_j) { /* Put a large value in there. */ matrix[next][j] = max + 1; } else { char c2; c2 = word2[j-1]; if (c1 == c2) { /* The character at position i in word1 is the same as the character at position j in word2. */ matrix[next][j] = matrix[prev][j-1]; } else { /* The character at position i in word1 is not the same as the character at position j in word2, so work out what the minimum cost for getting to cell i, j is. */ int del; int insert; int substitute; int minimum; del = matrix[prev][j] + 1; insert = matrix[next][j-1] + 1; substitute = matrix[prev][j-1] + 1; minimum = del; if (insert < minimum) { minimum = insert; } if (substitute < minimum) { minimum = substitute; } matrix[next][j] = minimum; } } /* Find the minimum value in the ith column. */ if (matrix[next][j] < col_min) { col_min = matrix[next][j]; } } if (col_min > max) { /* All the elements of the ith column are greater than the maximum, so no match less than or equal to max can be found by looking at succeeding columns. */ return max + 1; } } int returnval = matrix[len1 % 2][len2]; if (returnval > max + 1) returnval = max + 1; return returnval; } LineSegment::LineSegment() { init(0, 0, 0, 0); } LineSegment::LineSegment(Point p1, Point p2) { init(p1.x, p1.y, p2.x, p2.y); } LineSegment::LineSegment(int x1, int y1, int x2, int y2) { init(x1, y1, x2, y2); } void LineSegment::init(int x1, int y1, int x2, int y2) { this->p1 = Point(x1, y1); this->p2 = Point(x2, y2); if (p2.x - p1.x == 0) this->slope = 0.00000000001; else this->slope = (float) (p2.y - p1.y) / (float) (p2.x - p1.x); this->length = distanceBetweenPoints(p1, p2); this->angle = angleBetweenPoints(p1, p2); } bool LineSegment::isPointBelowLine( Point tp ) { return ((p2.x - p1.x)*(tp.y - p1.y) - (p2.y - p1.y)*(tp.x - p1.x)) > 0; } float LineSegment::getPointAt(float x) { return slope * (x - p2.x) + p2.y; } float LineSegment::getXPointAt(float y) { float y_intercept = getPointAt(0); return (y - y_intercept) / slope; } Point LineSegment::closestPointOnSegmentTo(Point p) { float top = (p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y)*(p2.y - p1.y); float bottom = distanceBetweenPoints(p2, p1); bottom = bottom * bottom; float u = top / bottom; float x = p1.x + u * (p2.x - p1.x); float y = p1.y + u * (p2.y - p1.y); return Point(x, y); } Point LineSegment::intersection(LineSegment line) { float c1, c2; float intersection_X = -1, intersection_Y= -1; c1 = p1.y - slope * p1.x; // which is same as y2 - slope * x2 c2 = line.p2.y - line.slope * line.p2.x; // which is same as y2 - slope * x2 if( (slope - line.slope) == 0) { //std::cout << "No Intersection between the lines" << endl; } else if (p1.x == p2.x) { // Line1 is vertical return Point(p1.x, line.getPointAt(p1.x)); } else if (line.p1.x == line.p2.x) { // Line2 is vertical return Point(line.p1.x, getPointAt(line.p1.x)); } else { intersection_X = (c2 - c1) / (slope - line.slope); intersection_Y = slope * intersection_X + c1; } return Point(intersection_X, intersection_Y); } Point LineSegment::midpoint() { // Handle the case where the line is vertical if (p1.x == p2.x) { float ydiff = p2.y-p1.y; float y = p1.y + (ydiff/2); return Point(p1.x, y); } float diff = p2.x - p1.x; float midX = ((float) p1.x) + (diff / 2); int midY = getPointAt(midX); return Point(midX, midY); } LineSegment LineSegment::getParallelLine(float distance) { float diff_x = p2.x - p1.x; float diff_y = p2.y - p1.y; float angle = atan2( diff_x, diff_y); float dist_x = distance * cos(angle); float dist_y = -distance * sin(angle); int offsetX = (int)round(dist_x); int offsetY = (int)round(dist_y); LineSegment result(p1.x + offsetX, p1.y + offsetY, p2.x + offsetX, p2.y + offsetY); return result; } // Given a contour and a mask, this function determines what percentage of the contour (area) // is inside the masked area. float getContourAreaPercentInsideMask(cv::Mat mask, std::vector > contours, std::vector hierarchy, int contourIndex) { Mat innerArea = Mat::zeros(mask.size(), CV_8U); drawContours(innerArea, contours, contourIndex, // draw this contour cv::Scalar(255,255,255), // in CV_FILLED, 8, hierarchy, 2 ); int startingPixels = cv::countNonZero(innerArea); //drawAndWait(&innerArea); bitwise_and(innerArea, mask, innerArea); int endingPixels = cv::countNonZero(innerArea); //drawAndWait(&innerArea); return ((float) endingPixels) / ((float) startingPixels); } std::string toString(int value) { stringstream ss; ss << value; return ss.str(); } std::string toString(long value) { stringstream ss; ss << value; return ss.str(); } std::string toString(unsigned int value) { return toString((int) value); } std::string toString(float value) { stringstream ss; ss << value; return ss.str(); } std::string toString(double value) { stringstream ss; ss << value; return ss.str(); } // trim from start std::string <rim(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); return s; } // trim from end std::string &rtrim(std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); return s; } // trim from both ends std::string &trim(std::string &s) { return ltrim(rtrim(s)); } }openalpr_2.2.4.orig/src/openalpr/utility.h000066400000000000000000000067411266464252400206660ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_UTILITY_H #define OPENALPR_UTILITY_H #include #include #include #include "constants.h" #include "support/timing.h" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/core/core.hpp" #include "binarize_wolf.h" #include #include "config.h" namespace alpr { class LineSegment { public: cv::Point p1, p2; float slope; float length; float angle; // LineSegment(Point point1, Point point2); LineSegment(); LineSegment(int x1, int y1, int x2, int y2); LineSegment(cv::Point p1, cv::Point p2); void init(int x1, int y1, int x2, int y2); bool isPointBelowLine(cv::Point tp); float getPointAt(float x); float getXPointAt(float y); cv::Point closestPointOnSegmentTo(cv::Point p); cv::Point intersection(LineSegment line); LineSegment getParallelLine(float distance); cv::Point midpoint(); inline std::string str() { std::stringstream ss; ss << "(" << p1.x << ", " << p1.y << ") : (" << p2.x << ", " << p2.y << ")"; return ss.str() ; } }; double median(int array[], int arraySize); std::vector produceThresholds(const cv::Mat img_gray, Config* config); cv::Mat drawImageDashboard(std::vector images, int imageType, unsigned int numColumns); void displayImage(Config* config, std::string windowName, cv::Mat frame); void drawAndWait(cv::Mat frame); void drawAndWait(cv::Mat* frame); double distanceBetweenPoints(cv::Point p1, cv::Point p2); void drawRotatedRect(cv::Mat* img, cv::RotatedRect rect, cv::Scalar color, int thickness); void drawX(cv::Mat img, cv::Rect rect, cv::Scalar color, int thickness); void fillMask(cv::Mat img, const cv::Mat mask, cv::Scalar color); float angleBetweenPoints(cv::Point p1, cv::Point p2); cv::Size getSizeMaintainingAspect(cv::Mat inputImg, int maxWidth, int maxHeight); float getContourAreaPercentInsideMask(cv::Mat mask, std::vector > contours, std::vector hierarchy, int contourIndex); cv::Mat equalizeBrightness(cv::Mat img); cv::Rect expandRect(cv::Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY); cv::Mat addLabel(cv::Mat input, std::string label); int levenshteinDistance (const std::string &s1, const std::string &s2, int max); std::string toString(int value); std::string toString(long value); std::string toString(unsigned int value); std::string toString(float value); std::string toString(double value); std::string <rim(std::string &s); std::string &rtrim(std::string &s); std::string &trim(std::string &s); } #endif // OPENALPR_UTILITY_H openalpr_2.2.4.orig/src/plate_push.py000066400000000000000000000002701266464252400176770ustar00rootroot00000000000000#!/usr/bin/python import beanstalkc beanstalk = beanstalkc.Connection(host='localhost', port=11300) beanstalk.watch('alpr') job = beanstalk.reserve() print job.body job.delete() openalpr_2.2.4.orig/src/statedetection/000077500000000000000000000000001266464252400202015ustar00rootroot00000000000000openalpr_2.2.4.orig/src/statedetection/CMakeLists.txt000066400000000000000000000011531266464252400227410ustar00rootroot00000000000000 set(statedetector_source_files state_detector.cpp featurematcher.cpp line_segment.cpp state_detector_impl.cpp ) if (WIN32 OR IOS) add_library(statedetection STATIC ${statedetector_source_files} ) ELSE() add_library(statedetection SHARED ${statedetector_source_files} ) ENDIF() set_target_properties(statedetection PROPERTIES SOVERSION ${OPENALPR_MAJOR_VERSION}) TARGET_LINK_LIBRARIES(statedetection ${OpenCV_LIBS} support ) install (FILES state_detector.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include) install (TARGETS statedetection DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) openalpr_2.2.4.orig/src/statedetection/featurematcher.cpp000066400000000000000000000315011266464252400237040ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "featurematcher.h" using namespace cv; using namespace std; namespace alpr { //const int DEFAULT_QUERY_FEATURES = 305; //const int DEFAULT_TRAINING_FEATURES = 305; const float MAX_DISTANCE_TO_MATCH = 100.0f; FeatureMatcher::FeatureMatcher() { //this->descriptorMatcher = DescriptorMatcher::create( "BruteForce-HammingLUT" ); this->descriptorMatcher = new BFMatcher(NORM_HAMMING, false); //this->descriptorMatcher = DescriptorMatcher::create( "FlannBased" ); #if OPENCV_MAJOR_VERSION == 2 this->detector = new FastFeatureDetector(10, true); this->extractor = new BRISK(10, 1, 0.9); #else // OpenCV 3 this->detector = FastFeatureDetector::create(); this->extractor = BRISK::create(10, 1, 0.9); #endif } FeatureMatcher::~FeatureMatcher() { for (unsigned int i = 0; i < trainingImgKeypoints.size(); i++) trainingImgKeypoints[i].clear(); trainingImgKeypoints.clear(); descriptorMatcher.release(); detector.release(); extractor.release(); } bool FeatureMatcher::isLoaded() { if( detector.empty() || extractor.empty() || descriptorMatcher.empty() ) { return false; } return true; } int FeatureMatcher::numTrainingElements() { return billMapping.size(); } void FeatureMatcher::surfStyleMatching( const Mat& queryDescriptors, vector queryKeypoints, vector& matches12 ) { vector > matchesKnn; this->descriptorMatcher->radiusMatch(queryDescriptors, matchesKnn, MAX_DISTANCE_TO_MATCH); vector tempMatches; _surfStyleMatching(queryDescriptors, matchesKnn, tempMatches); crisscrossFiltering(queryKeypoints, tempMatches, matches12); } void FeatureMatcher::_surfStyleMatching(const Mat& queryDescriptors, vector > matchesKnn, vector& matches12) { //objectMatches.clear(); //objectMatches.resize(objectIds.size()); //cout << "starting matcher" << matchesKnn.size() << endl; for (int descInd = 0; descInd < queryDescriptors.rows; descInd++) { //const std::vector & matches = matchesKnn[descInd]; //cout << "two: " << descInd << ":" << matches.size() << endl; // Check to make sure we have 2 matches. I think this is always the case, but it doesn't hurt to be sure if (matchesKnn[descInd].size() > 1) { // Next throw out matches with a crappy score // Ignore... already handled by the radiusMatch //if (matchesKnn[descInd][0].distance < MAX_DISTANCE_TO_MATCH) //{ float ratioThreshold = 0.75; // Check if both matches came from the same image. If they both came from the same image, score them slightly less harshly if (matchesKnn[descInd][0].imgIdx == matchesKnn[descInd][1].imgIdx) { ratioThreshold = 0.85; } if ((matchesKnn[descInd][0].distance / matchesKnn[descInd][1].distance) < ratioThreshold) { bool already_exists = false; // Quickly run through the matches we've already added and make sure it's not a duplicate... for (unsigned int q = 0; q < matches12.size(); q++) { if (matchesKnn[descInd][0].queryIdx == matches12[q].queryIdx) { already_exists = true; break; } else if ((matchesKnn[descInd][0].trainIdx == matches12[q].trainIdx) && (matchesKnn[descInd][0].imgIdx == matches12[q].imgIdx)) { already_exists = true; break; } } // Good match. if (already_exists == false) matches12.push_back(matchesKnn[descInd][0]); } //} } else if (matchesKnn[descInd].size() == 1) { // Only match? Does this ever happen? matches12.push_back(matchesKnn[descInd][0]); } // In the ratio test, we will compare the quality of a match with the next match that is not from the same object: // we can accept several matches with similar scores as long as they are for the same object. Those should not be // part of the model anyway as they are not discriminative enough //for (unsigned int first_index = 0; first_index < matches.size(); ++first_index) //{ //matches12.push_back(match); //} } } // Compares the matches keypoints for parallel lines. Removes matches that are criss-crossing too much // We assume that license plates won't be upside-down or backwards. So expect lines to be closely parallel void FeatureMatcher::crisscrossFiltering(const vector queryKeypoints, const vector inputMatches, vector &outputMatches) { Rect crissCrossAreaVertical(0, 0, w, h * 2); Rect crissCrossAreaHorizontal(0, 0, w * 2, h); for (unsigned int i = 0; i < billMapping.size(); i++) { vector matchesForOnePlate; for (unsigned int j = 0; j < inputMatches.size(); j++) { if (inputMatches[j].imgIdx == (int) i) matchesForOnePlate.push_back(inputMatches[j]); } // For each plate, compare the lines for the keypoints (training image and query image) // go through each line between keypoints and filter out matches that are criss-crossing vector vlines; vector hlines; vector matchIdx; for (unsigned int j = 0; j < matchesForOnePlate.size(); j++) { KeyPoint tkp = trainingImgKeypoints[i][matchesForOnePlate[j].trainIdx]; KeyPoint qkp = queryKeypoints[matchesForOnePlate[j].queryIdx]; vlines.push_back(LineSegment(tkp.pt.x, tkp.pt.y + h, qkp.pt.x, qkp.pt.y)); hlines.push_back(LineSegment(tkp.pt.x, tkp.pt.y, qkp.pt.x + w, qkp.pt.y)); matchIdx.push_back(j); } // Iterate through each line (n^2) removing the one with the most criss-crosses until there are none left. int mostIntersections = 1; while (mostIntersections > 0 && vlines.size() > 0) { int mostIntersectionsIndex = -1; mostIntersections = 0; for (unsigned int j = 0; j < vlines.size(); j++) { int intrCount = 0; for (unsigned int q = 0; q < vlines.size(); q++) { Point vintr = vlines[j].intersection(vlines[q]); Point hintr = hlines[j].intersection(hlines[q]); float vangleDiff = abs(vlines[j].angle - vlines[q].angle); float hangleDiff = abs(hlines[j].angle - hlines[q].angle); if (vintr.inside(crissCrossAreaVertical) && vangleDiff > 10) { intrCount++; } else if (hintr.inside(crissCrossAreaHorizontal) && hangleDiff > 10) { intrCount++; } } if (intrCount > mostIntersections) { mostIntersections = intrCount; mostIntersectionsIndex = j; } } if (mostIntersectionsIndex >= 0) { // if (this->config->debugStateId) // cout << "Filtered intersection! " << billMapping[i] << endl; vlines.erase(vlines.begin() + mostIntersectionsIndex); hlines.erase(hlines.begin() + mostIntersectionsIndex); matchIdx.erase(matchIdx.begin() + mostIntersectionsIndex); } } // Push the non-crisscrosses back on the list for (unsigned int j = 0; j < matchIdx.size(); j++) { outputMatches.push_back(matchesForOnePlate[matchIdx[j]]); } } } // Returns true if successful, false otherwise bool FeatureMatcher::loadRecognitionSet(string directory, string country) { std::ostringstream out; out << directory << "/keypoints/" << country << "/"; string country_dir = out.str(); if (DirectoryExists(country_dir.c_str())) { vector trainImages; vector plateFiles = getFilesInDir(country_dir.c_str()); for (unsigned int i = 0; i < plateFiles.size(); i++) { if (hasEnding(plateFiles[i], ".jpg") == false) continue; string fullpath = country_dir + plateFiles[i]; Mat img = imread( fullpath ); // convert to gray and resize to the size of the templates cvtColor(img, img, CV_BGR2GRAY); if( img.empty() ) { cout << "Can not read images" << endl; return -1; } Mat descriptors; vector keypoints; detector->detect( img, keypoints ); extractor->compute(img, keypoints, descriptors); if (descriptors.cols > 0) { billMapping.push_back(plateFiles[i].substr(0, 2)); trainImages.push_back(descriptors); trainingImgKeypoints.push_back(keypoints); } } this->descriptorMatcher->add(trainImages); this->descriptorMatcher->train(); return true; } return false; } RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnImage, Mat* outputImage, bool debug_on, vector debug_matches_array ) { RecognitionResult result; this->w = queryImg.cols; this->h = queryImg.rows; result.haswinner = false; result.confidence = 0; Mat queryDescriptors; vector queryKeypoints; detector->detect( queryImg, queryKeypoints ); extractor->compute(queryImg, queryKeypoints, queryDescriptors); if (queryKeypoints.size() <= 5) { // Cut it loose if there's less than 5 keypoints... nothing would ever match anyway and it could crash the matcher. if (drawOnImage) { drawKeypoints( queryImg, queryKeypoints, *outputImage, CV_RGB(0, 255, 0), DrawMatchesFlags::DEFAULT ); } return result; } vector filteredMatches; surfStyleMatching( queryDescriptors, queryKeypoints, filteredMatches ); // Create and initialize the counts to 0 std::vector bill_match_counts( billMapping.size() ); for (unsigned int i = 0; i < billMapping.size(); i++) { bill_match_counts[i] = 0; } for (unsigned int i = 0; i < filteredMatches.size(); i++) { bill_match_counts[filteredMatches[i].imgIdx]++; //if (filteredMatches[i].imgIdx } float max_count = 0; // represented as a percent (0 to 100) int secondmost_count = 0; int maxcount_index = -1; for (unsigned int i = 0; i < billMapping.size(); i++) { if (bill_match_counts[i] > max_count && bill_match_counts[i] >= 4) { secondmost_count = max_count; if (secondmost_count <= 2) // A value of 1 or 2 is effectively 0 secondmost_count = 0; max_count = bill_match_counts[i]; maxcount_index = i; } } float score = ((max_count - secondmost_count - 3) / 10) * 100; if (score < 0) score = 0; else if (score > 100) score = 100; if (score > 0) { result.haswinner = true; result.winner = billMapping[maxcount_index]; result.confidence = score; if (drawOnImage) { vector positiveMatches; for (unsigned int i = 0; i < filteredMatches.size(); i++) { if (filteredMatches[i].imgIdx == maxcount_index) { positiveMatches.push_back( queryKeypoints[filteredMatches[i].queryIdx] ); } } Mat tmpImg; drawKeypoints( queryImg, queryKeypoints, tmpImg, CV_RGB(185, 0, 0), DrawMatchesFlags::DEFAULT ); drawKeypoints( tmpImg, positiveMatches, *outputImage, CV_RGB(0, 255, 0), DrawMatchesFlags::DEFAULT ); if (result.haswinner) { std::ostringstream out; out << result.winner << " (" << result.confidence << "%)"; // we detected a bill, let the people know! //putText(*outputImage, out.str(), Point(15, 27), FONT_HERSHEY_DUPLEX, 1.1, CV_RGB(0, 0, 0), 2); } } } // if (this->config->debugStateId) // { // for (unsigned int i = 0; i < billMapping.size(); i++) // { // cout << billMapping[i] << " : " << bill_match_counts[i] << endl; // } // } return result; } }openalpr_2.2.4.orig/src/statedetection/featurematcher.h000066400000000000000000000046511266464252400233570ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_FEATUREMATCHER_H #define OPENALPR_FEATUREMATCHER_H #include "opencv2/calib3d/calib3d.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/features2d/features2d.hpp" #include "opencv2/video/tracking.hpp" #include "opencv2/highgui/highgui.hpp" #include "line_segment.h" #include "support/filesystem.h" namespace alpr { struct RecognitionResult { bool haswinner; std::string winner; int confidence; } ; class FeatureMatcher { public: FeatureMatcher(); virtual ~FeatureMatcher(); RecognitionResult recognize( const cv::Mat& queryImg, bool drawOnImage, cv::Mat* outputImage, bool debug_on, std::vector debug_matches_array ); bool loadRecognitionSet(std::string runtime_dir, std::string country); bool isLoaded(); int numTrainingElements(); private: cv::Ptr descriptorMatcher; cv::Ptr detector; cv::Ptr extractor; std::vector > trainingImgKeypoints; void _surfStyleMatching(const cv::Mat& queryDescriptors, std::vector > matchesKnn, std::vector& matches12); void crisscrossFiltering(const std::vector queryKeypoints, const std::vector inputMatches, std::vector &outputMatches); std::vector billMapping; void surfStyleMatching( const cv::Mat& queryDescriptors, std::vector queryKeypoints, std::vector& matches12 ); int w; int h; }; } #endif // OPENALPR_FEATUREMATCHER_H openalpr_2.2.4.orig/src/statedetection/line_segment.cpp000066400000000000000000000073741266464252400233710ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "line_segment.h" using namespace cv; LineSegment::LineSegment() { init(0, 0, 0, 0); } LineSegment::LineSegment(Point p1, Point p2) { init(p1.x, p1.y, p2.x, p2.y); } LineSegment::LineSegment(int x1, int y1, int x2, int y2) { init(x1, y1, x2, y2); } void LineSegment::init(int x1, int y1, int x2, int y2) { this->p1 = Point(x1, y1); this->p2 = Point(x2, y2); if (p2.x - p1.x == 0) this->slope = 0.00000000001; else this->slope = (float) (p2.y - p1.y) / (float) (p2.x - p1.x); this->length = distanceBetweenPoints(p1, p2); this->angle = angleBetweenPoints(p1, p2); } bool LineSegment::isPointBelowLine( Point tp ) { return ((p2.x - p1.x)*(tp.y - p1.y) - (p2.y - p1.y)*(tp.x - p1.x)) > 0; } float LineSegment::getPointAt(float x) { return slope * (x - p2.x) + p2.y; } Point LineSegment::closestPointOnSegmentTo(Point p) { float top = (p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y)*(p2.y - p1.y); float bottom = distanceBetweenPoints(p2, p1); bottom = bottom * bottom; float u = top / bottom; float x = p1.x + u * (p2.x - p1.x); float y = p1.y + u * (p2.y - p1.y); return Point(x, y); } Point LineSegment::intersection(LineSegment line) { float c1, c2; float intersection_X = -1, intersection_Y= -1; c1 = p1.y - slope * p1.x; // which is same as y2 - slope * x2 c2 = line.p2.y - line.slope * line.p2.x; // which is same as y2 - slope * x2 if( (slope - line.slope) == 0) { //std::cout << "No Intersection between the lines" << endl; } else if (p1.x == p2.x) { // Line1 is vertical return Point(p1.x, line.getPointAt(p1.x)); } else if (line.p1.x == line.p2.x) { // Line2 is vertical return Point(line.p1.x, getPointAt(line.p1.x)); } else { intersection_X = (c2 - c1) / (slope - line.slope); intersection_Y = slope * intersection_X + c1; } return Point(intersection_X, intersection_Y); } Point LineSegment::midpoint() { // Handle the case where the line is vertical if (p1.x == p2.x) { float ydiff = p2.y-p1.y; float y = p1.y + (ydiff/2); return Point(p1.x, y); } float diff = p2.x - p1.x; float midX = ((float) p1.x) + (diff / 2); int midY = getPointAt(midX); return Point(midX, midY); } LineSegment LineSegment::getParallelLine(float distance) { float diff_x = p2.x - p1.x; float diff_y = p2.y - p1.y; float angle = atan2( diff_x, diff_y); float dist_x = distance * cos(angle); float dist_y = -distance * sin(angle); int offsetX = (int)round(dist_x); int offsetY = (int)round(dist_y); LineSegment result(p1.x + offsetX, p1.y + offsetY, p2.x + offsetX, p2.y + offsetY); return result; } double LineSegment::distanceBetweenPoints(Point p1, Point p2) { float asquared = (p2.x - p1.x)*(p2.x - p1.x); float bsquared = (p2.y - p1.y)*(p2.y - p1.y); return sqrt(asquared + bsquared); } float LineSegment::angleBetweenPoints(Point p1, Point p2) { int deltaY = p2.y - p1.y; int deltaX = p2.x - p1.x; return atan2((float) deltaY, (float) deltaX) * (180 / CV_PI); }openalpr_2.2.4.orig/src/statedetection/line_segment.h000066400000000000000000000033051266464252400230240ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_LINE_SEGMENT_H #define OPENALPR_LINE_SEGMENT_H #include "opencv2/core/core.hpp" class LineSegment { public: cv::Point p1, p2; float slope; float length; float angle; // LineSegment(Point point1, Point point2); LineSegment(); LineSegment(int x1, int y1, int x2, int y2); LineSegment(cv::Point p1, cv::Point p2); void init(int x1, int y1, int x2, int y2); bool isPointBelowLine(cv::Point tp); float getPointAt(float x); cv::Point closestPointOnSegmentTo(cv::Point p); cv::Point intersection(LineSegment line); LineSegment getParallelLine(float distance); cv::Point midpoint(); inline std::string str() { std::stringstream ss; ss << "(" << p1.x << ", " << p1.y << ") : (" << p2.x << ", " << p2.y << ")"; return ss.str() ; } private: double distanceBetweenPoints(cv::Point p1, cv::Point p2); float angleBetweenPoints(cv::Point p1, cv::Point p2); }; #endif //OPENALPR_LINE_SEGMENT_H openalpr_2.2.4.orig/src/statedetection/state_detector.cpp000066400000000000000000000031241266464252400237160ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "state_detector.h" #include "state_detector_impl.h" using namespace std; namespace alpr { StateDetector::StateDetector(const std::string country, const std::string configFile, const std::string runtimeDir) { impl = new StateDetectorImpl(country, runtimeDir); } StateDetector::~StateDetector() { delete impl; } bool StateDetector::isLoaded() { return impl->isLoaded(); } void StateDetector::setTopN(int topN) { impl->setTopN(topN); } vector StateDetector::detect(vector imageBytes) { return impl->detect(imageBytes); } vector StateDetector::detect(unsigned char *pixelData, int bytesPerPixel, int imgWidth, int imgHeight) { return impl->detect(pixelData, bytesPerPixel, imgWidth, imgHeight); } } openalpr_2.2.4.orig/src/statedetection/state_detector.h000066400000000000000000000031341266464252400233640ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef OPENALPR_STATE_DETECTOR_H #define OPENALPR_STATE_DETECTOR_H #include #include namespace alpr { struct StateCandidate { std::string state_code; float confidence; }; class StateDetectorImpl; class StateDetector { public: StateDetector(const std::string country, const std::string configFile, const std::string runtimeDir); virtual ~StateDetector(); bool isLoaded(); // Maximum number of candidates to return void setTopN(int topN); // Given an image of a license plate, provide the likely state candidates std::vector detect(std::vector imageBytes); std::vector detect(unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight); StateDetectorImpl* impl; }; } #endif //OPENALPR_STATE_DETECTOR_H openalpr_2.2.4.orig/src/statedetection/state_detector_impl.cpp000066400000000000000000000047641266464252400247520ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "state_detector_impl.h" namespace alpr { StateDetectorImpl::StateDetectorImpl(const std::string country, const std::string runtimeDir) { if (featureMatcher.isLoaded() == false) { std::cout << "Can not create detector or descriptor extractor or descriptor matcher of given types" << std::endl; return; } featureMatcher.loadRecognitionSet(runtimeDir, country); } StateDetectorImpl::~StateDetectorImpl() { } bool StateDetectorImpl::isLoaded() { return false; } void StateDetectorImpl::setTopN(int topN) { } std::vector StateDetectorImpl::detect(std::vector imageBytes) { cv::Mat img = cv::imdecode(cv::Mat(imageBytes), 1); return this->detect(img); } std::vector StateDetectorImpl::detect(unsigned char *pixelData, int bytesPerPixel, int imgWidth, int imgHeight) { int arraySize = imgWidth * imgHeight * bytesPerPixel; cv::Mat imgData = cv::Mat(arraySize, 1, CV_8U, pixelData); cv::Mat img = imgData.reshape(bytesPerPixel, imgHeight); return this->detect(img); } std::vector StateDetectorImpl::detect(cv::Mat image) { std::vector results; cv::Mat debugImg(image.size(), image.type()); image.copyTo(debugImg); std::vector matchesArray(featureMatcher.numTrainingElements()); RecognitionResult result = featureMatcher.recognize(image, true, &debugImg, true, matchesArray ); if (result.haswinner == false) return results; StateCandidate top_candidate; top_candidate.confidence = result.confidence; top_candidate.state_code = result.winner; results.push_back(top_candidate); return results; } }openalpr_2.2.4.orig/src/statedetection/state_detector_impl.h000066400000000000000000000030451266464252400244060ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef SRC_STATE_DETECTOR_IMPL_H #define SRC_STATE_DETECTOR_IMPL_H #include "state_detector.h" #include "featurematcher.h" #include #include namespace alpr { class StateDetectorImpl { public: StateDetectorImpl(const std::string country, const std::string runtimeDir); virtual ~StateDetectorImpl(); bool isLoaded(); // Maximum number of candidates to return void setTopN(int topN); std::vector detect(std::vector imageBytes); std::vector detect(unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight); std::vector detect(cv::Mat image); FeatureMatcher featureMatcher; }; } #endif //SRC_STATE_DETECTOR_IMPL_H openalpr_2.2.4.orig/src/tclap/000077500000000000000000000000001266464252400162655ustar00rootroot00000000000000openalpr_2.2.4.orig/src/tclap/Arg.h000066400000000000000000000440671266464252400171620ustar00rootroot00000000000000// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- /****************************************************************************** * * file: Arg.h * * Copyright (c) 2003, Michael E. Smoot . * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno . * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_ARGUMENT_H #define TCLAP_ARGUMENT_H #ifdef HAVE_CONFIG_H #include #else #define HAVE_SSTREAM #endif #include #include #include #include #include #include #if defined(HAVE_SSTREAM) #include typedef std::istringstream istringstream; #elif defined(HAVE_STRSTREAM) #include typedef std::istrstream istringstream; #else #error "Need a stringstream (sstream or strstream) to compile!" #endif #include "ArgException.h" #include "Visitor.h" #include "CmdLineInterface.h" #include "ArgTraits.h" #include "StandardTraits.h" namespace TCLAP { /** * A virtual base class that defines the essential data for all arguments. * This class, or one of its existing children, must be subclassed to do * anything. */ class Arg { private: /** * Prevent accidental copying. */ Arg(const Arg& rhs); /** * Prevent accidental copying. */ Arg& operator=(const Arg& rhs); /** * Indicates whether the rest of the arguments should be ignored. */ static bool& ignoreRestRef() { static bool ign = false; return ign; } /** * The delimiter that separates an argument flag/name from the * value. */ static char& delimiterRef() { static char delim = ' '; return delim; } protected: /** * The single char flag used to identify the argument. * This value (preceded by a dash {-}), can be used to identify * an argument on the command line. The _flag can be blank, * in fact this is how unlabeled args work. Unlabeled args must * override appropriate functions to get correct handling. Note * that the _flag does NOT include the dash as part of the flag. */ std::string _flag; /** * A single work namd indentifying the argument. * This value (preceded by two dashed {--}) can also be used * to identify an argument on the command line. Note that the * _name does NOT include the two dashes as part of the _name. The * _name cannot be blank. */ std::string _name; /** * Description of the argument. */ std::string _description; /** * Indicating whether the argument is required. */ bool _required; /** * Label to be used in usage description. Normally set to * "required", but can be changed when necessary. */ std::string _requireLabel; /** * Indicates whether a value is required for the argument. * Note that the value may be required but the argument/value * combination may not be, as specified by _required. */ bool _valueRequired; /** * Indicates whether the argument has been set. * Indicates that a value on the command line has matched the * name/flag of this argument and the values have been set accordingly. */ bool _alreadySet; /** * A pointer to a vistitor object. * The visitor allows special handling to occur as soon as the * argument is matched. This defaults to NULL and should not * be used unless absolutely necessary. */ Visitor* _visitor; /** * Whether this argument can be ignored, if desired. */ bool _ignoreable; /** * Indicates that the arg was set as part of an XOR and not on the * command line. */ bool _xorSet; bool _acceptsMultipleValues; /** * Performs the special handling described by the Vistitor. */ void _checkWithVisitor() const; /** * Primary constructor. YOU (yes you) should NEVER construct an Arg * directly, this is a base class that is extended by various children * that are meant to be used. Use SwitchArg, ValueArg, MultiArg, * UnlabeledValueArg, or UnlabeledMultiArg instead. * * \param flag - The flag identifying the argument. * \param name - The name identifying the argument. * \param desc - The description of the argument, used in the usage. * \param req - Whether the argument is required. * \param valreq - Whether the a value is required for the argument. * \param v - The visitor checked by the argument. Defaults to NULL. */ Arg( const std::string& flag, const std::string& name, const std::string& desc, bool req, bool valreq, Visitor* v = NULL ); public: /** * Destructor. */ virtual ~Arg(); /** * Adds this to the specified list of Args. * \param argList - The list to add this to. */ virtual void addToList( std::list& argList ) const; /** * Begin ignoring arguments since the "--" argument was specified. */ static void beginIgnoring() { ignoreRestRef() = true; } /** * Whether to ignore the rest. */ static bool ignoreRest() { return ignoreRestRef(); } /** * The delimiter that separates an argument flag/name from the * value. */ static char delimiter() { return delimiterRef(); } /** * The char used as a place holder when SwitchArgs are combined. * Currently set to the bell char (ASCII 7). */ static char blankChar() { return (char)7; } /** * The char that indicates the beginning of a flag. Defaults to '-', but * clients can define TCLAP_FLAGSTARTCHAR to override. */ #ifndef TCLAP_FLAGSTARTCHAR #define TCLAP_FLAGSTARTCHAR '-' #endif static char flagStartChar() { return TCLAP_FLAGSTARTCHAR; } /** * The sting that indicates the beginning of a flag. Defaults to "-", but * clients can define TCLAP_FLAGSTARTSTRING to override. Should be the same * as TCLAP_FLAGSTARTCHAR. */ #ifndef TCLAP_FLAGSTARTSTRING #define TCLAP_FLAGSTARTSTRING "-" #endif static const std::string flagStartString() { return TCLAP_FLAGSTARTSTRING; } /** * The sting that indicates the beginning of a name. Defaults to "--", but * clients can define TCLAP_NAMESTARTSTRING to override. */ #ifndef TCLAP_NAMESTARTSTRING #define TCLAP_NAMESTARTSTRING "--" #endif static const std::string nameStartString() { return TCLAP_NAMESTARTSTRING; } /** * The name used to identify the ignore rest argument. */ static const std::string ignoreNameString() { return "ignore_rest"; } /** * Sets the delimiter for all arguments. * \param c - The character that delimits flags/names from values. */ static void setDelimiter( char c ) { delimiterRef() = c; } /** * Pure virtual method meant to handle the parsing and value assignment * of the string on the command line. * \param i - Pointer the the current argument in the list. * \param args - Mutable list of strings. What is * passed in from main. */ virtual bool processArg(int *i, std::vector& args) = 0; /** * Operator ==. * Equality operator. Must be virtual to handle unlabeled args. * \param a - The Arg to be compared to this. */ virtual bool operator==(const Arg& a) const; /** * Returns the argument flag. */ const std::string& getFlag() const; /** * Returns the argument name. */ const std::string& getName() const; /** * Returns the argument description. */ std::string getDescription() const; /** * Indicates whether the argument is required. */ virtual bool isRequired() const; /** * Sets _required to true. This is used by the XorHandler. * You really have no reason to ever use it. */ void forceRequired(); /** * Sets the _alreadySet value to true. This is used by the XorHandler. * You really have no reason to ever use it. */ void xorSet(); /** * Indicates whether a value must be specified for argument. */ bool isValueRequired() const; /** * Indicates whether the argument has already been set. Only true * if the arg has been matched on the command line. */ bool isSet() const; /** * Indicates whether the argument can be ignored, if desired. */ bool isIgnoreable() const; /** * A method that tests whether a string matches this argument. * This is generally called by the processArg() method. This * method could be re-implemented by a child to change how * arguments are specified on the command line. * \param s - The string to be compared to the flag/name to determine * whether the arg matches. */ virtual bool argMatches( const std::string& s ) const; /** * Returns a simple string representation of the argument. * Primarily for debugging. */ virtual std::string toString() const; /** * Returns a short ID for the usage. * \param valueId - The value used in the id. */ virtual std::string shortID( const std::string& valueId = "val" ) const; /** * Returns a long ID for the usage. * \param valueId - The value used in the id. */ virtual std::string longID( const std::string& valueId = "val" ) const; /** * Trims a value off of the flag. * \param flag - The string from which the flag and value will be * trimmed. Contains the flag once the value has been trimmed. * \param value - Where the value trimmed from the string will * be stored. */ virtual void trimFlag( std::string& flag, std::string& value ) const; /** * Checks whether a given string has blank chars, indicating that * it is a combined SwitchArg. If so, return true, otherwise return * false. * \param s - string to be checked. */ bool _hasBlanks( const std::string& s ) const; /** * Sets the requireLabel. Used by XorHandler. You shouldn't ever * use this. * \param s - Set the requireLabel to this value. */ void setRequireLabel( const std::string& s ); /** * Used for MultiArgs and XorHandler to determine whether args * can still be set. */ virtual bool allowMore(); /** * Use by output classes to determine whether an Arg accepts * multiple values. */ virtual bool acceptsMultipleValues(); /** * Clears the Arg object and allows it to be reused by new * command lines. */ virtual void reset(); }; /** * Typedef of an Arg list iterator. */ typedef std::list::iterator ArgListIterator; /** * Typedef of an Arg vector iterator. */ typedef std::vector::iterator ArgVectorIterator; /** * Typedef of a Visitor list iterator. */ typedef std::list::iterator VisitorListIterator; /* * Extract a value of type T from it's string representation contained * in strVal. The ValueLike parameter used to select the correct * specialization of ExtractValue depending on the value traits of T. * ValueLike traits use operator>> to assign the value from strVal. */ template void ExtractValue(T &destVal, const std::string& strVal, ValueLike vl) { static_cast(vl); // Avoid warning about unused vl std::istringstream is(strVal); int valuesRead = 0; while ( is.good() ) { if ( is.peek() != EOF ) #ifdef TCLAP_SETBASE_ZERO is >> std::setbase(0) >> destVal; #else is >> destVal; #endif else break; valuesRead++; } if ( is.fail() ) throw( ArgParseException("Couldn't read argument value " "from string '" + strVal + "'")); if ( valuesRead > 1 ) throw( ArgParseException("More than one valid value parsed from " "string '" + strVal + "'")); } /* * Extract a value of type T from it's string representation contained * in strVal. The ValueLike parameter used to select the correct * specialization of ExtractValue depending on the value traits of T. * StringLike uses assignment (operator=) to assign from strVal. */ template void ExtractValue(T &destVal, const std::string& strVal, StringLike sl) { static_cast(sl); // Avoid warning about unused sl SetString(destVal, strVal); } ////////////////////////////////////////////////////////////////////// //BEGIN Arg.cpp ////////////////////////////////////////////////////////////////////// inline Arg::Arg(const std::string& flag, const std::string& name, const std::string& desc, bool req, bool valreq, Visitor* v) : _flag(flag), _name(name), _description(desc), _required(req), _requireLabel("required"), _valueRequired(valreq), _alreadySet(false), _visitor( v ), _ignoreable(true), _xorSet(false), _acceptsMultipleValues(false) { if ( _flag.length() > 1 ) throw(SpecificationException( "Argument flag can only be one character long", toString() ) ); if ( _name != ignoreNameString() && ( _flag == Arg::flagStartString() || _flag == Arg::nameStartString() || _flag == " " ) ) throw(SpecificationException("Argument flag cannot be either '" + Arg::flagStartString() + "' or '" + Arg::nameStartString() + "' or a space.", toString() ) ); if ( ( _name.substr( 0, Arg::flagStartString().length() ) == Arg::flagStartString() ) || ( _name.substr( 0, Arg::nameStartString().length() ) == Arg::nameStartString() ) || ( _name.find( " ", 0 ) != std::string::npos ) ) throw(SpecificationException("Argument name begin with either '" + Arg::flagStartString() + "' or '" + Arg::nameStartString() + "' or space.", toString() ) ); } inline Arg::~Arg() { } inline std::string Arg::shortID( const std::string& valueId ) const { std::string id = ""; if ( _flag != "" ) id = Arg::flagStartString() + _flag; else id = Arg::nameStartString() + _name; if ( _valueRequired ) id += std::string( 1, Arg::delimiter() ) + "<" + valueId + ">"; if ( !_required ) id = "[" + id + "]"; return id; } inline std::string Arg::longID( const std::string& valueId ) const { std::string id = ""; if ( _flag != "" ) { id += Arg::flagStartString() + _flag; if ( _valueRequired ) id += std::string( 1, Arg::delimiter() ) + "<" + valueId + ">"; id += ", "; } id += Arg::nameStartString() + _name; if ( _valueRequired ) id += std::string( 1, Arg::delimiter() ) + "<" + valueId + ">"; return id; } inline bool Arg::operator==(const Arg& a) const { if ( ( _flag != "" && _flag == a._flag ) || _name == a._name) return true; else return false; } inline std::string Arg::getDescription() const { std::string desc = ""; if ( _required ) desc = "(" + _requireLabel + ") "; // if ( _valueRequired ) // desc += "(value required) "; desc += _description; return desc; } inline const std::string& Arg::getFlag() const { return _flag; } inline const std::string& Arg::getName() const { return _name; } inline bool Arg::isRequired() const { return _required; } inline bool Arg::isValueRequired() const { return _valueRequired; } inline bool Arg::isSet() const { if ( _alreadySet && !_xorSet ) return true; else return false; } inline bool Arg::isIgnoreable() const { return _ignoreable; } inline void Arg::setRequireLabel( const std::string& s) { _requireLabel = s; } inline bool Arg::argMatches( const std::string& argFlag ) const { if ( ( argFlag == Arg::flagStartString() + _flag && _flag != "" ) || argFlag == Arg::nameStartString() + _name ) return true; else return false; } inline std::string Arg::toString() const { std::string s = ""; if ( _flag != "" ) s += Arg::flagStartString() + _flag + " "; s += "(" + Arg::nameStartString() + _name + ")"; return s; } inline void Arg::_checkWithVisitor() const { if ( _visitor != NULL ) _visitor->visit(); } /** * Implementation of trimFlag. */ inline void Arg::trimFlag(std::string& flag, std::string& value) const { int stop = 0; for ( int i = 0; static_cast(i) < flag.length(); i++ ) if ( flag[i] == Arg::delimiter() ) { stop = i; break; } if ( stop > 1 ) { value = flag.substr(stop+1); flag = flag.substr(0,stop); } } /** * Implementation of _hasBlanks. */ inline bool Arg::_hasBlanks( const std::string& s ) const { for ( int i = 1; static_cast(i) < s.length(); i++ ) if ( s[i] == Arg::blankChar() ) return true; return false; } inline void Arg::forceRequired() { _required = true; } inline void Arg::xorSet() { _alreadySet = true; _xorSet = true; } /** * Overridden by Args that need to added to the end of the list. */ inline void Arg::addToList( std::list& argList ) const { argList.push_front( const_cast(this) ); } inline bool Arg::allowMore() { return false; } inline bool Arg::acceptsMultipleValues() { return _acceptsMultipleValues; } inline void Arg::reset() { _xorSet = false; _alreadySet = false; } ////////////////////////////////////////////////////////////////////// //END Arg.cpp ////////////////////////////////////////////////////////////////////// } //namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/ArgException.h000066400000000000000000000124721266464252400210340ustar00rootroot00000000000000// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- /****************************************************************************** * * file: ArgException.h * * Copyright (c) 2003, Michael E. Smoot . * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_ARG_EXCEPTION_H #define TCLAP_ARG_EXCEPTION_H #include #include namespace TCLAP { /** * A simple class that defines and argument exception. Should be caught * whenever a CmdLine is created and parsed. */ class ArgException : public std::exception { public: /** * Constructor. * \param text - The text of the exception. * \param id - The text identifying the argument source. * \param td - Text describing the type of ArgException it is. * of the exception. */ ArgException( const std::string& text = "undefined exception", const std::string& id = "undefined", const std::string& td = "Generic ArgException") : std::exception(), _errorText(text), _argId( id ), _typeDescription(td) { } /** * Destructor. */ virtual ~ArgException() throw() { } /** * Returns the error text. */ std::string error() const { return ( _errorText ); } /** * Returns the argument id. */ std::string argId() const { if ( _argId == "undefined" ) return " "; else return ( "Argument: " + _argId ); } /** * Returns the arg id and error text. */ const char* what() const throw() { static std::string ex; ex = _argId + " -- " + _errorText; return ex.c_str(); } /** * Returns the type of the exception. Used to explain and distinguish * between different child exceptions. */ std::string typeDescription() const { return _typeDescription; } private: /** * The text of the exception message. */ std::string _errorText; /** * The argument related to this exception. */ std::string _argId; /** * Describes the type of the exception. Used to distinguish * between different child exceptions. */ std::string _typeDescription; }; /** * Thrown from within the child Arg classes when it fails to properly * parse the argument it has been passed. */ class ArgParseException : public ArgException { public: /** * Constructor. * \param text - The text of the exception. * \param id - The text identifying the argument source * of the exception. */ ArgParseException( const std::string& text = "undefined exception", const std::string& id = "undefined" ) : ArgException( text, id, std::string( "Exception found while parsing " ) + std::string( "the value the Arg has been passed." )) { } }; /** * Thrown from CmdLine when the arguments on the command line are not * properly specified, e.g. too many arguments, required argument missing, etc. */ class CmdLineParseException : public ArgException { public: /** * Constructor. * \param text - The text of the exception. * \param id - The text identifying the argument source * of the exception. */ CmdLineParseException( const std::string& text = "undefined exception", const std::string& id = "undefined" ) : ArgException( text, id, std::string( "Exception found when the values ") + std::string( "on the command line do not meet ") + std::string( "the requirements of the defined ") + std::string( "Args." )) { } }; /** * Thrown from Arg and CmdLine when an Arg is improperly specified, e.g. * same flag as another Arg, same name, etc. */ class SpecificationException : public ArgException { public: /** * Constructor. * \param text - The text of the exception. * \param id - The text identifying the argument source * of the exception. */ SpecificationException( const std::string& text = "undefined exception", const std::string& id = "undefined" ) : ArgException( text, id, std::string("Exception found when an Arg object ")+ std::string("is improperly defined by the ") + std::string("developer." )) { } }; class ExitException { public: ExitException(int estat) : _estat(estat) {} int getExitStatus() const { return _estat; } private: int _estat; }; } // namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/ArgTraits.h000066400000000000000000000050751266464252400203450ustar00rootroot00000000000000// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- /****************************************************************************** * * file: ArgTraits.h * * Copyright (c) 2007, Daniel Aarno, Michael E. Smoot . * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ // This is an internal tclap file, you should probably not have to // include this directly #ifndef TCLAP_ARGTRAITS_H #define TCLAP_ARGTRAITS_H namespace TCLAP { // We use two empty structs to get compile type specialization // function to work /** * A value like argument value type is a value that can be set using * operator>>. This is the default value type. */ struct ValueLike { typedef ValueLike ValueCategory; virtual ~ValueLike() {} }; /** * A string like argument value type is a value that can be set using * operator=(string). Useful if the value type contains spaces which * will be broken up into individual tokens by operator>>. */ struct StringLike { virtual ~StringLike() {} }; /** * A class can inherit from this object to make it have string like * traits. This is a compile time thing and does not add any overhead * to the inherenting class. */ struct StringLikeTrait { typedef StringLike ValueCategory; virtual ~StringLikeTrait() {} }; /** * A class can inherit from this object to make it have value like * traits. This is a compile time thing and does not add any overhead * to the inherenting class. */ struct ValueLikeTrait { typedef ValueLike ValueCategory; virtual ~ValueLikeTrait() {} }; /** * Arg traits are used to get compile type specialization when parsing * argument values. Using an ArgTraits you can specify the way that * values gets assigned to any particular type during parsing. The two * supported types are StringLike and ValueLike. */ template struct ArgTraits { typedef typename T::ValueCategory ValueCategory; virtual ~ArgTraits() {} //typedef ValueLike ValueCategory; }; #endif } // namespace openalpr_2.2.4.orig/src/tclap/CmdLine.h000066400000000000000000000346631266464252400177650ustar00rootroot00000000000000// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- /****************************************************************************** * * file: CmdLine.h * * Copyright (c) 2003, Michael E. Smoot . * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_CMDLINE_H #define TCLAP_CMDLINE_H #include "SwitchArg.h" #include "MultiSwitchArg.h" #include "UnlabeledValueArg.h" #include "UnlabeledMultiArg.h" #include "XorHandler.h" #include "HelpVisitor.h" #include "VersionVisitor.h" #include "IgnoreRestVisitor.h" #include "CmdLineOutput.h" #include "StdOutput.h" #include "Constraint.h" #include "ValuesConstraint.h" #include #include #include #include #include #include #include // Needed for exit(), which isn't defined in some envs. namespace TCLAP { template void DelPtr(T ptr) { delete ptr; } template void ClearContainer(C &c) { typedef typename C::value_type value_type; std::for_each(c.begin(), c.end(), DelPtr); c.clear(); } /** * The base class that manages the command line definition and passes * along the parsing to the appropriate Arg classes. */ class CmdLine : public CmdLineInterface { protected: /** * The list of arguments that will be tested against the * command line. */ std::list _argList; /** * The name of the program. Set to argv[0]. */ std::string _progName; /** * A message used to describe the program. Used in the usage output. */ std::string _message; /** * The version to be displayed with the --version switch. */ std::string _version; /** * The number of arguments that are required to be present on * the command line. This is set dynamically, based on the * Args added to the CmdLine object. */ int _numRequired; /** * The character that is used to separate the argument flag/name * from the value. Defaults to ' ' (space). */ char _delimiter; /** * The handler that manages xoring lists of args. */ XorHandler _xorHandler; /** * A list of Args to be explicitly deleted when the destructor * is called. At the moment, this only includes the three default * Args. */ std::list _argDeleteOnExitList; /** * A list of Visitors to be explicitly deleted when the destructor * is called. At the moment, these are the Visitors created for the * default Args. */ std::list _visitorDeleteOnExitList; /** * Object that handles all output for the CmdLine. */ CmdLineOutput* _output; /** * Should CmdLine handle parsing exceptions internally? */ bool _handleExceptions; /** * Throws an exception listing the missing args. */ void missingArgsException(); /** * Checks whether a name/flag string matches entirely matches * the Arg::blankChar. Used when multiple switches are combined * into a single argument. * \param s - The message to be used in the usage. */ bool _emptyCombined(const std::string& s); /** * Perform a delete ptr; operation on ptr when this object is deleted. */ void deleteOnExit(Arg* ptr); /** * Perform a delete ptr; operation on ptr when this object is deleted. */ void deleteOnExit(Visitor* ptr); private: /** * Prevent accidental copying. */ CmdLine(const CmdLine& rhs); CmdLine& operator=(const CmdLine& rhs); /** * Encapsulates the code common to the constructors * (which is all of it). */ void _constructor(); /** * Is set to true when a user sets the output object. We use this so * that we don't delete objects that are created outside of this lib. */ bool _userSetOutput; /** * Whether or not to automatically create help and version switches. */ bool _helpAndVersion; public: /** * Command line constructor. Defines how the arguments will be * parsed. * \param message - The message to be used in the usage * output. * \param delimiter - The character that is used to separate * the argument flag/name from the value. Defaults to ' ' (space). * \param version - The version number to be used in the * --version switch. * \param helpAndVersion - Whether or not to create the Help and * Version switches. Defaults to true. */ CmdLine(const std::string& message, const char delimiter = ' ', const std::string& version = "none", bool helpAndVersion = true); /** * Deletes any resources allocated by a CmdLine object. */ virtual ~CmdLine(); /** * Adds an argument to the list of arguments to be parsed. * \param a - Argument to be added. */ void add( Arg& a ); /** * An alternative add. Functionally identical. * \param a - Argument to be added. */ void add( Arg* a ); /** * Add two Args that will be xor'd. If this method is used, add does * not need to be called. * \param a - Argument to be added and xor'd. * \param b - Argument to be added and xor'd. */ void xorAdd( Arg& a, Arg& b ); /** * Add a list of Args that will be xor'd. If this method is used, * add does not need to be called. * \param xors - List of Args to be added and xor'd. */ void xorAdd( std::vector& xors ); /** * Parses the command line. * \param argc - Number of arguments. * \param argv - Array of arguments. */ bool parse(int argc, const char * const * argv); /** * Parses the command line. * \param args - A vector of strings representing the args. * args[0] is still the program name. */ bool parse(std::vector& args); /** * */ CmdLineOutput* getOutput(); /** * */ void setOutput(CmdLineOutput* co); /** * */ std::string& getVersion(); /** * */ std::string& getProgramName(); /** * */ std::list& getArgList(); /** * */ XorHandler& getXorHandler(); /** * */ char getDelimiter(); /** * */ std::string& getMessage(); /** * */ bool hasHelpAndVersion(); /** * Disables or enables CmdLine's internal parsing exception handling. * * @param state Should CmdLine handle parsing exceptions internally? */ void setExceptionHandling(const bool state); /** * Returns the current state of the internal exception handling. * * @retval true Parsing exceptions are handled internally. * @retval false Parsing exceptions are propagated to the caller. */ bool getExceptionHandling() const; /** * Allows the CmdLine object to be reused. */ void reset(); }; /////////////////////////////////////////////////////////////////////////////// //Begin CmdLine.cpp /////////////////////////////////////////////////////////////////////////////// inline CmdLine::CmdLine(const std::string& m, char delim, const std::string& v, bool help ) : _argList(std::list()), _progName("not_set_yet"), _message(m), _version(v), _numRequired(0), _delimiter(delim), _xorHandler(XorHandler()), _argDeleteOnExitList(std::list()), _visitorDeleteOnExitList(std::list()), _output(0), _handleExceptions(true), _userSetOutput(false), _helpAndVersion(help) { _constructor(); } inline CmdLine::~CmdLine() { ClearContainer(_argDeleteOnExitList); ClearContainer(_visitorDeleteOnExitList); if ( !_userSetOutput ) { delete _output; _output = 0; } } inline void CmdLine::_constructor() { _output = new StdOutput; Arg::setDelimiter( _delimiter ); Visitor* v; if ( _helpAndVersion ) { v = new HelpVisitor( this, &_output ); SwitchArg* help = new SwitchArg("h","help", "Displays usage information and exits.", false, v); add( help ); deleteOnExit(help); deleteOnExit(v); v = new VersionVisitor( this, &_output ); SwitchArg* vers = new SwitchArg("","version", "Displays version information and exits.", false, v); add( vers ); deleteOnExit(vers); deleteOnExit(v); } v = new IgnoreRestVisitor(); SwitchArg* ignore = new SwitchArg(Arg::flagStartString(), Arg::ignoreNameString(), "Ignores the rest of the labeled arguments following this flag.", false, v); add( ignore ); deleteOnExit(ignore); deleteOnExit(v); } inline void CmdLine::xorAdd( std::vector& ors ) { _xorHandler.add( ors ); for (ArgVectorIterator it = ors.begin(); it != ors.end(); it++) { (*it)->forceRequired(); (*it)->setRequireLabel( "OR required" ); add( *it ); } } inline void CmdLine::xorAdd( Arg& a, Arg& b ) { std::vector ors; ors.push_back( &a ); ors.push_back( &b ); xorAdd( ors ); } inline void CmdLine::add( Arg& a ) { add( &a ); } inline void CmdLine::add( Arg* a ) { for( ArgListIterator it = _argList.begin(); it != _argList.end(); it++ ) if ( *a == *(*it) ) throw( SpecificationException( "Argument with same flag/name already exists!", a->longID() ) ); a->addToList( _argList ); if ( a->isRequired() ) _numRequired++; } inline bool CmdLine::parse(int argc, const char * const * argv) { // this step is necessary so that we have easy access to // mutable strings. std::vector args; for (int i = 0; i < argc; i++) args.push_back(argv[i]); return parse(args); } inline bool CmdLine::parse(std::vector& args) { bool shouldExit = false; int estat = 0; try { _progName = args.front(); args.erase(args.begin()); int requiredCount = 0; for (int i = 0; static_cast(i) < args.size(); i++) { bool matched = false; for (ArgListIterator it = _argList.begin(); it != _argList.end(); it++) { if ( (*it)->processArg( &i, args ) ) { requiredCount += _xorHandler.check( *it ); matched = true; break; } } // checks to see if the argument is an empty combined // switch and if so, then we've actually matched it if ( !matched && _emptyCombined( args[i] ) ) matched = true; if ( !matched && !Arg::ignoreRest() ) throw(CmdLineParseException("Couldn't find match " "for argument", args[i])); } if ( requiredCount < _numRequired ) missingArgsException(); if ( requiredCount > _numRequired ) throw(CmdLineParseException("Too many arguments!")); } catch ( ArgException& e ) { // If we're not handling the exceptions, rethrow. if ( !_handleExceptions) { throw; } try { _output->failure(*this,e); } catch ( ExitException &ee ) { estat = ee.getExitStatus(); shouldExit = true; } } catch (ExitException &ee) { // If we're not handling the exceptions, rethrow. if ( !_handleExceptions) { throw; } estat = ee.getExitStatus(); shouldExit = true; } if (shouldExit) { //exit(estat); return false; } return true; } inline bool CmdLine::_emptyCombined(const std::string& s) { if ( s.length() > 0 && s[0] != Arg::flagStartChar() ) return false; for ( int i = 1; static_cast(i) < s.length(); i++ ) if ( s[i] != Arg::blankChar() ) return false; return true; } inline void CmdLine::missingArgsException() { int count = 0; std::string missingArgList; for (ArgListIterator it = _argList.begin(); it != _argList.end(); it++) { if ( (*it)->isRequired() && !(*it)->isSet() ) { missingArgList += (*it)->getName(); missingArgList += ", "; count++; } } missingArgList = missingArgList.substr(0,missingArgList.length()-2); std::string msg; if ( count > 1 ) msg = "Required arguments missing: "; else msg = "Required argument missing: "; msg += missingArgList; throw(CmdLineParseException(msg)); } inline void CmdLine::deleteOnExit(Arg* ptr) { _argDeleteOnExitList.push_back(ptr); } inline void CmdLine::deleteOnExit(Visitor* ptr) { _visitorDeleteOnExitList.push_back(ptr); } inline CmdLineOutput* CmdLine::getOutput() { return _output; } inline void CmdLine::setOutput(CmdLineOutput* co) { if ( !_userSetOutput ) delete _output; _userSetOutput = true; _output = co; } inline std::string& CmdLine::getVersion() { return _version; } inline std::string& CmdLine::getProgramName() { return _progName; } inline std::list& CmdLine::getArgList() { return _argList; } inline XorHandler& CmdLine::getXorHandler() { return _xorHandler; } inline char CmdLine::getDelimiter() { return _delimiter; } inline std::string& CmdLine::getMessage() { return _message; } inline bool CmdLine::hasHelpAndVersion() { return _helpAndVersion; } inline void CmdLine::setExceptionHandling(const bool state) { _handleExceptions = state; } inline bool CmdLine::getExceptionHandling() const { return _handleExceptions; } inline void CmdLine::reset() { for( ArgListIterator it = _argList.begin(); it != _argList.end(); it++ ) (*it)->reset(); _progName.clear(); } /////////////////////////////////////////////////////////////////////////////// //End CmdLine.cpp /////////////////////////////////////////////////////////////////////////////// } //namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/CmdLineInterface.h000066400000000000000000000072101266464252400215720ustar00rootroot00000000000000 /****************************************************************************** * * file: CmdLineInterface.h * * Copyright (c) 2003, Michael E. Smoot . * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_COMMANDLINE_INTERFACE_H #define TCLAP_COMMANDLINE_INTERFACE_H #include #include #include #include #include namespace TCLAP { class Arg; class CmdLineOutput; class XorHandler; /** * The base class that manages the command line definition and passes * along the parsing to the appropriate Arg classes. */ class CmdLineInterface { public: /** * Destructor */ virtual ~CmdLineInterface() {} /** * Adds an argument to the list of arguments to be parsed. * \param a - Argument to be added. */ virtual void add( Arg& a )=0; /** * An alternative add. Functionally identical. * \param a - Argument to be added. */ virtual void add( Arg* a )=0; /** * Add two Args that will be xor'd. * If this method is used, add does * not need to be called. * \param a - Argument to be added and xor'd. * \param b - Argument to be added and xor'd. */ virtual void xorAdd( Arg& a, Arg& b )=0; /** * Add a list of Args that will be xor'd. If this method is used, * add does not need to be called. * \param xors - List of Args to be added and xor'd. */ virtual void xorAdd( std::vector& xors )=0; /** * Parses the command line. * \param argc - Number of arguments. * \param argv - Array of arguments. */ virtual bool parse(int argc, const char * const * argv)=0; /** * Parses the command line. * \param args - A vector of strings representing the args. * args[0] is still the program name. */ bool parse(std::vector& args); /** * Returns the CmdLineOutput object. */ virtual CmdLineOutput* getOutput()=0; /** * \param co - CmdLineOutput object that we want to use instead. */ virtual void setOutput(CmdLineOutput* co)=0; /** * Returns the version string. */ virtual std::string& getVersion()=0; /** * Returns the program name string. */ virtual std::string& getProgramName()=0; /** * Returns the argList. */ virtual std::list& getArgList()=0; /** * Returns the XorHandler. */ virtual XorHandler& getXorHandler()=0; /** * Returns the delimiter string. */ virtual char getDelimiter()=0; /** * Returns the message string. */ virtual std::string& getMessage()=0; /** * Indicates whether or not the help and version switches were created * automatically. */ virtual bool hasHelpAndVersion()=0; /** * Resets the instance as if it had just been constructed so that the * instance can be reused. */ virtual void reset()=0; }; } //namespace #endif openalpr_2.2.4.orig/src/tclap/CmdLineOutput.h000066400000000000000000000036411266464252400211760ustar00rootroot00000000000000 /****************************************************************************** * * file: CmdLineOutput.h * * Copyright (c) 2004, Michael E. Smoot * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_CMDLINEOUTPUT_H #define TCLAP_CMDLINEOUTPUT_H #include #include #include #include #include #include namespace TCLAP { class CmdLineInterface; class ArgException; /** * The interface that any output object must implement. */ class CmdLineOutput { public: /** * Virtual destructor. */ virtual ~CmdLineOutput() {} /** * Generates some sort of output for the USAGE. * \param c - The CmdLine object the output is generated for. */ virtual void usage(CmdLineInterface& c)=0; /** * Generates some sort of output for the version. * \param c - The CmdLine object the output is generated for. */ virtual void version(CmdLineInterface& c)=0; /** * Generates some sort of output for a failure. * \param c - The CmdLine object the output is generated for. * \param e - The ArgException that caused the failure. */ virtual void failure( CmdLineInterface& c, ArgException& e )=0; }; } //namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/Constraint.h000066400000000000000000000034771266464252400205750ustar00rootroot00000000000000 /****************************************************************************** * * file: Constraint.h * * Copyright (c) 2005, Michael E. Smoot * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_CONSTRAINT_H #define TCLAP_CONSTRAINT_H #include #include #include #include #include #include namespace TCLAP { /** * The interface that defines the interaction between the Arg and Constraint. */ template class Constraint { public: /** * Returns a description of the Constraint. */ virtual std::string description() const =0; /** * Returns the short ID for the Constraint. */ virtual std::string shortID() const =0; /** * The method used to verify that the value parsed from the command * line meets the constraint. * \param value - The value that will be checked. */ virtual bool check(const T& value) const =0; /** * Destructor. * Silences warnings about Constraint being a base class with virtual * functions but without a virtual destructor. */ virtual ~Constraint() { ; } }; } //namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/DocBookOutput.h000066400000000000000000000205151266464252400212020ustar00rootroot00000000000000// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- /****************************************************************************** * * file: DocBookOutput.h * * Copyright (c) 2004, Michael E. Smoot * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_DOCBOOKOUTPUT_H #define TCLAP_DOCBOOKOUTPUT_H #include #include #include #include #include #include #include #include #include namespace TCLAP { /** * A class that generates DocBook output for usage() method for the * given CmdLine and its Args. */ class DocBookOutput : public CmdLineOutput { public: /** * Prints the usage to stdout. Can be overridden to * produce alternative behavior. * \param c - The CmdLine object the output is generated for. */ virtual void usage(CmdLineInterface& c); /** * Prints the version to stdout. Can be overridden * to produce alternative behavior. * \param c - The CmdLine object the output is generated for. */ virtual void version(CmdLineInterface& c); /** * Prints (to stderr) an error message, short usage * Can be overridden to produce alternative behavior. * \param c - The CmdLine object the output is generated for. * \param e - The ArgException that caused the failure. */ virtual void failure(CmdLineInterface& c, ArgException& e ); protected: /** * Substitutes the char r for string x in string s. * \param s - The string to operate on. * \param r - The char to replace. * \param x - What to replace r with. */ void substituteSpecialChars( std::string& s, char r, std::string& x ); void removeChar( std::string& s, char r); void basename( std::string& s ); void printShortArg(Arg* it); void printLongArg(Arg* it); char theDelimiter; }; inline void DocBookOutput::version(CmdLineInterface& _cmd) { std::cout << _cmd.getVersion() << std::endl; } inline void DocBookOutput::usage(CmdLineInterface& _cmd ) { std::list argList = _cmd.getArgList(); std::string progName = _cmd.getProgramName(); std::string xversion = _cmd.getVersion(); theDelimiter = _cmd.getDelimiter(); XorHandler xorHandler = _cmd.getXorHandler(); std::vector< std::vector > xorList = xorHandler.getXorList(); basename(progName); std::cout << "" << std::endl; std::cout << "" << std::endl << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << progName << "" << std::endl; std::cout << "1" << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << progName << "" << std::endl; std::cout << "" << _cmd.getMessage() << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << progName << "" << std::endl; // xor for ( int i = 0; (unsigned int)i < xorList.size(); i++ ) { std::cout << "" << std::endl; for ( ArgVectorIterator it = xorList[i].begin(); it != xorList[i].end(); it++ ) printShortArg((*it)); std::cout << "" << std::endl; } // rest of args for (ArgListIterator it = argList.begin(); it != argList.end(); it++) if ( !xorHandler.contains( (*it) ) ) printShortArg((*it)); std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "Description" << std::endl; std::cout << "" << std::endl; std::cout << _cmd.getMessage() << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "Options" << std::endl; std::cout << "" << std::endl; for (ArgListIterator it = argList.begin(); it != argList.end(); it++) printLongArg((*it)); std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "Version" << std::endl; std::cout << "" << std::endl; std::cout << xversion << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; } inline void DocBookOutput::failure( CmdLineInterface& _cmd, ArgException& e ) { static_cast(_cmd); // unused std::cout << e.what() << std::endl; throw ExitException(1); } inline void DocBookOutput::substituteSpecialChars( std::string& s, char r, std::string& x ) { size_t p; while ( (p = s.find_first_of(r)) != std::string::npos ) { s.erase(p,1); s.insert(p,x); } } inline void DocBookOutput::removeChar( std::string& s, char r) { size_t p; while ( (p = s.find_first_of(r)) != std::string::npos ) { s.erase(p,1); } } inline void DocBookOutput::basename( std::string& s ) { size_t p = s.find_last_of('/'); if ( p != std::string::npos ) { s.erase(0, p + 1); } } inline void DocBookOutput::printShortArg(Arg* a) { std::string lt = "<"; std::string gt = ">"; std::string id = a->shortID(); substituteSpecialChars(id,'<',lt); substituteSpecialChars(id,'>',gt); removeChar(id,'['); removeChar(id,']'); std::string choice = "opt"; if ( a->isRequired() ) choice = "plain"; std::cout << "acceptsMultipleValues() ) std::cout << " rep='repeat'"; std::cout << '>'; if ( !a->getFlag().empty() ) std::cout << a->flagStartChar() << a->getFlag(); else std::cout << a->nameStartString() << a->getName(); if ( a->isValueRequired() ) { std::string arg = a->shortID(); removeChar(arg,'['); removeChar(arg,']'); removeChar(arg,'<'); removeChar(arg,'>'); arg.erase(0, arg.find_last_of(theDelimiter) + 1); std::cout << theDelimiter; std::cout << "" << arg << ""; } std::cout << "" << std::endl; } inline void DocBookOutput::printLongArg(Arg* a) { std::string lt = "<"; std::string gt = ">"; std::string desc = a->getDescription(); substituteSpecialChars(desc,'<',lt); substituteSpecialChars(desc,'>',gt); std::cout << "" << std::endl; if ( !a->getFlag().empty() ) { std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; } std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << desc << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "" << std::endl; } } //namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/HelpVisitor.h000066400000000000000000000036671266464252400207220ustar00rootroot00000000000000 /****************************************************************************** * * file: HelpVisitor.h * * Copyright (c) 2003, Michael E. Smoot . * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_HELP_VISITOR_H #define TCLAP_HELP_VISITOR_H #include "CmdLineInterface.h" #include "CmdLineOutput.h" #include "Visitor.h" namespace TCLAP { /** * A Visitor object that calls the usage method of the given CmdLineOutput * object for the specified CmdLine object. */ class HelpVisitor: public Visitor { private: /** * Prevent accidental copying. */ HelpVisitor(const HelpVisitor& rhs); HelpVisitor& operator=(const HelpVisitor& rhs); protected: /** * The CmdLine the output will be generated for. */ CmdLineInterface* _cmd; /** * The output object. */ CmdLineOutput** _out; public: /** * Constructor. * \param cmd - The CmdLine the output will be generated for. * \param out - The type of output. */ HelpVisitor(CmdLineInterface* cmd, CmdLineOutput** out) : Visitor(), _cmd( cmd ), _out( out ) { } /** * Calls the usage method of the CmdLineOutput for the * specified CmdLine. */ void visit() { (*_out)->usage(*_cmd); throw ExitException(0); } }; } #endif openalpr_2.2.4.orig/src/tclap/IgnoreRestVisitor.h000066400000000000000000000024701266464252400221020ustar00rootroot00000000000000 /****************************************************************************** * * file: IgnoreRestVisitor.h * * Copyright (c) 2003, Michael E. Smoot . * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_IGNORE_REST_VISITOR_H #define TCLAP_IGNORE_REST_VISITOR_H #include "Visitor.h" #include "Arg.h" namespace TCLAP { /** * A Vistor that tells the CmdLine to begin ignoring arguments after * this one is parsed. */ class IgnoreRestVisitor: public Visitor { public: /** * Constructor. */ IgnoreRestVisitor() : Visitor() {} /** * Sets Arg::_ignoreRest. */ void visit() { Arg::beginIgnoring(); } }; } #endif openalpr_2.2.4.orig/src/tclap/MultiArg.h000066400000000000000000000303621266464252400201660ustar00rootroot00000000000000/****************************************************************************** * * file: MultiArg.h * * Copyright (c) 2003, Michael E. Smoot . * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_MULTIPLE_ARGUMENT_H #define TCLAP_MULTIPLE_ARGUMENT_H #include #include #include "Arg.h" #include "Constraint.h" namespace TCLAP { /** * An argument that allows multiple values of type T to be specified. Very * similar to a ValueArg, except a vector of values will be returned * instead of just one. */ template class MultiArg : public Arg { public: typedef std::vector container_type; typedef typename container_type::iterator iterator; typedef typename container_type::const_iterator const_iterator; protected: /** * The list of values parsed from the CmdLine. */ std::vector _values; /** * The description of type T to be used in the usage. */ std::string _typeDesc; /** * A list of constraint on this Arg. */ Constraint* _constraint; /** * Extracts the value from the string. * Attempts to parse string as type T, if this fails an exception * is thrown. * \param val - The string to be read. */ void _extractValue( const std::string& val ); /** * Used by XorHandler to decide whether to keep parsing for this arg. */ bool _allowMore; public: /** * Constructor. * \param flag - The one character flag that identifies this * argument on the command line. * \param name - A one word name for the argument. Can be * used as a long flag on the command line. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param typeDesc - A short, human readable description of the * type that this object expects. This is used in the generation * of the USAGE statement. The goal is to be helpful to the end user * of the program. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ MultiArg( const std::string& flag, const std::string& name, const std::string& desc, bool req, const std::string& typeDesc, Visitor* v = NULL); /** * Constructor. * \param flag - The one character flag that identifies this * argument on the command line. * \param name - A one word name for the argument. Can be * used as a long flag on the command line. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param typeDesc - A short, human readable description of the * type that this object expects. This is used in the generation * of the USAGE statement. The goal is to be helpful to the end user * of the program. * \param parser - A CmdLine parser object to add this Arg to * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ MultiArg( const std::string& flag, const std::string& name, const std::string& desc, bool req, const std::string& typeDesc, CmdLineInterface& parser, Visitor* v = NULL ); /** * Constructor. * \param flag - The one character flag that identifies this * argument on the command line. * \param name - A one word name for the argument. Can be * used as a long flag on the command line. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param constraint - A pointer to a Constraint object used * to constrain this Arg. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ MultiArg( const std::string& flag, const std::string& name, const std::string& desc, bool req, Constraint* constraint, Visitor* v = NULL ); /** * Constructor. * \param flag - The one character flag that identifies this * argument on the command line. * \param name - A one word name for the argument. Can be * used as a long flag on the command line. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param constraint - A pointer to a Constraint object used * to constrain this Arg. * \param parser - A CmdLine parser object to add this Arg to * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ MultiArg( const std::string& flag, const std::string& name, const std::string& desc, bool req, Constraint* constraint, CmdLineInterface& parser, Visitor* v = NULL ); /** * Handles the processing of the argument. * This re-implements the Arg version of this method to set the * _value of the argument appropriately. It knows the difference * between labeled and unlabeled. * \param i - Pointer the the current argument in the list. * \param args - Mutable list of strings. Passed from main(). */ virtual bool processArg(int* i, std::vector& args); /** * Returns a vector of type T containing the values parsed from * the command line. */ const std::vector& getValue(); /** * Returns an iterator over the values parsed from the command * line. */ const_iterator begin() const { return _values.begin(); } /** * Returns the end of the values parsed from the command * line. */ const_iterator end() const { return _values.end(); } /** * Returns the a short id string. Used in the usage. * \param val - value to be used. */ virtual std::string shortID(const std::string& val="val") const; /** * Returns the a long id string. Used in the usage. * \param val - value to be used. */ virtual std::string longID(const std::string& val="val") const; /** * Once we've matched the first value, then the arg is no longer * required. */ virtual bool isRequired() const; virtual bool allowMore(); virtual void reset(); private: /** * Prevent accidental copying */ MultiArg(const MultiArg& rhs); MultiArg& operator=(const MultiArg& rhs); }; template MultiArg::MultiArg(const std::string& flag, const std::string& name, const std::string& desc, bool req, const std::string& typeDesc, Visitor* v) : Arg( flag, name, desc, req, true, v ), _values(std::vector()), _typeDesc( typeDesc ), _constraint( NULL ), _allowMore(false) { _acceptsMultipleValues = true; } template MultiArg::MultiArg(const std::string& flag, const std::string& name, const std::string& desc, bool req, const std::string& typeDesc, CmdLineInterface& parser, Visitor* v) : Arg( flag, name, desc, req, true, v ), _values(std::vector()), _typeDesc( typeDesc ), _constraint( NULL ), _allowMore(false) { parser.add( this ); _acceptsMultipleValues = true; } /** * */ template MultiArg::MultiArg(const std::string& flag, const std::string& name, const std::string& desc, bool req, Constraint* constraint, Visitor* v) : Arg( flag, name, desc, req, true, v ), _values(std::vector()), _typeDesc( constraint->shortID() ), _constraint( constraint ), _allowMore(false) { _acceptsMultipleValues = true; } template MultiArg::MultiArg(const std::string& flag, const std::string& name, const std::string& desc, bool req, Constraint* constraint, CmdLineInterface& parser, Visitor* v) : Arg( flag, name, desc, req, true, v ), _values(std::vector()), _typeDesc( constraint->shortID() ), _constraint( constraint ), _allowMore(false) { parser.add( this ); _acceptsMultipleValues = true; } template const std::vector& MultiArg::getValue() { return _values; } template bool MultiArg::processArg(int *i, std::vector& args) { if ( _ignoreable && Arg::ignoreRest() ) return false; if ( _hasBlanks( args[*i] ) ) return false; std::string flag = args[*i]; std::string value = ""; trimFlag( flag, value ); if ( argMatches( flag ) ) { if ( Arg::delimiter() != ' ' && value == "" ) throw( ArgParseException( "Couldn't find delimiter for this argument!", toString() ) ); // always take the first one, regardless of start string if ( value == "" ) { (*i)++; if ( static_cast(*i) < args.size() ) _extractValue( args[*i] ); else throw( ArgParseException("Missing a value for this argument!", toString() ) ); } else _extractValue( value ); /* // continuing taking the args until we hit one with a start string while ( (unsigned int)(*i)+1 < args.size() && args[(*i)+1].find_first_of( Arg::flagStartString() ) != 0 && args[(*i)+1].find_first_of( Arg::nameStartString() ) != 0 ) _extractValue( args[++(*i)] ); */ _alreadySet = true; _checkWithVisitor(); return true; } else return false; } /** * */ template std::string MultiArg::shortID(const std::string& val) const { static_cast(val); // Ignore input, don't warn return Arg::shortID(_typeDesc) + " ... "; } /** * */ template std::string MultiArg::longID(const std::string& val) const { static_cast(val); // Ignore input, don't warn return Arg::longID(_typeDesc) + " (accepted multiple times)"; } /** * Once we've matched the first value, then the arg is no longer * required. */ template bool MultiArg::isRequired() const { if ( _required ) { if ( _values.size() > 1 ) return false; else return true; } else return false; } template void MultiArg::_extractValue( const std::string& val ) { try { T tmp; ExtractValue(tmp, val, typename ArgTraits::ValueCategory()); _values.push_back(tmp); } catch( ArgParseException &e) { throw ArgParseException(e.error(), toString()); } if ( _constraint != NULL ) if ( ! _constraint->check( _values.back() ) ) throw( CmdLineParseException( "Value '" + val + "' does not meet constraint: " + _constraint->description(), toString() ) ); } template bool MultiArg::allowMore() { bool am = _allowMore; _allowMore = true; return am; } template void MultiArg::reset() { Arg::reset(); _values.clear(); } } // namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/MultiSwitchArg.h000066400000000000000000000141161266464252400213470ustar00rootroot00000000000000 /****************************************************************************** * * file: MultiSwitchArg.h * * Copyright (c) 2003, Michael E. Smoot . * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. * Copyright (c) 2005, Michael E. Smoot, Daniel Aarno, Erik Zeek. * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_MULTI_SWITCH_ARG_H #define TCLAP_MULTI_SWITCH_ARG_H #include #include #include "SwitchArg.h" namespace TCLAP { /** * A multiple switch argument. If the switch is set on the command line, then * the getValue method will return the number of times the switch appears. */ class MultiSwitchArg : public SwitchArg { protected: /** * The value of the switch. */ int _value; /** * Used to support the reset() method so that ValueArg can be * reset to their constructed value. */ int _default; public: /** * MultiSwitchArg constructor. * \param flag - The one character flag that identifies this * argument on the command line. * \param name - A one word name for the argument. Can be * used as a long flag on the command line. * \param desc - A description of what the argument is for or * does. * \param init - Optional. The initial/default value of this Arg. * Defaults to 0. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ MultiSwitchArg(const std::string& flag, const std::string& name, const std::string& desc, int init = 0, Visitor* v = NULL); /** * MultiSwitchArg constructor. * \param flag - The one character flag that identifies this * argument on the command line. * \param name - A one word name for the argument. Can be * used as a long flag on the command line. * \param desc - A description of what the argument is for or * does. * \param parser - A CmdLine parser object to add this Arg to * \param init - Optional. The initial/default value of this Arg. * Defaults to 0. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ MultiSwitchArg(const std::string& flag, const std::string& name, const std::string& desc, CmdLineInterface& parser, int init = 0, Visitor* v = NULL); /** * Handles the processing of the argument. * This re-implements the SwitchArg version of this method to set the * _value of the argument appropriately. * \param i - Pointer the the current argument in the list. * \param args - Mutable list of strings. Passed * in from main(). */ virtual bool processArg(int* i, std::vector& args); /** * Returns int, the number of times the switch has been set. */ int getValue(); /** * Returns the shortID for this Arg. */ std::string shortID(const std::string& val) const; /** * Returns the longID for this Arg. */ std::string longID(const std::string& val) const; void reset(); }; ////////////////////////////////////////////////////////////////////// //BEGIN MultiSwitchArg.cpp ////////////////////////////////////////////////////////////////////// inline MultiSwitchArg::MultiSwitchArg(const std::string& flag, const std::string& name, const std::string& desc, int init, Visitor* v ) : SwitchArg(flag, name, desc, false, v), _value( init ), _default( init ) { } inline MultiSwitchArg::MultiSwitchArg(const std::string& flag, const std::string& name, const std::string& desc, CmdLineInterface& parser, int init, Visitor* v ) : SwitchArg(flag, name, desc, false, v), _value( init ), _default( init ) { parser.add( this ); } inline int MultiSwitchArg::getValue() { return _value; } inline bool MultiSwitchArg::processArg(int *i, std::vector& args) { if ( _ignoreable && Arg::ignoreRest() ) return false; if ( argMatches( args[*i] )) { // so the isSet() method will work _alreadySet = true; // Matched argument: increment value. ++_value; _checkWithVisitor(); return true; } else if ( combinedSwitchesMatch( args[*i] ) ) { // so the isSet() method will work _alreadySet = true; // Matched argument: increment value. ++_value; // Check for more in argument and increment value. while ( combinedSwitchesMatch( args[*i] ) ) ++_value; _checkWithVisitor(); return false; } else return false; } inline std::string MultiSwitchArg::shortID(const std::string& val) const { return Arg::shortID(val) + " ... "; } inline std::string MultiSwitchArg::longID(const std::string& val) const { return Arg::longID(val) + " (accepted multiple times)"; } inline void MultiSwitchArg::reset() { MultiSwitchArg::_value = MultiSwitchArg::_default; } ////////////////////////////////////////////////////////////////////// //END MultiSwitchArg.cpp ////////////////////////////////////////////////////////////////////// } //namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/OptionalUnlabeledTracker.h000066400000000000000000000033341266464252400233560ustar00rootroot00000000000000 /****************************************************************************** * * file: OptionalUnlabeledTracker.h * * Copyright (c) 2005, Michael E. Smoot . * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_OPTIONAL_UNLABELED_TRACKER_H #define TCLAP_OPTIONAL_UNLABELED_TRACKER_H #include namespace TCLAP { class OptionalUnlabeledTracker { public: static void check( bool req, const std::string& argName ); static void gotOptional() { alreadyOptionalRef() = true; } static bool& alreadyOptional() { return alreadyOptionalRef(); } private: static bool& alreadyOptionalRef() { static bool ct = false; return ct; } }; inline void OptionalUnlabeledTracker::check( bool req, const std::string& argName ) { if ( OptionalUnlabeledTracker::alreadyOptional() ) throw( SpecificationException( "You can't specify ANY Unlabeled Arg following an optional Unlabeled Arg", argName ) ); if ( !req ) OptionalUnlabeledTracker::gotOptional(); } } // namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/StandardTraits.h000066400000000000000000000106101266464252400213630ustar00rootroot00000000000000// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- /****************************************************************************** * * file: StandardTraits.h * * Copyright (c) 2007, Daniel Aarno, Michael E. Smoot . * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ // This is an internal tclap file, you should probably not have to // include this directly #ifndef TCLAP_STANDARD_TRAITS_H #define TCLAP_STANDARD_TRAITS_H #ifdef HAVE_CONFIG_H #include // To check for long long #endif // If Microsoft has already typedef'd wchar_t as an unsigned // short, then compiles will break because it's as if we're // creating ArgTraits twice for unsigned short. Thus... #ifdef _MSC_VER #ifndef _NATIVE_WCHAR_T_DEFINED #define TCLAP_DONT_DECLARE_WCHAR_T_ARGTRAITS #endif #endif namespace TCLAP { // ====================================================================== // Integer types // ====================================================================== /** * longs have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; /** * ints have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; /** * shorts have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; /** * chars have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; #ifdef HAVE_LONG_LONG /** * long longs have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; #endif // ====================================================================== // Unsigned integer types // ====================================================================== /** * unsigned longs have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; /** * unsigned ints have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; /** * unsigned shorts have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; /** * unsigned chars have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; // Microsoft implements size_t awkwardly. #if defined(_MSC_VER) && defined(_M_X64) /** * size_ts have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; #endif #ifdef HAVE_LONG_LONG /** * unsigned long longs have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; #endif // ====================================================================== // Float types // ====================================================================== /** * floats have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; /** * doubles have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; // ====================================================================== // Other types // ====================================================================== /** * bools have value-like semantics. */ template<> struct ArgTraits { typedef ValueLike ValueCategory; }; /** * wchar_ts have value-like semantics. */ #ifndef TCLAP_DONT_DECLARE_WCHAR_T_ARGTRAITS template<> struct ArgTraits { typedef ValueLike ValueCategory; }; #endif /** * Strings have string like argument traits. */ template<> struct ArgTraits { typedef StringLike ValueCategory; }; template void SetString(T &dst, const std::string &src) { dst = src; } } // namespace #endif openalpr_2.2.4.orig/src/tclap/StdOutput.h000066400000000000000000000210641266464252400204140ustar00rootroot00000000000000// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- /****************************************************************************** * * file: StdOutput.h * * Copyright (c) 2004, Michael E. Smoot * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_STDCMDLINEOUTPUT_H #define TCLAP_STDCMDLINEOUTPUT_H #include #include #include #include #include #include "CmdLineInterface.h" #include "CmdLineOutput.h" #include "XorHandler.h" #include "Arg.h" namespace TCLAP { /** * A class that isolates any output from the CmdLine object so that it * may be easily modified. */ class StdOutput : public CmdLineOutput { public: /** * Prints the usage to stdout. Can be overridden to * produce alternative behavior. * \param c - The CmdLine object the output is generated for. */ virtual void usage(CmdLineInterface& c); /** * Prints the version to stdout. Can be overridden * to produce alternative behavior. * \param c - The CmdLine object the output is generated for. */ virtual void version(CmdLineInterface& c); /** * Prints (to stderr) an error message, short usage * Can be overridden to produce alternative behavior. * \param c - The CmdLine object the output is generated for. * \param e - The ArgException that caused the failure. */ virtual void failure(CmdLineInterface& c, ArgException& e ); protected: /** * Writes a brief usage message with short args. * \param c - The CmdLine object the output is generated for. * \param os - The stream to write the message to. */ void _shortUsage( CmdLineInterface& c, std::ostream& os ) const; /** * Writes a longer usage message with long and short args, * provides descriptions and prints message. * \param c - The CmdLine object the output is generated for. * \param os - The stream to write the message to. */ void _longUsage( CmdLineInterface& c, std::ostream& os ) const; /** * This function inserts line breaks and indents long strings * according the params input. It will only break lines at spaces, * commas and pipes. * \param os - The stream to be printed to. * \param s - The string to be printed. * \param maxWidth - The maxWidth allowed for the output line. * \param indentSpaces - The number of spaces to indent the first line. * \param secondLineOffset - The number of spaces to indent the second * and all subsequent lines in addition to indentSpaces. */ void spacePrint( std::ostream& os, const std::string& s, int maxWidth, int indentSpaces, int secondLineOffset ) const; }; inline void StdOutput::version(CmdLineInterface& _cmd) { std::string progName = _cmd.getProgramName(); std::string xversion = _cmd.getVersion(); std::cout << std::endl << progName << " version: " << xversion << std::endl << std::endl; } inline void StdOutput::usage(CmdLineInterface& _cmd ) { std::cout << std::endl << "USAGE: " << std::endl << std::endl; _shortUsage( _cmd, std::cout ); std::cout << std::endl << std::endl << "Where: " << std::endl << std::endl; _longUsage( _cmd, std::cout ); std::cout << std::endl; } inline void StdOutput::failure( CmdLineInterface& _cmd, ArgException& e ) { std::string progName = _cmd.getProgramName(); std::cerr << "PARSE ERROR: " << e.argId() << std::endl << " " << e.error() << std::endl << std::endl; if ( _cmd.hasHelpAndVersion() ) { std::cerr << "Brief USAGE: " << std::endl; _shortUsage( _cmd, std::cerr ); std::cerr << std::endl << "For complete USAGE and HELP type: " << std::endl << " " << progName << " --help" << std::endl << std::endl; } else usage(_cmd); throw ExitException(1); } inline void StdOutput::_shortUsage( CmdLineInterface& _cmd, std::ostream& os ) const { std::list argList = _cmd.getArgList(); std::string progName = _cmd.getProgramName(); XorHandler xorHandler = _cmd.getXorHandler(); std::vector< std::vector > xorList = xorHandler.getXorList(); std::string s = progName + " "; // first the xor for ( int i = 0; static_cast(i) < xorList.size(); i++ ) { s += " {"; for ( ArgVectorIterator it = xorList[i].begin(); it != xorList[i].end(); it++ ) s += (*it)->shortID() + "|"; s[s.length()-1] = '}'; } // then the rest for (ArgListIterator it = argList.begin(); it != argList.end(); it++) if ( !xorHandler.contains( (*it) ) ) s += " " + (*it)->shortID(); // if the program name is too long, then adjust the second line offset int secondLineOffset = static_cast(progName.length()) + 2; if ( secondLineOffset > 75/2 ) secondLineOffset = static_cast(75/2); spacePrint( os, s, 75, 3, secondLineOffset ); } inline void StdOutput::_longUsage( CmdLineInterface& _cmd, std::ostream& os ) const { std::list argList = _cmd.getArgList(); std::string message = _cmd.getMessage(); XorHandler xorHandler = _cmd.getXorHandler(); std::vector< std::vector > xorList = xorHandler.getXorList(); // first the xor for ( int i = 0; static_cast(i) < xorList.size(); i++ ) { for ( ArgVectorIterator it = xorList[i].begin(); it != xorList[i].end(); it++ ) { spacePrint( os, (*it)->longID(), 75, 3, 3 ); spacePrint( os, (*it)->getDescription(), 75, 5, 0 ); if ( it+1 != xorList[i].end() ) spacePrint(os, "-- OR --", 75, 9, 0); } os << std::endl << std::endl; } // then the rest for (ArgListIterator it = argList.begin(); it != argList.end(); it++) if ( !xorHandler.contains( (*it) ) ) { spacePrint( os, (*it)->longID(), 75, 3, 3 ); spacePrint( os, (*it)->getDescription(), 75, 5, 0 ); os << std::endl; } os << std::endl; spacePrint( os, message, 75, 3, 0 ); } inline void StdOutput::spacePrint( std::ostream& os, const std::string& s, int maxWidth, int indentSpaces, int secondLineOffset ) const { int len = static_cast(s.length()); if ( (len + indentSpaces > maxWidth) && maxWidth > 0 ) { int allowedLen = maxWidth - indentSpaces; int start = 0; while ( start < len ) { // find the substring length // int stringLen = std::min( len - start, allowedLen ); // doing it this way to support a VisualC++ 2005 bug using namespace std; int stringLen = min( len - start, allowedLen ); // trim the length so it doesn't end in middle of a word if ( stringLen == allowedLen ) while ( stringLen >= 0 && s[stringLen+start] != ' ' && s[stringLen+start] != ',' && s[stringLen+start] != '|' ) stringLen--; // ok, the word is longer than the line, so just split // wherever the line ends if ( stringLen <= 0 ) stringLen = allowedLen; // check for newlines for ( int i = 0; i < stringLen; i++ ) if ( s[start+i] == '\n' ) stringLen = i+1; // print the indent for ( int i = 0; i < indentSpaces; i++ ) os << " "; if ( start == 0 ) { // handle second line offsets indentSpaces += secondLineOffset; // adjust allowed len allowedLen -= secondLineOffset; } os << s.substr(start,stringLen) << std::endl; // so we don't start a line with a space while ( s[stringLen+start] == ' ' && start < len ) start++; start += stringLen; } } else { for ( int i = 0; i < indentSpaces; i++ ) os << " "; os << s << std::endl; } } } //namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/SwitchArg.h000066400000000000000000000172511266464252400203370ustar00rootroot00000000000000 /****************************************************************************** * * file: SwitchArg.h * * Copyright (c) 2003, Michael E. Smoot . * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_SWITCH_ARG_H #define TCLAP_SWITCH_ARG_H #include #include #include "Arg.h" namespace TCLAP { /** * A simple switch argument. If the switch is set on the command line, then * the getValue method will return the opposite of the default value for the * switch. */ class SwitchArg : public Arg { protected: /** * The value of the switch. */ bool _value; /** * Used to support the reset() method so that ValueArg can be * reset to their constructed value. */ bool _default; public: /** * SwitchArg constructor. * \param flag - The one character flag that identifies this * argument on the command line. * \param name - A one word name for the argument. Can be * used as a long flag on the command line. * \param desc - A description of what the argument is for or * does. * \param def - The default value for this Switch. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ SwitchArg(const std::string& flag, const std::string& name, const std::string& desc, bool def = false, Visitor* v = NULL); /** * SwitchArg constructor. * \param flag - The one character flag that identifies this * argument on the command line. * \param name - A one word name for the argument. Can be * used as a long flag on the command line. * \param desc - A description of what the argument is for or * does. * \param parser - A CmdLine parser object to add this Arg to * \param def - The default value for this Switch. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ SwitchArg(const std::string& flag, const std::string& name, const std::string& desc, CmdLineInterface& parser, bool def = false, Visitor* v = NULL); /** * Handles the processing of the argument. * This re-implements the Arg version of this method to set the * _value of the argument appropriately. * \param i - Pointer the the current argument in the list. * \param args - Mutable list of strings. Passed * in from main(). */ virtual bool processArg(int* i, std::vector& args); /** * Checks a string to see if any of the chars in the string * match the flag for this Switch. */ bool combinedSwitchesMatch(std::string& combined); /** * Returns bool, whether or not the switch has been set. */ bool getValue(); virtual void reset(); private: /** * Checks to see if we've found the last match in * a combined string. */ bool lastCombined(std::string& combined); /** * Does the common processing of processArg. */ void commonProcessing(); }; ////////////////////////////////////////////////////////////////////// //BEGIN SwitchArg.cpp ////////////////////////////////////////////////////////////////////// inline SwitchArg::SwitchArg(const std::string& flag, const std::string& name, const std::string& desc, bool default_val, Visitor* v ) : Arg(flag, name, desc, false, false, v), _value( default_val ), _default( default_val ) { } inline SwitchArg::SwitchArg(const std::string& flag, const std::string& name, const std::string& desc, CmdLineInterface& parser, bool default_val, Visitor* v ) : Arg(flag, name, desc, false, false, v), _value( default_val ), _default(default_val) { parser.add( this ); } inline bool SwitchArg::getValue() { return _value; } inline bool SwitchArg::lastCombined(std::string& combinedSwitches ) { for ( unsigned int i = 1; i < combinedSwitches.length(); i++ ) if ( combinedSwitches[i] != Arg::blankChar() ) return false; return true; } inline bool SwitchArg::combinedSwitchesMatch(std::string& combinedSwitches ) { // make sure this is actually a combined switch if ( combinedSwitches.length() > 0 && combinedSwitches[0] != Arg::flagStartString()[0] ) return false; // make sure it isn't a long name if ( combinedSwitches.substr( 0, Arg::nameStartString().length() ) == Arg::nameStartString() ) return false; // make sure the delimiter isn't in the string if ( combinedSwitches.find_first_of( Arg::delimiter() ) != std::string::npos ) return false; // ok, we're not specifying a ValueArg, so we know that we have // a combined switch list. for ( unsigned int i = 1; i < combinedSwitches.length(); i++ ) if ( _flag.length() > 0 && combinedSwitches[i] == _flag[0] && _flag[0] != Arg::flagStartString()[0] ) { // update the combined switches so this one is no longer present // this is necessary so that no unlabeled args are matched // later in the processing. //combinedSwitches.erase(i,1); combinedSwitches[i] = Arg::blankChar(); return true; } // none of the switches passed in the list match. return false; } inline void SwitchArg::commonProcessing() { if ( _xorSet ) throw(CmdLineParseException( "Mutually exclusive argument already set!", toString())); if ( _alreadySet ) throw(CmdLineParseException("Argument already set!", toString())); _alreadySet = true; if ( _value == true ) _value = false; else _value = true; _checkWithVisitor(); } inline bool SwitchArg::processArg(int *i, std::vector& args) { if ( _ignoreable && Arg::ignoreRest() ) return false; // if the whole string matches the flag or name string if ( argMatches( args[*i] ) ) { commonProcessing(); return true; } // if a substring matches the flag as part of a combination else if ( combinedSwitchesMatch( args[*i] ) ) { // check again to ensure we don't misinterpret // this as a MultiSwitchArg if ( combinedSwitchesMatch( args[*i] ) ) throw(CmdLineParseException("Argument already set!", toString())); commonProcessing(); // We only want to return true if we've found the last combined // match in the string, otherwise we return true so that other // switches in the combination will have a chance to match. return lastCombined( args[*i] ); } else return false; } inline void SwitchArg::reset() { Arg::reset(); _value = _default; } ////////////////////////////////////////////////////////////////////// //End SwitchArg.cpp ////////////////////////////////////////////////////////////////////// } //namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/UnlabeledMultiArg.h000066400000000000000000000244671266464252400220130ustar00rootroot00000000000000 /****************************************************************************** * * file: UnlabeledMultiArg.h * * Copyright (c) 2003, Michael E. Smoot. * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_MULTIPLE_UNLABELED_ARGUMENT_H #define TCLAP_MULTIPLE_UNLABELED_ARGUMENT_H #include #include #include "MultiArg.h" #include "OptionalUnlabeledTracker.h" namespace TCLAP { /** * Just like a MultiArg, except that the arguments are unlabeled. Basically, * this Arg will slurp up everything that hasn't been matched to another * Arg. */ template class UnlabeledMultiArg : public MultiArg { // If compiler has two stage name lookup (as gcc >= 3.4 does) // this is requried to prevent undef. symbols using MultiArg::_ignoreable; using MultiArg::_hasBlanks; using MultiArg::_extractValue; using MultiArg::_typeDesc; using MultiArg::_name; using MultiArg::_description; using MultiArg::_alreadySet; using MultiArg::toString; public: /** * Constructor. * \param name - The name of the Arg. Note that this is used for * identification, not as a long flag. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param typeDesc - A short, human readable description of the * type that this object expects. This is used in the generation * of the USAGE statement. The goal is to be helpful to the end user * of the program. * \param ignoreable - Whether or not this argument can be ignored * using the "--" flag. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ UnlabeledMultiArg( const std::string& name, const std::string& desc, bool req, const std::string& typeDesc, bool ignoreable = false, Visitor* v = NULL ); /** * Constructor. * \param name - The name of the Arg. Note that this is used for * identification, not as a long flag. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param typeDesc - A short, human readable description of the * type that this object expects. This is used in the generation * of the USAGE statement. The goal is to be helpful to the end user * of the program. * \param parser - A CmdLine parser object to add this Arg to * \param ignoreable - Whether or not this argument can be ignored * using the "--" flag. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ UnlabeledMultiArg( const std::string& name, const std::string& desc, bool req, const std::string& typeDesc, CmdLineInterface& parser, bool ignoreable = false, Visitor* v = NULL ); /** * Constructor. * \param name - The name of the Arg. Note that this is used for * identification, not as a long flag. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param constraint - A pointer to a Constraint object used * to constrain this Arg. * \param ignoreable - Whether or not this argument can be ignored * using the "--" flag. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ UnlabeledMultiArg( const std::string& name, const std::string& desc, bool req, Constraint* constraint, bool ignoreable = false, Visitor* v = NULL ); /** * Constructor. * \param name - The name of the Arg. Note that this is used for * identification, not as a long flag. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param constraint - A pointer to a Constraint object used * to constrain this Arg. * \param parser - A CmdLine parser object to add this Arg to * \param ignoreable - Whether or not this argument can be ignored * using the "--" flag. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ UnlabeledMultiArg( const std::string& name, const std::string& desc, bool req, Constraint* constraint, CmdLineInterface& parser, bool ignoreable = false, Visitor* v = NULL ); /** * Handles the processing of the argument. * This re-implements the Arg version of this method to set the * _value of the argument appropriately. It knows the difference * between labeled and unlabeled. * \param i - Pointer the the current argument in the list. * \param args - Mutable list of strings. Passed from main(). */ virtual bool processArg(int* i, std::vector& args); /** * Returns the a short id string. Used in the usage. * \param val - value to be used. */ virtual std::string shortID(const std::string& val="val") const; /** * Returns the a long id string. Used in the usage. * \param val - value to be used. */ virtual std::string longID(const std::string& val="val") const; /** * Opertor ==. * \param a - The Arg to be compared to this. */ virtual bool operator==(const Arg& a) const; /** * Pushes this to back of list rather than front. * \param argList - The list this should be added to. */ virtual void addToList( std::list& argList ) const; }; template UnlabeledMultiArg::UnlabeledMultiArg(const std::string& name, const std::string& desc, bool req, const std::string& typeDesc, bool ignoreable, Visitor* v) : MultiArg("", name, desc, req, typeDesc, v) { _ignoreable = ignoreable; OptionalUnlabeledTracker::check(true, toString()); } template UnlabeledMultiArg::UnlabeledMultiArg(const std::string& name, const std::string& desc, bool req, const std::string& typeDesc, CmdLineInterface& parser, bool ignoreable, Visitor* v) : MultiArg("", name, desc, req, typeDesc, v) { _ignoreable = ignoreable; OptionalUnlabeledTracker::check(true, toString()); parser.add( this ); } template UnlabeledMultiArg::UnlabeledMultiArg(const std::string& name, const std::string& desc, bool req, Constraint* constraint, bool ignoreable, Visitor* v) : MultiArg("", name, desc, req, constraint, v) { _ignoreable = ignoreable; OptionalUnlabeledTracker::check(true, toString()); } template UnlabeledMultiArg::UnlabeledMultiArg(const std::string& name, const std::string& desc, bool req, Constraint* constraint, CmdLineInterface& parser, bool ignoreable, Visitor* v) : MultiArg("", name, desc, req, constraint, v) { _ignoreable = ignoreable; OptionalUnlabeledTracker::check(true, toString()); parser.add( this ); } template bool UnlabeledMultiArg::processArg(int *i, std::vector& args) { if ( _hasBlanks( args[*i] ) ) return false; // never ignore an unlabeled multi arg // always take the first value, regardless of the start string _extractValue( args[(*i)] ); /* // continue taking args until we hit the end or a start string while ( (unsigned int)(*i)+1 < args.size() && args[(*i)+1].find_first_of( Arg::flagStartString() ) != 0 && args[(*i)+1].find_first_of( Arg::nameStartString() ) != 0 ) _extractValue( args[++(*i)] ); */ _alreadySet = true; return true; } template std::string UnlabeledMultiArg::shortID(const std::string& val) const { static_cast(val); // Ignore input, don't warn return std::string("<") + _typeDesc + "> ..."; } template std::string UnlabeledMultiArg::longID(const std::string& val) const { static_cast(val); // Ignore input, don't warn return std::string("<") + _typeDesc + "> (accepted multiple times)"; } template bool UnlabeledMultiArg::operator==(const Arg& a) const { if ( _name == a.getName() || _description == a.getDescription() ) return true; else return false; } template void UnlabeledMultiArg::addToList( std::list& argList ) const { argList.push_back( const_cast(static_cast(this)) ); } } #endif openalpr_2.2.4.orig/src/tclap/UnlabeledValueArg.h000066400000000000000000000277721266464252400217770ustar00rootroot00000000000000 /****************************************************************************** * * file: UnlabeledValueArg.h * * Copyright (c) 2003, Michael E. Smoot . * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_UNLABELED_VALUE_ARGUMENT_H #define TCLAP_UNLABELED_VALUE_ARGUMENT_H #include #include #include "ValueArg.h" #include "OptionalUnlabeledTracker.h" namespace TCLAP { /** * The basic unlabeled argument that parses a value. * This is a template class, which means the type T defines the type * that a given object will attempt to parse when an UnlabeledValueArg * is reached in the list of args that the CmdLine iterates over. */ template class UnlabeledValueArg : public ValueArg { // If compiler has two stage name lookup (as gcc >= 3.4 does) // this is requried to prevent undef. symbols using ValueArg::_ignoreable; using ValueArg::_hasBlanks; using ValueArg::_extractValue; using ValueArg::_typeDesc; using ValueArg::_name; using ValueArg::_description; using ValueArg::_alreadySet; using ValueArg::toString; public: /** * UnlabeledValueArg constructor. * \param name - A one word name for the argument. Note that this is used for * identification, not as a long flag. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param value - The default value assigned to this argument if it * is not present on the command line. * \param typeDesc - A short, human readable description of the * type that this object expects. This is used in the generation * of the USAGE statement. The goal is to be helpful to the end user * of the program. * \param ignoreable - Allows you to specify that this argument can be * ignored if the '--' flag is set. This defaults to false (cannot * be ignored) and should generally stay that way unless you have * some special need for certain arguments to be ignored. * \param v - Optional Vistor. You should leave this blank unless * you have a very good reason. */ UnlabeledValueArg( const std::string& name, const std::string& desc, bool req, T value, const std::string& typeDesc, bool ignoreable = false, Visitor* v = NULL); /** * UnlabeledValueArg constructor. * \param name - A one word name for the argument. Note that this is used for * identification, not as a long flag. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param value - The default value assigned to this argument if it * is not present on the command line. * \param typeDesc - A short, human readable description of the * type that this object expects. This is used in the generation * of the USAGE statement. The goal is to be helpful to the end user * of the program. * \param parser - A CmdLine parser object to add this Arg to * \param ignoreable - Allows you to specify that this argument can be * ignored if the '--' flag is set. This defaults to false (cannot * be ignored) and should generally stay that way unless you have * some special need for certain arguments to be ignored. * \param v - Optional Vistor. You should leave this blank unless * you have a very good reason. */ UnlabeledValueArg( const std::string& name, const std::string& desc, bool req, T value, const std::string& typeDesc, CmdLineInterface& parser, bool ignoreable = false, Visitor* v = NULL ); /** * UnlabeledValueArg constructor. * \param name - A one word name for the argument. Note that this is used for * identification, not as a long flag. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param value - The default value assigned to this argument if it * is not present on the command line. * \param constraint - A pointer to a Constraint object used * to constrain this Arg. * \param ignoreable - Allows you to specify that this argument can be * ignored if the '--' flag is set. This defaults to false (cannot * be ignored) and should generally stay that way unless you have * some special need for certain arguments to be ignored. * \param v - Optional Vistor. You should leave this blank unless * you have a very good reason. */ UnlabeledValueArg( const std::string& name, const std::string& desc, bool req, T value, Constraint* constraint, bool ignoreable = false, Visitor* v = NULL ); /** * UnlabeledValueArg constructor. * \param name - A one word name for the argument. Note that this is used for * identification, not as a long flag. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param value - The default value assigned to this argument if it * is not present on the command line. * \param constraint - A pointer to a Constraint object used * to constrain this Arg. * \param parser - A CmdLine parser object to add this Arg to * \param ignoreable - Allows you to specify that this argument can be * ignored if the '--' flag is set. This defaults to false (cannot * be ignored) and should generally stay that way unless you have * some special need for certain arguments to be ignored. * \param v - Optional Vistor. You should leave this blank unless * you have a very good reason. */ UnlabeledValueArg( const std::string& name, const std::string& desc, bool req, T value, Constraint* constraint, CmdLineInterface& parser, bool ignoreable = false, Visitor* v = NULL); /** * Handles the processing of the argument. * This re-implements the Arg version of this method to set the * _value of the argument appropriately. Handling specific to * unlabled arguments. * \param i - Pointer the the current argument in the list. * \param args - Mutable list of strings. */ virtual bool processArg(int* i, std::vector& args); /** * Overrides shortID for specific behavior. */ virtual std::string shortID(const std::string& val="val") const; /** * Overrides longID for specific behavior. */ virtual std::string longID(const std::string& val="val") const; /** * Overrides operator== for specific behavior. */ virtual bool operator==(const Arg& a ) const; /** * Instead of pushing to the front of list, push to the back. * \param argList - The list to add this to. */ virtual void addToList( std::list& argList ) const; }; /** * Constructor implemenation. */ template UnlabeledValueArg::UnlabeledValueArg(const std::string& name, const std::string& desc, bool req, T val, const std::string& typeDesc, bool ignoreable, Visitor* v) : ValueArg("", name, desc, req, val, typeDesc, v) { _ignoreable = ignoreable; OptionalUnlabeledTracker::check(req, toString()); } template UnlabeledValueArg::UnlabeledValueArg(const std::string& name, const std::string& desc, bool req, T val, const std::string& typeDesc, CmdLineInterface& parser, bool ignoreable, Visitor* v) : ValueArg("", name, desc, req, val, typeDesc, v) { _ignoreable = ignoreable; OptionalUnlabeledTracker::check(req, toString()); parser.add( this ); } /** * Constructor implemenation. */ template UnlabeledValueArg::UnlabeledValueArg(const std::string& name, const std::string& desc, bool req, T val, Constraint* constraint, bool ignoreable, Visitor* v) : ValueArg("", name, desc, req, val, constraint, v) { _ignoreable = ignoreable; OptionalUnlabeledTracker::check(req, toString()); } template UnlabeledValueArg::UnlabeledValueArg(const std::string& name, const std::string& desc, bool req, T val, Constraint* constraint, CmdLineInterface& parser, bool ignoreable, Visitor* v) : ValueArg("", name, desc, req, val, constraint, v) { _ignoreable = ignoreable; OptionalUnlabeledTracker::check(req, toString()); parser.add( this ); } /** * Implementation of processArg(). */ template bool UnlabeledValueArg::processArg(int *i, std::vector& args) { if ( _alreadySet ) return false; if ( _hasBlanks( args[*i] ) ) return false; // never ignore an unlabeled arg _extractValue( args[*i] ); _alreadySet = true; return true; } /** * Overriding shortID for specific output. */ template std::string UnlabeledValueArg::shortID(const std::string& val) const { static_cast(val); // Ignore input, don't warn return std::string("<") + _typeDesc + ">"; } /** * Overriding longID for specific output. */ template std::string UnlabeledValueArg::longID(const std::string& val) const { static_cast(val); // Ignore input, don't warn // Ideally we would like to be able to use RTTI to return the name // of the type required for this argument. However, g++ at least, // doesn't appear to return terribly useful "names" of the types. return std::string("<") + _typeDesc + ">"; } /** * Overriding operator== for specific behavior. */ template bool UnlabeledValueArg::operator==(const Arg& a ) const { if ( _name == a.getName() || _description == a.getDescription() ) return true; else return false; } template void UnlabeledValueArg::addToList( std::list& argList ) const { argList.push_back( const_cast(static_cast(this)) ); } } #endif openalpr_2.2.4.orig/src/tclap/ValueArg.h000066400000000000000000000321521266464252400201470ustar00rootroot00000000000000/****************************************************************************** * * file: ValueArg.h * * Copyright (c) 2003, Michael E. Smoot . * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_VALUE_ARGUMENT_H #define TCLAP_VALUE_ARGUMENT_H #include #include #include "Arg.h" #include "Constraint.h" namespace TCLAP { /** * The basic labeled argument that parses a value. * This is a template class, which means the type T defines the type * that a given object will attempt to parse when the flag/name is matched * on the command line. While there is nothing stopping you from creating * an unflagged ValueArg, it is unwise and would cause significant problems. * Instead use an UnlabeledValueArg. */ template class ValueArg : public Arg { protected: /** * The value parsed from the command line. * Can be of any type, as long as the >> operator for the type * is defined. */ T _value; /** * Used to support the reset() method so that ValueArg can be * reset to their constructed value. */ T _default; /** * A human readable description of the type to be parsed. * This is a hack, plain and simple. Ideally we would use RTTI to * return the name of type T, but until there is some sort of * consistent support for human readable names, we are left to our * own devices. */ std::string _typeDesc; /** * A Constraint this Arg must conform to. */ Constraint* _constraint; /** * Extracts the value from the string. * Attempts to parse string as type T, if this fails an exception * is thrown. * \param val - value to be parsed. */ void _extractValue( const std::string& val ); public: /** * Labeled ValueArg constructor. * You could conceivably call this constructor with a blank flag, * but that would make you a bad person. It would also cause * an exception to be thrown. If you want an unlabeled argument, * use the other constructor. * \param flag - The one character flag that identifies this * argument on the command line. * \param name - A one word name for the argument. Can be * used as a long flag on the command line. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param value - The default value assigned to this argument if it * is not present on the command line. * \param typeDesc - A short, human readable description of the * type that this object expects. This is used in the generation * of the USAGE statement. The goal is to be helpful to the end user * of the program. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ ValueArg( const std::string& flag, const std::string& name, const std::string& desc, bool req, T value, const std::string& typeDesc, Visitor* v = NULL); /** * Labeled ValueArg constructor. * You could conceivably call this constructor with a blank flag, * but that would make you a bad person. It would also cause * an exception to be thrown. If you want an unlabeled argument, * use the other constructor. * \param flag - The one character flag that identifies this * argument on the command line. * \param name - A one word name for the argument. Can be * used as a long flag on the command line. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param value - The default value assigned to this argument if it * is not present on the command line. * \param typeDesc - A short, human readable description of the * type that this object expects. This is used in the generation * of the USAGE statement. The goal is to be helpful to the end user * of the program. * \param parser - A CmdLine parser object to add this Arg to * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ ValueArg( const std::string& flag, const std::string& name, const std::string& desc, bool req, T value, const std::string& typeDesc, CmdLineInterface& parser, Visitor* v = NULL ); /** * Labeled ValueArg constructor. * You could conceivably call this constructor with a blank flag, * but that would make you a bad person. It would also cause * an exception to be thrown. If you want an unlabeled argument, * use the other constructor. * \param flag - The one character flag that identifies this * argument on the command line. * \param name - A one word name for the argument. Can be * used as a long flag on the command line. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param value - The default value assigned to this argument if it * is not present on the command line. * \param constraint - A pointer to a Constraint object used * to constrain this Arg. * \param parser - A CmdLine parser object to add this Arg to. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ ValueArg( const std::string& flag, const std::string& name, const std::string& desc, bool req, T value, Constraint* constraint, CmdLineInterface& parser, Visitor* v = NULL ); /** * Labeled ValueArg constructor. * You could conceivably call this constructor with a blank flag, * but that would make you a bad person. It would also cause * an exception to be thrown. If you want an unlabeled argument, * use the other constructor. * \param flag - The one character flag that identifies this * argument on the command line. * \param name - A one word name for the argument. Can be * used as a long flag on the command line. * \param desc - A description of what the argument is for or * does. * \param req - Whether the argument is required on the command * line. * \param value - The default value assigned to this argument if it * is not present on the command line. * \param constraint - A pointer to a Constraint object used * to constrain this Arg. * \param v - An optional visitor. You probably should not * use this unless you have a very good reason. */ ValueArg( const std::string& flag, const std::string& name, const std::string& desc, bool req, T value, Constraint* constraint, Visitor* v = NULL ); /** * Handles the processing of the argument. * This re-implements the Arg version of this method to set the * _value of the argument appropriately. It knows the difference * between labeled and unlabeled. * \param i - Pointer the the current argument in the list. * \param args - Mutable list of strings. Passed * in from main(). */ virtual bool processArg(int* i, std::vector& args); /** * Returns the value of the argument. */ T& getValue() ; /** * Specialization of shortID. * \param val - value to be used. */ virtual std::string shortID(const std::string& val = "val") const; /** * Specialization of longID. * \param val - value to be used. */ virtual std::string longID(const std::string& val = "val") const; virtual void reset() ; private: /** * Prevent accidental copying */ ValueArg(const ValueArg& rhs); ValueArg& operator=(const ValueArg& rhs); }; /** * Constructor implementation. */ template ValueArg::ValueArg(const std::string& flag, const std::string& name, const std::string& desc, bool req, T val, const std::string& typeDesc, Visitor* v) : Arg(flag, name, desc, req, true, v), _value( val ), _default( val ), _typeDesc( typeDesc ), _constraint( NULL ) { } template ValueArg::ValueArg(const std::string& flag, const std::string& name, const std::string& desc, bool req, T val, const std::string& typeDesc, CmdLineInterface& parser, Visitor* v) : Arg(flag, name, desc, req, true, v), _value( val ), _default( val ), _typeDesc( typeDesc ), _constraint( NULL ) { parser.add( this ); } template ValueArg::ValueArg(const std::string& flag, const std::string& name, const std::string& desc, bool req, T val, Constraint* constraint, Visitor* v) : Arg(flag, name, desc, req, true, v), _value( val ), _default( val ), _typeDesc( constraint->shortID() ), _constraint( constraint ) { } template ValueArg::ValueArg(const std::string& flag, const std::string& name, const std::string& desc, bool req, T val, Constraint* constraint, CmdLineInterface& parser, Visitor* v) : Arg(flag, name, desc, req, true, v), _value( val ), _default( val ), _typeDesc( constraint->shortID() ), _constraint( constraint ) { parser.add( this ); } /** * Implementation of getValue(). */ template T& ValueArg::getValue() { return _value; } /** * Implementation of processArg(). */ template bool ValueArg::processArg(int *i, std::vector& args) { if ( _ignoreable && Arg::ignoreRest() ) return false; if ( _hasBlanks( args[*i] ) ) return false; std::string flag = args[*i]; std::string value = ""; trimFlag( flag, value ); if ( argMatches( flag ) ) { if ( _alreadySet ) { if ( _xorSet ) throw( CmdLineParseException( "Mutually exclusive argument already set!", toString()) ); else throw( CmdLineParseException("Argument already set!", toString()) ); } if ( Arg::delimiter() != ' ' && value == "" ) throw( ArgParseException( "Couldn't find delimiter for this argument!", toString() ) ); if ( value == "" ) { (*i)++; if ( static_cast(*i) < args.size() ) _extractValue( args[*i] ); else throw( ArgParseException("Missing a value for this argument!", toString() ) ); } else _extractValue( value ); _alreadySet = true; _checkWithVisitor(); return true; } else return false; } /** * Implementation of shortID. */ template std::string ValueArg::shortID(const std::string& val) const { static_cast(val); // Ignore input, don't warn return Arg::shortID( _typeDesc ); } /** * Implementation of longID. */ template std::string ValueArg::longID(const std::string& val) const { static_cast(val); // Ignore input, don't warn return Arg::longID( _typeDesc ); } template void ValueArg::_extractValue( const std::string& val ) { try { ExtractValue(_value, val, typename ArgTraits::ValueCategory()); } catch( ArgParseException &e) { throw ArgParseException(e.error(), toString()); } if ( _constraint != NULL ) if ( ! _constraint->check( _value ) ) throw( CmdLineParseException( "Value '" + val + + "' does not meet constraint: " + _constraint->description(), toString() ) ); } template void ValueArg::reset() { Arg::reset(); _value = _default; } } // namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/ValuesConstraint.h000066400000000000000000000062031266464252400217430ustar00rootroot00000000000000 /****************************************************************************** * * file: ValuesConstraint.h * * Copyright (c) 2005, Michael E. Smoot * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_VALUESCONSTRAINT_H #define TCLAP_VALUESCONSTRAINT_H #include #include #include "Constraint.h" #ifdef HAVE_CONFIG_H #include #else #define HAVE_SSTREAM #endif #if defined(HAVE_SSTREAM) #include #elif defined(HAVE_STRSTREAM) #include #else #error "Need a stringstream (sstream or strstream) to compile!" #endif namespace TCLAP { /** * A Constraint that constrains the Arg to only those values specified * in the constraint. */ template class ValuesConstraint : public Constraint { public: /** * Constructor. * \param allowed - vector of allowed values. */ ValuesConstraint(std::vector& allowed); /** * Virtual destructor. */ virtual ~ValuesConstraint() {} /** * Returns a description of the Constraint. */ virtual std::string description() const; /** * Returns the short ID for the Constraint. */ virtual std::string shortID() const; /** * The method used to verify that the value parsed from the command * line meets the constraint. * \param value - The value that will be checked. */ virtual bool check(const T& value) const; protected: /** * The list of valid values. */ std::vector _allowed; /** * The string used to describe the allowed values of this constraint. */ std::string _typeDesc; }; template ValuesConstraint::ValuesConstraint(std::vector& allowed) : _allowed(allowed), _typeDesc("") { for ( unsigned int i = 0; i < _allowed.size(); i++ ) { #if defined(HAVE_SSTREAM) std::ostringstream os; #elif defined(HAVE_STRSTREAM) std::ostrstream os; #else #error "Need a stringstream (sstream or strstream) to compile!" #endif os << _allowed[i]; std::string temp( os.str() ); if ( i > 0 ) _typeDesc += "|"; _typeDesc += temp; } } template bool ValuesConstraint::check( const T& val ) const { if ( std::find(_allowed.begin(),_allowed.end(),val) == _allowed.end() ) return false; else return true; } template std::string ValuesConstraint::shortID() const { return _typeDesc; } template std::string ValuesConstraint::description() const { return _typeDesc; } } //namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/VersionVisitor.h000066400000000000000000000037741266464252400214560ustar00rootroot00000000000000// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- /****************************************************************************** * * file: VersionVisitor.h * * Copyright (c) 2003, Michael E. Smoot . * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_VERSION_VISITOR_H #define TCLAP_VERSION_VISITOR_H #include "CmdLineInterface.h" #include "CmdLineOutput.h" #include "Visitor.h" namespace TCLAP { /** * A Vistor that will call the version method of the given CmdLineOutput * for the specified CmdLine object and then exit. */ class VersionVisitor: public Visitor { private: /** * Prevent accidental copying */ VersionVisitor(const VersionVisitor& rhs); VersionVisitor& operator=(const VersionVisitor& rhs); protected: /** * The CmdLine of interest. */ CmdLineInterface* _cmd; /** * The output object. */ CmdLineOutput** _out; public: /** * Constructor. * \param cmd - The CmdLine the output is generated for. * \param out - The type of output. */ VersionVisitor( CmdLineInterface* cmd, CmdLineOutput** out ) : Visitor(), _cmd( cmd ), _out( out ) { } /** * Calls the version method of the output object using the * specified CmdLine. */ void visit() { (*_out)->version(*_cmd); throw ExitException(0); } }; } #endif openalpr_2.2.4.orig/src/tclap/Visitor.h000066400000000000000000000023621266464252400201000ustar00rootroot00000000000000 /****************************************************************************** * * file: Visitor.h * * Copyright (c) 2003, Michael E. Smoot . * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_VISITOR_H #define TCLAP_VISITOR_H namespace TCLAP { /** * A base class that defines the interface for visitors. */ class Visitor { public: /** * Constructor. Does nothing. */ Visitor() { } /** * Destructor. Does nothing. */ virtual ~Visitor() { } /** * Does nothing. Should be overridden by child. */ virtual void visit() { } }; } #endif openalpr_2.2.4.orig/src/tclap/XorHandler.h000066400000000000000000000106521266464252400205100ustar00rootroot00000000000000 /****************************************************************************** * * file: XorHandler.h * * Copyright (c) 2003, Michael E. Smoot . * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_XORHANDLER_H #define TCLAP_XORHANDLER_H #include "Arg.h" #include #include #include #include namespace TCLAP { /** * This class handles lists of Arg's that are to be XOR'd on the command * line. This is used by CmdLine and you shouldn't ever use it. */ class XorHandler { protected: /** * The list of of lists of Arg's to be or'd together. */ std::vector< std::vector > _orList; public: /** * Constructor. Does nothing. */ XorHandler( ) : _orList(std::vector< std::vector >()) {} /** * Add a list of Arg*'s that will be orred together. * \param ors - list of Arg* that will be xor'd. */ void add( std::vector& ors ); /** * Checks whether the specified Arg is in one of the xor lists and * if it does match one, returns the size of the xor list that the * Arg matched. If the Arg matches, then it also sets the rest of * the Arg's in the list. You shouldn't use this. * \param a - The Arg to be checked. */ int check( const Arg* a ); /** * Returns the XOR specific short usage. */ std::string shortUsage(); /** * Prints the XOR specific long usage. * \param os - Stream to print to. */ void printLongUsage(std::ostream& os); /** * Simply checks whether the Arg is contained in one of the arg * lists. * \param a - The Arg to be checked. */ bool contains( const Arg* a ); std::vector< std::vector >& getXorList(); }; ////////////////////////////////////////////////////////////////////// //BEGIN XOR.cpp ////////////////////////////////////////////////////////////////////// inline void XorHandler::add( std::vector& ors ) { _orList.push_back( ors ); } inline int XorHandler::check( const Arg* a ) { // iterate over each XOR list for ( int i = 0; static_cast(i) < _orList.size(); i++ ) { // if the XOR list contains the arg.. ArgVectorIterator ait = std::find( _orList[i].begin(), _orList[i].end(), a ); if ( ait != _orList[i].end() ) { // first check to see if a mutually exclusive switch // has not already been set for ( ArgVectorIterator it = _orList[i].begin(); it != _orList[i].end(); it++ ) if ( a != (*it) && (*it)->isSet() ) throw(CmdLineParseException( "Mutually exclusive argument already set!", (*it)->toString())); // go through and set each arg that is not a for ( ArgVectorIterator it = _orList[i].begin(); it != _orList[i].end(); it++ ) if ( a != (*it) ) (*it)->xorSet(); // return the number of required args that have now been set if ( (*ait)->allowMore() ) return 0; else return static_cast(_orList[i].size()); } } if ( a->isRequired() ) return 1; else return 0; } inline bool XorHandler::contains( const Arg* a ) { for ( int i = 0; static_cast(i) < _orList.size(); i++ ) for ( ArgVectorIterator it = _orList[i].begin(); it != _orList[i].end(); it++ ) if ( a == (*it) ) return true; return false; } inline std::vector< std::vector >& XorHandler::getXorList() { return _orList; } ////////////////////////////////////////////////////////////////////// //END XOR.cpp ////////////////////////////////////////////////////////////////////// } //namespace TCLAP #endif openalpr_2.2.4.orig/src/tclap/ZshCompletionOutput.h000066400000000000000000000203371266464252400224620ustar00rootroot00000000000000// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- /****************************************************************************** * * file: ZshCompletionOutput.h * * Copyright (c) 2006, Oliver Kiddle * All rights reverved. * * See the file COPYING in the top directory of this distribution for * more information. * * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ #ifndef TCLAP_ZSHCOMPLETIONOUTPUT_H #define TCLAP_ZSHCOMPLETIONOUTPUT_H #include #include #include #include #include #include #include #include #include namespace TCLAP { /** * A class that generates a Zsh completion function as output from the usage() * method for the given CmdLine and its Args. */ class ZshCompletionOutput : public CmdLineOutput { public: ZshCompletionOutput(); /** * Prints the usage to stdout. Can be overridden to * produce alternative behavior. * \param c - The CmdLine object the output is generated for. */ virtual void usage(CmdLineInterface& c); /** * Prints the version to stdout. Can be overridden * to produce alternative behavior. * \param c - The CmdLine object the output is generated for. */ virtual void version(CmdLineInterface& c); /** * Prints (to stderr) an error message, short usage * Can be overridden to produce alternative behavior. * \param c - The CmdLine object the output is generated for. * \param e - The ArgException that caused the failure. */ virtual void failure(CmdLineInterface& c, ArgException& e ); protected: void basename( std::string& s ); void quoteSpecialChars( std::string& s ); std::string getMutexList( CmdLineInterface& _cmd, Arg* a ); void printOption( Arg* it, std::string mutex ); void printArg( Arg* it ); std::map common; char theDelimiter; }; ZshCompletionOutput::ZshCompletionOutput() : common(std::map()), theDelimiter('=') { common["host"] = "_hosts"; common["hostname"] = "_hosts"; common["file"] = "_files"; common["filename"] = "_files"; common["user"] = "_users"; common["username"] = "_users"; common["directory"] = "_directories"; common["path"] = "_directories"; common["url"] = "_urls"; } inline void ZshCompletionOutput::version(CmdLineInterface& _cmd) { std::cout << _cmd.getVersion() << std::endl; } inline void ZshCompletionOutput::usage(CmdLineInterface& _cmd ) { std::list argList = _cmd.getArgList(); std::string progName = _cmd.getProgramName(); std::string xversion = _cmd.getVersion(); theDelimiter = _cmd.getDelimiter(); basename(progName); std::cout << "#compdef " << progName << std::endl << std::endl << "# " << progName << " version " << _cmd.getVersion() << std::endl << std::endl << "_arguments -s -S"; for (ArgListIterator it = argList.begin(); it != argList.end(); it++) { if ( (*it)->shortID().at(0) == '<' ) printArg((*it)); else if ( (*it)->getFlag() != "-" ) printOption((*it), getMutexList(_cmd, *it)); } std::cout << std::endl; } inline void ZshCompletionOutput::failure( CmdLineInterface& _cmd, ArgException& e ) { static_cast(_cmd); // unused std::cout << e.what() << std::endl; } inline void ZshCompletionOutput::quoteSpecialChars( std::string& s ) { size_t idx = s.find_last_of(':'); while ( idx != std::string::npos ) { s.insert(idx, 1, '\\'); idx = s.find_last_of(':', idx); } idx = s.find_last_of('\''); while ( idx != std::string::npos ) { s.insert(idx, "'\\'"); if (idx == 0) idx = std::string::npos; else idx = s.find_last_of('\'', --idx); } } inline void ZshCompletionOutput::basename( std::string& s ) { size_t p = s.find_last_of('/'); if ( p != std::string::npos ) { s.erase(0, p + 1); } } inline void ZshCompletionOutput::printArg(Arg* a) { static int count = 1; std::cout << " \\" << std::endl << " '"; if ( a->acceptsMultipleValues() ) std::cout << '*'; else std::cout << count++; std::cout << ':'; if ( !a->isRequired() ) std::cout << ':'; std::cout << a->getName() << ':'; std::map::iterator compArg = common.find(a->getName()); if ( compArg != common.end() ) { std::cout << compArg->second; } else { std::cout << "_guard \"^-*\" " << a->getName(); } std::cout << '\''; } inline void ZshCompletionOutput::printOption(Arg* a, std::string mutex) { std::string flag = a->flagStartChar() + a->getFlag(); std::string name = a->nameStartString() + a->getName(); std::string desc = a->getDescription(); // remove full stop and capitalisation from description as // this is the convention for zsh function if (!desc.compare(0, 12, "(required) ")) { desc.erase(0, 12); } if (!desc.compare(0, 15, "(OR required) ")) { desc.erase(0, 15); } size_t len = desc.length(); if (len && desc.at(--len) == '.') { desc.erase(len); } if (len) { desc.replace(0, 1, 1, tolower(desc.at(0))); } std::cout << " \\" << std::endl << " '" << mutex; if ( a->getFlag().empty() ) { std::cout << name; } else { std::cout << "'{" << flag << ',' << name << "}'"; } if ( theDelimiter == '=' && a->isValueRequired() ) std::cout << "=-"; quoteSpecialChars(desc); std::cout << '[' << desc << ']'; if ( a->isValueRequired() ) { std::string arg = a->shortID(); arg.erase(0, arg.find_last_of(theDelimiter) + 1); if ( arg.at(arg.length()-1) == ']' ) arg.erase(arg.length()-1); if ( arg.at(arg.length()-1) == ']' ) { arg.erase(arg.length()-1); } if ( arg.at(0) == '<' ) { arg.erase(arg.length()-1); arg.erase(0, 1); } size_t p = arg.find('|'); if ( p != std::string::npos ) { do { arg.replace(p, 1, 1, ' '); } while ( (p = arg.find_first_of('|', p)) != std::string::npos ); quoteSpecialChars(arg); std::cout << ": :(" << arg << ')'; } else { std::cout << ':' << arg; std::map::iterator compArg = common.find(arg); if ( compArg != common.end() ) { std::cout << ':' << compArg->second; } } } std::cout << '\''; } inline std::string ZshCompletionOutput::getMutexList( CmdLineInterface& _cmd, Arg* a) { XorHandler xorHandler = _cmd.getXorHandler(); std::vector< std::vector > xorList = xorHandler.getXorList(); if (a->getName() == "help" || a->getName() == "version") { return "(-)"; } std::ostringstream list; if ( a->acceptsMultipleValues() ) { list << '*'; } for ( int i = 0; static_cast(i) < xorList.size(); i++ ) { for ( ArgVectorIterator it = xorList[i].begin(); it != xorList[i].end(); it++) if ( a == (*it) ) { list << '('; for ( ArgVectorIterator iu = xorList[i].begin(); iu != xorList[i].end(); iu++ ) { bool notCur = (*iu) != a; bool hasFlag = !(*iu)->getFlag().empty(); if ( iu != xorList[i].begin() && (notCur || hasFlag) ) list << ' '; if (hasFlag) list << (*iu)->flagStartChar() << (*iu)->getFlag() << ' '; if ( notCur || hasFlag ) list << (*iu)->nameStartString() << (*iu)->getName(); } list << ')'; return list.str(); } } // wasn't found in xor list if (!a->getFlag().empty()) { list << "(" << a->flagStartChar() << a->getFlag() << ' ' << a->nameStartString() << a->getName() << ')'; } return list.str(); } } //namespace TCLAP #endif openalpr_2.2.4.orig/src/tests/000077500000000000000000000000001266464252400163245ustar00rootroot00000000000000openalpr_2.2.4.orig/src/tests/CMakeLists.txt000066400000000000000000000006701266464252400210670ustar00rootroot00000000000000enable_testing() add_definitions( -DOPENALPR_TESTING_CONFIG_PATH="${CMAKE_SOURCE_DIR}/../config/openalpr.conf.in") add_definitions( -DOPENALPR_TESTING_RUNTIME_DIR="${CMAKE_SOURCE_DIR}/../runtime_data/") ADD_EXECUTABLE( unittests test_api.cpp test_utility.cpp test_config.cpp test_regex.cpp ) TARGET_LINK_LIBRARIES(unittests openalpr ) add_test(unittests unittests) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})openalpr_2.2.4.orig/src/tests/catch.hpp000066400000000000000000013176241266464252400201350ustar00rootroot00000000000000/* * Catch v1.3.0 * Generated: 2015-12-04 10:18:17.055188 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_CATCH_HPP_INCLUDED #ifdef __clang__ # pragma clang system_header #elif defined __GNUC__ # pragma GCC system_header #endif // #included from: internal/catch_suppress_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC # pragma clang diagnostic ignored "-Wglobal-constructors" # pragma clang diagnostic ignored "-Wvariadic-macros" # pragma clang diagnostic ignored "-Wc99-extensions" # pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wc++98-compat" # pragma clang diagnostic ignored "-Wc++98-compat-pedantic" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ # pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL #endif #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED # define CLARA_CONFIG_MAIN # endif #endif // #included from: internal/catch_notimplemented_exception.h #define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED // #included from: catch_common.h #define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) #define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #define INTERNAL_CATCH_STRINGIFY2( expr ) #expr #define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) #include #include #include // #included from: catch_compiler_capabilities.h #define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED // Detect a number of compiler features - mostly C++11/14 conformance - by compiler // The following features are defined: // // CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? // CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? // CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods // CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? // CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported // CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? // CATCH_CONFIG_CPP11_OVERRIDE : is override supported? // CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too // **************** // In general each macro has a _NO_ form // (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. // All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 #ifdef __clang__ # if __has_feature(cxx_nullptr) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # endif # if __has_feature(cxx_noexcept) # define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT # endif #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// // Borland #ifdef __BORLANDC__ #endif // __BORLANDC__ //////////////////////////////////////////////////////////////////////////////// // EDG #ifdef __EDG_VERSION__ #endif // __EDG_VERSION__ //////////////////////////////////////////////////////////////////////////////// // Digital Mars #ifdef __DMC__ #endif // __DMC__ //////////////////////////////////////////////////////////////////////////////// // GCC #ifdef __GNUC__ #if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR #endif // - otherwise more recent versions define __cplusplus >= 201103L // and will get picked up below #endif // __GNUC__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ #ifdef _MSC_VER #if (_MSC_VER >= 1600) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR #endif #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) #define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS #endif #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// // Use variadic macros if the compiler supports them #if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ ( defined __GNUC__ && __GNUC__ >= 3 ) || \ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) #define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS #endif //////////////////////////////////////////////////////////////////////////////// // C++ language feature support // catch all support for C++11 #if defined(__cplusplus) && __cplusplus >= 201103L # define CATCH_CPP11_OR_GREATER # if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # endif # ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT # define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT # endif # ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS # define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS # endif # ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM # define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM # endif # ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE # define CATCH_INTERNAL_CONFIG_CPP11_TUPLE # endif # ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS # define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS # endif # if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) # define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG # endif # if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) # define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE # endif # if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR # endif #endif // __cplusplus >= 201103L // Now set the actual defines based on the above + anything the user has configured #if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_NULLPTR #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_NOEXCEPT #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_GENERATED_METHODS #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_IS_ENUM #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_TUPLE #endif #if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) # define CATCH_CONFIG_VARIADIC_MACROS #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_LONG_LONG #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_OVERRIDE #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_UNIQUE_PTR #endif // noexcept support: #if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) # define CATCH_NOEXCEPT noexcept # define CATCH_NOEXCEPT_IS(x) noexcept(x) #else # define CATCH_NOEXCEPT throw() # define CATCH_NOEXCEPT_IS(x) #endif // nullptr support #ifdef CATCH_CONFIG_CPP11_NULLPTR # define CATCH_NULL nullptr #else # define CATCH_NULL NULL #endif // override support #ifdef CATCH_CONFIG_CPP11_OVERRIDE # define CATCH_OVERRIDE override #else # define CATCH_OVERRIDE #endif // unique_ptr support #ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR # define CATCH_AUTO_PTR( T ) std::unique_ptr #else # define CATCH_AUTO_PTR( T ) std::auto_ptr #endif namespace Catch { struct IConfig; struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { #ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; #else NonCopyable( NonCopyable const& info ); NonCopyable& operator = ( NonCopyable const& ); #endif protected: NonCopyable() {} virtual ~NonCopyable(); }; class SafeBool { public: typedef void (SafeBool::*type)() const; static type makeSafe( bool value ) { return value ? &SafeBool::trueValue : 0; } private: void trueValue() const {} }; template inline void deleteAll( ContainerT& container ) { typename ContainerT::const_iterator it = container.begin(); typename ContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) delete *it; } template inline void deleteAllValues( AssociativeContainerT& container ) { typename AssociativeContainerT::const_iterator it = container.begin(); typename AssociativeContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) delete it->second; } bool startsWith( std::string const& s, std::string const& prefix ); bool endsWith( std::string const& s, std::string const& suffix ); bool contains( std::string const& s, std::string const& infix ); void toLowerInPlace( std::string& s ); std::string toLower( std::string const& s ); std::string trim( std::string const& str ); bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); struct pluralise { pluralise( std::size_t count, std::string const& label ); friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); std::size_t m_count; std::string m_label; }; struct SourceLineInfo { SourceLineInfo(); SourceLineInfo( char const* _file, std::size_t _line ); SourceLineInfo( SourceLineInfo const& other ); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo& operator = ( SourceLineInfo && ) = default; # endif bool empty() const; bool operator == ( SourceLineInfo const& other ) const; bool operator < ( SourceLineInfo const& other ) const; std::string file; std::size_t line; }; std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); // This is just here to avoid compiler warnings with macro constants and boolean literals inline bool isTrue( bool value ){ return value; } inline bool alwaysTrue() { return true; } inline bool alwaysFalse() { return false; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); void seedRng( IConfig const& config ); unsigned int rngSeed(); // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { std::string operator+() { return std::string(); } }; template T const& operator + ( T const& value, StreamEndStop ) { return value; } } #define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) #define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); #include namespace Catch { class NotImplementedException : public std::exception { public: NotImplementedException( SourceLineInfo const& lineInfo ); NotImplementedException( NotImplementedException const& ) {} virtual ~NotImplementedException() CATCH_NOEXCEPT {} virtual const char* what() const CATCH_NOEXCEPT; private: std::string m_what; SourceLineInfo m_lineInfo; }; } // end namespace Catch /////////////////////////////////////////////////////////////////////////////// #define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) // #included from: internal/catch_context.h #define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED // #included from: catch_interfaces_generators.h #define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED #include namespace Catch { struct IGeneratorInfo { virtual ~IGeneratorInfo(); virtual bool moveNext() = 0; virtual std::size_t getCurrentIndex() const = 0; }; struct IGeneratorsForTest { virtual ~IGeneratorsForTest(); virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; virtual bool moveNext() = 0; }; IGeneratorsForTest* createGeneratorsForTest(); } // end namespace Catch // #included from: catch_ptr.hpp #define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif namespace Catch { // An intrusive reference counting smart pointer. // T must implement addRef() and release() methods // typically implementing the IShared interface template class Ptr { public: Ptr() : m_p( CATCH_NULL ){} Ptr( T* p ) : m_p( p ){ if( m_p ) m_p->addRef(); } Ptr( Ptr const& other ) : m_p( other.m_p ){ if( m_p ) m_p->addRef(); } ~Ptr(){ if( m_p ) m_p->release(); } void reset() { if( m_p ) m_p->release(); m_p = CATCH_NULL; } Ptr& operator = ( T* p ){ Ptr temp( p ); swap( temp ); return *this; } Ptr& operator = ( Ptr const& other ){ Ptr temp( other ); swap( temp ); return *this; } void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } T* get() const{ return m_p; } T& operator*() const { return *m_p; } T* operator->() const { return m_p; } bool operator !() const { return m_p == CATCH_NULL; } operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } private: T* m_p; }; struct IShared : NonCopyable { virtual ~IShared(); virtual void addRef() const = 0; virtual void release() const = 0; }; template struct SharedImpl : T { SharedImpl() : m_rc( 0 ){} virtual void addRef() const { ++m_rc; } virtual void release() const { if( --m_rc == 0 ) delete this; } mutable unsigned int m_rc; }; } // end namespace Catch #ifdef __clang__ #pragma clang diagnostic pop #endif #include #include #include namespace Catch { class TestCase; class Stream; struct IResultCapture; struct IRunner; struct IGeneratorsForTest; struct IConfig; struct IContext { virtual ~IContext(); virtual IResultCapture* getResultCapture() = 0; virtual IRunner* getRunner() = 0; virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; virtual bool advanceGeneratorsForCurrentTest() = 0; virtual Ptr getConfig() const = 0; }; struct IMutableContext : IContext { virtual ~IMutableContext(); virtual void setResultCapture( IResultCapture* resultCapture ) = 0; virtual void setRunner( IRunner* runner ) = 0; virtual void setConfig( Ptr const& config ) = 0; }; IContext& getCurrentContext(); IMutableContext& getCurrentMutableContext(); void cleanUpContext(); Stream createStream( std::string const& streamName ); } // #included from: internal/catch_test_registry.hpp #define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED // #included from: catch_interfaces_testcase.h #define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED #include namespace Catch { class TestSpec; struct ITestCase : IShared { virtual void invoke () const = 0; protected: virtual ~ITestCase(); }; class TestCase; struct IConfig; struct ITestCaseRegistry { virtual ~ITestCaseRegistry(); virtual std::vector const& getAllTests() const = 0; virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); } namespace Catch { template class MethodTestCase : public SharedImpl { public: MethodTestCase( void (C::*method)() ) : m_method( method ) {} virtual void invoke() const { C obj; (obj.*m_method)(); } private: virtual ~MethodTestCase() {} void (C::*m_method)(); }; typedef void(*TestFunction)(); struct NameAndDesc { NameAndDesc( const char* _name = "", const char* _description= "" ) : name( _name ), description( _description ) {} const char* name; const char* description; }; void registerTestCase ( ITestCase* testCase, char const* className, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ); struct AutoReg { AutoReg ( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ); template AutoReg ( void (C::*method)(), char const* className, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ) { registerTestCase ( new MethodTestCase( method ), className, nameAndDesc, lineInfo ); } ~AutoReg(); private: AutoReg( AutoReg const& ); void operator= ( AutoReg const& ); }; void registerTestCaseFunction ( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ); } // end namespace Catch #ifdef CATCH_CONFIG_VARIADIC_MACROS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE( ... ) \ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\ namespace{ \ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ void test(); \ }; \ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ } \ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); #else /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ namespace{ \ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ void test(); \ }; \ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ } \ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); #endif // #included from: internal/catch_capture.hpp #define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED // #included from: catch_result_builder.h #define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED // #included from: catch_result_type.h #define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED namespace Catch { // ResultWas::OfType enum struct ResultWas { enum OfType { Unknown = -1, Ok = 0, Info = 1, Warning = 2, FailureBit = 0x10, ExpressionFailed = FailureBit | 1, ExplicitFailure = FailureBit | 2, Exception = 0x100 | FailureBit, ThrewException = Exception | 1, DidntThrowException = Exception | 2, FatalErrorCondition = 0x200 | FailureBit }; }; inline bool isOk( ResultWas::OfType resultType ) { return ( resultType & ResultWas::FailureBit ) == 0; } inline bool isJustInfo( int flags ) { return flags == ResultWas::Info; } // ResultDisposition::Flags enum struct ResultDisposition { enum Flags { Normal = 0x01, ContinueOnFailure = 0x02, // Failures fail test, but execution continues FalseTest = 0x04, // Prefix expression with ! SuppressFail = 0x08 // Failures are reported but do not fail the test }; }; inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { return static_cast( static_cast( lhs ) | static_cast( rhs ) ); } inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } } // end namespace Catch // #included from: catch_assertionresult.h #define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED #include namespace Catch { struct AssertionInfo { AssertionInfo() {} AssertionInfo( std::string const& _macroName, SourceLineInfo const& _lineInfo, std::string const& _capturedExpression, ResultDisposition::Flags _resultDisposition ); std::string macroName; SourceLineInfo lineInfo; std::string capturedExpression; ResultDisposition::Flags resultDisposition; }; struct AssertionResultData { AssertionResultData() : resultType( ResultWas::Unknown ) {} std::string reconstructedExpression; std::string message; ResultWas::OfType resultType; }; class AssertionResult { public: AssertionResult(); AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); ~AssertionResult(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS AssertionResult( AssertionResult const& ) = default; AssertionResult( AssertionResult && ) = default; AssertionResult& operator = ( AssertionResult const& ) = default; AssertionResult& operator = ( AssertionResult && ) = default; # endif bool isOk() const; bool succeeded() const; ResultWas::OfType getResultType() const; bool hasExpression() const; bool hasMessage() const; std::string getExpression() const; std::string getExpressionInMacro() const; bool hasExpandedExpression() const; std::string getExpandedExpression() const; std::string getMessage() const; SourceLineInfo getSourceInfo() const; std::string getTestMacroName() const; protected: AssertionInfo m_info; AssertionResultData m_resultData; }; } // end namespace Catch // #included from: catch_matchers.hpp #define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED namespace Catch { namespace Matchers { namespace Impl { namespace Generic { template class AllOf; template class AnyOf; template class Not; } template struct Matcher : SharedImpl { typedef ExpressionT ExpressionType; virtual ~Matcher() {} virtual Ptr clone() const = 0; virtual bool match( ExpressionT const& expr ) const = 0; virtual std::string toString() const = 0; Generic::AllOf operator && ( Matcher const& other ) const; Generic::AnyOf operator || ( Matcher const& other ) const; Generic::Not operator ! () const; }; template struct MatcherImpl : Matcher { virtual Ptr > clone() const { return Ptr >( new DerivedT( static_cast( *this ) ) ); } }; namespace Generic { template class Not : public MatcherImpl, ExpressionT> { public: explicit Not( Matcher const& matcher ) : m_matcher(matcher.clone()) {} Not( Not const& other ) : m_matcher( other.m_matcher ) {} virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { return !m_matcher->match( expr ); } virtual std::string toString() const CATCH_OVERRIDE { return "not " + m_matcher->toString(); } private: Ptr< Matcher > m_matcher; }; template class AllOf : public MatcherImpl, ExpressionT> { public: AllOf() {} AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} AllOf& add( Matcher const& matcher ) { m_matchers.push_back( matcher.clone() ); return *this; } virtual bool match( ExpressionT const& expr ) const { for( std::size_t i = 0; i < m_matchers.size(); ++i ) if( !m_matchers[i]->match( expr ) ) return false; return true; } virtual std::string toString() const { std::ostringstream oss; oss << "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) oss << " and "; oss << m_matchers[i]->toString(); } oss << " )"; return oss.str(); } AllOf operator && ( Matcher const& other ) const { AllOf allOfExpr( *this ); allOfExpr.add( other ); return allOfExpr; } private: std::vector > > m_matchers; }; template class AnyOf : public MatcherImpl, ExpressionT> { public: AnyOf() {} AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} AnyOf& add( Matcher const& matcher ) { m_matchers.push_back( matcher.clone() ); return *this; } virtual bool match( ExpressionT const& expr ) const { for( std::size_t i = 0; i < m_matchers.size(); ++i ) if( m_matchers[i]->match( expr ) ) return true; return false; } virtual std::string toString() const { std::ostringstream oss; oss << "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) oss << " or "; oss << m_matchers[i]->toString(); } oss << " )"; return oss.str(); } AnyOf operator || ( Matcher const& other ) const { AnyOf anyOfExpr( *this ); anyOfExpr.add( other ); return anyOfExpr; } private: std::vector > > m_matchers; }; } // namespace Generic template Generic::AllOf Matcher::operator && ( Matcher const& other ) const { Generic::AllOf allOfExpr; allOfExpr.add( *this ); allOfExpr.add( other ); return allOfExpr; } template Generic::AnyOf Matcher::operator || ( Matcher const& other ) const { Generic::AnyOf anyOfExpr; anyOfExpr.add( *this ); anyOfExpr.add( other ); return anyOfExpr; } template Generic::Not Matcher::operator ! () const { return Generic::Not( *this ); } namespace StdString { inline std::string makeString( std::string const& str ) { return str; } inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } struct CasedString { CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) : m_caseSensitivity( caseSensitivity ), m_str( adjustString( str ) ) {} std::string adjustString( std::string const& str ) const { return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; } std::string toStringSuffix() const { return m_caseSensitivity == CaseSensitive::No ? " (case insensitive)" : ""; } CaseSensitive::Choice m_caseSensitivity; std::string m_str; }; struct Equals : MatcherImpl { Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) : m_data( str, caseSensitivity ) {} Equals( Equals const& other ) : m_data( other.m_data ){} virtual ~Equals(); virtual bool match( std::string const& expr ) const { return m_data.m_str == m_data.adjustString( expr );; } virtual std::string toString() const { return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); } CasedString m_data; }; struct Contains : MatcherImpl { Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) : m_data( substr, caseSensitivity ){} Contains( Contains const& other ) : m_data( other.m_data ){} virtual ~Contains(); virtual bool match( std::string const& expr ) const { return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; } virtual std::string toString() const { return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); } CasedString m_data; }; struct StartsWith : MatcherImpl { StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) : m_data( substr, caseSensitivity ){} StartsWith( StartsWith const& other ) : m_data( other.m_data ){} virtual ~StartsWith(); virtual bool match( std::string const& expr ) const { return m_data.adjustString( expr ).find( m_data.m_str ) == 0; } virtual std::string toString() const { return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); } CasedString m_data; }; struct EndsWith : MatcherImpl { EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) : m_data( substr, caseSensitivity ){} EndsWith( EndsWith const& other ) : m_data( other.m_data ){} virtual ~EndsWith(); virtual bool match( std::string const& expr ) const { return m_data.adjustString( expr ).find( m_data.m_str ) == expr.size() - m_data.m_str.size(); } virtual std::string toString() const { return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); } CasedString m_data; }; } // namespace StdString } // namespace Impl // The following functions create the actual matcher objects. // This allows the types to be inferred template inline Impl::Generic::Not Not( Impl::Matcher const& m ) { return Impl::Generic::Not( m ); } template inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, Impl::Matcher const& m2 ) { return Impl::Generic::AllOf().add( m1 ).add( m2 ); } template inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, Impl::Matcher const& m2, Impl::Matcher const& m3 ) { return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); } template inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, Impl::Matcher const& m2 ) { return Impl::Generic::AnyOf().add( m1 ).add( m2 ); } template inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, Impl::Matcher const& m2, Impl::Matcher const& m3 ) { return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); } inline Impl::StdString::Equals Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { return Impl::StdString::Equals( str, caseSensitivity ); } inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity ); } inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { return Impl::StdString::Contains( substr, caseSensitivity ); } inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity ); } inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { return Impl::StdString::StartsWith( substr ); } inline Impl::StdString::StartsWith StartsWith( const char* substr ) { return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); } inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { return Impl::StdString::EndsWith( substr ); } inline Impl::StdString::EndsWith EndsWith( const char* substr ) { return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); } } // namespace Matchers using namespace Matchers; } // namespace Catch namespace Catch { struct TestFailureException{}; template class ExpressionLhs; struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; struct CopyableStream { CopyableStream() {} CopyableStream( CopyableStream const& other ) { oss << other.oss.str(); } CopyableStream& operator=( CopyableStream const& other ) { oss.str(""); oss << other.oss.str(); return *this; } std::ostringstream oss; }; class ResultBuilder { public: ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg = "" ); template ExpressionLhs operator <= ( T const& operand ); ExpressionLhs operator <= ( bool value ); template ResultBuilder& operator << ( T const& value ) { m_stream.oss << value; return *this; } template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); ResultBuilder& setResultType( ResultWas::OfType result ); ResultBuilder& setResultType( bool result ); ResultBuilder& setLhs( std::string const& lhs ); ResultBuilder& setRhs( std::string const& rhs ); ResultBuilder& setOp( std::string const& op ); void endExpression(); std::string reconstructExpression() const; AssertionResult build() const; void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); void captureResult( ResultWas::OfType resultType ); void captureExpression(); void captureExpectedException( std::string const& expectedMessage ); void captureExpectedException( Matchers::Impl::Matcher const& matcher ); void handleResult( AssertionResult const& result ); void react(); bool shouldDebugBreak() const; bool allowThrows() const; private: AssertionInfo m_assertionInfo; AssertionResultData m_data; struct ExprComponents { ExprComponents() : testFalse( false ) {} bool testFalse; std::string lhs, rhs, op; } m_exprComponents; CopyableStream m_stream; bool m_shouldDebugBreak; bool m_shouldThrow; }; } // namespace Catch // Include after due to circular dependency: // #included from: catch_expression_lhs.hpp #define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED // #included from: catch_evaluate.hpp #define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4389) // '==' : signed/unsigned mismatch #endif #include namespace Catch { namespace Internal { enum Operator { IsEqualTo, IsNotEqualTo, IsLessThan, IsGreaterThan, IsLessThanOrEqualTo, IsGreaterThanOrEqualTo }; template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; template inline T& opCast(T const& t) { return const_cast(t); } // nullptr_t support based on pull request #154 from Konstantin Baumann #ifdef CATCH_CONFIG_CPP11_NULLPTR inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } #endif // CATCH_CONFIG_CPP11_NULLPTR // So the compare overloads can be operator agnostic we convey the operator as a template // enum, which is used to specialise an Evaluator for doing the comparison. template class Evaluator{}; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs) { return opCast( lhs ) == opCast( rhs ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return opCast( lhs ) != opCast( rhs ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return opCast( lhs ) < opCast( rhs ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return opCast( lhs ) > opCast( rhs ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return opCast( lhs ) >= opCast( rhs ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return opCast( lhs ) <= opCast( rhs ); } }; template bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { return Evaluator::evaluate( lhs, rhs ); } // This level of indirection allows us to specialise for integer types // to avoid signed/ unsigned warnings // "base" overload template bool compare( T1 const& lhs, T2 const& rhs ) { return Evaluator::evaluate( lhs, rhs ); } // unsigned X to int template bool compare( unsigned int lhs, int rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned long lhs, int rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned char lhs, int rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } // unsigned X to long template bool compare( unsigned int lhs, long rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned long lhs, long rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned char lhs, long rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } // int to unsigned X template bool compare( int lhs, unsigned int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( int lhs, unsigned long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( int lhs, unsigned char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // long to unsigned X template bool compare( long lhs, unsigned int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long lhs, unsigned long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long lhs, unsigned char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // pointer to long (when comparing against NULL) template bool compare( long lhs, T* rhs ) { return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); } template bool compare( T* lhs, long rhs ) { return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); } // pointer to int (when comparing against NULL) template bool compare( int lhs, T* rhs ) { return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); } template bool compare( T* lhs, int rhs ) { return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); } #ifdef CATCH_CONFIG_CPP11_LONG_LONG // long long to unsigned X template bool compare( long long lhs, unsigned int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long long lhs, unsigned long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long long lhs, unsigned long long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long long lhs, unsigned char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // unsigned long long to X template bool compare( unsigned long long lhs, int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( unsigned long long lhs, long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( unsigned long long lhs, long long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( unsigned long long lhs, char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // pointer to long long (when comparing against NULL) template bool compare( long long lhs, T* rhs ) { return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); } template bool compare( T* lhs, long long rhs ) { return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); } #endif // CATCH_CONFIG_CPP11_LONG_LONG #ifdef CATCH_CONFIG_CPP11_NULLPTR // pointer to nullptr_t (when comparing against nullptr) template bool compare( std::nullptr_t, T* rhs ) { return Evaluator::evaluate( nullptr, rhs ); } template bool compare( T* lhs, std::nullptr_t ) { return Evaluator::evaluate( lhs, nullptr ); } #endif // CATCH_CONFIG_CPP11_NULLPTR } // end of namespace Internal } // end of namespace Catch #ifdef _MSC_VER #pragma warning(pop) #endif // #included from: catch_tostring.h #define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED #include #include #include #include #include #ifdef __OBJC__ // #included from: catch_objc_arc.hpp #define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED #import #ifdef __has_feature #define CATCH_ARC_ENABLED __has_feature(objc_arc) #else #define CATCH_ARC_ENABLED 0 #endif void arcSafeRelease( NSObject* obj ); id performOptionalSelector( id obj, SEL sel ); #if !CATCH_ARC_ENABLED inline void arcSafeRelease( NSObject* obj ) { [obj release]; } inline id performOptionalSelector( id obj, SEL sel ) { if( [obj respondsToSelector: sel] ) return [obj performSelector: sel]; return nil; } #define CATCH_UNSAFE_UNRETAINED #define CATCH_ARC_STRONG #else inline void arcSafeRelease( NSObject* ){} inline id performOptionalSelector( id obj, SEL sel ) { #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" #endif if( [obj respondsToSelector: sel] ) return [obj performSelector: sel]; #ifdef __clang__ #pragma clang diagnostic pop #endif return nil; } #define CATCH_UNSAFE_UNRETAINED __unsafe_unretained #define CATCH_ARC_STRONG __strong #endif #endif #ifdef CATCH_CONFIG_CPP11_TUPLE #include #endif #ifdef CATCH_CONFIG_CPP11_IS_ENUM #include #endif namespace Catch { // Why we're here. template std::string toString( T const& value ); // Built in overloads std::string toString( std::string const& value ); std::string toString( std::wstring const& value ); std::string toString( const char* const value ); std::string toString( char* const value ); std::string toString( const wchar_t* const value ); std::string toString( wchar_t* const value ); std::string toString( int value ); std::string toString( unsigned long value ); std::string toString( unsigned int value ); std::string toString( const double value ); std::string toString( const float value ); std::string toString( bool value ); std::string toString( char value ); std::string toString( signed char value ); std::string toString( unsigned char value ); #ifdef CATCH_CONFIG_CPP11_LONG_LONG std::string toString( long long value ); std::string toString( unsigned long long value ); #endif #ifdef CATCH_CONFIG_CPP11_NULLPTR std::string toString( std::nullptr_t ); #endif #ifdef __OBJC__ std::string toString( NSString const * const& nsstring ); std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); std::string toString( NSObject* const& nsObject ); #endif namespace Detail { extern const std::string unprintableString; struct BorgType { template BorgType( T const& ); }; struct TrueType { char sizer[1]; }; struct FalseType { char sizer[2]; }; TrueType& testStreamable( std::ostream& ); FalseType testStreamable( FalseType ); FalseType operator<<( std::ostream const&, BorgType const& ); template struct IsStreamInsertable { static std::ostream &s; static T const&t; enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; }; #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template::value > struct EnumStringMaker { static std::string convert( T const& ) { return unprintableString; } }; template struct EnumStringMaker { static std::string convert( T const& v ) { return ::Catch::toString( static_cast::type>(v) ); } }; #endif template struct StringMakerBase { #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template static std::string convert( T const& v ) { return EnumStringMaker::convert( v ); } #else template static std::string convert( T const& ) { return unprintableString; } #endif }; template<> struct StringMakerBase { template static std::string convert( T const& _value ) { std::ostringstream oss; oss << _value; return oss.str(); } }; std::string rawMemoryToString( const void *object, std::size_t size ); template inline std::string rawMemoryToString( const T& object ) { return rawMemoryToString( &object, sizeof(object) ); } } // end namespace Detail template struct StringMaker : Detail::StringMakerBase::value> {}; template struct StringMaker { template static std::string convert( U* p ) { if( !p ) return "NULL"; else return Detail::rawMemoryToString( p ); } }; template struct StringMaker { static std::string convert( R C::* p ) { if( !p ) return "NULL"; else return Detail::rawMemoryToString( p ); } }; namespace Detail { template std::string rangeToString( InputIterator first, InputIterator last ); } //template //struct StringMaker > { // static std::string convert( std::vector const& v ) { // return Detail::rangeToString( v.begin(), v.end() ); // } //}; template std::string toString( std::vector const& v ) { return Detail::rangeToString( v.begin(), v.end() ); } #ifdef CATCH_CONFIG_CPP11_TUPLE // toString for tuples namespace TupleDetail { template< typename Tuple, std::size_t N = 0, bool = (N < std::tuple_size::value) > struct ElementPrinter { static void print( const Tuple& tuple, std::ostream& os ) { os << ( N ? ", " : " " ) << Catch::toString(std::get(tuple)); ElementPrinter::print(tuple,os); } }; template< typename Tuple, std::size_t N > struct ElementPrinter { static void print( const Tuple&, std::ostream& ) {} }; } template struct StringMaker> { static std::string convert( const std::tuple& tuple ) { std::ostringstream os; os << '{'; TupleDetail::ElementPrinter>::print( tuple, os ); os << " }"; return os.str(); } }; #endif // CATCH_CONFIG_CPP11_TUPLE namespace Detail { template std::string makeString( T const& value ) { return StringMaker::convert( value ); } } // end namespace Detail /// \brief converts any type to a string /// /// The default template forwards on to ostringstream - except when an /// ostringstream overload does not exist - in which case it attempts to detect /// that and writes {?}. /// Overload (not specialise) this template for custom typs that you don't want /// to provide an ostream overload for. template std::string toString( T const& value ) { return StringMaker::convert( value ); } namespace Detail { template std::string rangeToString( InputIterator first, InputIterator last ) { std::ostringstream oss; oss << "{ "; if( first != last ) { oss << Catch::toString( *first ); for( ++first ; first != last ; ++first ) oss << ", " << Catch::toString( *first ); } oss << " }"; return oss.str(); } } } // end namespace Catch namespace Catch { // Wraps the LHS of an expression and captures the operator and RHS (if any) - // wrapping them all in a ResultBuilder object template class ExpressionLhs { ExpressionLhs& operator = ( ExpressionLhs const& ); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS ExpressionLhs& operator = ( ExpressionLhs && ) = delete; # endif public: ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS ExpressionLhs( ExpressionLhs const& ) = default; ExpressionLhs( ExpressionLhs && ) = default; # endif template ResultBuilder& operator == ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator != ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator < ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator > ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator <= ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator >= ( RhsT const& rhs ) { return captureExpression( rhs ); } ResultBuilder& operator == ( bool rhs ) { return captureExpression( rhs ); } ResultBuilder& operator != ( bool rhs ) { return captureExpression( rhs ); } void endExpression() { bool value = m_lhs ? true : false; m_rb .setLhs( Catch::toString( value ) ) .setResultType( value ) .endExpression(); } // Only simple binary expressions are allowed on the LHS. // If more complex compositions are required then place the sub expression in parentheses template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); private: template ResultBuilder& captureExpression( RhsT const& rhs ) { return m_rb .setResultType( Internal::compare( m_lhs, rhs ) ) .setLhs( Catch::toString( m_lhs ) ) .setRhs( Catch::toString( rhs ) ) .setOp( Internal::OperatorTraits::getName() ); } private: ResultBuilder& m_rb; T m_lhs; }; } // end namespace Catch namespace Catch { template inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { return ExpressionLhs( *this, operand ); } inline ExpressionLhs ResultBuilder::operator <= ( bool value ) { return ExpressionLhs( *this, value ); } } // namespace Catch // #included from: catch_message.h #define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED #include namespace Catch { struct MessageInfo { MessageInfo( std::string const& _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ); std::string macroName; SourceLineInfo lineInfo; ResultWas::OfType type; std::string message; unsigned int sequence; bool operator == ( MessageInfo const& other ) const { return sequence == other.sequence; } bool operator < ( MessageInfo const& other ) const { return sequence < other.sequence; } private: static unsigned int globalCount; }; struct MessageBuilder { MessageBuilder( std::string const& macroName, SourceLineInfo const& lineInfo, ResultWas::OfType type ) : m_info( macroName, lineInfo, type ) {} template MessageBuilder& operator << ( T const& value ) { m_stream << value; return *this; } MessageInfo m_info; std::ostringstream m_stream; }; class ScopedMessage { public: ScopedMessage( MessageBuilder const& builder ); ScopedMessage( ScopedMessage const& other ); ~ScopedMessage(); MessageInfo m_info; }; } // end namespace Catch // #included from: catch_interfaces_capture.h #define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED #include namespace Catch { class TestCase; class AssertionResult; struct AssertionInfo; struct SectionInfo; struct SectionEndInfo; struct MessageInfo; class ScopedMessageBuilder; struct Counts; struct IResultCapture { virtual ~IResultCapture(); virtual void assertionEnded( AssertionResult const& result ) = 0; virtual bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) = 0; virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; virtual void pushScopedMessage( MessageInfo const& message ) = 0; virtual void popScopedMessage( MessageInfo const& message ) = 0; virtual std::string getCurrentTestName() const = 0; virtual const AssertionResult* getLastResult() const = 0; virtual void handleFatalErrorCondition( std::string const& message ) = 0; }; IResultCapture& getResultCapture(); } // #included from: catch_debugger.h #define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED // #included from: catch_platform.h #define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) #define CATCH_PLATFORM_MAC #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #define CATCH_PLATFORM_IPHONE #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) #define CATCH_PLATFORM_WINDOWS #endif #include namespace Catch{ bool isDebuggerActive(); void writeToDebugConsole( std::string const& text ); } #ifdef CATCH_PLATFORM_MAC // The following code snippet based on: // http://cocoawithlove.com/2008/03/break-into-debugger.html #ifdef DEBUG #if defined(__ppc64__) || defined(__ppc__) #define CATCH_BREAK_INTO_DEBUGGER() \ if( Catch::isDebuggerActive() ) { \ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ : : : "memory","r0","r3","r4" ); \ } #else #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} #endif #endif #elif defined(_MSC_VER) #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } #elif defined(__MINGW32__) extern "C" __declspec(dllimport) void __stdcall DebugBreak(); #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } #endif #ifndef CATCH_BREAK_INTO_DEBUGGER #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); #endif // #included from: catch_interfaces_runner.h #define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED namespace Catch { class TestCase; struct IRunner { virtual ~IRunner(); virtual bool aborting() const = 0; }; } /////////////////////////////////////////////////////////////////////////////// // In the event of a failure works out if the debugger needs to be invoked // and/or an exception thrown and takes appropriate action. // This needs to be done as a macro so the debugger will stop in the user // source code rather than in Catch library code #define INTERNAL_CATCH_REACT( resultBuilder ) \ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ resultBuilder.react(); /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ try { \ ( __catchResult <= expr ).endExpression(); \ } \ catch( ... ) { \ __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ if( Catch::getResultCapture().getLastResult()->succeeded() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ if( !Catch::getResultCapture().getLastResult()->succeeded() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ try { \ expr; \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ __catchResult.useActiveException( resultDisposition ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ if( __catchResult.allowThrows() ) \ try { \ expr; \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ __catchResult.captureExpectedException( matcher ); \ } \ else \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ if( __catchResult.allowThrows() ) \ try { \ expr; \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( exceptionType ) { \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ __catchResult.useActiveException( resultDisposition ); \ } \ else \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #ifdef CATCH_CONFIG_VARIADIC_MACROS #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ __catchResult.captureResult( messageType ); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) #else #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << log + ::Catch::StreamEndStop(); \ __catchResult.captureResult( messageType ); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) #endif /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_INFO( log, macroName ) \ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \ try { \ std::string matcherAsString = (matcher).toString(); \ __catchResult \ .setLhs( Catch::toString( arg ) ) \ .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ .setOp( "matches" ) \ .setResultType( (matcher).match( arg ) ); \ __catchResult.captureExpression(); \ } catch( ... ) { \ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) // #included from: internal/catch_section.h #define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED // #included from: catch_section_info.h #define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED // #included from: catch_totals.hpp #define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED #include namespace Catch { struct Counts { Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} Counts operator - ( Counts const& other ) const { Counts diff; diff.passed = passed - other.passed; diff.failed = failed - other.failed; diff.failedButOk = failedButOk - other.failedButOk; return diff; } Counts& operator += ( Counts const& other ) { passed += other.passed; failed += other.failed; failedButOk += other.failedButOk; return *this; } std::size_t total() const { return passed + failed + failedButOk; } bool allPassed() const { return failed == 0 && failedButOk == 0; } bool allOk() const { return failed == 0; } std::size_t passed; std::size_t failed; std::size_t failedButOk; }; struct Totals { Totals operator - ( Totals const& other ) const { Totals diff; diff.assertions = assertions - other.assertions; diff.testCases = testCases - other.testCases; return diff; } Totals delta( Totals const& prevTotals ) const { Totals diff = *this - prevTotals; if( diff.assertions.failed > 0 ) ++diff.testCases.failed; else if( diff.assertions.failedButOk > 0 ) ++diff.testCases.failedButOk; else ++diff.testCases.passed; return diff; } Totals& operator += ( Totals const& other ) { assertions += other.assertions; testCases += other.testCases; return *this; } Counts assertions; Counts testCases; }; } namespace Catch { struct SectionInfo { SectionInfo ( SourceLineInfo const& _lineInfo, std::string const& _name, std::string const& _description = std::string() ); std::string name; std::string description; SourceLineInfo lineInfo; }; struct SectionEndInfo { SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) {} SectionInfo sectionInfo; Counts prevAssertions; double durationInSeconds; }; } // end namespace Catch // #included from: catch_timer.h #define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED #ifdef CATCH_PLATFORM_WINDOWS typedef unsigned long long uint64_t; #else #include #endif namespace Catch { class Timer { public: Timer() : m_ticks( 0 ) {} void start(); unsigned int getElapsedMicroseconds() const; unsigned int getElapsedMilliseconds() const; double getElapsedSeconds() const; private: uint64_t m_ticks; }; } // namespace Catch #include namespace Catch { class Section : NonCopyable { public: Section( SectionInfo const& info ); ~Section(); // This indicates whether the section should be executed or not operator bool() const; private: SectionInfo m_info; std::string m_name; Counts m_assertions; bool m_sectionIncluded; Timer m_timer; }; } // end namespace Catch #ifdef CATCH_CONFIG_VARIADIC_MACROS #define INTERNAL_CATCH_SECTION( ... ) \ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) #else #define INTERNAL_CATCH_SECTION( name, desc ) \ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) #endif // #included from: internal/catch_generators.hpp #define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED #include #include #include #include namespace Catch { template struct IGenerator { virtual ~IGenerator() {} virtual T getValue( std::size_t index ) const = 0; virtual std::size_t size () const = 0; }; template class BetweenGenerator : public IGenerator { public: BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} virtual T getValue( std::size_t index ) const { return m_from+static_cast( index ); } virtual std::size_t size() const { return static_cast( 1+m_to-m_from ); } private: T m_from; T m_to; }; template class ValuesGenerator : public IGenerator { public: ValuesGenerator(){} void add( T value ) { m_values.push_back( value ); } virtual T getValue( std::size_t index ) const { return m_values[index]; } virtual std::size_t size() const { return m_values.size(); } private: std::vector m_values; }; template class CompositeGenerator { public: CompositeGenerator() : m_totalSize( 0 ) {} // *** Move semantics, similar to auto_ptr *** CompositeGenerator( CompositeGenerator& other ) : m_fileInfo( other.m_fileInfo ), m_totalSize( 0 ) { move( other ); } CompositeGenerator& setFileInfo( const char* fileInfo ) { m_fileInfo = fileInfo; return *this; } ~CompositeGenerator() { deleteAll( m_composed ); } operator T () const { size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); typename std::vector*>::const_iterator it = m_composed.begin(); typename std::vector*>::const_iterator itEnd = m_composed.end(); for( size_t index = 0; it != itEnd; ++it ) { const IGenerator* generator = *it; if( overallIndex >= index && overallIndex < index + generator->size() ) { return generator->getValue( overallIndex-index ); } index += generator->size(); } CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so } void add( const IGenerator* generator ) { m_totalSize += generator->size(); m_composed.push_back( generator ); } CompositeGenerator& then( CompositeGenerator& other ) { move( other ); return *this; } CompositeGenerator& then( T value ) { ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( value ); add( valuesGen ); return *this; } private: void move( CompositeGenerator& other ) { std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); m_totalSize += other.m_totalSize; other.m_composed.clear(); } std::vector*> m_composed; std::string m_fileInfo; size_t m_totalSize; }; namespace Generators { template CompositeGenerator between( T from, T to ) { CompositeGenerator generators; generators.add( new BetweenGenerator( from, to ) ); return generators; } template CompositeGenerator values( T val1, T val2 ) { CompositeGenerator generators; ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( val1 ); valuesGen->add( val2 ); generators.add( valuesGen ); return generators; } template CompositeGenerator values( T val1, T val2, T val3 ){ CompositeGenerator generators; ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( val1 ); valuesGen->add( val2 ); valuesGen->add( val3 ); generators.add( valuesGen ); return generators; } template CompositeGenerator values( T val1, T val2, T val3, T val4 ) { CompositeGenerator generators; ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( val1 ); valuesGen->add( val2 ); valuesGen->add( val3 ); valuesGen->add( val4 ); generators.add( valuesGen ); return generators; } } // end namespace Generators using namespace Generators; } // end namespace Catch #define INTERNAL_CATCH_LINESTR2( line ) #line #define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) #define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) // #included from: internal/catch_interfaces_exception.h #define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED #include #include // #included from: catch_interfaces_registry_hub.h #define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED #include namespace Catch { class TestCase; struct ITestCaseRegistry; struct IExceptionTranslatorRegistry; struct IExceptionTranslator; struct IReporterRegistry; struct IReporterFactory; struct IRegistryHub { virtual ~IRegistryHub(); virtual IReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; }; struct IMutableRegistryHub { virtual ~IMutableRegistryHub(); virtual void registerReporter( std::string const& name, Ptr const& factory ) = 0; virtual void registerListener( Ptr const& factory ) = 0; virtual void registerTest( TestCase const& testInfo ) = 0; virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; }; IRegistryHub& getRegistryHub(); IMutableRegistryHub& getMutableRegistryHub(); void cleanUp(); std::string translateActiveException(); } namespace Catch { typedef std::string(*exceptionTranslateFunction)(); struct IExceptionTranslator; typedef std::vector ExceptionTranslators; struct IExceptionTranslator { virtual ~IExceptionTranslator(); virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; }; struct IExceptionTranslatorRegistry { virtual ~IExceptionTranslatorRegistry(); virtual std::string translateActiveException() const = 0; }; class ExceptionTranslatorRegistrar { template class ExceptionTranslator : public IExceptionTranslator { public: ExceptionTranslator( std::string(*translateFunction)( T& ) ) : m_translateFunction( translateFunction ) {} virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { try { if( it == itEnd ) throw; else return (*it)->translate( it+1, itEnd ); } catch( T& ex ) { return m_translateFunction( ex ); } } protected: std::string(*m_translateFunction)( T& ); }; public: template ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { getMutableRegistryHub().registerTranslator ( new ExceptionTranslator( translateFunction ) ); } }; } /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \ static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\ static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ) // #included from: internal/catch_approx.hpp #define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED #include #include namespace Catch { namespace Detail { class Approx { public: explicit Approx ( double value ) : m_epsilon( std::numeric_limits::epsilon()*100 ), m_scale( 1.0 ), m_value( value ) {} Approx( Approx const& other ) : m_epsilon( other.m_epsilon ), m_scale( other.m_scale ), m_value( other.m_value ) {} static Approx custom() { return Approx( 0 ); } Approx operator()( double value ) { Approx approx( value ); approx.epsilon( m_epsilon ); approx.scale( m_scale ); return approx; } friend bool operator == ( double lhs, Approx const& rhs ) { // Thanks to Richard Harris for his help refining this formula return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); } friend bool operator == ( Approx const& lhs, double rhs ) { return operator==( rhs, lhs ); } friend bool operator != ( double lhs, Approx const& rhs ) { return !operator==( lhs, rhs ); } friend bool operator != ( Approx const& lhs, double rhs ) { return !operator==( rhs, lhs ); } Approx& epsilon( double newEpsilon ) { m_epsilon = newEpsilon; return *this; } Approx& scale( double newScale ) { m_scale = newScale; return *this; } std::string toString() const { std::ostringstream oss; oss << "Approx( " << Catch::toString( m_value ) << " )"; return oss.str(); } private: double m_epsilon; double m_scale; double m_value; }; } template<> inline std::string toString( Detail::Approx const& value ) { return value.toString(); } } // end namespace Catch // #included from: internal/catch_interfaces_tag_alias_registry.h #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED // #included from: catch_tag_alias.h #define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED #include namespace Catch { struct TagAlias { TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} std::string tag; SourceLineInfo lineInfo; }; struct RegistrarForTagAliases { RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); }; } // end namespace Catch #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } // #included from: catch_option.hpp #define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED namespace Catch { // An optional type template class Option { public: Option() : nullableValue( CATCH_NULL ) {} Option( T const& _value ) : nullableValue( new( storage ) T( _value ) ) {} Option( Option const& _other ) : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) {} ~Option() { reset(); } Option& operator= ( Option const& _other ) { if( &_other != this ) { reset(); if( _other ) nullableValue = new( storage ) T( *_other ); } return *this; } Option& operator = ( T const& _value ) { reset(); nullableValue = new( storage ) T( _value ); return *this; } void reset() { if( nullableValue ) nullableValue->~T(); nullableValue = CATCH_NULL; } T& operator*() { return *nullableValue; } T const& operator*() const { return *nullableValue; } T* operator->() { return nullableValue; } const T* operator->() const { return nullableValue; } T valueOr( T const& defaultValue ) const { return nullableValue ? *nullableValue : defaultValue; } bool some() const { return nullableValue != CATCH_NULL; } bool none() const { return nullableValue == CATCH_NULL; } bool operator !() const { return nullableValue == CATCH_NULL; } operator SafeBool::type() const { return SafeBool::makeSafe( some() ); } private: T* nullableValue; char storage[sizeof(T)]; }; } // end namespace Catch namespace Catch { struct ITagAliasRegistry { virtual ~ITagAliasRegistry(); virtual Option find( std::string const& alias ) const = 0; virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; static ITagAliasRegistry const& get(); }; } // end namespace Catch // These files are included here so the single_include script doesn't put them // in the conditionally compiled sections // #included from: internal/catch_test_case_info.h #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED #include #include #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif namespace Catch { struct ITestCase; struct TestCaseInfo { enum SpecialProperties{ None = 0, IsHidden = 1 << 1, ShouldFail = 1 << 2, MayFail = 1 << 3, Throws = 1 << 4 }; TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, std::set const& _tags, SourceLineInfo const& _lineInfo ); TestCaseInfo( TestCaseInfo const& other ); friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); bool isHidden() const; bool throws() const; bool okToFail() const; bool expectedToFail() const; std::string name; std::string className; std::string description; std::set tags; std::set lcaseTags; std::string tagsAsString; SourceLineInfo lineInfo; SpecialProperties properties; }; class TestCase : public TestCaseInfo { public: TestCase( ITestCase* testCase, TestCaseInfo const& info ); TestCase( TestCase const& other ); TestCase withName( std::string const& _newName ) const; void invoke() const; TestCaseInfo const& getTestCaseInfo() const; void swap( TestCase& other ); bool operator == ( TestCase const& other ) const; bool operator < ( TestCase const& other ) const; TestCase& operator = ( TestCase const& other ); private: Ptr test; }; TestCase makeTestCase( ITestCase* testCase, std::string const& className, std::string const& name, std::string const& description, SourceLineInfo const& lineInfo ); } #ifdef __clang__ #pragma clang diagnostic pop #endif #ifdef __OBJC__ // #included from: internal/catch_objc.hpp #define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED #import #include // NB. Any general catch headers included here must be included // in catch.hpp first to make sure they are included by the single // header for non obj-usage /////////////////////////////////////////////////////////////////////////////// // This protocol is really only here for (self) documenting purposes, since // all its methods are optional. @protocol OcFixture @optional -(void) setUp; -(void) tearDown; @end namespace Catch { class OcMethod : public SharedImpl { public: OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} virtual void invoke() const { id obj = [[m_cls alloc] init]; performOptionalSelector( obj, @selector(setUp) ); performOptionalSelector( obj, m_sel ); performOptionalSelector( obj, @selector(tearDown) ); arcSafeRelease( obj ); } private: virtual ~OcMethod() {} Class m_cls; SEL m_sel; }; namespace Detail{ inline std::string getAnnotation( Class cls, std::string const& annotationName, std::string const& testCaseName ) { NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; SEL sel = NSSelectorFromString( selStr ); arcSafeRelease( selStr ); id value = performOptionalSelector( cls, sel ); if( value ) return [(NSString*)value UTF8String]; return ""; } } inline size_t registerTestMethods() { size_t noTestMethods = 0; int noClasses = objc_getClassList( CATCH_NULL, 0 ); Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); objc_getClassList( classes, noClasses ); for( int c = 0; c < noClasses; c++ ) { Class cls = classes[c]; { u_int count; Method* methods = class_copyMethodList( cls, &count ); for( u_int m = 0; m < count ; m++ ) { SEL selector = method_getName(methods[m]); std::string methodName = sel_getName(selector); if( startsWith( methodName, "Catch_TestCase_" ) ) { std::string testCaseName = methodName.substr( 15 ); std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); const char* className = class_getName( cls ); getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); noTestMethods++; } } free(methods); } } return noTestMethods; } namespace Matchers { namespace Impl { namespace NSStringMatchers { template struct StringHolder : MatcherImpl{ StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolder() { arcSafeRelease( m_substr ); } NSString* m_substr; }; struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} virtual bool match( ExpressionType const& str ) const { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } virtual std::string toString() const { return "equals string: " + Catch::toString( m_substr ); } }; struct Contains : StringHolder { Contains( NSString* substr ) : StringHolder( substr ){} virtual bool match( ExpressionType const& str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location != NSNotFound; } virtual std::string toString() const { return "contains string: " + Catch::toString( m_substr ); } }; struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} virtual bool match( ExpressionType const& str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } virtual std::string toString() const { return "starts with: " + Catch::toString( m_substr ); } }; struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} virtual bool match( ExpressionType const& str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } virtual std::string toString() const { return "ends with: " + Catch::toString( m_substr ); } }; } // namespace NSStringMatchers } // namespace Impl inline Impl::NSStringMatchers::Equals Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } inline Impl::NSStringMatchers::Contains Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } inline Impl::NSStringMatchers::StartsWith StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } inline Impl::NSStringMatchers::EndsWith EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } } // namespace Matchers using namespace Matchers; } // namespace Catch /////////////////////////////////////////////////////////////////////////////// #define OC_TEST_CASE( name, desc )\ +(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ {\ return @ name; \ }\ +(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ { \ return @ desc; \ } \ -(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) #endif #ifdef CATCH_IMPL // #included from: internal/catch_impl.hpp #define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED // Collect all the implementation files together here // These are the equivalent of what would usually be cpp files #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wweak-vtables" #endif // #included from: ../catch_session.hpp #define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED // #included from: internal/catch_commandline.hpp #define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED // #included from: catch_config.hpp #define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED // #included from: catch_test_spec_parser.hpp #define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif // #included from: catch_test_spec.hpp #define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif // #included from: catch_wildcard_pattern.hpp #define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED namespace Catch { class WildcardPattern { enum WildcardPosition { NoWildcard = 0, WildcardAtStart = 1, WildcardAtEnd = 2, WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd }; public: WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) : m_caseSensitivity( caseSensitivity ), m_wildcard( NoWildcard ), m_pattern( adjustCase( pattern ) ) { if( startsWith( m_pattern, "*" ) ) { m_pattern = m_pattern.substr( 1 ); m_wildcard = WildcardAtStart; } if( endsWith( m_pattern, "*" ) ) { m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); } } virtual ~WildcardPattern(); virtual bool matches( std::string const& str ) const { switch( m_wildcard ) { case NoWildcard: return m_pattern == adjustCase( str ); case WildcardAtStart: return endsWith( adjustCase( str ), m_pattern ); case WildcardAtEnd: return startsWith( adjustCase( str ), m_pattern ); case WildcardAtBothEnds: return contains( adjustCase( str ), m_pattern ); } #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunreachable-code" #endif throw std::logic_error( "Unknown enum" ); #ifdef __clang__ #pragma clang diagnostic pop #endif } private: std::string adjustCase( std::string const& str ) const { return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; } CaseSensitive::Choice m_caseSensitivity; WildcardPosition m_wildcard; std::string m_pattern; }; } #include #include namespace Catch { class TestSpec { struct Pattern : SharedImpl<> { virtual ~Pattern(); virtual bool matches( TestCaseInfo const& testCase ) const = 0; }; class NamePattern : public Pattern { public: NamePattern( std::string const& name ) : m_wildcardPattern( toLower( name ), CaseSensitive::No ) {} virtual ~NamePattern(); virtual bool matches( TestCaseInfo const& testCase ) const { return m_wildcardPattern.matches( toLower( testCase.name ) ); } private: WildcardPattern m_wildcardPattern; }; class TagPattern : public Pattern { public: TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} virtual ~TagPattern(); virtual bool matches( TestCaseInfo const& testCase ) const { return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); } private: std::string m_tag; }; class ExcludedPattern : public Pattern { public: ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} virtual ~ExcludedPattern(); virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } private: Ptr m_underlyingPattern; }; struct Filter { std::vector > m_patterns; bool matches( TestCaseInfo const& testCase ) const { // All patterns in a filter must match for the filter to be a match for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) if( !(*it)->matches( testCase ) ) return false; return true; } }; public: bool hasFilters() const { return !m_filters.empty(); } bool matches( TestCaseInfo const& testCase ) const { // A TestSpec matches if any filter matches for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) if( it->matches( testCase ) ) return true; return false; } private: std::vector m_filters; friend class TestSpecParser; }; } #ifdef __clang__ #pragma clang diagnostic pop #endif namespace Catch { class TestSpecParser { enum Mode{ None, Name, QuotedName, Tag }; Mode m_mode; bool m_exclusion; std::size_t m_start, m_pos; std::string m_arg; TestSpec::Filter m_currentFilter; TestSpec m_testSpec; ITagAliasRegistry const* m_tagAliases; public: TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} TestSpecParser& parse( std::string const& arg ) { m_mode = None; m_exclusion = false; m_start = std::string::npos; m_arg = m_tagAliases->expandAliases( arg ); for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) visitChar( m_arg[m_pos] ); if( m_mode == Name ) addPattern(); return *this; } TestSpec testSpec() { addFilter(); return m_testSpec; } private: void visitChar( char c ) { if( m_mode == None ) { switch( c ) { case ' ': return; case '~': m_exclusion = true; return; case '[': return startNewMode( Tag, ++m_pos ); case '"': return startNewMode( QuotedName, ++m_pos ); default: startNewMode( Name, m_pos ); break; } } if( m_mode == Name ) { if( c == ',' ) { addPattern(); addFilter(); } else if( c == '[' ) { if( subString() == "exclude:" ) m_exclusion = true; else addPattern(); startNewMode( Tag, ++m_pos ); } } else if( m_mode == QuotedName && c == '"' ) addPattern(); else if( m_mode == Tag && c == ']' ) addPattern(); } void startNewMode( Mode mode, std::size_t start ) { m_mode = mode; m_start = start; } std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } template void addPattern() { std::string token = subString(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; token = token.substr( 8 ); } if( !token.empty() ) { Ptr pattern = new T( token ); if( m_exclusion ) pattern = new TestSpec::ExcludedPattern( pattern ); m_currentFilter.m_patterns.push_back( pattern ); } m_exclusion = false; m_mode = None; } void addFilter() { if( !m_currentFilter.m_patterns.empty() ) { m_testSpec.m_filters.push_back( m_currentFilter ); m_currentFilter = TestSpec::Filter(); } } }; inline TestSpec parseTestSpec( std::string const& arg ) { return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); } } // namespace Catch #ifdef __clang__ #pragma clang diagnostic pop #endif // #included from: catch_interfaces_config.h #define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED #include #include #include namespace Catch { struct Verbosity { enum Level { NoOutput = 0, Quiet, Normal }; }; struct WarnAbout { enum What { Nothing = 0x00, NoAssertions = 0x01 }; }; struct ShowDurations { enum OrNot { DefaultForReporter, Always, Never }; }; struct RunTests { enum InWhatOrder { InDeclarationOrder, InLexicographicalOrder, InRandomOrder }; }; class TestSpec; struct IConfig : IShared { virtual ~IConfig(); virtual bool allowThrows() const = 0; virtual std::ostream& stream() const = 0; virtual std::string name() const = 0; virtual bool includeSuccessfulResults() const = 0; virtual bool shouldDebugBreak() const = 0; virtual bool warnAboutMissingAssertions() const = 0; virtual int abortAfter() const = 0; virtual bool showInvisibles() const = 0; virtual ShowDurations::OrNot showDurations() const = 0; virtual TestSpec const& testSpec() const = 0; virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; virtual bool forceColour() const = 0; }; } // #included from: catch_stream.h #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED // #included from: catch_streambuf.h #define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED #include namespace Catch { class StreamBufBase : public std::streambuf { public: virtual ~StreamBufBase() CATCH_NOEXCEPT; }; } #include #include #include namespace Catch { std::ostream& cout(); std::ostream& cerr(); struct IStream { virtual ~IStream() CATCH_NOEXCEPT; virtual std::ostream& stream() const = 0; }; class FileStream : public IStream { mutable std::ofstream m_ofs; public: FileStream( std::string const& filename ); virtual ~FileStream() CATCH_NOEXCEPT; public: // IStream virtual std::ostream& stream() const CATCH_OVERRIDE; }; class CoutStream : public IStream { mutable std::ostream m_os; public: CoutStream(); virtual ~CoutStream() CATCH_NOEXCEPT; public: // IStream virtual std::ostream& stream() const CATCH_OVERRIDE; }; class DebugOutStream : public IStream { std::auto_ptr m_streamBuf; mutable std::ostream m_os; public: DebugOutStream(); virtual ~DebugOutStream() CATCH_NOEXCEPT; public: // IStream virtual std::ostream& stream() const CATCH_OVERRIDE; }; } #include #include #include #include #include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 #endif namespace Catch { struct ConfigData { ConfigData() : listTests( false ), listTags( false ), listReporters( false ), listTestNamesOnly( false ), showSuccessfulTests( false ), shouldDebugBreak( false ), noThrow( false ), showHelp( false ), showInvisibles( false ), forceColour( false ), filenamesAsTags( false ), abortAfter( -1 ), rngSeed( 0 ), verbosity( Verbosity::Normal ), warnings( WarnAbout::Nothing ), showDurations( ShowDurations::DefaultForReporter ), runOrder( RunTests::InDeclarationOrder ) {} bool listTests; bool listTags; bool listReporters; bool listTestNamesOnly; bool showSuccessfulTests; bool shouldDebugBreak; bool noThrow; bool showHelp; bool showInvisibles; bool forceColour; bool filenamesAsTags; int abortAfter; unsigned int rngSeed; Verbosity::Level verbosity; WarnAbout::What warnings; ShowDurations::OrNot showDurations; RunTests::InWhatOrder runOrder; std::string outputFilename; std::string name; std::string processName; std::vector reporterNames; std::vector testsOrTags; }; class Config : public SharedImpl { private: Config( Config const& other ); Config& operator = ( Config const& other ); virtual void dummy(); public: Config() {} Config( ConfigData const& data ) : m_data( data ), m_stream( openStream() ) { if( !data.testsOrTags.empty() ) { TestSpecParser parser( ITagAliasRegistry::get() ); for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) parser.parse( data.testsOrTags[i] ); m_testSpec = parser.testSpec(); } } virtual ~Config() { } std::string const& getFilename() const { return m_data.outputFilename ; } bool listTests() const { return m_data.listTests; } bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } bool listTags() const { return m_data.listTags; } bool listReporters() const { return m_data.listReporters; } std::string getProcessName() const { return m_data.processName; } bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } std::vector getReporterNames() const { return m_data.reporterNames; } int abortAfter() const { return m_data.abortAfter; } TestSpec const& testSpec() const { return m_testSpec; } bool showHelp() const { return m_data.showHelp; } bool showInvisibles() const { return m_data.showInvisibles; } // IConfig interface virtual bool allowThrows() const { return !m_data.noThrow; } virtual std::ostream& stream() const { return m_stream->stream(); } virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } virtual unsigned int rngSeed() const { return m_data.rngSeed; } virtual bool forceColour() const { return m_data.forceColour; } private: IStream const* openStream() { if( m_data.outputFilename.empty() ) return new CoutStream(); else if( m_data.outputFilename[0] == '%' ) { if( m_data.outputFilename == "%debug" ) return new DebugOutStream(); else throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); } else return new FileStream( m_data.outputFilename ); } ConfigData m_data; std::auto_ptr m_stream; TestSpec m_testSpec; }; } // end namespace Catch // #included from: catch_clara.h #define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED // Use Catch's value for console width (store Clara's off to the side, if present) #ifdef CLARA_CONFIG_CONSOLE_WIDTH #define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH #undef CLARA_CONFIG_CONSOLE_WIDTH #endif #define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH // Declare Clara inside the Catch namespace #define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { // #included from: ../external/clara.h // Only use header guard if we are not using an outer namespace #if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) #ifndef STITCH_CLARA_OPEN_NAMESPACE #define TWOBLUECUBES_CLARA_H_INCLUDED #define STITCH_CLARA_OPEN_NAMESPACE #define STITCH_CLARA_CLOSE_NAMESPACE #else #define STITCH_CLARA_CLOSE_NAMESPACE } #endif #define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE // ----------- #included from tbc_text_format.h ----------- // Only use header guard if we are not using an outer namespace #if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) #ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE #define TBC_TEXT_FORMAT_H_INCLUDED #endif #include #include #include // Use optional outer namespace #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { #endif namespace Tbc { #ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; #else const unsigned int consoleWidth = 80; #endif struct TextAttributes { TextAttributes() : initialIndent( std::string::npos ), indent( 0 ), width( consoleWidth-1 ), tabChar( '\t' ) {} TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } std::size_t initialIndent; // indent of first line, or npos std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos std::size_t width; // maximum width of text, including indent. Longer text will wrap char tabChar; // If this char is seen the indent is changed to current pos }; class Text { public: Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) : attr( _attr ) { std::string wrappableChars = " [({.,/|\\-"; std::size_t indent = _attr.initialIndent != std::string::npos ? _attr.initialIndent : _attr.indent; std::string remainder = _str; while( !remainder.empty() ) { if( lines.size() >= 1000 ) { lines.push_back( "... message truncated due to excessive size" ); return; } std::size_t tabPos = std::string::npos; std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); std::size_t pos = remainder.find_first_of( '\n' ); if( pos <= width ) { width = pos; } pos = remainder.find_last_of( _attr.tabChar, width ); if( pos != std::string::npos ) { tabPos = pos; if( remainder[width] == '\n' ) width--; remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); } if( width == remainder.size() ) { spliceLine( indent, remainder, width ); } else if( remainder[width] == '\n' ) { spliceLine( indent, remainder, width ); if( width <= 1 || remainder.size() != 1 ) remainder = remainder.substr( 1 ); indent = _attr.indent; } else { pos = remainder.find_last_of( wrappableChars, width ); if( pos != std::string::npos && pos > 0 ) { spliceLine( indent, remainder, pos ); if( remainder[0] == ' ' ) remainder = remainder.substr( 1 ); } else { spliceLine( indent, remainder, width-1 ); lines.back() += "-"; } if( lines.size() == 1 ) indent = _attr.indent; if( tabPos != std::string::npos ) indent += tabPos; } } } void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); _remainder = _remainder.substr( _pos ); } typedef std::vector::const_iterator const_iterator; const_iterator begin() const { return lines.begin(); } const_iterator end() const { return lines.end(); } std::string const& last() const { return lines.back(); } std::size_t size() const { return lines.size(); } std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } std::string toString() const { std::ostringstream oss; oss << *this; return oss.str(); } inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); it != itEnd; ++it ) { if( it != _text.begin() ) _stream << "\n"; _stream << *it; } return _stream; } private: std::string str; TextAttributes attr; std::vector lines; }; } // end namespace Tbc #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE } // end outer namespace #endif #endif // TBC_TEXT_FORMAT_H_INCLUDED // ----------- end of #include from tbc_text_format.h ----------- // ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h #undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE #include #include #include #include // Use optional outer namespace #ifdef STITCH_CLARA_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE #endif namespace Clara { struct UnpositionalTag {}; extern UnpositionalTag _; #ifdef CLARA_CONFIG_MAIN UnpositionalTag _; #endif namespace Detail { #ifdef CLARA_CONSOLE_WIDTH const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; #else const unsigned int consoleWidth = 80; #endif using namespace Tbc; inline bool startsWith( std::string const& str, std::string const& prefix ) { return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; } template struct RemoveConstRef{ typedef T type; }; template struct RemoveConstRef{ typedef T type; }; template struct RemoveConstRef{ typedef T type; }; template struct RemoveConstRef{ typedef T type; }; template struct IsBool { static const bool value = false; }; template<> struct IsBool { static const bool value = true; }; template void convertInto( std::string const& _source, T& _dest ) { std::stringstream ss; ss << _source; ss >> _dest; if( ss.fail() ) throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); } inline void convertInto( std::string const& _source, std::string& _dest ) { _dest = _source; } inline void convertInto( std::string const& _source, bool& _dest ) { std::string sourceLC = _source; std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) _dest = true; else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) _dest = false; else throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); } inline void convertInto( bool _source, bool& _dest ) { _dest = _source; } template inline void convertInto( bool, T& ) { throw std::runtime_error( "Invalid conversion" ); } template struct IArgFunction { virtual ~IArgFunction() {} # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS IArgFunction() = default; IArgFunction( IArgFunction const& ) = default; # endif virtual void set( ConfigT& config, std::string const& value ) const = 0; virtual void setFlag( ConfigT& config ) const = 0; virtual bool takesArg() const = 0; virtual IArgFunction* clone() const = 0; }; template class BoundArgFunction { public: BoundArgFunction() : functionObj( CATCH_NULL ) {} BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CATCH_NULL ) {} BoundArgFunction& operator = ( BoundArgFunction const& other ) { IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CATCH_NULL; delete functionObj; functionObj = newFunctionObj; return *this; } ~BoundArgFunction() { delete functionObj; } void set( ConfigT& config, std::string const& value ) const { functionObj->set( config, value ); } void setFlag( ConfigT& config ) const { functionObj->setFlag( config ); } bool takesArg() const { return functionObj->takesArg(); } bool isSet() const { return functionObj != CATCH_NULL; } private: IArgFunction* functionObj; }; template struct NullBinder : IArgFunction{ virtual void set( C&, std::string const& ) const {} virtual void setFlag( C& ) const {} virtual bool takesArg() const { return true; } virtual IArgFunction* clone() const { return new NullBinder( *this ); } }; template struct BoundDataMember : IArgFunction{ BoundDataMember( M C::* _member ) : member( _member ) {} virtual void set( C& p, std::string const& stringValue ) const { convertInto( stringValue, p.*member ); } virtual void setFlag( C& p ) const { convertInto( true, p.*member ); } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } M C::* member; }; template struct BoundUnaryMethod : IArgFunction{ BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} virtual void set( C& p, std::string const& stringValue ) const { typename RemoveConstRef::type value; convertInto( stringValue, value ); (p.*member)( value ); } virtual void setFlag( C& p ) const { typename RemoveConstRef::type value; convertInto( true, value ); (p.*member)( value ); } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } void (C::*member)( M ); }; template struct BoundNullaryMethod : IArgFunction{ BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} virtual void set( C& p, std::string const& stringValue ) const { bool value; convertInto( stringValue, value ); if( value ) (p.*member)(); } virtual void setFlag( C& p ) const { (p.*member)(); } virtual bool takesArg() const { return false; } virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } void (C::*member)(); }; template struct BoundUnaryFunction : IArgFunction{ BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} virtual void set( C& obj, std::string const& stringValue ) const { bool value; convertInto( stringValue, value ); if( value ) function( obj ); } virtual void setFlag( C& p ) const { function( p ); } virtual bool takesArg() const { return false; } virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } void (*function)( C& ); }; template struct BoundBinaryFunction : IArgFunction{ BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} virtual void set( C& obj, std::string const& stringValue ) const { typename RemoveConstRef::type value; convertInto( stringValue, value ); function( obj, value ); } virtual void setFlag( C& obj ) const { typename RemoveConstRef::type value; convertInto( true, value ); function( obj, value ); } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } void (*function)( C&, T ); }; } // namespace Detail struct Parser { Parser() : separators( " \t=:" ) {} struct Token { enum Type { Positional, ShortOpt, LongOpt }; Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} Type type; std::string data; }; void parseIntoTokens( int argc, char const * const * argv, std::vector& tokens ) const { const std::string doubleDash = "--"; for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) parseIntoTokens( argv[i] , tokens); } void parseIntoTokens( std::string arg, std::vector& tokens ) const { while( !arg.empty() ) { Parser::Token token( Parser::Token::Positional, arg ); arg = ""; if( token.data[0] == '-' ) { if( token.data.size() > 1 && token.data[1] == '-' ) { token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); } else { token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) ); if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) { arg = "-" + token.data.substr( 1 ); token.data = token.data.substr( 0, 1 ); } } } if( token.type != Parser::Token::Positional ) { std::size_t pos = token.data.find_first_of( separators ); if( pos != std::string::npos ) { arg = token.data.substr( pos+1 ); token.data = token.data.substr( 0, pos ); } } tokens.push_back( token ); } } std::string separators; }; template struct CommonArgProperties { CommonArgProperties() {} CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} Detail::BoundArgFunction boundField; std::string description; std::string detail; std::string placeholder; // Only value if boundField takes an arg bool takesArg() const { return !placeholder.empty(); } void validate() const { if( !boundField.isSet() ) throw std::logic_error( "option not bound" ); } }; struct OptionArgProperties { std::vector shortNames; std::string longName; bool hasShortName( std::string const& shortName ) const { return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); } bool hasLongName( std::string const& _longName ) const { return _longName == longName; } }; struct PositionalArgProperties { PositionalArgProperties() : position( -1 ) {} int position; // -1 means non-positional (floating) bool isFixedPositional() const { return position != -1; } }; template class CommandLine { struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { Arg() {} Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} using CommonArgProperties::placeholder; // !TBD std::string dbgName() const { if( !longName.empty() ) return "--" + longName; if( !shortNames.empty() ) return "-" + shortNames[0]; return "positional args"; } std::string commands() const { std::ostringstream oss; bool first = true; std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); for(; it != itEnd; ++it ) { if( first ) first = false; else oss << ", "; oss << "-" << *it; } if( !longName.empty() ) { if( !first ) oss << ", "; oss << "--" << longName; } if( !placeholder.empty() ) oss << " <" << placeholder << ">"; return oss.str(); } }; typedef CATCH_AUTO_PTR( Arg ) ArgAutoPtr; friend void addOptName( Arg& arg, std::string const& optName ) { if( optName.empty() ) return; if( Detail::startsWith( optName, "--" ) ) { if( !arg.longName.empty() ) throw std::logic_error( "Only one long opt may be specified. '" + arg.longName + "' already specified, now attempting to add '" + optName + "'" ); arg.longName = optName.substr( 2 ); } else if( Detail::startsWith( optName, "-" ) ) arg.shortNames.push_back( optName.substr( 1 ) ); else throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); } friend void setPositionalArg( Arg& arg, int position ) { arg.position = position; } class ArgBuilder { public: ArgBuilder( Arg* arg ) : m_arg( arg ) {} // Bind a non-boolean data member (requires placeholder string) template void bind( M C::* field, std::string const& placeholder ) { m_arg->boundField = new Detail::BoundDataMember( field ); m_arg->placeholder = placeholder; } // Bind a boolean data member (no placeholder required) template void bind( bool C::* field ) { m_arg->boundField = new Detail::BoundDataMember( field ); } // Bind a method taking a single, non-boolean argument (requires a placeholder string) template void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); m_arg->placeholder = placeholder; } // Bind a method taking a single, boolean argument (no placeholder string required) template void bind( void (C::* unaryMethod)( bool ) ) { m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); } // Bind a method that takes no arguments (will be called if opt is present) template void bind( void (C::* nullaryMethod)() ) { m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); } // Bind a free function taking a single argument - the object to operate on (no placeholder string required) template void bind( void (* unaryFunction)( C& ) ) { m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); } // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) template void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); m_arg->placeholder = placeholder; } ArgBuilder& describe( std::string const& description ) { m_arg->description = description; return *this; } ArgBuilder& detail( std::string const& _detail ) { m_arg->detail = _detail; return *this; } protected: Arg* m_arg; }; class OptBuilder : public ArgBuilder { public: OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} OptBuilder& operator[]( std::string const& optName ) { addOptName( *ArgBuilder::m_arg, optName ); return *this; } }; public: CommandLine() : m_boundProcessName( new Detail::NullBinder() ), m_highestSpecifiedArgPosition( 0 ), m_throwOnUnrecognisedTokens( false ) {} CommandLine( CommandLine const& other ) : m_boundProcessName( other.m_boundProcessName ), m_options ( other.m_options ), m_positionalArgs( other.m_positionalArgs ), m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) { if( other.m_floatingArg.get() ) m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); } CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { m_throwOnUnrecognisedTokens = shouldThrow; return *this; } OptBuilder operator[]( std::string const& optName ) { m_options.push_back( Arg() ); addOptName( m_options.back(), optName ); OptBuilder builder( &m_options.back() ); return builder; } ArgBuilder operator[]( int position ) { m_positionalArgs.insert( std::make_pair( position, Arg() ) ); if( position > m_highestSpecifiedArgPosition ) m_highestSpecifiedArgPosition = position; setPositionalArg( m_positionalArgs[position], position ); ArgBuilder builder( &m_positionalArgs[position] ); return builder; } // Invoke this with the _ instance ArgBuilder operator[]( UnpositionalTag ) { if( m_floatingArg.get() ) throw std::logic_error( "Only one unpositional argument can be added" ); m_floatingArg.reset( new Arg() ); ArgBuilder builder( m_floatingArg.get() ); return builder; } template void bindProcessName( M C::* field ) { m_boundProcessName = new Detail::BoundDataMember( field ); } template void bindProcessName( void (C::*_unaryMethod)( M ) ) { m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); } void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; std::size_t maxWidth = 0; for( it = itBegin; it != itEnd; ++it ) maxWidth = (std::max)( maxWidth, it->commands().size() ); for( it = itBegin; it != itEnd; ++it ) { Detail::Text usageText( it->commands(), Detail::TextAttributes() .setWidth( maxWidth+indent ) .setIndent( indent ) ); Detail::Text desc( it->description, Detail::TextAttributes() .setWidth( width - maxWidth - 3 ) ); for( std::size_t i = 0; i < (std::max)( usageText.size(), desc.size() ); ++i ) { std::string usageCol = i < usageText.size() ? usageText[i] : ""; os << usageCol; if( i < desc.size() && !desc[i].empty() ) os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) << desc[i]; os << "\n"; } } } std::string optUsage() const { std::ostringstream oss; optUsage( oss ); return oss.str(); } void argSynopsis( std::ostream& os ) const { for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { if( i > 1 ) os << " "; typename std::map::const_iterator it = m_positionalArgs.find( i ); if( it != m_positionalArgs.end() ) os << "<" << it->second.placeholder << ">"; else if( m_floatingArg.get() ) os << "<" << m_floatingArg->placeholder << ">"; else throw std::logic_error( "non consecutive positional arguments with no floating args" ); } // !TBD No indication of mandatory args if( m_floatingArg.get() ) { if( m_highestSpecifiedArgPosition > 1 ) os << " "; os << "[<" << m_floatingArg->placeholder << "> ...]"; } } std::string argSynopsis() const { std::ostringstream oss; argSynopsis( oss ); return oss.str(); } void usage( std::ostream& os, std::string const& procName ) const { validate(); os << "usage:\n " << procName << " "; argSynopsis( os ); if( !m_options.empty() ) { os << " [options]\n\nwhere options are: \n"; optUsage( os, 2 ); } os << "\n"; } std::string usage( std::string const& procName ) const { std::ostringstream oss; usage( oss, procName ); return oss.str(); } ConfigT parse( int argc, char const * const * argv ) const { ConfigT config; parseInto( argc, argv, config ); return config; } std::vector parseInto( int argc, char const * const * argv, ConfigT& config ) const { std::string processName = argv[0]; std::size_t lastSlash = processName.find_last_of( "/\\" ); if( lastSlash != std::string::npos ) processName = processName.substr( lastSlash+1 ); m_boundProcessName.set( config, processName ); std::vector tokens; Parser parser; parser.parseIntoTokens( argc, argv, tokens ); return populate( tokens, config ); } std::vector populate( std::vector const& tokens, ConfigT& config ) const { validate(); std::vector unusedTokens = populateOptions( tokens, config ); unusedTokens = populateFixedArgs( unusedTokens, config ); unusedTokens = populateFloatingArgs( unusedTokens, config ); return unusedTokens; } std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { std::vector unusedTokens; std::vector errors; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); for(; it != itEnd; ++it ) { Arg const& arg = *it; try { if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { if( arg.takesArg() ) { if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) errors.push_back( "Expected argument to option: " + token.data ); else arg.boundField.set( config, tokens[++i].data ); } else { arg.boundField.setFlag( config ); } break; } } catch( std::exception& ex ) { errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); } } if( it == itEnd ) { if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) unusedTokens.push_back( token ); else if( errors.empty() && m_throwOnUnrecognisedTokens ) errors.push_back( "unrecognised option: " + token.data ); } } if( !errors.empty() ) { std::ostringstream oss; for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); it != itEnd; ++it ) { if( it != errors.begin() ) oss << "\n"; oss << *it; } throw std::runtime_error( oss.str() ); } return unusedTokens; } std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { std::vector unusedTokens; int position = 1; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; typename std::map::const_iterator it = m_positionalArgs.find( position ); if( it != m_positionalArgs.end() ) it->second.boundField.set( config, token.data ); else unusedTokens.push_back( token ); if( token.type == Parser::Token::Positional ) position++; } return unusedTokens; } std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { if( !m_floatingArg.get() ) return tokens; std::vector unusedTokens; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; if( token.type == Parser::Token::Positional ) m_floatingArg->boundField.set( config, token.data ); else unusedTokens.push_back( token ); } return unusedTokens; } void validate() const { if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) throw std::logic_error( "No options or arguments specified" ); for( typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); it != itEnd; ++it ) it->validate(); } private: Detail::BoundArgFunction m_boundProcessName; std::vector m_options; std::map m_positionalArgs; ArgAutoPtr m_floatingArg; int m_highestSpecifiedArgPosition; bool m_throwOnUnrecognisedTokens; }; } // end namespace Clara STITCH_CLARA_CLOSE_NAMESPACE #undef STITCH_CLARA_OPEN_NAMESPACE #undef STITCH_CLARA_CLOSE_NAMESPACE #endif // TWOBLUECUBES_CLARA_H_INCLUDED #undef STITCH_CLARA_OPEN_NAMESPACE // Restore Clara's value for console width, if present #ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #endif #include namespace Catch { inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } inline void abortAfterX( ConfigData& config, int x ) { if( x < 1 ) throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); config.abortAfter = x; } inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } inline void addWarning( ConfigData& config, std::string const& _warning ) { if( _warning == "NoAssertions" ) config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); else throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); } inline void setOrder( ConfigData& config, std::string const& order ) { if( startsWith( "declared", order ) ) config.runOrder = RunTests::InDeclarationOrder; else if( startsWith( "lexical", order ) ) config.runOrder = RunTests::InLexicographicalOrder; else if( startsWith( "random", order ) ) config.runOrder = RunTests::InRandomOrder; else throw std::runtime_error( "Unrecognised ordering: '" + order + "'" ); } inline void setRngSeed( ConfigData& config, std::string const& seed ) { if( seed == "time" ) { config.rngSeed = static_cast( std::time(0) ); } else { std::stringstream ss; ss << seed; ss >> config.rngSeed; if( ss.fail() ) throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" ); } } inline void setVerbosity( ConfigData& config, int level ) { // !TBD: accept strings? config.verbosity = static_cast( level ); } inline void setShowDurations( ConfigData& config, bool _showDurations ) { config.showDurations = _showDurations ? ShowDurations::Always : ShowDurations::Never; } inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { std::ifstream f( _filename.c_str() ); if( !f.is_open() ) throw std::domain_error( "Unable to load input file: " + _filename ); std::string line; while( std::getline( f, line ) ) { line = trim(line); if( !line.empty() && !startsWith( line, "#" ) ) addTestOrTags( config, "\"" + line + "\"," ); } } inline Clara::CommandLine makeCommandLineParser() { using namespace Clara; CommandLine cli; cli.bindProcessName( &ConfigData::processName ); cli["-?"]["-h"]["--help"] .describe( "display usage information" ) .bind( &ConfigData::showHelp ); cli["-l"]["--list-tests"] .describe( "list all/matching test cases" ) .bind( &ConfigData::listTests ); cli["-t"]["--list-tags"] .describe( "list all/matching tags" ) .bind( &ConfigData::listTags ); cli["-s"]["--success"] .describe( "include successful tests in output" ) .bind( &ConfigData::showSuccessfulTests ); cli["-b"]["--break"] .describe( "break into debugger on failure" ) .bind( &ConfigData::shouldDebugBreak ); cli["-e"]["--nothrow"] .describe( "skip exception tests" ) .bind( &ConfigData::noThrow ); cli["-i"]["--invisibles"] .describe( "show invisibles (tabs, newlines)" ) .bind( &ConfigData::showInvisibles ); cli["-o"]["--out"] .describe( "output filename" ) .bind( &ConfigData::outputFilename, "filename" ); cli["-r"]["--reporter"] // .placeholder( "name[:filename]" ) .describe( "reporter to use (defaults to console)" ) .bind( &addReporterName, "name" ); cli["-n"]["--name"] .describe( "suite name" ) .bind( &ConfigData::name, "name" ); cli["-a"]["--abort"] .describe( "abort at first failure" ) .bind( &abortAfterFirst ); cli["-x"]["--abortx"] .describe( "abort after x failures" ) .bind( &abortAfterX, "no. failures" ); cli["-w"]["--warn"] .describe( "enable warnings" ) .bind( &addWarning, "warning name" ); // - needs updating if reinstated // cli.into( &setVerbosity ) // .describe( "level of verbosity (0=no output)" ) // .shortOpt( "v") // .longOpt( "verbosity" ) // .placeholder( "level" ); cli[_] .describe( "which test or tests to use" ) .bind( &addTestOrTags, "test name, pattern or tags" ); cli["-d"]["--durations"] .describe( "show test durations" ) .bind( &setShowDurations, "yes/no" ); cli["-f"]["--input-file"] .describe( "load test names to run from a file" ) .bind( &loadTestNamesFromFile, "filename" ); cli["-#"]["--filenames-as-tags"] .describe( "adds a tag for the filename" ) .bind( &ConfigData::filenamesAsTags ); // Less common commands which don't have a short form cli["--list-test-names-only"] .describe( "list all/matching test cases names only" ) .bind( &ConfigData::listTestNamesOnly ); cli["--list-reporters"] .describe( "list all reporters" ) .bind( &ConfigData::listReporters ); cli["--order"] .describe( "test case order (defaults to decl)" ) .bind( &setOrder, "decl|lex|rand" ); cli["--rng-seed"] .describe( "set a specific seed for random numbers" ) .bind( &setRngSeed, "'time'|number" ); cli["--force-colour"] .describe( "force colourised output" ) .bind( &ConfigData::forceColour ); return cli; } } // end namespace Catch // #included from: internal/catch_list.hpp #define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED // #included from: catch_text.h #define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED #define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH #define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch // #included from: ../external/tbc_text_format.h // Only use header guard if we are not using an outer namespace #ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE # ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED # ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED # define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED # endif # else # define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED # endif #endif #ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED #include #include #include // Use optional outer namespace #ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { #endif namespace Tbc { #ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; #else const unsigned int consoleWidth = 80; #endif struct TextAttributes { TextAttributes() : initialIndent( std::string::npos ), indent( 0 ), width( consoleWidth-1 ), tabChar( '\t' ) {} TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } std::size_t initialIndent; // indent of first line, or npos std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos std::size_t width; // maximum width of text, including indent. Longer text will wrap char tabChar; // If this char is seen the indent is changed to current pos }; class Text { public: Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) : attr( _attr ) { std::string wrappableChars = " [({.,/|\\-"; std::size_t indent = _attr.initialIndent != std::string::npos ? _attr.initialIndent : _attr.indent; std::string remainder = _str; while( !remainder.empty() ) { if( lines.size() >= 1000 ) { lines.push_back( "... message truncated due to excessive size" ); return; } std::size_t tabPos = std::string::npos; std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); std::size_t pos = remainder.find_first_of( '\n' ); if( pos <= width ) { width = pos; } pos = remainder.find_last_of( _attr.tabChar, width ); if( pos != std::string::npos ) { tabPos = pos; if( remainder[width] == '\n' ) width--; remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); } if( width == remainder.size() ) { spliceLine( indent, remainder, width ); } else if( remainder[width] == '\n' ) { spliceLine( indent, remainder, width ); if( width <= 1 || remainder.size() != 1 ) remainder = remainder.substr( 1 ); indent = _attr.indent; } else { pos = remainder.find_last_of( wrappableChars, width ); if( pos != std::string::npos && pos > 0 ) { spliceLine( indent, remainder, pos ); if( remainder[0] == ' ' ) remainder = remainder.substr( 1 ); } else { spliceLine( indent, remainder, width-1 ); lines.back() += "-"; } if( lines.size() == 1 ) indent = _attr.indent; if( tabPos != std::string::npos ) indent += tabPos; } } } void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); _remainder = _remainder.substr( _pos ); } typedef std::vector::const_iterator const_iterator; const_iterator begin() const { return lines.begin(); } const_iterator end() const { return lines.end(); } std::string const& last() const { return lines.back(); } std::size_t size() const { return lines.size(); } std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } std::string toString() const { std::ostringstream oss; oss << *this; return oss.str(); } inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); it != itEnd; ++it ) { if( it != _text.begin() ) _stream << "\n"; _stream << *it; } return _stream; } private: std::string str; TextAttributes attr; std::vector lines; }; } // end namespace Tbc #ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE } // end outer namespace #endif #endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED #undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE namespace Catch { using Tbc::Text; using Tbc::TextAttributes; } // #included from: catch_console_colour.hpp #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED namespace Catch { struct Colour { enum Code { None = 0, White, Red, Green, Blue, Cyan, Yellow, Grey, Bright = 0x10, BrightRed = Bright | Red, BrightGreen = Bright | Green, LightGrey = Bright | Grey, BrightWhite = Bright | White, // By intention FileName = LightGrey, Warning = Yellow, ResultError = BrightRed, ResultSuccess = BrightGreen, ResultExpectedFailure = Warning, Error = BrightRed, Success = Green, OriginalExpression = Cyan, ReconstructedExpression = Yellow, SecondaryText = LightGrey, Headers = White }; // Use constructed object for RAII guard Colour( Code _colourCode ); Colour( Colour const& other ); ~Colour(); // Use static method for one-shot changes static void use( Code _colourCode ); private: bool m_moved; }; inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } } // end namespace Catch // #included from: catch_interfaces_reporter.h #define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED #include #include #include #include namespace Catch { struct ReporterConfig { explicit ReporterConfig( Ptr const& _fullConfig ) : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} std::ostream& stream() const { return *m_stream; } Ptr fullConfig() const { return m_fullConfig; } private: std::ostream* m_stream; Ptr m_fullConfig; }; struct ReporterPreferences { ReporterPreferences() : shouldRedirectStdOut( false ) {} bool shouldRedirectStdOut; }; template struct LazyStat : Option { LazyStat() : used( false ) {} LazyStat& operator=( T const& _value ) { Option::operator=( _value ); used = false; return *this; } void reset() { Option::reset(); used = false; } bool used; }; struct TestRunInfo { TestRunInfo( std::string const& _name ) : name( _name ) {} std::string name; }; struct GroupInfo { GroupInfo( std::string const& _name, std::size_t _groupIndex, std::size_t _groupsCount ) : name( _name ), groupIndex( _groupIndex ), groupsCounts( _groupsCount ) {} std::string name; std::size_t groupIndex; std::size_t groupsCounts; }; struct AssertionStats { AssertionStats( AssertionResult const& _assertionResult, std::vector const& _infoMessages, Totals const& _totals ) : assertionResult( _assertionResult ), infoMessages( _infoMessages ), totals( _totals ) { if( assertionResult.hasMessage() ) { // Copy message into messages list. // !TBD This should have been done earlier, somewhere MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); builder << assertionResult.getMessage(); builder.m_info.message = builder.m_stream.str(); infoMessages.push_back( builder.m_info ); } } virtual ~AssertionStats(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS AssertionStats( AssertionStats const& ) = default; AssertionStats( AssertionStats && ) = default; AssertionStats& operator = ( AssertionStats const& ) = default; AssertionStats& operator = ( AssertionStats && ) = default; # endif AssertionResult assertionResult; std::vector infoMessages; Totals totals; }; struct SectionStats { SectionStats( SectionInfo const& _sectionInfo, Counts const& _assertions, double _durationInSeconds, bool _missingAssertions ) : sectionInfo( _sectionInfo ), assertions( _assertions ), durationInSeconds( _durationInSeconds ), missingAssertions( _missingAssertions ) {} virtual ~SectionStats(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS SectionStats( SectionStats const& ) = default; SectionStats( SectionStats && ) = default; SectionStats& operator = ( SectionStats const& ) = default; SectionStats& operator = ( SectionStats && ) = default; # endif SectionInfo sectionInfo; Counts assertions; double durationInSeconds; bool missingAssertions; }; struct TestCaseStats { TestCaseStats( TestCaseInfo const& _testInfo, Totals const& _totals, std::string const& _stdOut, std::string const& _stdErr, bool _aborting ) : testInfo( _testInfo ), totals( _totals ), stdOut( _stdOut ), stdErr( _stdErr ), aborting( _aborting ) {} virtual ~TestCaseStats(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS TestCaseStats( TestCaseStats const& ) = default; TestCaseStats( TestCaseStats && ) = default; TestCaseStats& operator = ( TestCaseStats const& ) = default; TestCaseStats& operator = ( TestCaseStats && ) = default; # endif TestCaseInfo testInfo; Totals totals; std::string stdOut; std::string stdErr; bool aborting; }; struct TestGroupStats { TestGroupStats( GroupInfo const& _groupInfo, Totals const& _totals, bool _aborting ) : groupInfo( _groupInfo ), totals( _totals ), aborting( _aborting ) {} TestGroupStats( GroupInfo const& _groupInfo ) : groupInfo( _groupInfo ), aborting( false ) {} virtual ~TestGroupStats(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS TestGroupStats( TestGroupStats const& ) = default; TestGroupStats( TestGroupStats && ) = default; TestGroupStats& operator = ( TestGroupStats const& ) = default; TestGroupStats& operator = ( TestGroupStats && ) = default; # endif GroupInfo groupInfo; Totals totals; bool aborting; }; struct TestRunStats { TestRunStats( TestRunInfo const& _runInfo, Totals const& _totals, bool _aborting ) : runInfo( _runInfo ), totals( _totals ), aborting( _aborting ) {} virtual ~TestRunStats(); # ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS TestRunStats( TestRunStats const& _other ) : runInfo( _other.runInfo ), totals( _other.totals ), aborting( _other.aborting ) {} # else TestRunStats( TestRunStats const& ) = default; TestRunStats( TestRunStats && ) = default; TestRunStats& operator = ( TestRunStats const& ) = default; TestRunStats& operator = ( TestRunStats && ) = default; # endif TestRunInfo runInfo; Totals totals; bool aborting; }; struct IStreamingReporter : IShared { virtual ~IStreamingReporter(); // Implementing class must also provide the following static method: // static std::string getDescription(); virtual ReporterPreferences getPreferences() const = 0; virtual void noMatchingTestCases( std::string const& spec ) = 0; virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; // The return value indicates if the messages buffer should be cleared: virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; virtual void sectionEnded( SectionStats const& sectionStats ) = 0; virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; virtual void skipTest( TestCaseInfo const& testInfo ) = 0; }; struct IReporterFactory : IShared { virtual ~IReporterFactory(); virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; virtual std::string getDescription() const = 0; }; struct IReporterRegistry { typedef std::map > FactoryMap; typedef std::vector > Listeners; virtual ~IReporterRegistry(); virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; virtual FactoryMap const& getFactories() const = 0; virtual Listeners const& getListeners() const = 0; }; Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ); } #include #include namespace Catch { inline std::size_t listTests( Config const& config ) { TestSpec testSpec = config.testSpec(); if( config.testSpec().hasFilters() ) Catch::cout() << "Matching test cases:\n"; else { Catch::cout() << "All available test cases:\n"; testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); } std::size_t matchedTests = 0; TextAttributes nameAttr, tagsAttr; nameAttr.setInitialIndent( 2 ).setIndent( 4 ); tagsAttr.setIndent( 6 ); std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); Colour::Code colour = testCaseInfo.isHidden() ? Colour::SecondaryText : Colour::None; Colour colourGuard( colour ); Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; if( !testCaseInfo.tags.empty() ) Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; } if( !config.testSpec().hasFilters() ) Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; else Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; return matchedTests; } inline std::size_t listTestsNamesOnly( Config const& config ) { TestSpec testSpec = config.testSpec(); if( !config.testSpec().hasFilters() ) testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); std::size_t matchedTests = 0; std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); Catch::cout() << testCaseInfo.name << std::endl; } return matchedTests; } struct TagInfo { TagInfo() : count ( 0 ) {} void add( std::string const& spelling ) { ++count; spellings.insert( spelling ); } std::string all() const { std::string out; for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); it != itEnd; ++it ) out += "[" + *it + "]"; return out; } std::set spellings; std::size_t count; }; inline std::size_t listTags( Config const& config ) { TestSpec testSpec = config.testSpec(); if( config.testSpec().hasFilters() ) Catch::cout() << "Tags for matching test cases:\n"; else { Catch::cout() << "All available tags:\n"; testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); } std::map tagCounts; std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), tagItEnd = it->getTestCaseInfo().tags.end(); tagIt != tagItEnd; ++tagIt ) { std::string tagName = *tagIt; std::string lcaseTagName = toLower( tagName ); std::map::iterator countIt = tagCounts.find( lcaseTagName ); if( countIt == tagCounts.end() ) countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; countIt->second.add( tagName ); } } for( std::map::const_iterator countIt = tagCounts.begin(), countItEnd = tagCounts.end(); countIt != countItEnd; ++countIt ) { std::ostringstream oss; oss << " " << std::setw(2) << countIt->second.count << " "; Text wrapper( countIt->second.all(), TextAttributes() .setInitialIndent( 0 ) .setIndent( oss.str().size() ) .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); Catch::cout() << oss.str() << wrapper << "\n"; } Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; return tagCounts.size(); } inline std::size_t listReporters( Config const& /*config*/ ) { Catch::cout() << "Available reporters:\n"; IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; std::size_t maxNameLen = 0; for(it = itBegin; it != itEnd; ++it ) maxNameLen = (std::max)( maxNameLen, it->first.size() ); for(it = itBegin; it != itEnd; ++it ) { Text wrapper( it->second->getDescription(), TextAttributes() .setInitialIndent( 0 ) .setIndent( 7+maxNameLen ) .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); Catch::cout() << " " << it->first << ":" << std::string( maxNameLen - it->first.size() + 2, ' ' ) << wrapper << "\n"; } Catch::cout() << std::endl; return factories.size(); } inline Option list( Config const& config ) { Option listedCount; if( config.listTests() ) listedCount = listedCount.valueOr(0) + listTests( config ); if( config.listTestNamesOnly() ) listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); if( config.listTags() ) listedCount = listedCount.valueOr(0) + listTags( config ); if( config.listReporters() ) listedCount = listedCount.valueOr(0) + listReporters( config ); return listedCount; } } // end namespace Catch // #included from: internal/catch_run_context.hpp #define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED // #included from: catch_test_case_tracker.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED #include #include #include #include namespace Catch { namespace TestCaseTracking { struct ITracker : SharedImpl<> { virtual ~ITracker(); // static queries virtual std::string name() const = 0; // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed virtual bool isSuccessfullyCompleted() const = 0; virtual bool isOpen() const = 0; // Started but not complete virtual bool hasChildren() const = 0; virtual ITracker& parent() = 0; // actions virtual void close() = 0; // Successfully complete virtual void fail() = 0; virtual void markAsNeedingAnotherRun() = 0; virtual void addChild( Ptr const& child ) = 0; virtual ITracker* findChild( std::string const& name ) = 0; virtual void openChild() = 0; }; class TrackerContext { enum RunState { NotStarted, Executing, CompletedCycle }; Ptr m_rootTracker; ITracker* m_currentTracker; RunState m_runState; public: static TrackerContext& instance() { static TrackerContext s_instance; return s_instance; } TrackerContext() : m_currentTracker( CATCH_NULL ), m_runState( NotStarted ) {} ITracker& startRun(); void endRun() { m_rootTracker.reset(); m_currentTracker = CATCH_NULL; m_runState = NotStarted; } void startCycle() { m_currentTracker = m_rootTracker.get(); m_runState = Executing; } void completeCycle() { m_runState = CompletedCycle; } bool completedCycle() const { return m_runState == CompletedCycle; } ITracker& currentTracker() { return *m_currentTracker; } void setCurrentTracker( ITracker* tracker ) { m_currentTracker = tracker; } }; class TrackerBase : public ITracker { protected: enum CycleState { NotStarted, Executing, ExecutingChildren, NeedsAnotherRun, CompletedSuccessfully, Failed }; class TrackerHasName { std::string m_name; public: TrackerHasName( std::string const& name ) : m_name( name ) {} bool operator ()( Ptr const& tracker ) { return tracker->name() == m_name; } }; typedef std::vector > Children; std::string m_name; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; CycleState m_runState; public: TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent ) : m_name( name ), m_ctx( ctx ), m_parent( parent ), m_runState( NotStarted ) {} virtual ~TrackerBase(); virtual std::string name() const CATCH_OVERRIDE { return m_name; } virtual bool isComplete() const CATCH_OVERRIDE { return m_runState == CompletedSuccessfully || m_runState == Failed; } virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { return m_runState == CompletedSuccessfully; } virtual bool isOpen() const CATCH_OVERRIDE { return m_runState != NotStarted && !isComplete(); } virtual bool hasChildren() const CATCH_OVERRIDE { return !m_children.empty(); } virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { m_children.push_back( child ); } virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE { Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); return( it != m_children.end() ) ? it->get() : CATCH_NULL; } virtual ITracker& parent() CATCH_OVERRIDE { assert( m_parent ); // Should always be non-null except for root return *m_parent; } virtual void openChild() CATCH_OVERRIDE { if( m_runState != ExecutingChildren ) { m_runState = ExecutingChildren; if( m_parent ) m_parent->openChild(); } } void open() { m_runState = Executing; moveToThis(); if( m_parent ) m_parent->openChild(); } virtual void close() CATCH_OVERRIDE { // Close any still open children (e.g. generators) while( &m_ctx.currentTracker() != this ) m_ctx.currentTracker().close(); switch( m_runState ) { case NotStarted: case CompletedSuccessfully: case Failed: throw std::logic_error( "Illogical state" ); case NeedsAnotherRun: break;; case Executing: m_runState = CompletedSuccessfully; break; case ExecutingChildren: if( m_children.empty() || m_children.back()->isComplete() ) m_runState = CompletedSuccessfully; break; default: throw std::logic_error( "Unexpected state" ); } moveToParent(); m_ctx.completeCycle(); } virtual void fail() CATCH_OVERRIDE { m_runState = Failed; if( m_parent ) m_parent->markAsNeedingAnotherRun(); moveToParent(); m_ctx.completeCycle(); } virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { m_runState = NeedsAnotherRun; } private: void moveToParent() { assert( m_parent ); m_ctx.setCurrentTracker( m_parent ); } void moveToThis() { m_ctx.setCurrentTracker( this ); } }; class SectionTracker : public TrackerBase { public: SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) : TrackerBase( name, ctx, parent ) {} virtual ~SectionTracker(); static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { SectionTracker* section = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); if( ITracker* childTracker = currentTracker.findChild( name ) ) { section = dynamic_cast( childTracker ); assert( section ); } else { section = new SectionTracker( name, ctx, ¤tTracker ); currentTracker.addChild( section ); } if( !ctx.completedCycle() && !section->isComplete() ) { section->open(); } return *section; } }; class IndexTracker : public TrackerBase { int m_size; int m_index; public: IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size ) : TrackerBase( name, ctx, parent ), m_size( size ), m_index( -1 ) {} virtual ~IndexTracker(); static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { IndexTracker* tracker = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); if( ITracker* childTracker = currentTracker.findChild( name ) ) { tracker = dynamic_cast( childTracker ); assert( tracker ); } else { tracker = new IndexTracker( name, ctx, ¤tTracker, size ); currentTracker.addChild( tracker ); } if( !ctx.completedCycle() && !tracker->isComplete() ) { if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) tracker->moveNext(); tracker->open(); } return *tracker; } int index() const { return m_index; } void moveNext() { m_index++; m_children.clear(); } virtual void close() CATCH_OVERRIDE { TrackerBase::close(); if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) m_runState = Executing; } }; inline ITracker& TrackerContext::startRun() { m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); m_currentTracker = CATCH_NULL; m_runState = Executing; return *m_rootTracker; } } // namespace TestCaseTracking using TestCaseTracking::ITracker; using TestCaseTracking::TrackerContext; using TestCaseTracking::SectionTracker; using TestCaseTracking::IndexTracker; } // namespace Catch // #included from: catch_fatal_condition.hpp #define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED namespace Catch { // Report the error condition then exit the process inline void fatal( std::string const& message, int exitCode ) { IContext& context = Catch::getCurrentContext(); IResultCapture* resultCapture = context.getResultCapture(); resultCapture->handleFatalErrorCondition( message ); if( Catch::alwaysTrue() ) // avoids "no return" warnings exit( exitCode ); } } // namespace Catch #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// namespace Catch { struct FatalConditionHandler { void reset() {} }; } // namespace Catch #else // Not Windows - assumed to be POSIX compatible ////////////////////////// #include namespace Catch { struct SignalDefs { int id; const char* name; }; extern SignalDefs signalDefs[]; SignalDefs signalDefs[] = { { SIGINT, "SIGINT - Terminal interrupt signal" }, { SIGILL, "SIGILL - Illegal instruction signal" }, { SIGFPE, "SIGFPE - Floating point error signal" }, { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, { SIGTERM, "SIGTERM - Termination request signal" }, { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } }; struct FatalConditionHandler { static void handleSignal( int sig ) { for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) if( sig == signalDefs[i].id ) fatal( signalDefs[i].name, -sig ); fatal( "", -sig ); } FatalConditionHandler() : m_isSet( true ) { for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) signal( signalDefs[i].id, handleSignal ); } ~FatalConditionHandler() { reset(); } void reset() { if( m_isSet ) { for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) signal( signalDefs[i].id, SIG_DFL ); m_isSet = false; } } bool m_isSet; }; } // namespace Catch #endif // not Windows #include #include namespace Catch { class StreamRedirect { public: StreamRedirect( std::ostream& stream, std::string& targetString ) : m_stream( stream ), m_prevBuf( stream.rdbuf() ), m_targetString( targetString ) { stream.rdbuf( m_oss.rdbuf() ); } ~StreamRedirect() { m_targetString += m_oss.str(); m_stream.rdbuf( m_prevBuf ); } private: std::ostream& m_stream; std::streambuf* m_prevBuf; std::ostringstream m_oss; std::string& m_targetString; }; /////////////////////////////////////////////////////////////////////////// class RunContext : public IResultCapture, public IRunner { RunContext( RunContext const& ); void operator =( RunContext const& ); public: explicit RunContext( Ptr const& _config, Ptr const& reporter ) : m_runInfo( _config->name() ), m_context( getCurrentMutableContext() ), m_activeTestCase( CATCH_NULL ), m_config( _config ), m_reporter( reporter ) { m_context.setRunner( this ); m_context.setConfig( m_config ); m_context.setResultCapture( this ); m_reporter->testRunStarting( m_runInfo ); } virtual ~RunContext() { m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); } void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); } void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); } Totals runTest( TestCase const& testCase ) { Totals prevTotals = m_totals; std::string redirectedCout; std::string redirectedCerr; TestCaseInfo testInfo = testCase.getTestCaseInfo(); m_reporter->testCaseStarting( testInfo ); m_activeTestCase = &testCase; do { m_trackerContext.startRun(); do { m_trackerContext.startCycle(); m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); runCurrentTest( redirectedCout, redirectedCerr ); } while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); } // !TBD: deprecated - this will be replaced by indexed trackers while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); Totals deltaTotals = m_totals.delta( prevTotals ); m_totals.testCases += deltaTotals.testCases; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, redirectedCout, redirectedCerr, aborting() ) ); m_activeTestCase = CATCH_NULL; m_testCaseTracker = CATCH_NULL; return deltaTotals; } Ptr config() const { return m_config; } private: // IResultCapture virtual void assertionEnded( AssertionResult const& result ) { if( result.getResultType() == ResultWas::Ok ) { m_totals.assertions.passed++; } else if( !result.isOk() ) { m_totals.assertions.failed++; } if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) m_messages.clear(); // Reset working state m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); m_lastResult = result; } virtual bool sectionStarted ( SectionInfo const& sectionInfo, Counts& assertions ) { std::ostringstream oss; oss << sectionInfo.name << "@" << sectionInfo.lineInfo; ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); if( !sectionTracker.isOpen() ) return false; m_activeSections.push_back( §ionTracker ); m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; m_reporter->sectionStarting( sectionInfo ); assertions = m_totals.assertions; return true; } bool testForMissingAssertions( Counts& assertions ) { if( assertions.total() != 0 ) return false; if( !m_config->warnAboutMissingAssertions() ) return false; if( m_trackerContext.currentTracker().hasChildren() ) return false; m_totals.assertions.failed++; assertions.failed++; return true; } virtual void sectionEnded( SectionEndInfo const& endInfo ) { Counts assertions = m_totals.assertions - endInfo.prevAssertions; bool missingAssertions = testForMissingAssertions( assertions ); if( !m_activeSections.empty() ) { m_activeSections.back()->close(); m_activeSections.pop_back(); } m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); m_messages.clear(); } virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { if( m_unfinishedSections.empty() ) m_activeSections.back()->fail(); else m_activeSections.back()->close(); m_activeSections.pop_back(); m_unfinishedSections.push_back( endInfo ); } virtual void pushScopedMessage( MessageInfo const& message ) { m_messages.push_back( message ); } virtual void popScopedMessage( MessageInfo const& message ) { m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); } virtual std::string getCurrentTestName() const { return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name : ""; } virtual const AssertionResult* getLastResult() const { return &m_lastResult; } virtual void handleFatalErrorCondition( std::string const& message ) { ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); resultBuilder.setResultType( ResultWas::FatalErrorCondition ); resultBuilder << message; resultBuilder.captureExpression(); handleUnfinishedSections(); // Recreate section for test case (as we will lose the one that was in scope) TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); Counts assertions; assertions.failed = 1; SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); m_reporter->sectionEnded( testCaseSectionStats ); TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); Totals deltaTotals; deltaTotals.testCases.failed = 1; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, "", "", false ) ); m_totals.testCases.failed++; testGroupEnded( "", m_totals, 1, 1 ); m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); } public: // !TBD We need to do this another way! bool aborting() const { return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); } private: void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); m_reporter->sectionStarting( testCaseSection ); Counts prevAssertions = m_totals.assertions; double duration = 0; try { m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); seedRng( *m_config ); Timer timer; timer.start(); if( m_reporter->getPreferences().shouldRedirectStdOut ) { StreamRedirect coutRedir( Catch::cout(), redirectedCout ); StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); invokeActiveTestCase(); } else { invokeActiveTestCase(); } duration = timer.getElapsedSeconds(); } catch( TestFailureException& ) { // This just means the test was aborted due to failure } catch(...) { makeUnexpectedResultBuilder().useActiveException(); } m_testCaseTracker->close(); handleUnfinishedSections(); m_messages.clear(); Counts assertions = m_totals.assertions - prevAssertions; bool missingAssertions = testForMissingAssertions( assertions ); if( testCaseInfo.okToFail() ) { std::swap( assertions.failedButOk, assertions.failed ); m_totals.assertions.failed -= assertions.failedButOk; m_totals.assertions.failedButOk += assertions.failedButOk; } SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); m_reporter->sectionEnded( testCaseSectionStats ); } void invokeActiveTestCase() { FatalConditionHandler fatalConditionHandler; // Handle signals m_activeTestCase->invoke(); fatalConditionHandler.reset(); } private: ResultBuilder makeUnexpectedResultBuilder() const { return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), m_lastAssertionInfo.lineInfo, m_lastAssertionInfo.capturedExpression.c_str(), m_lastAssertionInfo.resultDisposition ); } void handleUnfinishedSections() { // If sections ended prematurely due to an exception we stored their // infos here so we can tear them down outside the unwind process. for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), itEnd = m_unfinishedSections.rend(); it != itEnd; ++it ) sectionEnded( *it ); m_unfinishedSections.clear(); } TestRunInfo m_runInfo; IMutableContext& m_context; TestCase const* m_activeTestCase; ITracker* m_testCaseTracker; ITracker* m_currentSectionTracker; AssertionResult m_lastResult; Ptr m_config; Totals m_totals; Ptr m_reporter; std::vector m_messages; AssertionInfo m_lastAssertionInfo; std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; }; IResultCapture& getResultCapture() { if( IResultCapture* capture = getCurrentContext().getResultCapture() ) return *capture; else throw std::logic_error( "No result capture instance" ); } } // end namespace Catch // #included from: internal/catch_version.h #define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED namespace Catch { // Versioning information struct Version { Version( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, std::string const& _branchName, unsigned int _buildNumber ); unsigned int const majorVersion; unsigned int const minorVersion; unsigned int const patchNumber; // buildNumber is only used if branchName is not null std::string const branchName; unsigned int const buildNumber; friend std::ostream& operator << ( std::ostream& os, Version const& version ); private: void operator=( Version const& ); }; extern Version libraryVersion; } #include #include #include namespace Catch { Ptr createReporter( std::string const& reporterName, Ptr const& config ) { Ptr reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); if( !reporter ) { std::ostringstream oss; oss << "No reporter registered with name: '" << reporterName << "'"; throw std::domain_error( oss.str() ); } return reporter; } Ptr makeReporter( Ptr const& config ) { std::vector reporters = config->getReporterNames(); if( reporters.empty() ) reporters.push_back( "console" ); Ptr reporter; for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); it != itEnd; ++it ) reporter = addReporter( reporter, createReporter( *it, config ) ); return reporter; } Ptr addListeners( Ptr const& config, Ptr reporters ) { IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); it != itEnd; ++it ) reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); return reporters; } Totals runTests( Ptr const& config ) { Ptr iconfig = config.get(); Ptr reporter = makeReporter( config ); reporter = addListeners( iconfig, reporter ); RunContext context( iconfig, reporter ); Totals totals; context.testGroupStarting( config->name(), 1, 1 ); TestSpec testSpec = config->testSpec(); if( !testSpec.hasFilters() ) testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests std::vector const& allTestCases = getAllTestCasesSorted( *iconfig ); for( std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); it != itEnd; ++it ) { if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) totals += context.runTest( *it ); else reporter->skipTest( *it ); } context.testGroupEnded( iconfig->name(), totals, 1, 1 ); return totals; } void applyFilenamesAsTags( IConfig const& config ) { std::vector const& tests = getAllTestCasesSorted( config ); for(std::size_t i = 0; i < tests.size(); ++i ) { TestCase& test = const_cast( tests[i] ); std::set tags = test.tags; std::string filename = test.lineInfo.file; std::string::size_type lastSlash = filename.find_last_of( "\\/" ); if( lastSlash != std::string::npos ) filename = filename.substr( lastSlash+1 ); std::string::size_type lastDot = filename.find_last_of( "." ); if( lastDot != std::string::npos ) filename = filename.substr( 0, lastDot ); tags.insert( "#" + filename ); setTags( test, tags ); } } class Session : NonCopyable { static bool alreadyInstantiated; public: struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; Session() : m_cli( makeCommandLineParser() ) { if( alreadyInstantiated ) { std::string msg = "Only one instance of Catch::Session can ever be used"; Catch::cerr() << msg << std::endl; throw std::logic_error( msg ); } alreadyInstantiated = true; } ~Session() { Catch::cleanUp(); } void showHelp( std::string const& processName ) { Catch::cout() << "\nCatch v" << libraryVersion << "\n"; m_cli.usage( Catch::cout(), processName ); Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; } int applyCommandLine( int argc, char const* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { try { m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); if( m_configData.showHelp ) showHelp( m_configData.processName ); m_config.reset(); } catch( std::exception& ex ) { { Colour colourGuard( Colour::Red ); Catch::cerr() << "\nError(s) in input:\n" << Text( ex.what(), TextAttributes().setIndent(2) ) << "\n\n"; } m_cli.usage( Catch::cout(), m_configData.processName ); return (std::numeric_limits::max)(); } return 0; } void useConfigData( ConfigData const& _configData ) { m_configData = _configData; m_config.reset(); } int run( int argc, char const* const argv[] ) { int returnCode = applyCommandLine( argc, argv ); if( returnCode == 0 ) returnCode = run(); return returnCode; } int run() { if( m_configData.showHelp ) return 0; try { config(); // Force config to be constructed seedRng( *m_config ); if( m_configData.filenamesAsTags ) applyFilenamesAsTags( *m_config ); // Handle list request if( Option listed = list( config() ) ) return static_cast( *listed ); return static_cast( runTests( m_config ).assertions.failed ); } catch( std::exception& ex ) { Catch::cerr() << ex.what() << std::endl; return (std::numeric_limits::max)(); } } Clara::CommandLine const& cli() const { return m_cli; } std::vector const& unusedTokens() const { return m_unusedTokens; } ConfigData& configData() { return m_configData; } Config& config() { if( !m_config ) m_config = new Config( m_configData ); return *m_config; } private: Clara::CommandLine m_cli; std::vector m_unusedTokens; ConfigData m_configData; Ptr m_config; }; bool Session::alreadyInstantiated = false; } // end namespace Catch // #included from: catch_registry_hub.hpp #define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED // #included from: catch_test_case_registry_impl.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED #include #include #include #include #include namespace Catch { struct LexSort { bool operator() (TestCase i,TestCase j) const { return (i sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { std::vector sorted = unsortedTestCases; switch( config.runOrder() ) { case RunTests::InLexicographicalOrder: std::sort( sorted.begin(), sorted.end(), LexSort() ); break; case RunTests::InRandomOrder: { seedRng( config ); RandomNumberGenerator rng; std::random_shuffle( sorted.begin(), sorted.end(), rng ); } break; case RunTests::InDeclarationOrder: // already in declaration order break; } return sorted; } bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); } void enforceNoDuplicateTestCases( std::vector const& functions ) { std::set seenFunctions; for( std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); it != itEnd; ++it ) { std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); if( !prev.second ){ Catch::cerr() << Colour( Colour::Red ) << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; exit(1); } } } std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { std::vector filtered; filtered.reserve( testCases.size() ); for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); it != itEnd; ++it ) if( matchTest( *it, testSpec, config ) ) filtered.push_back( *it ); return filtered; } std::vector const& getAllTestCasesSorted( IConfig const& config ) { return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); } class TestRegistry : public ITestCaseRegistry { public: TestRegistry() : m_unnamedCount( 0 ) {} virtual ~TestRegistry(); virtual void registerTest( TestCase const& testCase ) { std::string name = testCase.getTestCaseInfo().name; if( name == "" ) { std::ostringstream oss; oss << "Anonymous test case " << ++m_unnamedCount; return registerTest( testCase.withName( oss.str() ) ); } m_functions.push_back( testCase ); } virtual std::vector const& getAllTests() const { return m_functions; } virtual std::vector const& getAllTestsSorted( IConfig const& config ) const { if( m_sortedFunctions.empty() ) enforceNoDuplicateTestCases( m_functions ); if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { m_sortedFunctions = sortTests( config, m_functions ); m_currentSortOrder = config.runOrder(); } return m_sortedFunctions; } private: std::vector m_functions; mutable RunTests::InWhatOrder m_currentSortOrder; mutable std::vector m_sortedFunctions; size_t m_unnamedCount; std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised }; /////////////////////////////////////////////////////////////////////////// class FreeFunctionTestCase : public SharedImpl { public: FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} virtual void invoke() const { m_fun(); } private: virtual ~FreeFunctionTestCase(); TestFunction m_fun; }; inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { std::string className = classOrQualifiedMethodName; if( startsWith( className, "&" ) ) { std::size_t lastColons = className.rfind( "::" ); std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); if( penultimateColons == std::string::npos ) penultimateColons = 1; className = className.substr( penultimateColons, lastColons-penultimateColons ); } return className; } void registerTestCase ( ITestCase* testCase, char const* classOrQualifiedMethodName, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ) { getMutableRegistryHub().registerTest ( makeTestCase ( testCase, extractClassName( classOrQualifiedMethodName ), nameAndDesc.name, nameAndDesc.description, lineInfo ) ); } void registerTestCaseFunction ( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ) { registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); } /////////////////////////////////////////////////////////////////////////// AutoReg::AutoReg ( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ) { registerTestCaseFunction( function, lineInfo, nameAndDesc ); } AutoReg::~AutoReg() {} } // end namespace Catch // #included from: catch_reporter_registry.hpp #define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED #include namespace Catch { class ReporterRegistry : public IReporterRegistry { public: virtual ~ReporterRegistry() CATCH_OVERRIDE {} virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const CATCH_OVERRIDE { FactoryMap::const_iterator it = m_factories.find( name ); if( it == m_factories.end() ) return CATCH_NULL; return it->second->create( ReporterConfig( config ) ); } void registerReporter( std::string const& name, Ptr const& factory ) { m_factories.insert( std::make_pair( name, factory ) ); } void registerListener( Ptr const& factory ) { m_listeners.push_back( factory ); } virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { return m_factories; } virtual Listeners const& getListeners() const CATCH_OVERRIDE { return m_listeners; } private: FactoryMap m_factories; Listeners m_listeners; }; } // #included from: catch_exception_translator_registry.hpp #define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED #ifdef __OBJC__ #import "Foundation/Foundation.h" #endif namespace Catch { class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { public: ~ExceptionTranslatorRegistry() { deleteAll( m_translators ); } virtual void registerTranslator( const IExceptionTranslator* translator ) { m_translators.push_back( translator ); } virtual std::string translateActiveException() const { try { #ifdef __OBJC__ // In Objective-C try objective-c exceptions first @try { return tryTranslators(); } @catch (NSException *exception) { return Catch::toString( [exception description] ); } #else return tryTranslators(); #endif } catch( TestFailureException& ) { throw; } catch( std::exception& ex ) { return ex.what(); } catch( std::string& msg ) { return msg; } catch( const char* msg ) { return msg; } catch(...) { return "Unknown exception"; } } std::string tryTranslators() const { if( m_translators.empty() ) throw; else return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); } private: std::vector m_translators; }; } namespace Catch { namespace { class RegistryHub : public IRegistryHub, public IMutableRegistryHub { RegistryHub( RegistryHub const& ); void operator=( RegistryHub const& ); public: // IRegistryHub RegistryHub() { } virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { return m_reporterRegistry; } virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { return m_testCaseRegistry; } virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { return m_exceptionTranslatorRegistry; } public: // IMutableRegistryHub virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { m_reporterRegistry.registerReporter( name, factory ); } virtual void registerListener( Ptr const& factory ) CATCH_OVERRIDE { m_reporterRegistry.registerListener( factory ); } virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { m_testCaseRegistry.registerTest( testInfo ); } virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { m_exceptionTranslatorRegistry.registerTranslator( translator ); } private: TestRegistry m_testCaseRegistry; ReporterRegistry m_reporterRegistry; ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; }; // Single, global, instance inline RegistryHub*& getTheRegistryHub() { static RegistryHub* theRegistryHub = CATCH_NULL; if( !theRegistryHub ) theRegistryHub = new RegistryHub(); return theRegistryHub; } } IRegistryHub& getRegistryHub() { return *getTheRegistryHub(); } IMutableRegistryHub& getMutableRegistryHub() { return *getTheRegistryHub(); } void cleanUp() { delete getTheRegistryHub(); getTheRegistryHub() = CATCH_NULL; cleanUpContext(); } std::string translateActiveException() { return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); } } // end namespace Catch // #included from: catch_notimplemented_exception.hpp #define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED #include namespace Catch { NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) : m_lineInfo( lineInfo ) { std::ostringstream oss; oss << lineInfo << ": function "; oss << "not implemented"; m_what = oss.str(); } const char* NotImplementedException::what() const CATCH_NOEXCEPT { return m_what.c_str(); } } // end namespace Catch // #included from: catch_context_impl.hpp #define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED // #included from: catch_stream.hpp #define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED #include #include #include namespace Catch { template class StreamBufImpl : public StreamBufBase { char data[bufferSize]; WriterF m_writer; public: StreamBufImpl() { setp( data, data + sizeof(data) ); } ~StreamBufImpl() CATCH_NOEXCEPT { sync(); } private: int overflow( int c ) { sync(); if( c != EOF ) { if( pbase() == epptr() ) m_writer( std::string( 1, static_cast( c ) ) ); else sputc( static_cast( c ) ); } return 0; } int sync() { if( pbase() != pptr() ) { m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); setp( pbase(), epptr() ); } return 0; } }; /////////////////////////////////////////////////////////////////////////// FileStream::FileStream( std::string const& filename ) { m_ofs.open( filename.c_str() ); if( m_ofs.fail() ) { std::ostringstream oss; oss << "Unable to open file: '" << filename << "'"; throw std::domain_error( oss.str() ); } } std::ostream& FileStream::stream() const { return m_ofs; } struct OutputDebugWriter { void operator()( std::string const&str ) { writeToDebugConsole( str ); } }; DebugOutStream::DebugOutStream() : m_streamBuf( new StreamBufImpl() ), m_os( m_streamBuf.get() ) {} std::ostream& DebugOutStream::stream() const { return m_os; } // Store the streambuf from cout up-front because // cout may get redirected when running tests CoutStream::CoutStream() : m_os( Catch::cout().rdbuf() ) {} std::ostream& CoutStream::stream() const { return m_os; } #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions std::ostream& cout() { return std::cout; } std::ostream& cerr() { return std::cerr; } #endif } namespace Catch { class Context : public IMutableContext { Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} Context( Context const& ); void operator=( Context const& ); public: // IContext virtual IResultCapture* getResultCapture() { return m_resultCapture; } virtual IRunner* getRunner() { return m_runner; } virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { return getGeneratorsForCurrentTest() .getGeneratorInfo( fileInfo, totalSize ) .getCurrentIndex(); } virtual bool advanceGeneratorsForCurrentTest() { IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); return generators && generators->moveNext(); } virtual Ptr getConfig() const { return m_config; } public: // IMutableContext virtual void setResultCapture( IResultCapture* resultCapture ) { m_resultCapture = resultCapture; } virtual void setRunner( IRunner* runner ) { m_runner = runner; } virtual void setConfig( Ptr const& config ) { m_config = config; } friend IMutableContext& getCurrentMutableContext(); private: IGeneratorsForTest* findGeneratorsForCurrentTest() { std::string testName = getResultCapture()->getCurrentTestName(); std::map::const_iterator it = m_generatorsByTestName.find( testName ); return it != m_generatorsByTestName.end() ? it->second : CATCH_NULL; } IGeneratorsForTest& getGeneratorsForCurrentTest() { IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); if( !generators ) { std::string testName = getResultCapture()->getCurrentTestName(); generators = createGeneratorsForTest(); m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); } return *generators; } private: Ptr m_config; IRunner* m_runner; IResultCapture* m_resultCapture; std::map m_generatorsByTestName; }; namespace { Context* currentContext = CATCH_NULL; } IMutableContext& getCurrentMutableContext() { if( !currentContext ) currentContext = new Context(); return *currentContext; } IContext& getCurrentContext() { return getCurrentMutableContext(); } void cleanUpContext() { delete currentContext; currentContext = CATCH_NULL; } } // #included from: catch_console_colour_impl.hpp #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED namespace Catch { namespace { struct IColourImpl { virtual ~IColourImpl() {} virtual void use( Colour::Code _colourCode ) = 0; }; struct NoColourImpl : IColourImpl { void use( Colour::Code ) {} static IColourImpl* instance() { static NoColourImpl s_instance; return &s_instance; } }; } // anon namespace } // namespace Catch #if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) # ifdef CATCH_PLATFORM_WINDOWS # define CATCH_CONFIG_COLOUR_WINDOWS # else # define CATCH_CONFIG_COLOUR_ANSI # endif #endif #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// #ifndef NOMINMAX #define NOMINMAX #endif #ifdef __AFXDLL #include #else #include #endif namespace Catch { namespace { class Win32ColourImpl : public IColourImpl { public: Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) { CONSOLE_SCREEN_BUFFER_INFO csbiInfo; GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); } virtual void use( Colour::Code _colourCode ) { switch( _colourCode ) { case Colour::None: return setTextAttribute( originalForegroundAttributes ); case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); case Colour::Red: return setTextAttribute( FOREGROUND_RED ); case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); case Colour::Grey: return setTextAttribute( 0 ); case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); case Colour::Bright: throw std::logic_error( "not a colour" ); } } private: void setTextAttribute( WORD _textAttribute ) { SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); } HANDLE stdoutHandle; WORD originalForegroundAttributes; WORD originalBackgroundAttributes; }; IColourImpl* platformColourInstance() { static Win32ColourImpl s_instance; return &s_instance; } } // end anon namespace } // end namespace Catch #elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// #include namespace Catch { namespace { // use POSIX/ ANSI console terminal codes // Thanks to Adam Strzelecki for original contribution // (http://github.com/nanoant) // https://github.com/philsquared/Catch/pull/131 class PosixColourImpl : public IColourImpl { public: virtual void use( Colour::Code _colourCode ) { switch( _colourCode ) { case Colour::None: case Colour::White: return setColour( "[0m" ); case Colour::Red: return setColour( "[0;31m" ); case Colour::Green: return setColour( "[0;32m" ); case Colour::Blue: return setColour( "[0:34m" ); case Colour::Cyan: return setColour( "[0;36m" ); case Colour::Yellow: return setColour( "[0;33m" ); case Colour::Grey: return setColour( "[1;30m" ); case Colour::LightGrey: return setColour( "[0;37m" ); case Colour::BrightRed: return setColour( "[1;31m" ); case Colour::BrightGreen: return setColour( "[1;32m" ); case Colour::BrightWhite: return setColour( "[1;37m" ); case Colour::Bright: throw std::logic_error( "not a colour" ); } } static IColourImpl* instance() { static PosixColourImpl s_instance; return &s_instance; } private: void setColour( const char* _escapeCode ) { Catch::cout() << '\033' << _escapeCode; } }; IColourImpl* platformColourInstance() { Ptr config = getCurrentContext().getConfig(); return (config && config->forceColour()) || isatty(STDOUT_FILENO) ? PosixColourImpl::instance() : NoColourImpl::instance(); } } // end anon namespace } // end namespace Catch #else // not Windows or ANSI /////////////////////////////////////////////// namespace Catch { static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } } // end namespace Catch #endif // Windows/ ANSI/ None namespace Catch { Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } Colour::~Colour(){ if( !m_moved ) use( None ); } void Colour::use( Code _colourCode ) { static IColourImpl* impl = isDebuggerActive() ? NoColourImpl::instance() : platformColourInstance(); impl->use( _colourCode ); } } // end namespace Catch // #included from: catch_generators_impl.hpp #define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED #include #include #include namespace Catch { struct GeneratorInfo : IGeneratorInfo { GeneratorInfo( std::size_t size ) : m_size( size ), m_currentIndex( 0 ) {} bool moveNext() { if( ++m_currentIndex == m_size ) { m_currentIndex = 0; return false; } return true; } std::size_t getCurrentIndex() const { return m_currentIndex; } std::size_t m_size; std::size_t m_currentIndex; }; /////////////////////////////////////////////////////////////////////////// class GeneratorsForTest : public IGeneratorsForTest { public: ~GeneratorsForTest() { deleteAll( m_generatorsInOrder ); } IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { std::map::const_iterator it = m_generatorsByName.find( fileInfo ); if( it == m_generatorsByName.end() ) { IGeneratorInfo* info = new GeneratorInfo( size ); m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); m_generatorsInOrder.push_back( info ); return *info; } return *it->second; } bool moveNext() { std::vector::const_iterator it = m_generatorsInOrder.begin(); std::vector::const_iterator itEnd = m_generatorsInOrder.end(); for(; it != itEnd; ++it ) { if( (*it)->moveNext() ) return true; } return false; } private: std::map m_generatorsByName; std::vector m_generatorsInOrder; }; IGeneratorsForTest* createGeneratorsForTest() { return new GeneratorsForTest(); } } // end namespace Catch // #included from: catch_assertionresult.hpp #define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED namespace Catch { AssertionInfo::AssertionInfo( std::string const& _macroName, SourceLineInfo const& _lineInfo, std::string const& _capturedExpression, ResultDisposition::Flags _resultDisposition ) : macroName( _macroName ), lineInfo( _lineInfo ), capturedExpression( _capturedExpression ), resultDisposition( _resultDisposition ) {} AssertionResult::AssertionResult() {} AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) : m_info( info ), m_resultData( data ) {} AssertionResult::~AssertionResult() {} // Result was a success bool AssertionResult::succeeded() const { return Catch::isOk( m_resultData.resultType ); } // Result was a success, or failure is suppressed bool AssertionResult::isOk() const { return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); } ResultWas::OfType AssertionResult::getResultType() const { return m_resultData.resultType; } bool AssertionResult::hasExpression() const { return !m_info.capturedExpression.empty(); } bool AssertionResult::hasMessage() const { return !m_resultData.message.empty(); } std::string AssertionResult::getExpression() const { if( isFalseTest( m_info.resultDisposition ) ) return "!" + m_info.capturedExpression; else return m_info.capturedExpression; } std::string AssertionResult::getExpressionInMacro() const { if( m_info.macroName.empty() ) return m_info.capturedExpression; else return m_info.macroName + "( " + m_info.capturedExpression + " )"; } bool AssertionResult::hasExpandedExpression() const { return hasExpression() && getExpandedExpression() != getExpression(); } std::string AssertionResult::getExpandedExpression() const { return m_resultData.reconstructedExpression; } std::string AssertionResult::getMessage() const { return m_resultData.message; } SourceLineInfo AssertionResult::getSourceInfo() const { return m_info.lineInfo; } std::string AssertionResult::getTestMacroName() const { return m_info.macroName; } } // end namespace Catch // #included from: catch_test_case_info.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED namespace Catch { inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { if( startsWith( tag, "." ) || tag == "hide" || tag == "!hide" ) return TestCaseInfo::IsHidden; else if( tag == "!throws" ) return TestCaseInfo::Throws; else if( tag == "!shouldfail" ) return TestCaseInfo::ShouldFail; else if( tag == "!mayfail" ) return TestCaseInfo::MayFail; else return TestCaseInfo::None; } inline bool isReservedTag( std::string const& tag ) { return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] ); } inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { if( isReservedTag( tag ) ) { { Colour colourGuard( Colour::Red ); Catch::cerr() << "Tag name [" << tag << "] not allowed.\n" << "Tag names starting with non alpha-numeric characters are reserved\n"; } { Colour colourGuard( Colour::FileName ); Catch::cerr() << _lineInfo << std::endl; } exit(1); } } TestCase makeTestCase( ITestCase* _testCase, std::string const& _className, std::string const& _name, std::string const& _descOrTags, SourceLineInfo const& _lineInfo ) { bool isHidden( startsWith( _name, "./" ) ); // Legacy support // Parse out tags std::set tags; std::string desc, tag; bool inTag = false; for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { char c = _descOrTags[i]; if( !inTag ) { if( c == '[' ) inTag = true; else desc += c; } else { if( c == ']' ) { TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); if( prop == TestCaseInfo::IsHidden ) isHidden = true; else if( prop == TestCaseInfo::None ) enforceNotReservedTag( tag, _lineInfo ); tags.insert( tag ); tag.clear(); inTag = false; } else tag += c; } } if( isHidden ) { tags.insert( "hide" ); tags.insert( "." ); } TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); return TestCase( _testCase, info ); } void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ) { testCaseInfo.tags = tags; testCaseInfo.lcaseTags.clear(); std::ostringstream oss; for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { oss << "[" << *it << "]"; std::string lcaseTag = toLower( *it ); testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); testCaseInfo.lcaseTags.insert( lcaseTag ); } testCaseInfo.tagsAsString = oss.str(); } TestCaseInfo::TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, std::set const& _tags, SourceLineInfo const& _lineInfo ) : name( _name ), className( _className ), description( _description ), lineInfo( _lineInfo ), properties( None ) { setTags( *this, _tags ); } TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) : name( other.name ), className( other.className ), description( other.description ), tags( other.tags ), lcaseTags( other.lcaseTags ), tagsAsString( other.tagsAsString ), lineInfo( other.lineInfo ), properties( other.properties ) {} bool TestCaseInfo::isHidden() const { return ( properties & IsHidden ) != 0; } bool TestCaseInfo::throws() const { return ( properties & Throws ) != 0; } bool TestCaseInfo::okToFail() const { return ( properties & (ShouldFail | MayFail ) ) != 0; } bool TestCaseInfo::expectedToFail() const { return ( properties & (ShouldFail ) ) != 0; } TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} TestCase::TestCase( TestCase const& other ) : TestCaseInfo( other ), test( other.test ) {} TestCase TestCase::withName( std::string const& _newName ) const { TestCase other( *this ); other.name = _newName; return other; } void TestCase::swap( TestCase& other ) { test.swap( other.test ); name.swap( other.name ); className.swap( other.className ); description.swap( other.description ); tags.swap( other.tags ); lcaseTags.swap( other.lcaseTags ); tagsAsString.swap( other.tagsAsString ); std::swap( TestCaseInfo::properties, static_cast( other ).properties ); std::swap( lineInfo, other.lineInfo ); } void TestCase::invoke() const { test->invoke(); } bool TestCase::operator == ( TestCase const& other ) const { return test.get() == other.test.get() && name == other.name && className == other.className; } bool TestCase::operator < ( TestCase const& other ) const { return name < other.name; } TestCase& TestCase::operator = ( TestCase const& other ) { TestCase temp( other ); swap( temp ); return *this; } TestCaseInfo const& TestCase::getTestCaseInfo() const { return *this; } } // end namespace Catch // #included from: catch_version.hpp #define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED namespace Catch { Version::Version ( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, std::string const& _branchName, unsigned int _buildNumber ) : majorVersion( _majorVersion ), minorVersion( _minorVersion ), patchNumber( _patchNumber ), branchName( _branchName ), buildNumber( _buildNumber ) {} std::ostream& operator << ( std::ostream& os, Version const& version ) { os << version.majorVersion << "." << version.minorVersion << "." << version.patchNumber; if( !version.branchName.empty() ) { os << "-" << version.branchName << "." << version.buildNumber; } return os; } Version libraryVersion( 1, 3, 0, "", 0 ); } // #included from: catch_message.hpp #define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED namespace Catch { MessageInfo::MessageInfo( std::string const& _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ) : macroName( _macroName ), lineInfo( _lineInfo ), type( _type ), sequence( ++globalCount ) {} // This may need protecting if threading support is added unsigned int MessageInfo::globalCount = 0; //////////////////////////////////////////////////////////////////////////// ScopedMessage::ScopedMessage( MessageBuilder const& builder ) : m_info( builder.m_info ) { m_info.message = builder.m_stream.str(); getResultCapture().pushScopedMessage( m_info ); } ScopedMessage::ScopedMessage( ScopedMessage const& other ) : m_info( other.m_info ) {} ScopedMessage::~ScopedMessage() { getResultCapture().popScopedMessage( m_info ); } } // end namespace Catch // #included from: catch_legacy_reporter_adapter.hpp #define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED // #included from: catch_legacy_reporter_adapter.h #define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED namespace Catch { // Deprecated struct IReporter : IShared { virtual ~IReporter(); virtual bool shouldRedirectStdout() const = 0; virtual void StartTesting() = 0; virtual void EndTesting( Totals const& totals ) = 0; virtual void StartGroup( std::string const& groupName ) = 0; virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; virtual void Aborted() = 0; virtual void Result( AssertionResult const& result ) = 0; }; class LegacyReporterAdapter : public SharedImpl { public: LegacyReporterAdapter( Ptr const& legacyReporter ); virtual ~LegacyReporterAdapter(); virtual ReporterPreferences getPreferences() const; virtual void noMatchingTestCases( std::string const& ); virtual void testRunStarting( TestRunInfo const& ); virtual void testGroupStarting( GroupInfo const& groupInfo ); virtual void testCaseStarting( TestCaseInfo const& testInfo ); virtual void sectionStarting( SectionInfo const& sectionInfo ); virtual void assertionStarting( AssertionInfo const& ); virtual bool assertionEnded( AssertionStats const& assertionStats ); virtual void sectionEnded( SectionStats const& sectionStats ); virtual void testCaseEnded( TestCaseStats const& testCaseStats ); virtual void testGroupEnded( TestGroupStats const& testGroupStats ); virtual void testRunEnded( TestRunStats const& testRunStats ); virtual void skipTest( TestCaseInfo const& ); private: Ptr m_legacyReporter; }; } namespace Catch { LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) : m_legacyReporter( legacyReporter ) {} LegacyReporterAdapter::~LegacyReporterAdapter() {} ReporterPreferences LegacyReporterAdapter::getPreferences() const { ReporterPreferences prefs; prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); return prefs; } void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { m_legacyReporter->StartTesting(); } void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { m_legacyReporter->StartGroup( groupInfo.name ); } void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { m_legacyReporter->StartTestCase( testInfo ); } void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); } void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { // Not on legacy interface } bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); it != itEnd; ++it ) { if( it->type == ResultWas::Info ) { ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); rb << it->message; rb.setResultType( ResultWas::Info ); AssertionResult result = rb.build(); m_legacyReporter->Result( result ); } } } m_legacyReporter->Result( assertionStats.assertionResult ); return true; } void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { if( sectionStats.missingAssertions ) m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); } void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { m_legacyReporter->EndTestCase ( testCaseStats.testInfo, testCaseStats.totals, testCaseStats.stdOut, testCaseStats.stdErr ); } void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { if( testGroupStats.aborting ) m_legacyReporter->Aborted(); m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); } void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { m_legacyReporter->EndTesting( testRunStats.totals ); } void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { } } // #included from: catch_timer.hpp #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wc++11-long-long" #endif #ifdef CATCH_PLATFORM_WINDOWS #include #else #include #endif namespace Catch { namespace { #ifdef CATCH_PLATFORM_WINDOWS uint64_t getCurrentTicks() { static uint64_t hz=0, hzo=0; if (!hz) { QueryPerformanceFrequency( reinterpret_cast( &hz ) ); QueryPerformanceCounter( reinterpret_cast( &hzo ) ); } uint64_t t; QueryPerformanceCounter( reinterpret_cast( &t ) ); return ((t-hzo)*1000000)/hz; } #else uint64_t getCurrentTicks() { timeval t; gettimeofday(&t,CATCH_NULL); return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); } #endif } void Timer::start() { m_ticks = getCurrentTicks(); } unsigned int Timer::getElapsedMicroseconds() const { return static_cast(getCurrentTicks() - m_ticks); } unsigned int Timer::getElapsedMilliseconds() const { return static_cast(getElapsedMicroseconds()/1000); } double Timer::getElapsedSeconds() const { return getElapsedMicroseconds()/1000000.0; } } // namespace Catch #ifdef __clang__ #pragma clang diagnostic pop #endif // #included from: catch_common.hpp #define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED namespace Catch { bool startsWith( std::string const& s, std::string const& prefix ) { return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; } bool endsWith( std::string const& s, std::string const& suffix ) { return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; } bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } void toLowerInPlace( std::string& s ) { std::transform( s.begin(), s.end(), s.begin(), ::tolower ); } std::string toLower( std::string const& s ) { std::string lc = s; toLowerInPlace( lc ); return lc; } std::string trim( std::string const& str ) { static char const* whitespaceChars = "\n\r\t "; std::string::size_type start = str.find_first_not_of( whitespaceChars ); std::string::size_type end = str.find_last_not_of( whitespaceChars ); return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { bool replaced = false; std::size_t i = str.find( replaceThis ); while( i != std::string::npos ) { replaced = true; str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); if( i < str.size()-withThis.size() ) i = str.find( replaceThis, i+withThis.size() ); else i = std::string::npos; } return replaced; } pluralise::pluralise( std::size_t count, std::string const& label ) : m_count( count ), m_label( label ) {} std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { os << pluraliser.m_count << " " << pluraliser.m_label; if( pluraliser.m_count != 1 ) os << "s"; return os; } SourceLineInfo::SourceLineInfo() : line( 0 ){} SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) : file( _file ), line( _line ) {} SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) : file( other.file ), line( other.line ) {} bool SourceLineInfo::empty() const { return file.empty(); } bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { return line == other.line && file == other.file; } bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { return line < other.line || ( line == other.line && file < other.file ); } void seedRng( IConfig const& config ) { if( config.rngSeed() != 0 ) std::srand( config.rngSeed() ); } unsigned int rngSeed() { return getCurrentContext().getConfig()->rngSeed(); } std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { #ifndef __GNUG__ os << info.file << "(" << info.line << ")"; #else os << info.file << ":" << info.line; #endif return os; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { std::ostringstream oss; oss << locationInfo << ": Internal Catch error: '" << message << "'"; if( alwaysTrue() ) throw std::logic_error( oss.str() ); } } // #included from: catch_section.hpp #define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED namespace Catch { SectionInfo::SectionInfo ( SourceLineInfo const& _lineInfo, std::string const& _name, std::string const& _description ) : name( _name ), description( _description ), lineInfo( _lineInfo ) {} Section::Section( SectionInfo const& info ) : m_info( info ), m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) { m_timer.start(); } Section::~Section() { if( m_sectionIncluded ) { SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); if( std::uncaught_exception() ) getResultCapture().sectionEndedEarly( endInfo ); else getResultCapture().sectionEnded( endInfo ); } } // This indicates whether the section should be executed or not Section::operator bool() const { return m_sectionIncluded; } } // end namespace Catch // #included from: catch_debugger.hpp #define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED #include #ifdef CATCH_PLATFORM_MAC #include #include #include #include #include namespace Catch{ // The following function is taken directly from the following technical note: // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html // Returns true if the current process is being debugged (either // running under the debugger or has a debugger attached post facto). bool isDebuggerActive(){ int mib[4]; struct kinfo_proc info; size_t size; // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. info.kp_proc.p_flag = 0; // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid(); // Call sysctl. size = sizeof(info); if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; return false; } // We're being debugged if the P_TRACED flag is set. return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); } } // namespace Catch #elif defined(_MSC_VER) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace Catch { bool isDebuggerActive() { return IsDebuggerPresent() != 0; } } #elif defined(__MINGW32__) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace Catch { bool isDebuggerActive() { return IsDebuggerPresent() != 0; } } #else namespace Catch { inline bool isDebuggerActive() { return false; } } #endif // Platform #ifdef CATCH_PLATFORM_WINDOWS extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); namespace Catch { void writeToDebugConsole( std::string const& text ) { ::OutputDebugStringA( text.c_str() ); } } #else namespace Catch { void writeToDebugConsole( std::string const& text ) { // !TBD: Need a version for Mac/ XCode and other IDEs Catch::cout() << text; } } #endif // Platform // #included from: catch_tostring.hpp #define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED namespace Catch { namespace Detail { const std::string unprintableString = "{?}"; namespace { const int hexThreshold = 255; struct Endianness { enum Arch { Big, Little }; static Arch which() { union _{ int asInt; char asChar[sizeof (int)]; } u; u.asInt = 1; return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; } }; } std::string rawMemoryToString( const void *object, std::size_t size ) { // Reverse order for little endian architectures int i = 0, end = static_cast( size ), inc = 1; if( Endianness::which() == Endianness::Little ) { i = end-1; end = inc = -1; } unsigned char const *bytes = static_cast(object); std::ostringstream os; os << "0x" << std::setfill('0') << std::hex; for( ; i != end; i += inc ) os << std::setw(2) << static_cast(bytes[i]); return os.str(); } } std::string toString( std::string const& value ) { std::string s = value; if( getCurrentContext().getConfig()->showInvisibles() ) { for(size_t i = 0; i < s.size(); ++i ) { std::string subs; switch( s[i] ) { case '\n': subs = "\\n"; break; case '\t': subs = "\\t"; break; default: break; } if( !subs.empty() ) { s = s.substr( 0, i ) + subs + s.substr( i+1 ); ++i; } } } return "\"" + s + "\""; } std::string toString( std::wstring const& value ) { std::string s; s.reserve( value.size() ); for(size_t i = 0; i < value.size(); ++i ) s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; return Catch::toString( s ); } std::string toString( const char* const value ) { return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); } std::string toString( char* const value ) { return Catch::toString( static_cast( value ) ); } std::string toString( const wchar_t* const value ) { return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); } std::string toString( wchar_t* const value ) { return Catch::toString( static_cast( value ) ); } std::string toString( int value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ")"; return oss.str(); } std::string toString( unsigned long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ")"; return oss.str(); } std::string toString( unsigned int value ) { return Catch::toString( static_cast( value ) ); } template std::string fpToString( T value, int precision ) { std::ostringstream oss; oss << std::setprecision( precision ) << std::fixed << value; std::string d = oss.str(); std::size_t i = d.find_last_not_of( '0' ); if( i != std::string::npos && i != d.size()-1 ) { if( d[i] == '.' ) i++; d = d.substr( 0, i+1 ); } return d; } std::string toString( const double value ) { return fpToString( value, 10 ); } std::string toString( const float value ) { return fpToString( value, 5 ) + "f"; } std::string toString( bool value ) { return value ? "true" : "false"; } std::string toString( char value ) { return value < ' ' ? toString( static_cast( value ) ) : Detail::makeString( value ); } std::string toString( signed char value ) { return toString( static_cast( value ) ); } std::string toString( unsigned char value ) { return toString( static_cast( value ) ); } #ifdef CATCH_CONFIG_CPP11_LONG_LONG std::string toString( long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ")"; return oss.str(); } std::string toString( unsigned long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ")"; return oss.str(); } #endif #ifdef CATCH_CONFIG_CPP11_NULLPTR std::string toString( std::nullptr_t ) { return "nullptr"; } #endif #ifdef __OBJC__ std::string toString( NSString const * const& nsstring ) { if( !nsstring ) return "nil"; return "@" + toString([nsstring UTF8String]); } std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { if( !nsstring ) return "nil"; return "@" + toString([nsstring UTF8String]); } std::string toString( NSObject* const& nsObject ) { return toString( [nsObject description] ); } #endif } // end namespace Catch // #included from: catch_result_builder.hpp #define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED namespace Catch { std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { return secondArg.empty() || secondArg == "\"\"" ? capturedExpression : capturedExpression + ", " + secondArg; } ResultBuilder::ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg ) : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), m_shouldDebugBreak( false ), m_shouldThrow( false ) {} ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { m_data.resultType = result; return *this; } ResultBuilder& ResultBuilder::setResultType( bool result ) { m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; return *this; } ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) { m_exprComponents.lhs = lhs; return *this; } ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) { m_exprComponents.rhs = rhs; return *this; } ResultBuilder& ResultBuilder::setOp( std::string const& op ) { m_exprComponents.op = op; return *this; } void ResultBuilder::endExpression() { m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); captureExpression(); } void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { m_assertionInfo.resultDisposition = resultDisposition; m_stream.oss << Catch::translateActiveException(); captureResult( ResultWas::ThrewException ); } void ResultBuilder::captureResult( ResultWas::OfType resultType ) { setResultType( resultType ); captureExpression(); } void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { if( expectedMessage.empty() ) captureExpectedException( Matchers::Impl::Generic::AllOf() ); else captureExpectedException( Matchers::Equals( expectedMessage ) ); } void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) { assert( m_exprComponents.testFalse == false ); AssertionResultData data = m_data; data.resultType = ResultWas::Ok; data.reconstructedExpression = m_assertionInfo.capturedExpression; std::string actualMessage = Catch::translateActiveException(); if( !matcher.match( actualMessage ) ) { data.resultType = ResultWas::ExpressionFailed; data.reconstructedExpression = actualMessage; } AssertionResult result( m_assertionInfo, data ); handleResult( result ); } void ResultBuilder::captureExpression() { AssertionResult result = build(); handleResult( result ); } void ResultBuilder::handleResult( AssertionResult const& result ) { getResultCapture().assertionEnded( result ); if( !result.isOk() ) { if( getCurrentContext().getConfig()->shouldDebugBreak() ) m_shouldDebugBreak = true; if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) m_shouldThrow = true; } } void ResultBuilder::react() { if( m_shouldThrow ) throw Catch::TestFailureException(); } bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } AssertionResult ResultBuilder::build() const { assert( m_data.resultType != ResultWas::Unknown ); AssertionResultData data = m_data; // Flip bool results if testFalse is set if( m_exprComponents.testFalse ) { if( data.resultType == ResultWas::Ok ) data.resultType = ResultWas::ExpressionFailed; else if( data.resultType == ResultWas::ExpressionFailed ) data.resultType = ResultWas::Ok; } data.message = m_stream.oss.str(); data.reconstructedExpression = reconstructExpression(); if( m_exprComponents.testFalse ) { if( m_exprComponents.op == "" ) data.reconstructedExpression = "!" + data.reconstructedExpression; else data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; } return AssertionResult( m_assertionInfo, data ); } std::string ResultBuilder::reconstructExpression() const { if( m_exprComponents.op == "" ) return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; else if( m_exprComponents.op == "matches" ) return m_exprComponents.lhs + " " + m_exprComponents.rhs; else if( m_exprComponents.op != "!" ) { if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && m_exprComponents.lhs.find("\n") == std::string::npos && m_exprComponents.rhs.find("\n") == std::string::npos ) return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; else return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; } else return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; } } // end namespace Catch // #included from: catch_tag_alias_registry.hpp #define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED // #included from: catch_tag_alias_registry.h #define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED #include namespace Catch { class TagAliasRegistry : public ITagAliasRegistry { public: virtual ~TagAliasRegistry(); virtual Option find( std::string const& alias ) const; virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); static TagAliasRegistry& get(); private: std::map m_registry; }; } // end namespace Catch #include #include namespace Catch { TagAliasRegistry::~TagAliasRegistry() {} Option TagAliasRegistry::find( std::string const& alias ) const { std::map::const_iterator it = m_registry.find( alias ); if( it != m_registry.end() ) return it->second; else return Option(); } std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { std::string expandedTestSpec = unexpandedTestSpec; for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); it != itEnd; ++it ) { std::size_t pos = expandedTestSpec.find( it->first ); if( pos != std::string::npos ) { expandedTestSpec = expandedTestSpec.substr( 0, pos ) + it->second.tag + expandedTestSpec.substr( pos + it->first.size() ); } } return expandedTestSpec; } void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { std::ostringstream oss; oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; throw std::domain_error( oss.str().c_str() ); } if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { std::ostringstream oss; oss << "error: tag alias, \"" << alias << "\" already registered.\n" << "\tFirst seen at " << find(alias)->lineInfo << "\n" << "\tRedefined at " << lineInfo; throw std::domain_error( oss.str().c_str() ); } } TagAliasRegistry& TagAliasRegistry::get() { static TagAliasRegistry instance; return instance; } ITagAliasRegistry::~ITagAliasRegistry() {} ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { try { TagAliasRegistry::get().add( alias, tag, lineInfo ); } catch( std::exception& ex ) { Colour colourGuard( Colour::Red ); Catch::cerr() << ex.what() << std::endl; exit(1); } } } // end namespace Catch // #included from: ../reporters/catch_reporter_multi.hpp #define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED namespace Catch { class MultipleReporters : public SharedImpl { typedef std::vector > Reporters; Reporters m_reporters; public: void add( Ptr const& reporter ) { m_reporters.push_back( reporter ); } public: // IStreamingReporter virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { return m_reporters[0]->getPreferences(); } virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->noMatchingTestCases( spec ); } virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testRunStarting( testRunInfo ); } virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testGroupStarting( groupInfo ); } virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testCaseStarting( testInfo ); } virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->sectionStarting( sectionInfo ); } virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->assertionStarting( assertionInfo ); } // The return value indicates if the messages buffer should be cleared: virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { bool clearBuffer = false; for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) clearBuffer |= (*it)->assertionEnded( assertionStats ); return clearBuffer; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->sectionEnded( sectionStats ); } virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testCaseEnded( testCaseStats ); } virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testGroupEnded( testGroupStats ); } virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testRunEnded( testRunStats ); } virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->skipTest( testInfo ); } }; Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { Ptr resultingReporter; if( existingReporter ) { MultipleReporters* multi = dynamic_cast( existingReporter.get() ); if( !multi ) { multi = new MultipleReporters; resultingReporter = Ptr( multi ); if( existingReporter ) multi->add( existingReporter ); } else resultingReporter = existingReporter; multi->add( additionalReporter ); } else resultingReporter = additionalReporter; return resultingReporter; } } // end namespace Catch // #included from: ../reporters/catch_reporter_xml.hpp #define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED // #included from: catch_reporter_bases.hpp #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #include namespace Catch { struct StreamingReporterBase : SharedImpl { StreamingReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = false; } virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { return m_reporterPrefs; } virtual ~StreamingReporterBase() CATCH_OVERRIDE; virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { currentTestRunInfo = _testRunInfo; } virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { currentGroupInfo = _groupInfo; } virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { currentTestCaseInfo = _testInfo; } virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { m_sectionStack.push_back( _sectionInfo ); } virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { m_sectionStack.pop_back(); } virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { currentTestCaseInfo.reset(); } virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { currentGroupInfo.reset(); } virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { currentTestCaseInfo.reset(); currentGroupInfo.reset(); currentTestRunInfo.reset(); } virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { // Don't do anything with this by default. // It can optionally be overridden in the derived class. } Ptr m_config; std::ostream& stream; LazyStat currentTestRunInfo; LazyStat currentGroupInfo; LazyStat currentTestCaseInfo; std::vector m_sectionStack; ReporterPreferences m_reporterPrefs; }; struct CumulativeReporterBase : SharedImpl { template struct Node : SharedImpl<> { explicit Node( T const& _value ) : value( _value ) {} virtual ~Node() {} typedef std::vector > ChildNodes; T value; ChildNodes children; }; struct SectionNode : SharedImpl<> { explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} virtual ~SectionNode(); bool operator == ( SectionNode const& other ) const { return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; } bool operator == ( Ptr const& other ) const { return operator==( *other ); } SectionStats stats; typedef std::vector > ChildSections; typedef std::vector Assertions; ChildSections childSections; Assertions assertions; std::string stdOut; std::string stdErr; }; struct BySectionInfo { BySectionInfo( SectionInfo const& other ) : m_other( other ) {} BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} bool operator() ( Ptr const& node ) const { return node->stats.sectionInfo.lineInfo == m_other.lineInfo; } private: void operator=( BySectionInfo const& ); SectionInfo const& m_other; }; typedef Node TestCaseNode; typedef Node TestGroupNode; typedef Node TestRunNode; CumulativeReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = false; } ~CumulativeReporterBase(); virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { return m_reporterPrefs; } virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); Ptr node; if( m_sectionStack.empty() ) { if( !m_rootSection ) m_rootSection = new SectionNode( incompleteStats ); node = m_rootSection; } else { SectionNode& parentNode = *m_sectionStack.back(); SectionNode::ChildSections::const_iterator it = std::find_if( parentNode.childSections.begin(), parentNode.childSections.end(), BySectionInfo( sectionInfo ) ); if( it == parentNode.childSections.end() ) { node = new SectionNode( incompleteStats ); parentNode.childSections.push_back( node ); } else node = *it; } m_sectionStack.push_back( node ); m_deepestSection = node; } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} virtual bool assertionEnded( AssertionStats const& assertionStats ) { assert( !m_sectionStack.empty() ); SectionNode& sectionNode = *m_sectionStack.back(); sectionNode.assertions.push_back( assertionStats ); return true; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { assert( !m_sectionStack.empty() ); SectionNode& node = *m_sectionStack.back(); node.stats = sectionStats; m_sectionStack.pop_back(); } virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { Ptr node = new TestCaseNode( testCaseStats ); assert( m_sectionStack.size() == 0 ); node->children.push_back( m_rootSection ); m_testCases.push_back( node ); m_rootSection.reset(); assert( m_deepestSection ); m_deepestSection->stdOut = testCaseStats.stdOut; m_deepestSection->stdErr = testCaseStats.stdErr; } virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { Ptr node = new TestGroupNode( testGroupStats ); node->children.swap( m_testCases ); m_testGroups.push_back( node ); } virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { Ptr node = new TestRunNode( testRunStats ); node->children.swap( m_testGroups ); m_testRuns.push_back( node ); testRunEndedCumulative(); } virtual void testRunEndedCumulative() = 0; virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} Ptr m_config; std::ostream& stream; std::vector m_assertions; std::vector > > m_sections; std::vector > m_testCases; std::vector > m_testGroups; std::vector > m_testRuns; Ptr m_rootSection; Ptr m_deepestSection; std::vector > m_sectionStack; ReporterPreferences m_reporterPrefs; }; template char const* getLineOfChars() { static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; if( !*line ) { memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; } return line; } struct TestEventListenerBase : StreamingReporterBase { TestEventListenerBase( ReporterConfig const& _config ) : StreamingReporterBase( _config ) {} virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { return false; } }; } // end namespace Catch // #included from: ../internal/catch_reporter_registrars.hpp #define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED namespace Catch { template class LegacyReporterRegistrar { class ReporterFactory : public IReporterFactory { virtual IStreamingReporter* create( ReporterConfig const& config ) const { return new LegacyReporterAdapter( new T( config ) ); } virtual std::string getDescription() const { return T::getDescription(); } }; public: LegacyReporterRegistrar( std::string const& name ) { getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); } }; template class ReporterRegistrar { class ReporterFactory : public SharedImpl { // *** Please Note ***: // - If you end up here looking at a compiler error because it's trying to register // your custom reporter class be aware that the native reporter interface has changed // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. // However please consider updating to the new interface as the old one is now // deprecated and will probably be removed quite soon! // Please contact me via github if you have any questions at all about this. // In fact, ideally, please contact me anyway to let me know you've hit this - as I have // no idea who is actually using custom reporters at all (possibly no-one!). // The new interface is designed to minimise exposure to interface changes in the future. virtual IStreamingReporter* create( ReporterConfig const& config ) const { return new T( config ); } virtual std::string getDescription() const { return T::getDescription(); } }; public: ReporterRegistrar( std::string const& name ) { getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); } }; template class ListenerRegistrar { class ListenerFactory : public SharedImpl { virtual IStreamingReporter* create( ReporterConfig const& config ) const { return new T( config ); } virtual std::string getDescription() const { return ""; } }; public: ListenerRegistrar() { getMutableRegistryHub().registerListener( new ListenerFactory() ); } }; } #define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } #define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } #define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } // #included from: ../internal/catch_xmlwriter.hpp #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED #include #include #include #include namespace Catch { class XmlEncode { public: enum ForWhat { ForTextNodes, ForAttributes }; XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) : m_str( str ), m_forWhat( forWhat ) {} void encodeTo( std::ostream& os ) const { // Apostrophe escaping not necessary if we always use " to write attributes // (see: http://www.w3.org/TR/xml/#syntax) for( std::size_t i = 0; i < m_str.size(); ++ i ) { char c = m_str[i]; switch( c ) { case '<': os << "<"; break; case '&': os << "&"; break; case '>': // See: http://www.w3.org/TR/xml/#syntax if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) os << ">"; else os << c; break; case '\"': if( m_forWhat == ForAttributes ) os << """; else os << c; break; default: // Escape control chars - based on contribution by @espenalb in PR #465 if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) os << "&#x" << std::uppercase << std::hex << static_cast( c ); else os << c; } } } friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { xmlEncode.encodeTo( os ); return os; } private: std::string m_str; ForWhat m_forWhat; }; class XmlWriter { public: class ScopedElement { public: ScopedElement( XmlWriter* writer ) : m_writer( writer ) {} ScopedElement( ScopedElement const& other ) : m_writer( other.m_writer ){ other.m_writer = CATCH_NULL; } ~ScopedElement() { if( m_writer ) m_writer->endElement(); } ScopedElement& writeText( std::string const& text, bool indent = true ) { m_writer->writeText( text, indent ); return *this; } template ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { m_writer->writeAttribute( name, attribute ); return *this; } private: mutable XmlWriter* m_writer; }; XmlWriter() : m_tagIsOpen( false ), m_needsNewline( false ), m_os( &Catch::cout() ) {} XmlWriter( std::ostream& os ) : m_tagIsOpen( false ), m_needsNewline( false ), m_os( &os ) {} ~XmlWriter() { while( !m_tags.empty() ) endElement(); } XmlWriter& startElement( std::string const& name ) { ensureTagClosed(); newlineIfNecessary(); stream() << m_indent << "<" << name; m_tags.push_back( name ); m_indent += " "; m_tagIsOpen = true; return *this; } ScopedElement scopedElement( std::string const& name ) { ScopedElement scoped( this ); startElement( name ); return scoped; } XmlWriter& endElement() { newlineIfNecessary(); m_indent = m_indent.substr( 0, m_indent.size()-2 ); if( m_tagIsOpen ) { stream() << "/>\n"; m_tagIsOpen = false; } else { stream() << m_indent << "\n"; } m_tags.pop_back(); return *this; } XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { if( !name.empty() && !attribute.empty() ) stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\""; return *this; } XmlWriter& writeAttribute( std::string const& name, bool attribute ) { stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; return *this; } template XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { std::ostringstream oss; oss << attribute; return writeAttribute( name, oss.str() ); } XmlWriter& writeText( std::string const& text, bool indent = true ) { if( !text.empty() ){ bool tagWasOpen = m_tagIsOpen; ensureTagClosed(); if( tagWasOpen && indent ) stream() << m_indent; stream() << XmlEncode( text ); m_needsNewline = true; } return *this; } XmlWriter& writeComment( std::string const& text ) { ensureTagClosed(); stream() << m_indent << ""; m_needsNewline = true; return *this; } XmlWriter& writeBlankLine() { ensureTagClosed(); stream() << "\n"; return *this; } void setStream( std::ostream& os ) { m_os = &os; } private: XmlWriter( XmlWriter const& ); void operator=( XmlWriter const& ); std::ostream& stream() { return *m_os; } void ensureTagClosed() { if( m_tagIsOpen ) { stream() << ">\n"; m_tagIsOpen = false; } } void newlineIfNecessary() { if( m_needsNewline ) { stream() << "\n"; m_needsNewline = false; } } bool m_tagIsOpen; bool m_needsNewline; std::vector m_tags; std::string m_indent; std::ostream* m_os; }; } // #included from: catch_reenable_warnings.h #define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(pop) # else # pragma clang diagnostic pop # endif #elif defined __GNUC__ # pragma GCC diagnostic pop #endif namespace Catch { class XmlReporter : public StreamingReporterBase { public: XmlReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), m_sectionDepth( 0 ) { m_reporterPrefs.shouldRedirectStdOut = true; } virtual ~XmlReporter() CATCH_OVERRIDE; static std::string getDescription() { return "Reports test results as an XML document"; } public: // StreamingReporterBase virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { StreamingReporterBase::noMatchingTestCases( s ); } virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testRunStarting( testInfo ); m_xml.setStream( stream ); m_xml.startElement( "Catch" ); if( !m_config->name().empty() ) m_xml.writeAttribute( "name", m_config->name() ); } virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { StreamingReporterBase::testGroupStarting( groupInfo ); m_xml.startElement( "Group" ) .writeAttribute( "name", groupInfo.name ); } virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testCaseStarting(testInfo); m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); if ( m_config->showDurations() == ShowDurations::Always ) m_testCaseTimer.start(); } virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { StreamingReporterBase::sectionStarting( sectionInfo ); if( m_sectionDepth++ > 0 ) { m_xml.startElement( "Section" ) .writeAttribute( "name", trim( sectionInfo.name ) ) .writeAttribute( "description", sectionInfo.description ); } } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { const AssertionResult& assertionResult = assertionStats.assertionResult; // Print any info messages in tags. if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); it != itEnd; ++it ) { if( it->type == ResultWas::Info ) { m_xml.scopedElement( "Info" ) .writeText( it->message ); } else if ( it->type == ResultWas::Warning ) { m_xml.scopedElement( "Warning" ) .writeText( it->message ); } } } // Drop out if result was successful but we're not printing them. if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) return true; // Print the expression if there is one. if( assertionResult.hasExpression() ) { m_xml.startElement( "Expression" ) .writeAttribute( "success", assertionResult.succeeded() ) .writeAttribute( "type", assertionResult.getTestMacroName() ) .writeAttribute( "filename", assertionResult.getSourceInfo().file ) .writeAttribute( "line", assertionResult.getSourceInfo().line ); m_xml.scopedElement( "Original" ) .writeText( assertionResult.getExpression() ); m_xml.scopedElement( "Expanded" ) .writeText( assertionResult.getExpandedExpression() ); } // And... Print a result applicable to each result type. switch( assertionResult.getResultType() ) { case ResultWas::ThrewException: m_xml.scopedElement( "Exception" ) .writeAttribute( "filename", assertionResult.getSourceInfo().file ) .writeAttribute( "line", assertionResult.getSourceInfo().line ) .writeText( assertionResult.getMessage() ); break; case ResultWas::FatalErrorCondition: m_xml.scopedElement( "Fatal Error Condition" ) .writeAttribute( "filename", assertionResult.getSourceInfo().file ) .writeAttribute( "line", assertionResult.getSourceInfo().line ) .writeText( assertionResult.getMessage() ); break; case ResultWas::Info: m_xml.scopedElement( "Info" ) .writeText( assertionResult.getMessage() ); break; case ResultWas::Warning: // Warning will already have been written break; case ResultWas::ExplicitFailure: m_xml.scopedElement( "Failure" ) .writeText( assertionResult.getMessage() ); break; default: break; } if( assertionResult.hasExpression() ) m_xml.endElement(); return true; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { StreamingReporterBase::sectionEnded( sectionStats ); if( --m_sectionDepth > 0 ) { XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); e.writeAttribute( "successes", sectionStats.assertions.passed ); e.writeAttribute( "failures", sectionStats.assertions.failed ); e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); m_xml.endElement(); } } virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { StreamingReporterBase::testCaseEnded( testCaseStats ); XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); m_xml.endElement(); } virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { StreamingReporterBase::testGroupEnded( testGroupStats ); // TODO: Check testGroupStats.aborting and act accordingly. m_xml.scopedElement( "OverallResults" ) .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); m_xml.endElement(); } virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { StreamingReporterBase::testRunEnded( testRunStats ); m_xml.scopedElement( "OverallResults" ) .writeAttribute( "successes", testRunStats.totals.assertions.passed ) .writeAttribute( "failures", testRunStats.totals.assertions.failed ) .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); m_xml.endElement(); } private: Timer m_testCaseTimer; XmlWriter m_xml; int m_sectionDepth; }; INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) } // end namespace Catch // #included from: ../reporters/catch_reporter_junit.hpp #define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED #include namespace Catch { class JunitReporter : public CumulativeReporterBase { public: JunitReporter( ReporterConfig const& _config ) : CumulativeReporterBase( _config ), xml( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = true; } virtual ~JunitReporter() CATCH_OVERRIDE; static std::string getDescription() { return "Reports test results in an XML format that looks like Ant's junitreport target"; } virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { CumulativeReporterBase::testRunStarting( runInfo ); xml.startElement( "testsuites" ); } virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { suiteTimer.start(); stdOutForSuite.str(""); stdErrForSuite.str(""); unexpectedExceptions = 0; CumulativeReporterBase::testGroupStarting( groupInfo ); } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) unexpectedExceptions++; return CumulativeReporterBase::assertionEnded( assertionStats ); } virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { stdOutForSuite << testCaseStats.stdOut; stdErrForSuite << testCaseStats.stdErr; CumulativeReporterBase::testCaseEnded( testCaseStats ); } virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { double suiteTime = suiteTimer.getElapsedSeconds(); CumulativeReporterBase::testGroupEnded( testGroupStats ); writeGroup( *m_testGroups.back(), suiteTime ); } virtual void testRunEndedCumulative() CATCH_OVERRIDE { xml.endElement(); } void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); TestGroupStats const& stats = groupNode.value; xml.writeAttribute( "name", stats.groupInfo.name ); xml.writeAttribute( "errors", unexpectedExceptions ); xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); xml.writeAttribute( "tests", stats.totals.assertions.total() ); xml.writeAttribute( "hostname", "tbd" ); // !TBD if( m_config->showDurations() == ShowDurations::Never ) xml.writeAttribute( "time", "" ); else xml.writeAttribute( "time", suiteTime ); xml.writeAttribute( "timestamp", "tbd" ); // !TBD // Write test cases for( TestGroupNode::ChildNodes::const_iterator it = groupNode.children.begin(), itEnd = groupNode.children.end(); it != itEnd; ++it ) writeTestCase( **it ); xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); } void writeTestCase( TestCaseNode const& testCaseNode ) { TestCaseStats const& stats = testCaseNode.value; // All test cases have exactly one section - which represents the // test case itself. That section may have 0-n nested sections assert( testCaseNode.children.size() == 1 ); SectionNode const& rootSection = *testCaseNode.children.front(); std::string className = stats.testInfo.className; if( className.empty() ) { if( rootSection.childSections.empty() ) className = "global"; } writeSection( className, "", rootSection ); } void writeSection( std::string const& className, std::string const& rootName, SectionNode const& sectionNode ) { std::string name = trim( sectionNode.stats.sectionInfo.name ); if( !rootName.empty() ) name = rootName + "/" + name; if( !sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty() ) { XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); if( className.empty() ) { xml.writeAttribute( "classname", name ); xml.writeAttribute( "name", "root" ); } else { xml.writeAttribute( "classname", className ); xml.writeAttribute( "name", name ); } xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); writeAssertions( sectionNode ); if( !sectionNode.stdOut.empty() ) xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); if( !sectionNode.stdErr.empty() ) xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); } for( SectionNode::ChildSections::const_iterator it = sectionNode.childSections.begin(), itEnd = sectionNode.childSections.end(); it != itEnd; ++it ) if( className.empty() ) writeSection( name, "", **it ); else writeSection( className, name, **it ); } void writeAssertions( SectionNode const& sectionNode ) { for( SectionNode::Assertions::const_iterator it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); it != itEnd; ++it ) writeAssertion( *it ); } void writeAssertion( AssertionStats const& stats ) { AssertionResult const& result = stats.assertionResult; if( !result.isOk() ) { std::string elementName; switch( result.getResultType() ) { case ResultWas::ThrewException: case ResultWas::FatalErrorCondition: elementName = "error"; break; case ResultWas::ExplicitFailure: elementName = "failure"; break; case ResultWas::ExpressionFailed: elementName = "failure"; break; case ResultWas::DidntThrowException: elementName = "failure"; break; // We should never see these here: case ResultWas::Info: case ResultWas::Warning: case ResultWas::Ok: case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: elementName = "internalError"; break; } XmlWriter::ScopedElement e = xml.scopedElement( elementName ); xml.writeAttribute( "message", result.getExpandedExpression() ); xml.writeAttribute( "type", result.getTestMacroName() ); std::ostringstream oss; if( !result.getMessage().empty() ) oss << result.getMessage() << "\n"; for( std::vector::const_iterator it = stats.infoMessages.begin(), itEnd = stats.infoMessages.end(); it != itEnd; ++it ) if( it->type == ResultWas::Info ) oss << it->message << "\n"; oss << "at " << result.getSourceInfo(); xml.writeText( oss.str(), false ); } } XmlWriter xml; Timer suiteTimer; std::ostringstream stdOutForSuite; std::ostringstream stdErrForSuite; unsigned int unexpectedExceptions; }; INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) } // end namespace Catch // #included from: ../reporters/catch_reporter_console.hpp #define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED namespace Catch { struct ConsoleReporter : StreamingReporterBase { ConsoleReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), m_headerPrinted( false ) {} virtual ~ConsoleReporter() CATCH_OVERRIDE; static std::string getDescription() { return "Reports test results as plain lines of text"; } virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { stream << "No test cases matched '" << spec << "'" << std::endl; } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { AssertionResult const& result = _assertionStats.assertionResult; bool printInfoMessages = true; // Drop out if result was successful and we're not printing those if( !m_config->includeSuccessfulResults() && result.isOk() ) { if( result.getResultType() != ResultWas::Warning ) return false; printInfoMessages = false; } lazyPrint(); AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); printer.print(); stream << std::endl; return true; } virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { m_headerPrinted = false; StreamingReporterBase::sectionStarting( _sectionInfo ); } virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { if( _sectionStats.missingAssertions ) { lazyPrint(); Colour colour( Colour::ResultError ); if( m_sectionStack.size() > 1 ) stream << "\nNo assertions in section"; else stream << "\nNo assertions in test case"; stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; } if( m_headerPrinted ) { if( m_config->showDurations() == ShowDurations::Always ) stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; m_headerPrinted = false; } else { if( m_config->showDurations() == ShowDurations::Always ) stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; } StreamingReporterBase::sectionEnded( _sectionStats ); } virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { StreamingReporterBase::testCaseEnded( _testCaseStats ); m_headerPrinted = false; } virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { if( currentGroupInfo.used ) { printSummaryDivider(); stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; printTotals( _testGroupStats.totals ); stream << "\n" << std::endl; } StreamingReporterBase::testGroupEnded( _testGroupStats ); } virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { printTotalsDivider( _testRunStats.totals ); printTotals( _testRunStats.totals ); stream << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } private: class AssertionPrinter { void operator= ( AssertionPrinter const& ); public: AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) : stream( _stream ), stats( _stats ), result( _stats.assertionResult ), colour( Colour::None ), message( result.getMessage() ), messages( _stats.infoMessages ), printInfoMessages( _printInfoMessages ) { switch( result.getResultType() ) { case ResultWas::Ok: colour = Colour::Success; passOrFail = "PASSED"; //if( result.hasMessage() ) if( _stats.infoMessages.size() == 1 ) messageLabel = "with message"; if( _stats.infoMessages.size() > 1 ) messageLabel = "with messages"; break; case ResultWas::ExpressionFailed: if( result.isOk() ) { colour = Colour::Success; passOrFail = "FAILED - but was ok"; } else { colour = Colour::Error; passOrFail = "FAILED"; } if( _stats.infoMessages.size() == 1 ) messageLabel = "with message"; if( _stats.infoMessages.size() > 1 ) messageLabel = "with messages"; break; case ResultWas::ThrewException: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "due to unexpected exception with message"; break; case ResultWas::FatalErrorCondition: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "due to a fatal error condition"; break; case ResultWas::DidntThrowException: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "because no exception was thrown where one was expected"; break; case ResultWas::Info: messageLabel = "info"; break; case ResultWas::Warning: messageLabel = "warning"; break; case ResultWas::ExplicitFailure: passOrFail = "FAILED"; colour = Colour::Error; if( _stats.infoMessages.size() == 1 ) messageLabel = "explicitly with message"; if( _stats.infoMessages.size() > 1 ) messageLabel = "explicitly with messages"; break; // These cases are here to prevent compiler warnings case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: passOrFail = "** internal error **"; colour = Colour::Error; break; } } void print() const { printSourceInfo(); if( stats.totals.assertions.total() > 0 ) { if( result.isOk() ) stream << "\n"; printResultType(); printOriginalExpression(); printReconstructedExpression(); } else { stream << "\n"; } printMessage(); } private: void printResultType() const { if( !passOrFail.empty() ) { Colour colourGuard( colour ); stream << passOrFail << ":\n"; } } void printOriginalExpression() const { if( result.hasExpression() ) { Colour colourGuard( Colour::OriginalExpression ); stream << " "; stream << result.getExpressionInMacro(); stream << "\n"; } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { stream << "with expansion:\n"; Colour colourGuard( Colour::ReconstructedExpression ); stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; } } void printMessage() const { if( !messageLabel.empty() ) stream << messageLabel << ":" << "\n"; for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); it != itEnd; ++it ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || it->type != ResultWas::Info ) stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; } } void printSourceInfo() const { Colour colourGuard( Colour::FileName ); stream << result.getSourceInfo() << ": "; } std::ostream& stream; AssertionStats const& stats; AssertionResult const& result; Colour::Code colour; std::string passOrFail; std::string messageLabel; std::string message; std::vector messages; bool printInfoMessages; }; void lazyPrint() { if( !currentTestRunInfo.used ) lazyPrintRunInfo(); if( !currentGroupInfo.used ) lazyPrintGroupInfo(); if( !m_headerPrinted ) { printTestCaseAndSectionHeader(); m_headerPrinted = true; } } void lazyPrintRunInfo() { stream << "\n" << getLineOfChars<'~'>() << "\n"; Colour colour( Colour::SecondaryText ); stream << currentTestRunInfo->name << " is a Catch v" << libraryVersion << " host application.\n" << "Run with -? for options\n\n"; if( m_config->rngSeed() != 0 ) stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; currentTestRunInfo.used = true; } void lazyPrintGroupInfo() { if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { printClosedHeader( "Group: " + currentGroupInfo->name ); currentGroupInfo.used = true; } } void printTestCaseAndSectionHeader() { assert( !m_sectionStack.empty() ); printOpenHeader( currentTestCaseInfo->name ); if( m_sectionStack.size() > 1 ) { Colour colourGuard( Colour::Headers ); std::vector::const_iterator it = m_sectionStack.begin()+1, // Skip first section (test case) itEnd = m_sectionStack.end(); for( ; it != itEnd; ++it ) printHeaderString( it->name, 2 ); } SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; if( !lineInfo.empty() ){ stream << getLineOfChars<'-'>() << "\n"; Colour colourGuard( Colour::FileName ); stream << lineInfo << "\n"; } stream << getLineOfChars<'.'>() << "\n" << std::endl; } void printClosedHeader( std::string const& _name ) { printOpenHeader( _name ); stream << getLineOfChars<'.'>() << "\n"; } void printOpenHeader( std::string const& _name ) { stream << getLineOfChars<'-'>() << "\n"; { Colour colourGuard( Colour::Headers ); printHeaderString( _name ); } } // if string has a : in first line will set indent to follow it on // subsequent lines void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { std::size_t i = _string.find( ": " ); if( i != std::string::npos ) i+=2; else i = 0; stream << Text( _string, TextAttributes() .setIndent( indent+i) .setInitialIndent( indent ) ) << "\n"; } struct SummaryColumn { SummaryColumn( std::string const& _label, Colour::Code _colour ) : label( _label ), colour( _colour ) {} SummaryColumn addRow( std::size_t count ) { std::ostringstream oss; oss << count; std::string row = oss.str(); for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { while( it->size() < row.size() ) *it = " " + *it; while( it->size() > row.size() ) row = " " + row; } rows.push_back( row ); return *this; } std::string label; Colour::Code colour; std::vector rows; }; void printTotals( Totals const& totals ) { if( totals.testCases.total() == 0 ) { stream << Colour( Colour::Warning ) << "No tests ran\n"; } else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) { stream << Colour( Colour::ResultSuccess ) << "All tests passed"; stream << " (" << pluralise( totals.assertions.passed, "assertion" ) << " in " << pluralise( totals.testCases.passed, "test case" ) << ")" << "\n"; } else { std::vector columns; columns.push_back( SummaryColumn( "", Colour::None ) .addRow( totals.testCases.total() ) .addRow( totals.assertions.total() ) ); columns.push_back( SummaryColumn( "passed", Colour::Success ) .addRow( totals.testCases.passed ) .addRow( totals.assertions.passed ) ); columns.push_back( SummaryColumn( "failed", Colour::ResultError ) .addRow( totals.testCases.failed ) .addRow( totals.assertions.failed ) ); columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) .addRow( totals.testCases.failedButOk ) .addRow( totals.assertions.failedButOk ) ); printSummaryRow( "test cases", columns, 0 ); printSummaryRow( "assertions", columns, 1 ); } } void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { std::string value = it->rows[row]; if( it->label.empty() ) { stream << label << ": "; if( value != "0" ) stream << value; else stream << Colour( Colour::Warning ) << "- none -"; } else if( value != "0" ) { stream << Colour( Colour::LightGrey ) << " | "; stream << Colour( it->colour ) << value << " " << it->label; } } stream << "\n"; } static std::size_t makeRatio( std::size_t number, std::size_t total ) { std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; return ( ratio == 0 && number > 0 ) ? 1 : ratio; } static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { if( i > j && i > k ) return i; else if( j > k ) return j; else return k; } void printTotalsDivider( Totals const& totals ) { if( totals.testCases.total() > 0 ) { std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) findMax( failedRatio, failedButOkRatio, passedRatio )++; while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) findMax( failedRatio, failedButOkRatio, passedRatio )--; stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); if( totals.testCases.allPassed() ) stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); else stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); } else { stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); } stream << "\n"; } void printSummaryDivider() { stream << getLineOfChars<'-'>() << "\n"; } private: bool m_headerPrinted; }; INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) } // end namespace Catch // #included from: ../reporters/catch_reporter_compact.hpp #define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED namespace Catch { struct CompactReporter : StreamingReporterBase { CompactReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ) {} virtual ~CompactReporter(); static std::string getDescription() { return "Reports test results on a single line, suitable for IDEs"; } virtual ReporterPreferences getPreferences() const { ReporterPreferences prefs; prefs.shouldRedirectStdOut = false; return prefs; } virtual void noMatchingTestCases( std::string const& spec ) { stream << "No test cases matched '" << spec << "'" << std::endl; } virtual void assertionStarting( AssertionInfo const& ) { } virtual bool assertionEnded( AssertionStats const& _assertionStats ) { AssertionResult const& result = _assertionStats.assertionResult; bool printInfoMessages = true; // Drop out if result was successful and we're not printing those if( !m_config->includeSuccessfulResults() && result.isOk() ) { if( result.getResultType() != ResultWas::Warning ) return false; printInfoMessages = false; } AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); printer.print(); stream << std::endl; return true; } virtual void testRunEnded( TestRunStats const& _testRunStats ) { printTotals( _testRunStats.totals ); stream << "\n" << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } private: class AssertionPrinter { void operator= ( AssertionPrinter const& ); public: AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) : stream( _stream ) , stats( _stats ) , result( _stats.assertionResult ) , messages( _stats.infoMessages ) , itMessage( _stats.infoMessages.begin() ) , printInfoMessages( _printInfoMessages ) {} void print() { printSourceInfo(); itMessage = messages.begin(); switch( result.getResultType() ) { case ResultWas::Ok: printResultType( Colour::ResultSuccess, passedString() ); printOriginalExpression(); printReconstructedExpression(); if ( ! result.hasExpression() ) printRemainingMessages( Colour::None ); else printRemainingMessages(); break; case ResultWas::ExpressionFailed: if( result.isOk() ) printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); else printResultType( Colour::Error, failedString() ); printOriginalExpression(); printReconstructedExpression(); printRemainingMessages(); break; case ResultWas::ThrewException: printResultType( Colour::Error, failedString() ); printIssue( "unexpected exception with message:" ); printMessage(); printExpressionWas(); printRemainingMessages(); break; case ResultWas::FatalErrorCondition: printResultType( Colour::Error, failedString() ); printIssue( "fatal error condition with message:" ); printMessage(); printExpressionWas(); printRemainingMessages(); break; case ResultWas::DidntThrowException: printResultType( Colour::Error, failedString() ); printIssue( "expected exception, got none" ); printExpressionWas(); printRemainingMessages(); break; case ResultWas::Info: printResultType( Colour::None, "info" ); printMessage(); printRemainingMessages(); break; case ResultWas::Warning: printResultType( Colour::None, "warning" ); printMessage(); printRemainingMessages(); break; case ResultWas::ExplicitFailure: printResultType( Colour::Error, failedString() ); printIssue( "explicitly" ); printRemainingMessages( Colour::None ); break; // These cases are here to prevent compiler warnings case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: printResultType( Colour::Error, "** internal error **" ); break; } } private: // Colour::LightGrey static Colour::Code dimColour() { return Colour::FileName; } #ifdef CATCH_PLATFORM_MAC static const char* failedString() { return "FAILED"; } static const char* passedString() { return "PASSED"; } #else static const char* failedString() { return "failed"; } static const char* passedString() { return "passed"; } #endif void printSourceInfo() const { Colour colourGuard( Colour::FileName ); stream << result.getSourceInfo() << ":"; } void printResultType( Colour::Code colour, std::string passOrFail ) const { if( !passOrFail.empty() ) { { Colour colourGuard( colour ); stream << " " << passOrFail; } stream << ":"; } } void printIssue( std::string issue ) const { stream << " " << issue; } void printExpressionWas() { if( result.hasExpression() ) { stream << ";"; { Colour colour( dimColour() ); stream << " expression was:"; } printOriginalExpression(); } } void printOriginalExpression() const { if( result.hasExpression() ) { stream << " " << result.getExpression(); } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { { Colour colour( dimColour() ); stream << " for: "; } stream << result.getExpandedExpression(); } } void printMessage() { if ( itMessage != messages.end() ) { stream << " '" << itMessage->message << "'"; ++itMessage; } } void printRemainingMessages( Colour::Code colour = dimColour() ) { if ( itMessage == messages.end() ) return; // using messages.end() directly yields compilation error: std::vector::const_iterator itEnd = messages.end(); const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); { Colour colourGuard( colour ); stream << " with " << pluralise( N, "message" ) << ":"; } for(; itMessage != itEnd; ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || itMessage->type != ResultWas::Info ) { stream << " '" << itMessage->message << "'"; if ( ++itMessage != itEnd ) { Colour colourGuard( dimColour() ); stream << " and"; } } } } private: std::ostream& stream; AssertionStats const& stats; AssertionResult const& result; std::vector messages; std::vector::const_iterator itMessage; bool printInfoMessages; }; // Colour, message variants: // - white: No tests ran. // - red: Failed [both/all] N test cases, failed [both/all] M assertions. // - white: Passed [both/all] N test cases (no assertions). // - red: Failed N tests cases, failed M assertions. // - green: Passed [both/all] N tests cases with M assertions. std::string bothOrAll( std::size_t count ) const { return count == 1 ? "" : count == 2 ? "both " : "all " ; } void printTotals( const Totals& totals ) const { if( totals.testCases.total() == 0 ) { stream << "No tests ran."; } else if( totals.testCases.failed == totals.testCases.total() ) { Colour colour( Colour::ResultError ); const std::string qualify_assertions_failed = totals.assertions.failed == totals.assertions.total() ? bothOrAll( totals.assertions.failed ) : ""; stream << "Failed " << bothOrAll( totals.testCases.failed ) << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << qualify_assertions_failed << pluralise( totals.assertions.failed, "assertion" ) << "."; } else if( totals.assertions.total() == 0 ) { stream << "Passed " << bothOrAll( totals.testCases.total() ) << pluralise( totals.testCases.total(), "test case" ) << " (no assertions)."; } else if( totals.assertions.failed ) { Colour colour( Colour::ResultError ); stream << "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; } else { Colour colour( Colour::ResultSuccess ); stream << "Passed " << bothOrAll( totals.testCases.passed ) << pluralise( totals.testCases.passed, "test case" ) << " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; } } }; INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) } // end namespace Catch namespace Catch { // These are all here to avoid warnings about not having any out of line // virtual methods NonCopyable::~NonCopyable() {} IShared::~IShared() {} IStream::~IStream() CATCH_NOEXCEPT {} FileStream::~FileStream() CATCH_NOEXCEPT {} CoutStream::~CoutStream() CATCH_NOEXCEPT {} DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} IContext::~IContext() {} IResultCapture::~IResultCapture() {} ITestCase::~ITestCase() {} ITestCaseRegistry::~ITestCaseRegistry() {} IRegistryHub::~IRegistryHub() {} IMutableRegistryHub::~IMutableRegistryHub() {} IExceptionTranslator::~IExceptionTranslator() {} IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} IReporter::~IReporter() {} IReporterFactory::~IReporterFactory() {} IReporterRegistry::~IReporterRegistry() {} IStreamingReporter::~IStreamingReporter() {} AssertionStats::~AssertionStats() {} SectionStats::~SectionStats() {} TestCaseStats::~TestCaseStats() {} TestGroupStats::~TestGroupStats() {} TestRunStats::~TestRunStats() {} CumulativeReporterBase::SectionNode::~SectionNode() {} CumulativeReporterBase::~CumulativeReporterBase() {} StreamingReporterBase::~StreamingReporterBase() {} ConsoleReporter::~ConsoleReporter() {} CompactReporter::~CompactReporter() {} IRunner::~IRunner() {} IMutableContext::~IMutableContext() {} IConfig::~IConfig() {} XmlReporter::~XmlReporter() {} JunitReporter::~JunitReporter() {} TestRegistry::~TestRegistry() {} FreeFunctionTestCase::~FreeFunctionTestCase() {} IGeneratorInfo::~IGeneratorInfo() {} IGeneratorsForTest::~IGeneratorsForTest() {} WildcardPattern::~WildcardPattern() {} TestSpec::Pattern::~Pattern() {} TestSpec::NamePattern::~NamePattern() {} TestSpec::TagPattern::~TagPattern() {} TestSpec::ExcludedPattern::~ExcludedPattern() {} Matchers::Impl::StdString::Equals::~Equals() {} Matchers::Impl::StdString::Contains::~Contains() {} Matchers::Impl::StdString::StartsWith::~StartsWith() {} Matchers::Impl::StdString::EndsWith::~EndsWith() {} void Config::dummy() {} namespace TestCaseTracking { ITracker::~ITracker() {} TrackerBase::~TrackerBase() {} SectionTracker::~SectionTracker() {} IndexTracker::~IndexTracker() {} } } #ifdef __clang__ #pragma clang diagnostic pop #endif #endif #ifdef CATCH_CONFIG_MAIN // #included from: internal/catch_default_main.hpp #define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED #ifndef __OBJC__ // Standard C/C++ main entry point int main (int argc, char * const argv[]) { return Catch::Session().run( argc, argv ); } #else // __OBJC__ // Objective-C entry point int main (int argc, char * const argv[]) { #if !CATCH_ARC_ENABLED NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; #endif Catch::registerTestMethods(); int result = Catch::Session().run( argc, (char* const*)argv ); #if !CATCH_ARC_ENABLED [pool drain]; #endif return result; } #endif // __OBJC__ #endif #ifdef CLARA_CONFIG_MAIN_NOT_DEFINED # undef CLARA_CONFIG_MAIN #endif ////// // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL #define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) #define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) #define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" ) #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" ) #define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) #define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) #define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) #define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) #define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) #define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) #define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" ) #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) #define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" ) #define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) #define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) #define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) #define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( ... ) INTERNAL_CATCH_REGISTER_TESTCASE( __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) #else #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) #endif #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) #define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) #define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) #define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) // "BDD-style" convenience wrappers #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #else #define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) #define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif #define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) #define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) #define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) #define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) #define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else #define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) #define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) #define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" ) #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) #define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" ) #define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) #define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) #define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) #define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) #define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) #define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) #define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" ) #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) #define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" ) #define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) #define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) #define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) #define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) #define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) #define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) #ifdef CATCH_CONFIG_VARIADIC_MACROS #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define REGISTER_TEST_CASE( ... ) INTERNAL_CATCH_REGISTER_TESTCASE( __VA_ARGS__ ) #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) #else #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define REGISTER_TEST_CASE( ... ) INTERNAL_CATCH_REGISTER_TESTCASE( __VA_ARGS__ ) #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) #endif #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) #define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) #define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) #define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) #endif #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) // "BDD-style" convenience wrappers #ifdef CATCH_CONFIG_VARIADIC_MACROS #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #else #define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) #define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif #define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) #define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) #define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) #define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) #define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) using Catch::Detail::Approx; #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED openalpr_2.2.4.orig/src/tests/test_api.cpp000066400000000000000000000067461266464252400206550ustar00rootroot00000000000000/* * File: runtests.cpp * Author: mhill * * Created on October 22, 2014, 11:11 PM */ #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include #include "catch.hpp" #include "alpr.h" #include "support/timing.h" using namespace std; using namespace alpr; TEST_CASE( "JSON Serialization/Deserialization", "[json]" ) { AlprResults origResults; origResults.epoch_time = getEpochTimeMs(); origResults.img_width = 640; origResults.img_height = 480; origResults.total_processing_time_ms = 100; origResults.regionsOfInterest.push_back(AlprRegionOfInterest(0,0,100,200)); origResults.regionsOfInterest.push_back(AlprRegionOfInterest(259,260,50,150)); AlprPlateResult apr; for (int i = 0; i < 3; i++) { AlprPlate ap; ap.characters = "abc"; ap.matches_template = i%2; ap.overall_confidence = i * 10; apr.topNPlates.push_back(ap); } apr.bestPlate = apr.topNPlates[0]; for (int i = 0; i < 4; i++) { AlprCoordinate ac; ac.x = i; ac.y = i; apr.plate_points[i] = ac; } apr.processing_time_ms = 30; apr.requested_topn = 10; apr.region = "mo"; apr.regionConfidence = 80; origResults.plates.push_back(apr); std::string resultsJson = Alpr::toJson(origResults); AlprResults roundTrip = Alpr::fromJson(resultsJson); REQUIRE( roundTrip.epoch_time == origResults.epoch_time ); REQUIRE( roundTrip.img_width == origResults.img_width ); REQUIRE( roundTrip.img_height == origResults.img_height ); REQUIRE( roundTrip.total_processing_time_ms == origResults.total_processing_time_ms ); REQUIRE( roundTrip.regionsOfInterest.size() == origResults.regionsOfInterest.size() ); for (int i = 0; i < roundTrip.regionsOfInterest.size(); i++) { REQUIRE( roundTrip.regionsOfInterest[i].x == origResults.regionsOfInterest[i].x); REQUIRE( roundTrip.regionsOfInterest[i].y == origResults.regionsOfInterest[i].y); REQUIRE( roundTrip.regionsOfInterest[i].width == origResults.regionsOfInterest[i].width); REQUIRE( roundTrip.regionsOfInterest[i].height == origResults.regionsOfInterest[i].height); } REQUIRE( roundTrip.plates.size() == origResults.plates.size() ); for (int i = 0; i < roundTrip.plates.size(); i++) { REQUIRE( roundTrip.plates[i].processing_time_ms == origResults.plates[i].processing_time_ms); REQUIRE( roundTrip.plates[i].region == origResults.plates[i].region); REQUIRE( roundTrip.plates[i].regionConfidence == origResults.plates[i].regionConfidence); REQUIRE( roundTrip.plates[i].requested_topn == origResults.plates[i].requested_topn); REQUIRE( roundTrip.plates[i].bestPlate.characters == origResults.plates[i].bestPlate.characters); REQUIRE( roundTrip.plates[i].bestPlate.matches_template == origResults.plates[i].bestPlate.matches_template); REQUIRE( roundTrip.plates[i].bestPlate.overall_confidence == origResults.plates[i].bestPlate.overall_confidence); REQUIRE( roundTrip.plates[i].topNPlates.size() == origResults.plates[i].topNPlates.size()); for (int j = 0; j < roundTrip.plates[i].topNPlates.size(); j++) { REQUIRE( roundTrip.plates[i].topNPlates[j].characters == origResults.plates[i].topNPlates[j].characters); REQUIRE( roundTrip.plates[i].topNPlates[j].matches_template == origResults.plates[i].topNPlates[j].matches_template); REQUIRE( roundTrip.plates[i].topNPlates[j].overall_confidence == origResults.plates[i].topNPlates[j].overall_confidence); } } } openalpr_2.2.4.orig/src/tests/test_config.cpp000066400000000000000000000027651266464252400213460ustar00rootroot00000000000000/* * File: utility_tests.cpp * Author: mhill * * Created on October 23, 2014, 10:16 PM */ #include #include "catch.hpp" #include "config.h" #include "alpr.h" using namespace std; using namespace alpr; Config get_config(std::string countries) { Config config(countries, OPENALPR_TESTING_CONFIG_PATH, OPENALPR_TESTING_RUNTIME_DIR); return config; } TEST_CASE( "Loading Countries", "[Config]" ) { REQUIRE( get_config("us,eu").loaded_countries.size() == 2 ); REQUIRE( get_config("us").loaded_countries.size() == 1 ); REQUIRE( get_config("eu").loaded_countries.size() == 1 ); REQUIRE( get_config("us, eu").loaded_countries.size() == 2 ); REQUIRE( get_config("us, eu, au, kr").loaded_countries.size() == 4 ); REQUIRE( get_config("us , eu").loaded_countries.size() == 2 ); REQUIRE( get_config("us,eu,").loaded_countries.size() == 2 ); REQUIRE( get_config(",,us,eu,").loaded_countries.size() == 2 ); } TEST_CASE( "Modifying Countries", "[Config]" ) { Config config("us,eu", OPENALPR_TESTING_CONFIG_PATH, OPENALPR_TESTING_RUNTIME_DIR); REQUIRE(config.ocrLanguage == "lus"); config.setCountry("eu"); REQUIRE(config.ocrLanguage == "leu"); config.setCountry("us"); REQUIRE(config.ocrLanguage == "lus"); } TEST_CASE( "Reloading Countries", "[Config]" ) { Alpr alpr("us", OPENALPR_TESTING_CONFIG_PATH, OPENALPR_TESTING_RUNTIME_DIR); REQUIRE(alpr.getConfig()->ocrLanguage == "lus"); alpr.setCountry("eu"); REQUIRE(alpr.getConfig()->ocrLanguage == "leu"); }openalpr_2.2.4.orig/src/tests/test_regex.cpp000066400000000000000000000064761266464252400212160ustar00rootroot00000000000000#include #include "utility.h" #include "catch.hpp" #include "postprocess/regexrule.h" using namespace std; using namespace cv; using namespace alpr; TEST_CASE( "ASCII tests", "[Regex]" ) { RegexRule rule1("us", "@@@####", "[A-Za-z]", "[0-9]"); REQUIRE( rule1.match("123ABCD") == false); REQUIRE( rule1.match("123ABC") == false); REQUIRE( rule1.match("23ABCD") == false); REQUIRE( rule1.match("ABC123") == false); REQUIRE( rule1.match("BC1234") == false); REQUIRE( rule1.match("ABC12345") == false); REQUIRE( rule1.match("AABC1234") == false); REQUIRE( rule1.match("ABCD234") == false); REQUIRE( rule1.match("AB11234") == false); REQUIRE( rule1.match("ABC-234") == false); REQUIRE( rule1.match("ABC1234") == true); REQUIRE( rule1.match("AAA1111") == true); REQUIRE( rule1.match("zzz1111") == true); RegexRule rule2("us", "[ABC]@@####", "[A-Z]", "[0-9]"); REQUIRE( rule2.match("ZBC1234") == false); REQUIRE( rule2.match("DBC1234") == false); REQUIRE( rule2.match("1BC1234") == false); REQUIRE( rule2.match("ABC1234") == true); REQUIRE( rule2.match("BAA1111") == true); REQUIRE( rule2.match("CAA1111") == true); RegexRule rule3("us", "[A]@@###[12]", "[A-Z]", "[0-9]"); REQUIRE( rule3.match("ZBC1234") == false); REQUIRE( rule3.match("ZBC1231") == false); REQUIRE( rule3.match("ABC1234") == false); REQUIRE( rule3.match("ABC1231") == true); REQUIRE( rule3.match("ABC1232") == true); RegexRule rule4("us", "[A-C][E-G]1111", "[A-Z]", "[0-9]"); REQUIRE( rule4.match("DG1111") == false); REQUIRE( rule4.match("AD1111") == false); REQUIRE( rule4.match("AF1112") == false); REQUIRE( rule4.match("AF1111") == true); REQUIRE( rule4.match("BF1111") == true); REQUIRE( rule4.match("CF1111") == true); REQUIRE( rule4.match("BE1111") == true); REQUIRE( rule4.match("BF1111") == true); REQUIRE( rule4.match("BG1111") == true); RegexRule rule5("us", "\\d\\d\\D\\D", "[A-Z]", "[0-9]"); REQUIRE( rule5.match("AA11") == false); REQUIRE( rule5.match("11AA") == true); } TEST_CASE( "Unicode tests", "[Regex]" ) { RegexRule rule1("us", "@@@####", "\\pL", "\\pN"); REQUIRE( rule1.match("123与与与下") == false); REQUIRE( rule1.match("与万12345") == false); REQUIRE( rule1.match("与万口口234") == false); REQUIRE( rule1.match("与万口abcd") == false); REQUIRE( rule1.match("与万口1234") == true); RegexRule rule2("us", "[십팔]@@####", "\\pL", "\\pN"); REQUIRE( rule2.match("123与与与下") == false); REQUIRE( rule2.match("与万12345") == false); REQUIRE( rule2.match("与万口口234") == false); REQUIRE( rule2.match("与万口abcd") == false); REQUIRE( rule2.match("与万口1234") == false); REQUIRE( rule2.match("与팔십1234") == false); REQUIRE( rule2.match("십万口1234") == true); REQUIRE( rule2.match("팔万口1234") == true); } TEST_CASE( "Invalid tests", "[Regex]" ) { RegexRule rule1("us", "[A@@####", "\\pL", "\\pN"); REQUIRE( rule1.match("123ABCD") == false); REQUIRE( rule1.match("123ABC") == false); REQUIRE( rule1.match("23ABCD") == false); REQUIRE( rule1.match("ABC1234") == false); REQUIRE( rule1.match("AAA1111") == false); REQUIRE( rule1.match("zzz1111") == false); RegexRule rule2("us", "A####]", "\\pL", "\\pN"); REQUIRE( rule2.match("A1234") == false); }openalpr_2.2.4.orig/src/tests/test_utility.cpp000066400000000000000000000041671266464252400216020ustar00rootroot00000000000000/* * File: utility_tests.cpp * Author: mhill * * Created on October 23, 2014, 10:16 PM */ #include #include "utility.h" #include "catch.hpp" using namespace std; using namespace cv; using namespace alpr; TEST_CASE( "LineSegment Test", "[2d primitives]" ) { // Test a flat horizontal line LineSegment flat_horizontal(1,1, 21,1); REQUIRE( flat_horizontal.angle == 0 ); REQUIRE( flat_horizontal.slope == 0 ); REQUIRE( flat_horizontal.length == 20 ); REQUIRE( flat_horizontal.midpoint().y == 1 ); REQUIRE( flat_horizontal.midpoint().x == 11 ); REQUIRE( flat_horizontal.getPointAt(11) == 1 ); LineSegment rising_45(1,1, 5,5); REQUIRE( rising_45.getPointAt(3) == 3 ); REQUIRE( rising_45.getXPointAt(3) == 3 ); // Test distance between points calculation REQUIRE( distanceBetweenPoints(Point(10,10), Point(20,20)) == Approx(14.1421) ); REQUIRE( distanceBetweenPoints(Point(-5,10), Point(20,-12)) == Approx(33.3017) ); int testarray1[] = {1, 2, 3, 3, 4, 5 }; int testarray2[] = {0, 2, -3, 3, -4, 5 }; int *testarray3; REQUIRE( median(testarray1, 6) == 3 ); REQUIRE( median(testarray2, 6) == 1 ); REQUIRE( median(testarray3, 0) == 0 ); } TEST_CASE( "Test Levenshtein Distance", "[levenshtein]" ) { // Test the maximum works correctly REQUIRE( levenshteinDistance("asdf", "bbbb", 10) == 4 ); REQUIRE( levenshteinDistance("asdf", "bbbb", 4) == 4 ); REQUIRE( levenshteinDistance("asdf", "bbbb", 3) == 3 ); REQUIRE( levenshteinDistance("asdf", "bbbb", 2) == 2 ); REQUIRE( levenshteinDistance("asdf", "bbbb", 1) == 1 ); REQUIRE( levenshteinDistance("asdf", "bbbb", 0) == 0 ); // Test some substitutions REQUIRE( levenshteinDistance("P32RX", "PE32RX", 10) == 1 ); REQUIRE( levenshteinDistance("P32RX", "PE32RX", 2) == 1 ); REQUIRE( levenshteinDistance("ASDF11", "ASDF1", 10) == 1 ); REQUIRE( levenshteinDistance("1ASDF1", "ASDF1", 10) == 1 ); REQUIRE( levenshteinDistance("ASD", "ASDF1", 2) == 2 ); REQUIRE( levenshteinDistance("11111", "11I11", 2) == 1 ); REQUIRE( levenshteinDistance("", "AAAA", 2) == 2 ); REQUIRE( levenshteinDistance("BA", "AAAA", 2) == 2 ); }openalpr_2.2.4.orig/src/video/000077500000000000000000000000001266464252400162705ustar00rootroot00000000000000openalpr_2.2.4.orig/src/video/CMakeLists.txt000066400000000000000000000003141266464252400210260ustar00rootroot00000000000000 set(video_source_files videobuffer.cpp ) add_library(video STATIC ${video_source_files}) TARGET_LINK_LIBRARIES(video support ) #SET_TARGET_PROPERTIES( video PROPERTIES COMPILE_FLAGS -fPIC)openalpr_2.2.4.orig/src/video/logging_videobuffer.h000066400000000000000000000020531266464252400224470ustar00rootroot00000000000000#ifndef OPENALPR_LOGGING_VIDEOBUFFER_H #define OPENALPR_LOGGING_VIDEOBUFFER_H #include "videobuffer.h" #include #include class LoggingVideoDispatcher : public VideoDispatcher { public: LoggingVideoDispatcher(std::string mjpeg_url, int fps, log4cplus::Logger logger) : VideoDispatcher(mjpeg_url, fps) { this->logger = logger; } virtual void log_info(std::string message) { LOG4CPLUS_INFO(logger, message); } virtual void log_error(std::string error) { LOG4CPLUS_WARN(logger, error ); } private: log4cplus::Logger logger; }; class LoggingVideoBuffer : public VideoBuffer { public: LoggingVideoBuffer(log4cplus::Logger logger) : VideoBuffer() { this->logger = logger; } protected: virtual VideoDispatcher* createDispatcher(std::string mjpeg_url, int fps) { return new LoggingVideoDispatcher(mjpeg_url, fps, logger); } private: log4cplus::Logger logger; }; #endif // OPENALPR_LOGGING_VIDEOBUFFER_Hopenalpr_2.2.4.orig/src/video/videobuffer.cpp000066400000000000000000000123211266464252400212730ustar00rootroot00000000000000/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "videobuffer.h" using namespace alpr; void imageCollectionThread(void* arg); void getALPRImages(cv::VideoCapture cap, VideoDispatcher* dispatcher); VideoBuffer::VideoBuffer() { dispatcher = NULL; } VideoBuffer::~VideoBuffer() { if (dispatcher != NULL) { dispatcher->active = false; } } VideoDispatcher* VideoBuffer::createDispatcher(std::string mjpeg_url, int fps) { return new VideoDispatcher(mjpeg_url, fps); } void VideoBuffer::connect(std::string mjpeg_url, int fps) { if (startsWith(mjpeg_url, "http") && hasEnding(mjpeg_url, ".mjpg") == false) { // The filename doesn't end with ".mjpg" so the downstream processing may not treat it as such // OpenCV doesn't have a way to force the rendering, other than via URL path. So, let's add it to the URL std::size_t found = mjpeg_url.find("?"); if (found!=std::string::npos) { // URL already contains a "?" mjpeg_url = mjpeg_url + "&openalprfiletype=file.mjpg"; } else { // URL does not contain a "?" mjpeg_url = mjpeg_url + "?openalprfiletype=file.mjpg"; } } dispatcher = createDispatcher(mjpeg_url, fps); tthread::thread* t = new tthread::thread(imageCollectionThread, (void*) dispatcher); } int VideoBuffer::getLatestFrame(cv::Mat* frame, std::vector& regionsOfInterest) { if (dispatcher == NULL) return -1; return dispatcher->getLatestFrame(frame, regionsOfInterest); } void VideoBuffer::disconnect() { if (dispatcher != NULL) { dispatcher->active = false; } dispatcher = NULL; } void imageCollectionThread(void* arg) { VideoDispatcher* dispatcher = (VideoDispatcher*) arg; while (dispatcher->active) { try { cv::VideoCapture cap=cv::VideoCapture(); dispatcher->log_info("Video stream connecting..."); // Check if it's a webcam, if so, pass the device ID std::string video_prefix = "/dev/video"; if (startsWith(dispatcher->mjpeg_url, video_prefix)) { std::string device_number_str = dispatcher->mjpeg_url.substr(video_prefix.length()); dispatcher->log_info("Opening webcam video device " + device_number_str); int webcam_number = atoi(device_number_str.c_str()); cap.open(webcam_number); } else if (dispatcher->mjpeg_url == "webcam") { cap.open(0); } else { cap.open(dispatcher->mjpeg_url); } if (cap.isOpened()) { dispatcher->log_info("Video stream connected"); getALPRImages(cap, dispatcher); } else { std::stringstream ss; ss << "Stream " << dispatcher->mjpeg_url << " failed to open."; dispatcher->log_error(ss.str()); } } catch (const std::runtime_error& error) { // Error occured while trying to gather image. Retry, don't exit. std::stringstream ss; ss << "VideoBuffer exception: " << error.what(); dispatcher->log_error( ss.str() ); } catch (cv::Exception e) { // OpenCV Exception occured while trying to gather image. Retry, don't exit. std::stringstream ss; ss << "VideoBuffer OpenCV exception: " << e.what(); dispatcher->log_error( ss.str() ); } // Delay 1 second sleep_ms(1000); } } // Continuously grabs images from the video capture. If there is an error, // it returns so that the video capture can be recreated. void getALPRImages(cv::VideoCapture cap, VideoDispatcher* dispatcher) { while (dispatcher->active) { while (dispatcher->active) { bool hasImage = false; try { cv::Mat frame; hasImage = cap.read(frame); // Double check the image to make sure it's valid. if (!frame.data || frame.empty()) { std::stringstream ss; ss << "Stream " << dispatcher->mjpeg_url << " received invalid frame"; dispatcher->log_error(ss.str()); return; } dispatcher->mMutex.lock(); dispatcher->setLatestFrame(frame); dispatcher->mMutex.unlock(); } catch (const std::runtime_error& error) { // Error occured while trying to gather image. Retry, don't exit. std::stringstream ss; ss << "Exception happened " << error.what(); dispatcher->log_error(ss.str()); dispatcher->mMutex.unlock(); return; } dispatcher->mMutex.unlock(); if (hasImage == false) break; // Delay 15ms sleep_ms(15); } // Delay 100ms sleep_ms(100); } } openalpr_2.2.4.orig/src/video/videobuffer.h000066400000000000000000000053761266464252400207540ustar00rootroot00000000000000#ifndef OPENALPR_VIDEOBUFFER_H #define OPENALPR_VIDEOBUFFER_H #include #include #include #include "opencv2/highgui/highgui.hpp" #include "support/filesystem.h" #include "support/tinythread.h" #include "support/platform.h" class VideoDispatcher { public: VideoDispatcher(std::string mjpeg_url, int fps) { this->active = true; this->latestFrameNumber = -1; this->lastFrameRead = -1; this->fps = fps; this->mjpeg_url = mjpeg_url; } int getLatestFrame(cv::Mat* frame, std::vector& regionsOfInterest) { tthread::lock_guard guard(mMutex); if (latestFrameNumber == lastFrameRead) return -1; frame->create(latestFrame.size(), latestFrame.type()); latestFrame.copyTo(*frame); this->lastFrameRead = this->latestFrameNumber; // Copy the regionsOfInterest array for (int i = 0; i < this->latestRegionsOfInterest.size(); i++) regionsOfInterest.push_back(this->latestRegionsOfInterest[i]); return this->lastFrameRead; } void setLatestFrame(cv::Mat frame) { frame.copyTo(this->latestFrame); this->latestRegionsOfInterest = calculateRegionsOfInterest(&this->latestFrame); this->latestFrameNumber++; } virtual void log_info(std::string message) { std::cout << message << std::endl; } virtual void log_error(std::string error) { std::cerr << error << std::endl; } std::vector calculateRegionsOfInterest(cv::Mat* frame) { cv::Rect rect(0, 0, frame->cols, frame->rows); std::vector rois; rois.push_back(rect); return rois; } bool active; int latestFrameNumber; int lastFrameRead; std::string mjpeg_url; int fps; tthread::mutex mMutex; private: cv::Mat latestFrame; std::vector latestRegionsOfInterest; }; class VideoBuffer { public: VideoBuffer(); virtual ~VideoBuffer(); void connect(std::string mjpeg_url, int fps); // If a new frame is available, the function sets "frame" to it and returns the frame number // If no frames are available, or the latest has already been grabbed, returns -1. // regionsOfInterest is set to a list of good regions to check for license plates. Default is one rectangle for the entire frame. int getLatestFrame(cv::Mat* frame, std::vector& regionsOfInterest); void disconnect(); protected: virtual VideoDispatcher* createDispatcher(std::string mjpeg_url, int fps); private: VideoDispatcher* dispatcher; }; #endif // OPENALPR_VIDEOBUFFER_H