From e8d7b0d91d8a38d9fcc4af7232390fdd046aabfc Mon Sep 17 00:00:00 2001 From: Eleni Maria Stea Date: Thu, 4 Apr 2013 03:18:05 +0300 Subject: [PATCH 1/1] An optical flow driven virtual keyboard. --- COPYING | 674 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ Makefile | 19 ++ data/glyphs.png | Bin 0 -> 30084 bytes src/main.cc | 419 ++++++++++++++++++++++++++++++++++ src/matrix.cc | 87 +++++++ src/matrix.h | 36 +++ src/motion.cc | 187 +++++++++++++++ src/motion.h | 34 +++ src/vector.cc | 116 ++++++++++ src/vector.h | 55 +++++ src/vkeyb.cc | 155 +++++++++++++ src/vkeyb.h | 42 ++++ 12 files changed, 1824 insertions(+) create mode 100644 COPYING create mode 100644 Makefile create mode 100644 data/glyphs.png create mode 100644 src/main.cc create mode 100644 src/matrix.cc create mode 100644 src/matrix.h create mode 100644 src/motion.cc create mode 100644 src/motion.h create mode 100644 src/vector.cc create mode 100644 src/vector.h create mode 100644 src/vkeyb.cc create mode 100644 src/vkeyb.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6a17c1d --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +src = $(wildcard src/*.cc) +obj = $(src:.cc=.o) +bin = vkeyb + + +dbg = -g +opt = -O3 + +CXX = g++ +CXXFLAGS = -pedantic -Wall $(dbg) $(opt) +LDFLAGS = -lGL -lGLU -lX11 -limago -lglut \ + -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_video + +$(bin): $(obj) + $(CXX) -o $@ $(obj) $(LDFLAGS) + +.PHONY: clean +clean: + rm -f $(obj) $(bin) diff --git a/data/glyphs.png b/data/glyphs.png new file mode 100644 index 0000000000000000000000000000000000000000..c8467d4888ed300cfdc5f9249e0d40ddc8ad979f GIT binary patch literal 30084 zcmaHTcRZJS__tD0Asopln-FEEVQ-NUl7{RNsqD-$qDW@hls&Sg6v+rtc1C3@Bq4h} z@B8<>o+Ix5*YA@dz{oX zmG*oiWuRu?8zJjuhCgC-QZjT>aIm+xxamZqaL2;L$-5*eEOzyws#?dmv^?*r=d>L3kGPGOU#=Uy`tE$0zVi1Qha2C| zJ^ofoqpPSkuCBZ9yQ^lWSnK-o$E}HlXJ0&}yALPp&YWcpTOQS!U41*d?mSOIZJioG zZ}h+Xuv=y5My2e2!O}MQ|NjriQ!Qj|{`VKE;VY%!{oh|jP%@yIf|n)iBl(s4g4@l< zBPn;@lbw8{N4IOIT>6rhz3rK&7LGc5oEy$}{_Ea-B{U$z!m;d{Z8MYkw5#Sqo6`~R zCUL#QrNNwCPd%<%yxgAh-pUa@!2iNie=WCoIl8$qa@x6}{_KUL2lV^Xnm2MNJtF9h z>BGar+5Cm%ZEX1B#B4sOG1{$`1Z-@0zV%q9s;Q}|aGT>;njH*#?fCP#ey%}u$CArT z|L{jvA4OSNl56ko2DG$j&>cF&dFm9SiHS+?8|PyQiHXO`Hq+D78(UfwuUt9&;lqbp z6D^D_$ug{;KYwm(Z_n=7x+v~BdHU8pS z%+ePs?u_sIxiE{%>&iM3_o2CYUuUM)$MkbgsSh3uh>D^Msy9u`$ly4An(5-pD>W^# zqV1(No&1TT9O2}Ae0**T6O_lVzVT=EUfFT}{P}2+lQ}n7Rnv9HCMGz9g=tb#Q+r=I zu<~fe`QKffy4|yM+Ii~w{8&?OwU5lpYwsTZ{d+4dEiEN2ZP)A9uUA&)IOXN#Zw;H|%x4t6tNCnVYZO=1k8wJhngaI3y&^D>5;$b)})BBO-EsW@d)b z-O9qE$lGJGgS+8XeSJOu;Enag>5u)}hfZ8G-~93Q>(io)Z{NO2+EmBKGpFhD32r<7 z{H*`TgI9+~^30jYtSnw#Jw3(Z;^Hhex_wQLkDO5*D7OzTc8@-2RBoqs=gu9~n>UZ= zQ>;EoOw{b?=xD5TooyWb7}nGpf3hi7)T*(+>_%{We0+m5iR{M4hN_K?O{}vvx4D(o ze?^8GXQiaXb2=lqKPo^AT{>BV?*aF6CBa%r))e74Cwg; z1-Us@9_!`&K6dftt)AJ3&VLscF62iVgT($n^jH#7X=Iu*G zMMa9dbay(uoqL{$OdFSznVOk3`RzH-IN6!mR8YO8KG7Pl5_i(!rOUwhSDB}{lUpmJ zVf<3t2Ng_A*xk3cykadJdJFH>H8e!rzrQmgGEza>XG;TRn?C0mF%xj=)G4krXX3xT z^LSeE=8bLC&o|D~*6X|2{Wapm)JEU9{O%23SznI~389#heij#}{yABuKv#O+eQa#( z$-jS2cNV9*@BGZ*|LGIPBBiRMLsO=umacne>G!1`v~znkWo0sFpZ^}!b4f}Xj#ssJ zc5>nnbDwej!-a#*R&ebd2TmK8uyDAdqT-WJPftWze2SsjvuDrw&z$yI(&#$(qV{Oc zN)*4}jkeHqa?rE(QswMmb%EY%-R!8tr=5auO$P4k|NZx0)BN}+asK$+Evwq6Fa3DD zIx23P&HqgHiRkFiP5)VgYF5oP$d_dH%V~M>X0bZMId^JndvJT};~VK{_v%|x2P&op z=SDvsi>@-M4MYt(N^cW4>fgVAU7r;?%T7eAM_*f431i`=r>8e|I^ezZn~_n}lH#c2 z^Nb}q-0Hq+pK4_*(@K|_#?Ogo)LdN3)h(*M)?+l1r4xTmPy5W3T}yCuGOwxV;Nl^ zc;#^HYSPP>$K5yA#5uzf5)PwZ{?{WmF)_gXl5xxPFbj1I1Be^Nw*_rJkJ~ z9YL>OyE|7r;QtyFee5Dp06i2hUc5NvZdYnQ5YvA>U6=31lfmoUw0miAV2⪻JNlM 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 w8j8wgv8W2Kv$O)Y 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 N8mgyN@|Dc}{|jh*=uH3s literal 0 HcmV?d00001 diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..609ee9a --- /dev/null +++ b/src/main.cc @@ -0,0 +1,419 @@ +/* +vkeyb - camera motion detection virtual keyboard +Copyright (C) 2012 Eleni Maria Stea + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include "vkeyb.h" +#include "motion.h" + +int init(void); +void shutdown(void); +int create_window(int xsz, int ysz); +void display(void); +void show_frame(float frm_width); +int handle_event(XEvent *xev); +void reshape(int w, int h); +void keyb(int key, int pressed); +void send_key(KeySym key); +void motion(int x, int y); +void cam_motion(double orient); +void button(int x, int y, int bn, int state); +void activate(int enter); + +Display *dpy; /* X11 display structure (x server connection) */ +Window win; /* X window */ +GLXContext ctx; /* OpenGL context */ + +unsigned int frm_tex; +bool tex_created; + +double size = 0.1; +VKeyb *vkeyb; + +int must_redraw; + +static double orient = 0.0; + + +int main (int argc, char** argv) +{ + glutInit(&argc, argv); + + if(init() == -1) { + return 1; + } + atexit(shutdown); + + glEnable(GL_CULL_FACE); + + for(;;) { + fd_set fdset; + + FD_ZERO(&fdset); + + //retreive the X server socket + int xsock = ConnectionNumber(dpy); + FD_SET(xsock, &fdset); + FD_SET(pipefd[0], &fdset); + + int maxfd = (xsock > pipefd[0] ? xsock : pipefd[0]) + 1; + select(maxfd, &fdset, 0, 0, 0); + + if(FD_ISSET(pipefd[0], &fdset)) { + // process all pending events ... + while(XPending(dpy)) { + XEvent xev; + XNextEvent(dpy, &xev); + handle_event(&xev); + } + } + + if(FD_ISSET(pipefd[0], &fdset)) { + if(read(pipefd[0], &orient, sizeof orient) < (int)sizeof orient) { + fprintf(stderr, "read from pipe failed\n"); + } + else { + glBindTexture(GL_TEXTURE_2D, frm_tex); + if(!tex_created) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, frm.cols, frm.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, frm.data); + tex_created = true; + } + else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frm.cols, frm.rows, GL_BGR, GL_UNSIGNED_BYTE, frm.data); + } + + cam_motion(orient); + must_redraw = true; + } + } + + // ... and then do a single redisplay if needed + if(must_redraw) { + display(); + } + + } + + shutdown(); + return 0; +} + +int init(void) +{ + Screen *scr; + int width, height; + + if(!(dpy = XOpenDisplay(0))) { + fprintf(stderr, "failed to connect to the X server\n"); + return -1; + } + scr = ScreenOfDisplay(dpy, DefaultScreen(dpy)); + + width = WidthOfScreen(scr); + height = width / 16; + + if(create_window(width, height) == -1) { + XCloseDisplay(dpy); + return -1; + } + XMoveWindow(dpy, win, 0, HeightOfScreen(scr) - height); + + try { + vkeyb = new VKeyb; + } + catch(...) { + fprintf(stderr, "failed to initialize virtual keyboard\n"); + return -1; + } + + // register a passive grab + Window root = RootWindow(dpy, DefaultScreen(dpy)); + XGrabKey(dpy, XKeysymToKeycode(dpy, 'e'), ControlMask, root, False, GrabModeAsync, GrabModeAsync); + + // create texture + glGenTextures(1, &frm_tex); + glBindTexture(GL_TEXTURE_2D, frm_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + // start the capturing thread + if(!start_capture()) + return -1; + + return 0; +} + +void shutdown(void) +{ + glXMakeCurrent(dpy, None, 0); + glXDestroyContext(dpy, ctx); + XDestroyWindow(dpy, win); + XCloseDisplay(dpy); +} + +int create_window(int xsz, int ysz) +{ + int scr; + Window root; + XSetWindowAttributes xattr; + XVisualInfo *vis; + unsigned int attr_valid; + long evmask; + + int glattr[] = { + GLX_RGBA, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_DOUBLEBUFFER, + None + }; + + scr = DefaultScreen(dpy); + root = RootWindow(dpy, scr); + + if(!(vis = glXChooseVisual(dpy, scr, glattr))) { + fprintf(stderr, "failed to find a suitable visual\n"); + return -1; + } + + if(!(ctx = glXCreateContext(dpy, vis, 0, True))) { + fprintf(stderr, "failed to create OpenGL context\n"); + XFree(vis); + return -1; + } + + xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr); + xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone); + xattr.override_redirect = True; + attr_valid = CWColormap | CWBackPixel | CWBorderPixel | CWOverrideRedirect; + + if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput, + vis->visual, attr_valid, &xattr))) { + fprintf(stderr, "failed to create X window\n"); + glXDestroyContext(dpy, ctx); + XFree(vis); + return -1; + } + XFree(vis); + + evmask = StructureNotifyMask | VisibilityChangeMask | KeyPressMask | PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | ExposureMask | EnterWindowMask | LeaveWindowMask; + XSelectInput(dpy, win, evmask); + + XMapWindow(dpy, win); + + glXMakeCurrent(dpy, win, ctx); + return 0; +} + + +void display(void) +{ + glClearColor(1, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + vkeyb->show(); + show_frame(0.1); + + glRasterPos2i(-1, -1); + + char buf[64]; + snprintf(buf, sizeof buf, "%f %s", orient, orient > 0 ? "->" : orient < 0 ? "<-" : " "); + char *ptr = buf; + while(*ptr) { + glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *ptr++); + } + + glXSwapBuffers(dpy, win); + + must_redraw = 0; + assert(glGetError() == GL_NO_ERROR); +} + +void show_frame(float frm_width) +{ + frm_width *= 2; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, frm_tex); + + glBegin(GL_QUADS); + glColor3f(1.0, 1.0, 1.0); + glTexCoord2f(0, 1); glVertex2f(-1, -1); + glTexCoord2f(1, 1); glVertex2f(frm_width - 1, -1); + glTexCoord2f(1, 0); glVertex2f(frm_width - 1, 1); + glTexCoord2f(0, 0); glVertex2f(-1, 1); + glEnd(); + + glDisable(GL_TEXTURE_2D); +} + + +int handle_event(XEvent *xev) +{ + static int mapped; + KeySym sym; + + switch(xev->type) { + case ConfigureNotify: + reshape(xev->xconfigure.width, xev->xconfigure.height); + break; + + case MapNotify: + mapped = 1; + break; + case UnmapNotify: + mapped = 0; + break; + + case Expose: + if(mapped && xev->xexpose.count == 0) { + display(); + } + break; + + case KeyPress: + sym = XLookupKeysym(&xev->xkey, 0); + keyb(sym, 1); + break; + + case MotionNotify: + motion(xev->xmotion.x, xev->xmotion.y); + break; + + case ButtonPress: + button(xev->xbutton.x, xev->xbutton.y, xev->xbutton.button, 1); + break; + case ButtonRelease: + button(xev->xbutton.x, xev->xbutton.y, xev->xbutton.button, 0); + break; + + case EnterNotify: + activate(1); + break; + case LeaveNotify: + activate(0); + break; + + default: + break; + } + + return 0; +} + + +void reshape(int w, int h) +{ + glViewport(0, 0, w, h); +} + +void keyb(int key, int pressed) +{ + if(!pressed) { + return; + } + + switch (key) { + case XK_Escape: + exit(0); + + case 'e': + printf("sending key: %c\n", (char)vkeyb->active_key()); + send_key(vkeyb->active_key()); + break; + + } +} + +void send_key(KeySym key) +{ + Window win; + XEvent ev; + int junk; + + XGetInputFocus(dpy, &win, &junk); + + memset(&ev, 0, sizeof ev); + ev.type = KeyPress; + ev.xkey.window = win; + ev.xkey.keycode = XKeysymToKeycode(dpy, key); + ev.xkey.state = 0; + ev.xkey.time = CurrentTime; + + XSendEvent(dpy, InputFocus, False, NoEventMask, &ev); +} + +static int prev_x = -1; + +void motion(int x, int y) +{ + if(prev_x == -1) { + prev_x = x; + } + + vkeyb->move((x - prev_x) / 25.0); + prev_x = x; + + must_redraw = 1; +} + +void cam_motion(double orient) +{ + if(orient > 0) { + vkeyb->move(1); + } + + if(orient < 0) { + vkeyb->move(-1); + } + + must_redraw = 1; +} + +void button(int x, int y, int bn, int state) +{ + if(bn == 3 && state) { + exit(0); + } +} + + +void activate(int enter) +{ + prev_x = -1; +} diff --git a/src/matrix.cc b/src/matrix.cc new file mode 100644 index 0000000..e97bc76 --- /dev/null +++ b/src/matrix.cc @@ -0,0 +1,87 @@ +/* +vkeyb - camera motion detection virtual keyboard +Copyright (C) 2012 Eleni Maria Stea + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include "matrix.h" +#include "vector.h" + +Matrix4x4::Matrix4x4() { + for (int i=0; i<4; i++) { + for (int j=0; j<4; j++) { + matrix[i][j] = (i == j ? 1 : 0); + } + } +} + +void Matrix4x4::set_translation(const Vector3 &tr) { + matrix[0][3] = tr.x; + matrix[1][3] = tr.y; + matrix[2][3] = tr.z; +} + +void Matrix4x4::set_rotation(const Vector3 &axis, double angle) { + double sina = sin(angle); + double cosa = cos(angle); + double invcosa = 1 - cosa; + double sqx = axis.x * axis.x; + double sqy = axis.y * axis.y; + double sqz = axis.z * axis.z; + + matrix[0][0] = sqx + (1 - sqx) * cosa; + matrix[0][1] = axis.x * axis.y * invcosa + axis.z * sina; + matrix[0][2] = axis.x * axis.z * invcosa + axis.y * sina; + matrix[1][0] = axis.x * axis.y * invcosa + axis.z * sina; + matrix[1][1] = sqy + (1 - sqy) * cosa; + matrix[1][2] = axis.y * axis.z * invcosa - axis.x * sina; + matrix[2][0] = axis.x * axis.z * invcosa - axis.y * sina; + matrix[2][1] = axis.y * axis.z * invcosa + axis.x * sina; + matrix[2][2] = sqz + (1 - sqz) * cosa; +} + +void Matrix4x4::set_scaling(const Vector3 &sc) { + matrix[0][0] = sc.x; + matrix[1][1] = sc.y; + matrix[2][2] = sc.z; +} + +void Matrix4x4::transpose() { + double m[4][4]; + + memcpy(m, matrix, sizeof m); + + for(int i=0; i<4; i++) { + for(int j=0; j<4; j++) { + matrix[i][j] = m[j][i]; + } + } +} + +void Matrix4x4::print() { + printf("\n"); + for (int i=0; i<4; i++) { + for (int j=0; j<4; j++) { + printf("%f", matrix[i][j]); + char nxt = (j%4 == 3 ? '\n' : '\t'); + printf("%c", nxt); + } + } + printf("\n"); +} + diff --git a/src/matrix.h b/src/matrix.h new file mode 100644 index 0000000..96de1e5 --- /dev/null +++ b/src/matrix.h @@ -0,0 +1,36 @@ +/* +vkeyb - camera motion detection virtual keyboard +Copyright (C) 2012 Eleni Maria Stea + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef MATRIX_H_ +#define MATRIX_H_ + +class Vector3; + +class Matrix4x4 { +public: + double matrix[4][4]; + Matrix4x4(); + void set_translation(const Vector3 &tr); + void set_rotation(const Vector3 &axis, double angle); + void set_scaling(const Vector3 &sc); + void transpose(); + void print(); +}; + +#endif + diff --git a/src/motion.cc b/src/motion.cc new file mode 100644 index 0000000..7d8b618 --- /dev/null +++ b/src/motion.cc @@ -0,0 +1,187 @@ +/* +vkeyb - camera motion detection virtual keyboard +Copyright (C) 2012 Eleni Maria Stea + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include "motion.h" + +#define NUM_FEATURES 400 +#define MHI_DURATION 1000 +#define OFFSET 30 + +static unsigned long get_msec(); + +bool stop_capture = false; +int pipefd[2]; +cv::Mat frm; +pthread_t ptd; + +bool start_capture() +{ + if(pipe(pipefd) == -1) { + perror("failed to create synchronization pipe"); + return false; + } + + int res = pthread_create(&ptd, 0, capture_thread, 0); + if(res != 0) { + fprintf(stderr, "Failed to create capturing thread: %s\n", strerror(res)); + return false; + } + return true; +} + +void *capture_thread(void *arg) +{ + cv::VideoCapture cap(0); + if(!cap.isOpened()) { + fprintf(stderr, "failed to open video capture device\n"); + } + + cv::Mat next_frm, prev_frm, frm8b, prev_frm8b, colimg; + + cap >> next_frm; + cv::flip(next_frm, next_frm, 1); + colimg = next_frm.clone(); + cv::cvtColor(next_frm, next_frm, CV_RGB2GRAY); + next_frm.convertTo(frm8b, CV_8UC1); + + while(!stop_capture) { + prev_frm = next_frm.clone(); + prev_frm.convertTo(prev_frm8b, CV_8UC1); + + cap >> next_frm; + cv::flip(next_frm, next_frm, 1); + colimg = next_frm.clone(); + cv::cvtColor(next_frm, next_frm, CV_RGB2GRAY); + next_frm.convertTo(frm8b, CV_8UC1); + + double direction = calculate_motion_dir(frm8b, prev_frm8b, colimg); + write(pipefd[1], &direction, sizeof direction); + frm = colimg.clone(); + } + return 0; +} + +double calculate_motion_dir(cv::Mat &frm8b, cv::Mat &prev_frm8b, cv::Mat &colimg) +{ + std::vector prev_corners; + std::vector corners; + std::vector status; + std::vector err; + + cv::goodFeaturesToTrack(prev_frm8b, prev_corners, (float)NUM_FEATURES, 0.01, 3.0); + cv::goodFeaturesToTrack(frm8b, corners, (float)NUM_FEATURES, 0.01, 3.0); + cv::calcOpticalFlowPyrLK(prev_frm8b, frm8b, prev_corners, corners, status, err); + + cv::Point motion_vector = cv::Point((int)((double)colimg.cols / 2.0), (int)((double)colimg.rows / 2.0)); + + for(size_t i=0; i 3) { + cv::Point vec2d = cv::Point(q.x - p.x, q.y - p.y); + motion_vector = cv::Point(motion_vector.x + vec2d.x, motion_vector.y + vec2d.y); + } + +/* + q.x = (int)(p.x + cos(angle) + M_PI / 4.0); + q.y = (int)(p.y + sin(angle) + M_PI / 4.0); + + cv::line(colimg, q, p, cv::Scalar(0, 0, 255), 1, CV_AA, 0); + + q.x = (int)(p.x + cos(angle) - M_PI / 4.0); + q.y = (int)(p.y + sin(angle) - M_PI / 4.0); + + cv::line(colimg, q, p, cv::Scalar(255, 0, 0), 1, CV_AA, 0);*/ + + } + + cv::Point ctr = cv::Point((int)((double)colimg.cols / 2.0), (int)((double)colimg.rows / 2.0)); + cv::Point xproj = cv::Point(motion_vector.x, ctr.y); + + cv::line(colimg, motion_vector, ctr, cv::Scalar(255, 0, 0), 3, CV_AA, 0); + cv::line(colimg, xproj, ctr, cv::Scalar(0, 0, 255), 3, CV_AA, 0); + + return (xproj.x - ctr.x); +} + +double calculate_orientation(cv::Mat &frm, cv::Mat &prev_frm) +{ + cv::Mat silhouette; + cv::absdiff(frm, prev_frm, silhouette); + + cv::Mat sil8; + cv::cvtColor(silhouette, silhouette, CV_RGB2GRAY); + silhouette.convertTo(sil8, CV_8UC1); + + double max_val, min_val; + cv::minMaxLoc(sil8, &min_val, &max_val); + + cv::threshold(sil8, sil8, 119, max_val, CV_THRESH_BINARY); + + cv::Mat mhi; + sil8.convertTo(mhi, CV_32FC1, 1.0 / (float)UCHAR_MAX); + + cv::Mat mask = cv::Mat(mhi.size().width, mhi.size().height, CV_8UC1); + cv::Mat orientation = mhi.clone(); + + double duration = MHI_DURATION; + unsigned long timestamp = get_msec() + duration; + + double min_delta = abs(timestamp); + double max_delta = abs(timestamp) + 500.0; + + cv::updateMotionHistory(sil8, mhi, timestamp, duration); + cv::calcMotionGradient(mhi, mask, orientation, min_delta, max_delta, 3); + + double global_orientation = cv::calcGlobalOrientation(orientation, mask, mhi, timestamp, duration); + + return global_orientation; +} + + +static unsigned long get_msec() +{ + static struct timeval tv0; + struct timeval tv; + + gettimeofday(&tv, 0); + if(tv0.tv_sec == 0 && tv0.tv_usec == 0) { + tv0 = tv; + return 0; + } + return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000; +} diff --git a/src/motion.h b/src/motion.h new file mode 100644 index 0000000..39a4a7f --- /dev/null +++ b/src/motion.h @@ -0,0 +1,34 @@ +/* +vkeyb - camera motion detection virtual keyboard +Copyright (C) 2012 Eleni Maria Stea + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef MOTION_H_ +#define MOTION_H_ + +#include + +extern bool stop_capture; +extern int pipefd[2]; +extern cv::Mat frm; +extern pthread_t ptd; + +bool start_capture(); +void *capture_thread(void *arg); +double calculate_motion_dir(cv::Mat &frm8b, cv::Mat &prev_frm8b, cv::Mat &colimg); +double calculate_orientation(cv::Mat &frm, cv::Mat &prev_frm); + +#endif /* MOTION_H_ */ diff --git a/src/vector.cc b/src/vector.cc new file mode 100644 index 0000000..8af4741 --- /dev/null +++ b/src/vector.cc @@ -0,0 +1,116 @@ +/* +vkeyb - camera motion detection virtual keyboard +Copyright (C) 2012 Eleni Maria Stea + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include +#include +#include "vector.h" +#include "matrix.h" + +Vector3::Vector3() { + x = 0; + y = 0; + z = 0; +} + +Vector3::Vector3(double x, double y, double z) { + this->x = x; + this->y = y; + this->z = z; +} + +void Vector3::transform(const Matrix4x4 &tm) { + double x1 = tm.matrix[0][0]*x + tm.matrix[0][1]*y + tm.matrix[0][2]*z + tm.matrix[0][3]; + double y1 = tm.matrix[1][0]*x + tm.matrix[1][1]*y + tm.matrix[1][2]*z + tm.matrix[1][3]; + double z1 = tm.matrix[2][0]*x + tm.matrix[2][1]*y + tm.matrix[2][2]*z + tm.matrix[2][3]; + x = x1; + y = y1; + z = z1; +} + +void Vector3::printv() { + printf("%f\t%f\t%f\n", x, y, z); +} + + +bool operator < (const Vector3 &a, const Vector3 &b) { + return a.x < b.x && a.y < b.y && a.z < b.z; +} + +bool operator > (const Vector3 &a, const Vector3 &b) { + return a.x > b.x && a.y > b.y && a.z > b.z; +} + +bool operator == (const Vector3 &a, const Vector3 &b) { + return a.x == b.x && a.y == b.y && a.z == b.z; +} + +Vector3 operator + (const Vector3 &a, const Vector3 &b) { + return Vector3(a.x + b.x, a.y + b.y, a.z + b.z); +} + +Vector3 operator - (const Vector3 &a, const Vector3 &b) { + return Vector3(a.x - b.x, a.y - b.y, a.z - b.z); +} + +Vector3 operator - (const Vector3 &a) { + return Vector3(-a.x, -a.y, -a.z); +} + +Vector3 operator * (const Vector3 &a, const Vector3 &b) { + return Vector3(a.x * b.x, a.y * b.y, a.z * b.z); +} + +Vector3 operator * (const Vector3 &a, double b) { + return Vector3(a.x*b, a.y*b, a.z*b); +} + +Vector3 operator * (double b, const Vector3 &a) { + return Vector3(a.x*b, a.y*b, a.z*b); +} + +Vector3 operator / (const Vector3 &a, double b) { + return Vector3(a.x / b, a.y / b, a.z / b); +} + +const Vector3 &operator += (Vector3 &a, const Vector3 &b) { + a.x += b.x; + a.y += b.y; + a.z += b.z; + return a; +} + +double length(const Vector3 &a) { + return sqrt(a.x*a.x + a.y*a.y + a.z*a.z); +} + +double dot(const Vector3 &a, const Vector3 &b) { + return a.x*b.x + a.y*b.y + a.z*b.z; +} + +Vector3 cross(const Vector3 &a, const Vector3 &b) { + return Vector3(a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x); +} + +Vector3 normalize(const Vector3 &vec) { + double mag = sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z); + return vec / mag; +} + +Vector3 reflect(const Vector3 &v, const Vector3 &n) { + return 2.0 * dot(v, n) * n - v; +} diff --git a/src/vector.h b/src/vector.h new file mode 100644 index 0000000..30a8b21 --- /dev/null +++ b/src/vector.h @@ -0,0 +1,55 @@ +/* +vkeyb - camera motion detection virtual keyboard +Copyright (C) 2012 Eleni Maria Stea + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef VECTOR_H_ +#define VECTOR_H_ + +class Matrix4x4; + +class Vector3 { +public: + double x,y,z; + Vector3(); + Vector3(double x, double y, double z); + void transform(const Matrix4x4 &tm); + void printv(); +}; + +bool operator < (const Vector3 &a, const Vector3 &b); +bool operator > (const Vector3 &a, const Vector3 &b); +bool operator == (const Vector3 &a, const Vector3 &b); + +Vector3 operator + (const Vector3 &a, const Vector3 &b); +Vector3 operator - (const Vector3 &a, const Vector3 &b); +Vector3 operator - (const Vector3 &a); +Vector3 operator * (const Vector3 &a, const Vector3 &b); +Vector3 operator * (const Vector3 &a, double b); +Vector3 operator * (double b, const Vector3 &a); +Vector3 operator / (const Vector3 &a, double b); + +const Vector3 &operator += (Vector3 &a, const Vector3 &b); + +double length (const Vector3 &a); +double dot (const Vector3 &a, const Vector3 &b); +Vector3 cross (const Vector3 &a, const Vector3 &b); +Vector3 normalize (const Vector3 &a); + +Vector3 reflect(const Vector3 &v, const Vector3 &n); + +#endif + diff --git a/src/vkeyb.cc b/src/vkeyb.cc new file mode 100644 index 0000000..8acb4a1 --- /dev/null +++ b/src/vkeyb.cc @@ -0,0 +1,155 @@ +/* +vkeyb - camera motion detection virtual keyboard +Copyright (C) 2012 Eleni Maria Stea + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include "vkeyb.h" + +static KeySym charmap[] = { + XK_Greek_ALPHA, + XK_Greek_BETA, + XK_Greek_GAMMA, + XK_Greek_DELTA, + XK_Greek_EPSILON, + XK_Greek_ZETA, + XK_Greek_ETA, + XK_Greek_THETA, + XK_Greek_IOTA, + XK_Greek_KAPPA, + XK_Greek_LAMBDA, + XK_Greek_MU, + XK_Greek_NU, + XK_Greek_XI, + XK_Greek_OMICRON, + XK_Greek_PI, + XK_Greek_RHO, + XK_Greek_SIGMA, + XK_Greek_TAU, + XK_Greek_UPSILON, + XK_Greek_PHI, + XK_Greek_CHI, + XK_Greek_PSI, + XK_Greek_OMEGA, + ' ', '\b', '\n', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' +}; + +static unsigned int load_texture(const char *fname); + +VKeyb::VKeyb() +{ + offset = 0; + if(!(tex = load_texture("data/glyphs.png"))) { + throw 1; + } + num_glyphs = 53; + visible_glyphs = 24; +} + +VKeyb::~VKeyb() +{ + glDeleteTextures(1, &tex); +} + +void VKeyb::show() const +{ + float uoffs = floor(offset) / num_glyphs; + float umax = (float)visible_glyphs / num_glyphs; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, tex); + + glBegin(GL_QUADS); + glColor3f(1, 1, 1); + glTexCoord2f(uoffs, 1); + glVertex2f(-1, -1); + glTexCoord2f(uoffs + umax, 1); + glVertex2f(1, -1); + glTexCoord2f(uoffs + umax, 0); + glVertex2f(1, 1); + glTexCoord2f(uoffs, 0); + glVertex2f(-1, 1); + glEnd(); + + glDisable(GL_TEXTURE_2D); + + float rect_width = 2.0 / visible_glyphs; + + glLineWidth(2.0); + glBegin(GL_LINE_LOOP); + glColor3f(1, 0, 0); + glVertex2f(0, -1); + glVertex2f(rect_width, -1); + glVertex2f(rect_width, 1); + glVertex2f(0, 1); + glEnd(); +} + +void VKeyb::move(float offs) +{ + float tmp = offset + offs; + + if(tmp < 0.0) { + offset = fmod(num_glyphs + tmp, num_glyphs); + } else { + offset = fmod(tmp, num_glyphs); + } +} + + +static unsigned int load_texture(const char *fname) +{ + void *pixels; + int xsz, ysz; + unsigned int tex; + + if(!(pixels = img_load_pixels(fname, &xsz, &ysz, IMG_FMT_RGBA32))) { + fprintf(stderr, "failed to load image: %s\n", fname); + return 0; + } + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xsz, ysz, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + if(glGetError() != GL_NO_ERROR) { + img_free_pixels(pixels); + return 0; + } + + img_free_pixels(pixels); + return tex; +} + +int VKeyb::active_glyph() const +{ + return (int)(offset + visible_glyphs / 2) % num_glyphs; +} + +KeySym VKeyb::active_key() const +{ + return charmap[active_glyph()]; +} diff --git a/src/vkeyb.h b/src/vkeyb.h new file mode 100644 index 0000000..29e6845 --- /dev/null +++ b/src/vkeyb.h @@ -0,0 +1,42 @@ +/* +vkeyb - camera motion detection virtual keyboard +Copyright (C) 2012 Eleni Maria Stea + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef VKEYB_H_ +#define VKEYB_H_ + +#include + +class VKeyb { +private: + int num_glyphs; + int visible_glyphs; + float offset; + unsigned int tex; + +public: + VKeyb(); + ~VKeyb(); + + void show() const; + void move(float offs); + + int active_glyph() const; + KeySym active_key() const; +}; + +#endif -- 1.7.10.4