pax_global_header00006660000000000000000000000064147563520710014524gustar00rootroot0000000000000052 comment=6d1720fbe56843cb78575bdefe35115abce726c4 astra-toolbox-2.3.0/000077500000000000000000000000001475635207100143245ustar00rootroot00000000000000astra-toolbox-2.3.0/.github/000077500000000000000000000000001475635207100156645ustar00rootroot00000000000000astra-toolbox-2.3.0/.github/CODE_OF_CONDUCT.md000066400000000000000000000121521475635207100204640ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at astra@astra-toolbox.com. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. astra-toolbox-2.3.0/.github/CONTRIBUTING.md000066400000000000000000000030771475635207100201240ustar00rootroot00000000000000## Contributing to ASTRA Toolbox ### Questions and discussions If you have general questions about using the toolbox, feel free to open a [new discussion](https://github.com/astra-toolbox/astra-toolbox/discussions) on GitHub. We also suggest checking the available [documentation](https://astra-toolbox.com/docs/index.html) and [examples](https://github.com/astra-toolbox/astra-toolbox/tree/master/samples), as well as the existing [discussions](https://github.com/astra-toolbox/astra-toolbox/discussions) and [issues](https://github.com/astra-toolbox/astra-toolbox/issues) to get more information beforehand. ### Bugs and feature requests If you find a bug in the code or would like to suggest adding a new feature to the toolbox, please confirm that it hasn't already been reported/suggested in the [GitHub repository](https://github.com/astra-toolbox/astra-toolbox/issues) and open a new issue following the suggested guidelines. ### Contributions to the code We welcome contributions to the development of the toolbox and its [documentation](https://github.com/astra-toolbox/astra-toolbox.github.io), which can be submitted as [pull requests](https://github.com/astra-toolbox/astra-toolbox/pulls) on GitHub. The guidelines on building the development version of the toolbox can be found in the [documentation](https://astra-toolbox.com/docs/install.html). If you want to add a new feature or do a significant refactoring of the code, please first open a [new issue](https://github.com/astra-toolbox/astra-toolbox/issues) to coordinate how it fits into the scope and roadmap of the toolbox. astra-toolbox-2.3.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001475635207100200475ustar00rootroot00000000000000astra-toolbox-2.3.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000024041475635207100225410ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- For questions and issues related to using the toolbox (not related to actual bugs in the code of the toolbox), please open a new discussion instead (https://github.com/astra-toolbox/astra-toolbox/discussions). We also recommend inspecting the existing documentation (https://astra-toolbox.com/docs/index.html) and examples (https://github.com/astra-toolbox/astra-toolbox/tree/master/samples). 1. Ensure the bug is not already reported (https://github.com/astra-toolbox/astra-toolbox/issues). 2. Provide a minimal reproducible example, if possible in code. Please use the code block functionality of GitHub (by using `/Code block` command or enclosing the code in triple grave mark symbols ``` ```). 3. Provide the version of ASTRA and source of installation (e.g. `astra-toolbox` channel in conda, `conda-forge` channel, pre-compiled binaries or manual installation). 4. (optional) For issues related to installation of ASTRA or to the CUDA functionality, please report the platform (Windows/Linux), the version of your Nvidia drivers, and in the case of conda installation the full list of packages installed in the environment, which can be obtained with `conda list` command. astra-toolbox-2.3.0/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000007661475635207100236050ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. astra-toolbox-2.3.0/.gitignore000066400000000000000000000014151475635207100163150ustar00rootroot00000000000000.*.swp .*.swo *~ *.orig *.rej .nfs* *.o __pycache__ .vscode/* # Cython-generated files /python/astra/*.cpp /python/astra/config.pxi # Build files /python/dist/* /python/astra_toolbox.egg-info/* /python/build/ /python/astra.lib /build/conda/pkgs/* /build/pypi/pkgs/* /build/containers/podman/sw/* /build/linux/.libs/* /build/linux/Makefile /build/linux/aclocal.m4 /build/linux/autom4te.cache/* /build/linux/config.guess /build/linux/config.log /build/linux/config.status /build/linux/config.sub /build/linux/configure /build/linux/cuda/* /build/linux/install-sh /build/linux/libastra.la /build/linux/libastra.pc /build/linux/libtool /build/linux/ltmain.sh /build/linux/src/* /build/linux/matlab/* /build/linux/python/* # MSVC build output /build/msvc/bin/ /build/msvc/release/ astra-toolbox-2.3.0/.travis.yml000066400000000000000000000032171475635207100164400ustar00rootroot00000000000000language: python os: - linux sudo: false addons: apt: packages: - libboost-all-dev matrix: include: - env: CUDA=no CLANG=yes python: "3.6" - env: CUDA=no python: "2.7" - env: CUDA=no python: "3.6" - env: CUDA=yes python: "2.7" addons: apt: packages: - libboost-all-dev - nvidia-cuda-toolkit - nvidia-cuda-dev - nvidia-profiler - env: CUDA=yes python: "3.6" addons: apt: packages: - libboost-all-dev - nvidia-cuda-toolkit - nvidia-cuda-dev - nvidia-profiler exclude: - os: linux before_install: - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh; else wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; fi - bash miniconda.sh -b -p $HOME/miniconda - export PATH="$HOME/miniconda/bin:$PATH" - conda config --set always_yes yes --set changeps1 no - conda update conda install: - conda install python=$TRAVIS_PYTHON_VERSION six numpy scipy cython - conda info -a script: - cd build/linux - ./autogen.sh - if [ x$CLANG == xyes ]; then export CXX=clang++; export CC=clang; fi - if [ $CUDA == yes ]; then ./configure --prefix=$HOME/astra --with-python --with-cuda --with-install-type=module; else ./configure --prefix=$HOME/astra --with-python --without-cuda --with-install-type=module; fi - make -j 4 - make test - make install - python -c "import astra; astra.test_noCUDA()" astra-toolbox-2.3.0/COPYING000066400000000000000000001045131475635207100153630ustar00rootroot00000000000000 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 . astra-toolbox-2.3.0/NEWS.txt000066400000000000000000000130401475635207100156370ustar00rootroot00000000000000----------------------------------------------------------------------- This file is part of the ASTRA Toolbox Copyright: 2010-2024, imec Vision Lab, University of Antwerp 2014-2024, CWI, Amsterdam https://visielab.uantwerpen.be/ and https://www.cwi.nl/ License: Open Source under GPLv3 Contact: astra@astra-toolbox.com Website: https://www.astra-toolbox.com/ ----------------------------------------------------------------------- 2.3.0 (2025-02-22) * add support for zero-copy linking of GPU/CPU arrays from Python libraries supporting the DLPack standard, including PyTorch, TensorFlow, CuPy and JAX * add support for using different filter types in FDK_CUDA. The filter configuration for FDK_CUDA now matches that for FBP/FBP_CUDA. * add conda packages for Python 3.13, CUDA 12.6, 12.8, and NumPy 2 * add automatic asynchronous execution to `astra.experimental` functions in Python * add automatic splitting of FDK execution along angular dimension, allowing for larger maximum number of projections * add error reporting in `astra.algorithm.run` function * add an extensive Python unit test suite * fix incorrect results with GPUs with more than 48GB of memory * fix multiple minor bugs * require C++17, raising minimum system requirements to CUDA 11 * deprecate Python 3.7 conda packages * remove boost and six dependencies 2.2.0 (2024-07-12) * update Windows conda packages * add support for python 3.12, cuda 12.5, and numpy 2.0 2.1.3 (2023-10-04) * astra conda packages for cuda 11.6 and above now depend on nvidia's modular cuda packages instead of on the monolithic cudatoolkit package. This improves compatibility of our conda packages with pytorch. * speed up FDK, especially when using multiple GPUs * improve error reporting 2.1.2 (2022-09-09) * fix set_gpu_index with Python 3.10 * fix set_gpu_index initializing GPU #0 before switching to the desired GPU 2.1.1 (2022-06-30) * fix memory leak in cone beam FP 2.1.0 (2022-01-31) * fix potential hang in CUDA FFT, and speed it up (for FBP_CUDA and FDK_CUDA) * fix GPULink with rows not padded to multiple of 32 pixels * fix output value scaling for short-scan FDK * improve CUDA error logging * fix compatibility issues with the conda-forge and nvidia conda channels 2.0.0 (2021-10-27) * fix inconsistent scaling of output value depending on detector pixel size See the 2020-01-17 news entry on astra-toolbox.com for more details * add experimental interfaces to FP3D, BP3D, FDK to make ASTRA calls by 3rd party toolboxes more efficient and flexible * improve compatibility with modern Python, CUDA, Linux and Windows versions 1.9.0 (2018-2019) * add 2D parallel_vec geometry The old ExtraDetectorOffset option has been removed. Please use parallel_vec to achieve this effect now * fix inconsistent rotation direction in CPU fan beam code * fix scaling of output values for FDK and fan beam FBP in some geometries 1.8.3 (2017-11-06) * fix geometry memory leak in 3D FP/BP * fix FDK short scan weighting * add preliminary support for building on macOS * add experimental support for using externally managed GPU memory from python (see samples/python/s021_pygpu.py) * our Linux conda python packages now have variants depending on the cudatoolkit version * add basic post-install tests test_CUDA/test_noCUDA (see README) 1.8 (2016-12-05) * the Windows binary release now requires CUDA 8.0 * major changes to the way 'make install' works when building from source * removed GPU memory size restrictions for FDK * added multi-GPU support to 3D FP/BP/FDK * added relaxation factor option to SIRT, SART * fixed certain projections parallel to XZ or YZ planes * fixed accumulating multiple raylengths in SART * for matlab OpTomo, make output type match input type * for python OpTomo, add FP/BP functions with optional 'out' argument * fixed problems with non-US locales 1.7.1beta (2015-12-23) * NB: This release has a beta tag as it contains two new big experimental features. * fix crash with certain 2D CUDA FP calls 1.7beta (2015-12-04) * NB: This release has a beta tag as it contains two new big experimental features. * experimental MPI distributed computing support in Python * experimental support in Python for FP and BP of objects composited from multiple 3d data objects, at possibly different resolutions. This also removes some restrictions on data size for 3D GPU FP and BP. * support for Python algorithm plugins * removed restrictions on volume geometries: The volume no longer has to be centered. Voxels still have to be cubes, but no longer 1x1x1. * build fixes for newer platforms * various consistency and bug fixes 1.6 (2015-05-29) * integrate and improve python interface * integrate opSpot-based opTomo operator * build fixes for newer platforms * various consistency and bug fixes 1.5 (2015-01-30) * add support for fan beam FBP * remove limits on number of angles in GPU code (They are still limited by available memory, however) * update the included version of the DART algorithm * build fixes for newer platforms * various consistency and bug fixes 1.4 (2014-04-07) * various consistency and bug fixes * add global astra_set_gpu_index 1.3 (2013-07-02) * various consistency and bug fixes * add a version of the DART algorithm (written by Wim van Aarle) 1.2 (2013-03-01) * various consistency and bug fixes 1.1 (2012-10-24) * add support for matlab single arrays in mex interface 1.0 (2012-08-22) * first public release astra-toolbox-2.3.0/README.md000066400000000000000000000153131475635207100156060ustar00rootroot00000000000000# The ASTRA Toolbox The ASTRA Toolbox is a MATLAB and Python toolbox of high-performance GPU primitives for 2D and 3D tomography. We support 2D parallel and fan beam geometries, and 3D parallel and cone beam. All of them have highly flexible source/detector positioning. A large number of 2D and 3D algorithms are available, including FBP, SIRT, SART, CGLS. The basic forward and backward projection operations are GPU-accelerated, and directly callable from MATLAB and Python to enable building new algorithms. ## Documentation / samples See the MATLAB and Python code samples in the `samples/` directory and on https://www.astra-toolbox.com/. ## Installation instructions ### Windows/Linux, using conda for Python packages Requirements: [conda](https://conda.io/) Python environment, with 64 bit Python 3.9-3.13. We provide packages for the ASTRA Toolbox in the `astra-toolbox` channel for the conda package manager. We depend on CUDA packages available from the `nvidia` channel. To install ASTRA into the current conda environement, run: ``` conda install -c astra-toolbox -c nvidia astra-toolbox ``` We also provide development packages between releases occasionally: ``` conda install -c astra-toolbox/label/dev -c nvidia astra-toolbox ``` ### Linux, using pip for Python packages Requirements: Python environment with 64 bit Python 3.9-3.13. ``` pip install astra-toolbox ``` Note that, unlike conda packages, we only provide packages built for Linux platform, and only with a single reasonably recent version of CUDA toolkit. These packages depend on PyPI CUDA distribution provided by NVIDIA. ### Windows, binary Add the mex and tools subdirectories to your MATLAB path, or install the Python wheel using pip. We require the Microsoft Visual Studio 2017 redistributable package. If this is not already installed on your system, it is included as vc_redist.x64.exe in the ASTRA zip file. ### Linux, from source #### For MATLAB Requirements: g++ (7 or higher), CUDA (11.0 or higher), MATLAB (R2012a or higher) ``` cd build/linux ./autogen.sh # when building a git version ./configure --with-cuda=/usr/local/cuda \ --with-matlab=/usr/local/MATLAB/R2012a \ --prefix=$HOME/astra \ --with-install-type=module make make install ``` Add $HOME/astra/matlab and its subdirectories (tools, mex) to your MATLAB path. If you want to build the Octave interface instead of the MATLAB interface, specify `--enable-octave` instead of `--with-matlab=...`. The Octave files will be installed into $HOME/astra/octave . On some Linux distributions building the Astra Octave interface will require the Octave development package to be installed (e.g., liboctave-dev on Ubuntu). NB: Each MATLAB version only supports a specific range of g++ versions. Despite this, if you have a newer g++ and if you get errors related to missing GLIBCXX_3.4.xx symbols, it is often possible to work around this requirement by deleting the version of libstdc++ supplied by MATLAB in MATLAB_PATH/bin/glnx86 or MATLAB_PATH/bin/glnxa64 (at your own risk), or setting `LD_PRELOAD=/usr/lib64/libstdc++.so.6` (or similar) when starting MATLAB. #### For Python Requirements: g++ (7 or higher), CUDA (11.0 or higher), Python (3.x), setuptools, Cython, scipy ``` cd build/linux ./autogen.sh # when building a git version ./configure --with-cuda=/usr/local/cuda \ --with-python \ --with-install-type=module make make install ``` This will install Astra into your current Python environment. #### As a C++ library Requirements: g++ (7 or higher), CUDA (11.0 or higher) ``` cd build/linux ./autogen.sh # when building a git version ./configure --with-cuda=/usr/local/cuda make make install-dev ``` This will install the Astra library and C++ headers. ### macOS, from source Use the Homebrew package manager to install libtool, autoconf, automake. ``` cd build/linux ./autogen.sh CPPFLAGS="-I/usr/local/include" NVCCFLAGS="-I/usr/local/include" ./configure \ --with-cuda=/usr/local/cuda \ --with-matlab=/Applications/MATLAB_R2016b.app \ --prefix=$HOME/astra \ --with-install-type=module make make install ``` ### Windows, from source using Visual Studio 2017 Requirements: Visual Studio 2017 (full or community), CUDA (11.0 or higher), MATLAB (R2012a or higher) and/or Python 3.x + setuptools + Cython + scipy. Using the Visual Studio IDE: * Set the environment variable MATLAB_ROOT to your MATLAB install location. * Open build\msvc\astra_vc14.sln in Visual Studio. * Select the appropriate solution configuration (typically Release_CUDA|x64). * Build the solution. * Install by copying AstraCuda64.dll and all .mexw64 files from build\msvc\bin\x64\Release_CUDA and the entire matlab\tools directory to a directory to be added to your MATLAB path. Using .bat scripts in build\msvc: * Edit build_env.bat and set up the correct library versions and paths. * For MATLAB: Run build_matlab.bat. The .dll and .mexw64 files will be in build\msvc\bin\x64\Release_Cuda. * For Python: Run build_python3.bat. Astra will be directly installed into site-packages. ## Testing your installation To perform a (very) basic test of your ASTRA installation in Python, you can run the following Python command. ``` import astra astra.test() ``` To test your ASTRA installation in MATLAB, the equivalent command is: ``` astra_test ``` ## References If you use the ASTRA Toolbox for your research, we would appreciate it if you would refer to the following papers: W. van Aarle, W. J. Palenstijn, J. Cant, E. Janssens, F. Bleichrodt, A. Dabravolski, J. De Beenhouwer, K. J. Batenburg, and J. Sijbers, “Fast and Flexible X-ray Tomography Using the ASTRA Toolboxâ€, Optics Express, 24(22), 25129-25147, (2016), https://dx.doi.org/10.1364/OE.24.025129 W. van Aarle, W. J. Palenstijn, J. De Beenhouwer, T. Altantzis, S. Bals, K. J. Batenburg, and J. Sijbers, “The ASTRA Toolbox: A platform for advanced algorithm development in electron tomographyâ€, Ultramicroscopy, 157, 35–47, (2015), https://dx.doi.org/10.1016/j.ultramic.2015.05.002 Additionally, if you use parallel beam GPU code, we would appreciate it if you would refer to the following paper: W. J. Palenstijn, K J. Batenburg, and J. Sijbers, "Performance improvements for iterative electron tomography reconstruction using graphics processing units (GPUs)", Journal of Structural Biology, vol. 176, issue 2, pp. 250-253, 2011, https://dx.doi.org/10.1016/j.jsb.2011.07.017 ## License The ASTRA Toolbox is open source under the GPLv3 license. ## Contact email: astra@astra-toolbox.com website: https://www.astra-toolbox.com/ Copyright: 2010-2024, imec Vision Lab, University of Antwerp 2014-2024, CWI, Amsterdam https://visielab.uantwerpen.be/ and https://www.cwi.nl/ astra-toolbox-2.3.0/README.txt000066400000000000000000000155561475635207100160360ustar00rootroot00000000000000----------------------------------------------------------------------- This file is part of the ASTRA Toolbox Copyright: 2010-2024, imec Vision Lab, University of Antwerp 2014-2024, CWI, Amsterdam https://visielab.uantwerpen.be/ and https://www.cwi.nl/ License: Open Source under GPLv3 Contact: astra@astra-toolbox.com Website: https://www.astra-toolbox.com/ ----------------------------------------------------------------------- The ASTRA Toolbox is a MATLAB and Python toolbox of high-performance GPU primitives for 2D and 3D tomography. We support 2D parallel and fan beam geometries, and 3D parallel and cone beam. All of them have highly flexible source/detector positioning. A large number of 2D and 3D algorithms are available, including FBP, SIRT, SART, CGLS. The basic forward and backward projection operations are GPU-accelerated, and directly callable from MATLAB and Python to enable building new algorithms. Documentation / samples: ------------------------- See the MATLAB and Python code samples in the samples/ directory and on https://www.astra-toolbox.com/ . Installation instructions: --------------------------- Linux/Windows, using conda for Python: -------------------------------------- Requirements: conda Python environment, with 64 bit Python 3.9-3.13. We provide packages for the ASTRA Toolbox in the astra-toolbox channel for the conda package manager. We depend on CUDA packages available from the nvidia channel. To install ASTRA into the current conda environment, run: conda install -c astra-toolbox -c nvidia astra-toolbox We also provide development packages between releases occasionally: conda install -c astra-toolbox/label/dev -c nvidia astra-toolbox Linux, using pip for Python packages: ------------------------------------- Requirements: Python environment with 64 bit Python 3.9-3.13. pip install astra-toolbox Note that, unlike conda packages, we only provide packages built for Linux platform, and only with a single reasonably recent version of CUDA toolkit. These packages depend on PyPI CUDA distribution provided by NVIDIA. Windows, binary: ----------------- Add the mex and tools subdirectories to your MATLAB path, or install the Python wheel using pip. We require the Microsoft Visual Studio 2017 redistributable package. If this is not already installed on your system, it is included as vc_redist.x64.exe in the ASTRA zip file. Linux, from source: -------------------- For MATLAB: Requirements: g++ (7 or higher), CUDA (11.0 or higher), MATLAB (R2012a or higher) cd build/linux ./autogen.sh # when building a git version ./configure --with-cuda=/usr/local/cuda \ --with-matlab=/usr/local/MATLAB/R2012a \ --prefix=$HOME/astra \ --with-install-type=module make make install Add $HOME/astra/matlab and its subdirectories (tools, mex) to your MATLAB path. If you want to build the Octave interface instead of the MATLAB interface, specify --enable-octave instead of --with-matlab=... . The Octave files will be installed into $HOME/astra/octave . On some Linux distributions building the Astra Octave interface will require the Octave development package to be installed (e.g., liboctave-dev on Ubuntu). NB: Each MATLAB version only supports a specific range of g++ versions. Despite this, if you have a newer g++ and if you get errors related to missing GLIBCXX_3.4.xx symbols, it is often possible to work around this requirement by deleting the version of libstdc++ supplied by MATLAB in MATLAB_PATH/bin/glnx86 or MATLAB_PATH/bin/glnxa64 (at your own risk), or setting LD_PRELOAD=/usr/lib64/libstdc++.so.6 (or similar) when starting MATLAB. For Python: Requirements: g++ (7 or higher), CUDA (11.0 or higher), Python (3.x), setuptools, Cython, scipy cd build/linux ./autogen.sh # when building a git version ./configure --with-cuda=/usr/local/cuda \ --with-python \ --with-install-type=module make make install This will install Astra into your current Python environment. As a C++ library: Requirements: g++ (7 or higher), CUDA (11.0 or higher) cd build/linux ./autogen.sh # when building a git version ./configure --with-cuda=/usr/local/cuda make make install-dev This will install the Astra library and C++ headers. macOS, from source: -------------------- Use the Homebrew package manager to install libtool, autoconf, automake. cd build/linux ./autogen.sh CPPFLAGS="-I/usr/local/include" NVCCFLAGS="-I/usr/local/include" ./configure \ --with-cuda=/usr/local/cuda \ --with-matlab=/Applications/MATLAB_R2016b.app \ --prefix=$HOME/astra \ --with-install-type=module make make install Windows, from source using Visual Studio 2017: ----------------------------------------------- Requirements: Visual Studio 2017 (full or community), CUDA (11.0 or higher), MATLAB (R2012a or higher) and/or Python 3.x + setuptools + Cython + scipy. Using the Visual Studio IDE: * Set the environment variable MATLAB_ROOT to your MATLAB install location. * Open build\msvc\astra_vc14.sln in Visual Studio. * Select the appropriate solution configuration (typically Release_CUDA|x64). * Build the solution. * Install by copying AstraCuda64.dll and all .mexw64 files from build\msvc\bin\x64\Release_CUDA and the entire matlab/tools directory to a directory to be added to your MATLAB path. Using .bat scripts in build\msvc: * Edit build_env.bat and set up the correct library versions and paths. * For MATLAB: Run build_MATLAB.bat. The .dll and .mexw64 files will be in bin\x64\Release_Cuda. * For Python: Run build_python3.bat. Astra will be directly installed into site-packages. Testing your installation: --------------------------- To perform a (very) basic test of your ASTRA installation in Python, you can run the following Python command. import astra astra.test() To test your ASTRA installation in MATLAB, the equivalent command is: astra_test References: ------------ If you use the ASTRA Toolbox for your research, we would appreciate it if you would refer to the following papers: W. Van Aarle, W J. Palenstijn, J. Cant, E. Janssens, F. Bleichrodt, A. Dabravolski, J. De Beenhouwer, K. J. Batenburg, and J. Sijbers, "Fast and Flexible X-ray Tomography Using the ASTRA Toolbox", Optics Express, vol. 24, no. 22, pp. 25129-25147, 2016 W. Van Aarle, W J. Palenstijn, J. De Beenhouwer, T. Altantzis, S. Bals, K. J. Batenburg, and J. Sijbers, "The ASTRA Toolbox: a platform for advanced algorithm development in electron tomography", Ultramicroscopy, vol. 157, pp. 35–47, 2015 Additionally, if you use parallel beam GPU code, we would appreciate it if you would refer to the following paper: W. J. Palenstijn, K J. Batenburg, and J. Sijbers, "Performance improvements for iterative electron tomography reconstruction using graphics processing units (GPUs)", Journal of Structural Biology, vol. 176, issue 2, pp. 250-253, 2011, https://dx.doi.org/10.1016/j.jsb.2011.07.017 astra-toolbox-2.3.0/build/000077500000000000000000000000001475635207100154235ustar00rootroot00000000000000astra-toolbox-2.3.0/build/conda/000077500000000000000000000000001475635207100165075ustar00rootroot00000000000000astra-toolbox-2.3.0/build/conda/README.txt000066400000000000000000000011221475635207100202010ustar00rootroot00000000000000Building conda packages: Linux: Build container images by running the containers/setup*.sh scripts ./release.sh Windows: call C:\tools\miniconda3\condabin\activate.bat Change to astra-toolbox\build\conda directory # Build libastra packages, skipping the testing phase conda build -m libastra\win64_build_config.yaml -c nvidia --no-test libastra # Build and test astra-toolbox packages conda build -m astra-toolbox\win64_build_config.yaml -c nvidia astra-toolbox # Test the previously built libastra packages conda build -c nvidia --test C:\tools\miniconda3\conda-bld\win-64\libastra*.conda astra-toolbox-2.3.0/build/conda/astra-toolbox/000077500000000000000000000000001475635207100213055ustar00rootroot00000000000000astra-toolbox-2.3.0/build/conda/astra-toolbox/bld.bat000066400000000000000000000003771475635207100225450ustar00rootroot00000000000000@echo off set R=%SRC_DIR% cd /D %R% cd python set CL=/DASTRA_CUDA /DASTRA_PYTHON "/I%R%\include" "/I%R%\lib\include" "/I%CUDA_PATH%\include" /std:c++17 copy "%LIBRARY_LIB%\AstraCuda64.lib" astra.lib python builder.py build_ext --compiler=msvc install astra-toolbox-2.3.0/build/conda/astra-toolbox/build.sh000066400000000000000000000004121475635207100227350ustar00rootroot00000000000000#!/bin/sh case `uname` in Darwin*) CC="gcc -stdlib=libstdc++" ;; esac cd $SRC_DIR/python/ CPPFLAGS="-DASTRA_CUDA -DASTRA_PYTHON $CPPFLAGS -I$SRC_DIR/ -I$SRC_DIR/include -I$SRC_DIR/lib/include" CC=$CC CFLAGS="-std=c++17" python ./builder.py build install astra-toolbox-2.3.0/build/conda/astra-toolbox/linux_deb11_build_config.yaml000066400000000000000000000003301475635207100270040ustar00rootroot00000000000000python: - 3.10 - 3.11 numpy_host: - ">=2,<3" - ">=2,<3" numpy_run: - ">=1.21,<3" - ">=1.23,<3" zip_keys: - python - numpy_host - numpy_run c_compiler_version: - 9.3 cxx_compiler_version: - 9.3 astra-toolbox-2.3.0/build/conda/astra-toolbox/linux_deb12_build_config.yaml000066400000000000000000000003271475635207100270130ustar00rootroot00000000000000python: - 3.12 - 3.13 numpy_host: - ">=2,<3" - ">=2,<3" numpy_run: - ">=1.26,<3" - ">=2,<3" zip_keys: - python - numpy_host - numpy_run c_compiler_version: - 11.2 cxx_compiler_version: - 11.2 astra-toolbox-2.3.0/build/conda/astra-toolbox/linux_deb9_build_config.yaml000066400000000000000000000003221475635207100267340ustar00rootroot00000000000000python: - 3.8 - 3.9 numpy_host: - "1.11" - "1.16" numpy_run: - ">=1.11,<2" - ">=1.16,<2" zip_keys: - python - numpy_host - numpy_run c_compiler_version: - 7.3 cxx_compiler_version: - 7.3 astra-toolbox-2.3.0/build/conda/astra-toolbox/meta.yaml000066400000000000000000000027121475635207100231210ustar00rootroot00000000000000package: name: astra-toolbox version: '2.3.0' source: git_url: https://github.com/astra-toolbox/astra-toolbox.git git_tag: master build: number: 0 test: imports: - astra requires: # To avoid large downloads just for testing after build phase - nomkl # [not win] # import scipy.sparse.linalg fails with mkl-2017.0.4 on Windows - mkl !=2017.0.4 # [win] # scipy 1.1.0 packages are broken with python 2.7 and some numpy versions - scipy !=1.1.0 # [py27] requirements: build: - {{ compiler('c') }} # [win or linux] - {{ compiler('cxx') }} # [linux] host: - python - cython >=3 - nomkl # [not win] - numpy {{ numpy_host }} - scipy - libastra ==2.3.0 run: - python - numpy {{ numpy_run }} - scipy # We make this libastra requirement as strict as possible because we # want to enforce matching astra-toolbox and libastra packages to limit # chances of API/ABI incompatibility. # The 'cuda_*' build string is designed to match our packages but not # current conda-forge/libastra packages which use a different format. - libastra ==2.3.0 cuda_* # [win or linux] - libastra ==2.3.0 nocuda_* # [osx] about: home: https://www.astra-toolbox.com license: GPLv3 summary: 'The ASTRA Toolbox is a Python toolbox of high-performance GPU primitives for 2D and 3D tomography.' # See # http://docs.continuum.io/conda/build.html for # more information about meta.yaml astra-toolbox-2.3.0/build/conda/astra-toolbox/win64_build_config.yaml000066400000000000000000000004111475635207100256400ustar00rootroot00000000000000python: - 3.9 - 3.10 - 3.11 - 3.12 - 3.13 numpy_host: - "1.16" - ">=2,<3" - ">=2,<3" - ">=2,<3" - ">=2,<3" numpy_run: - ">=1.16,<2" - ">=1.21,<3" - ">=1.23,<3" - ">=1.26,<3" - ">=2,<3" zip_keys: - python - numpy_host - numpy_run astra-toolbox-2.3.0/build/conda/build.sh000077500000000000000000000034201475635207100201440ustar00rootroot00000000000000#!/bin/sh set -e BRANCH=master URL=https://github.com/astra-toolbox/astra-toolbox echo "Cloning from ${URL}" echo " branch: ${BRANCH}" cd /root git clone --depth 1 --branch ${BRANCH} ${URL} echo "Setting conda git_url/git_tag to match:" perl -pi -e "s~^(\s*git_url:\s*)https://github.com/.+$~\${1}${URL}~" astra-toolbox/build/conda/libastra/meta.yaml astra-toolbox/build/conda/astra-toolbox/meta.yaml perl -pi -e "s/^(\s*git_tag:\s*).+$/\${1}${BRANCH}/" astra-toolbox/build/conda/libastra/meta.yaml astra-toolbox/build/conda/astra-toolbox/meta.yaml grep git_ astra-toolbox/build/conda/libastra/meta.yaml [ $# -eq 0 ] || echo "Setting version-build to $1-$2" [ $# -eq 0 ] || perl -pi -e "s/^(\s*version:\s*)[0-9a-z+\.']+$/\${1}'$1'/" astra-toolbox/build/conda/libastra/meta.yaml astra-toolbox/build/conda/astra-toolbox/meta.yaml [ $# -eq 0 ] || perl -pi -e "s/^(\s*number:\s*)[0-9]+$/\${1}$2/" astra-toolbox/build/conda/libastra/meta.yaml astra-toolbox/build/conda/astra-toolbox/meta.yaml [ $# -eq 0 ] || perl -pi -e "s/^(\s*-\s*libastra\s*==\s*)[0-9a-z+\.]+$/\${1}$1\${2}/" astra-toolbox/build/conda/astra-toolbox/meta.yaml [ $# -eq 0 ] || perl -pi -e "s/^(\s*-\s*libastra\s*==\s*)[0-9a-z+\.]+(\s+[0-9a-z_+\.\*]+\s+#.*)$/\${1}$1\${2}/" astra-toolbox/build/conda/astra-toolbox/meta.yaml grep 'version:\|number:' astra-toolbox/build/conda/libastra/meta.yaml CONF=linux_$3_build_config.yaml conda build --no-test -c nvidia -m astra-toolbox/build/conda/libastra/${CONF} astra-toolbox/build/conda/libastra [ x$4 = xfull ] && conda build -c nvidia -m astra-toolbox/build/conda/astra-toolbox/${CONF} astra-toolbox/build/conda/astra-toolbox [ x$4 = xfull ] && conda build --test -c nvidia /root/miniconda3/conda-bld/linux-64/libastra*tar.bz2 cp /root/miniconda3/conda-bld/linux-64/*astra* /out astra-toolbox-2.3.0/build/conda/libastra/000077500000000000000000000000001475635207100203105ustar00rootroot00000000000000astra-toolbox-2.3.0/build/conda/libastra/bld.bat000066400000000000000000000004461475635207100215450ustar00rootroot00000000000000@echo off set R=%SRC_DIR% cd /D %R%/build/msvc python gen.py %cudatoolkit% msbuild astra_vc14.sln /p:Configuration=Release_CUDA /p:Platform=x64 /t:astra_vc14 /maxcpucount:20 copy bin\x64\Release_CUDA\AstraCuda64.dll "%LIBRARY_BIN%" copy bin\x64\Release_CUDA\AstraCuda64.lib "%LIBRARY_LIB%" astra-toolbox-2.3.0/build/conda/libastra/build.sh000077500000000000000000000017361475635207100217550ustar00rootroot00000000000000#!/bin/sh case `uname` in Darwin*) CUDA_ROOT=/usr/local/cuda CC=gcc CXX=g++ ;; Linux*) [ -n "$cudatoolkit" ] || exit 1 CUDA_ROOT=/usr/local/cuda-$cudatoolkit ;; esac [ -x "$CUDA_ROOT" ] || echo "CUDA_ROOT=$CUDA_ROOT not found" [ -x "$CUDA_ROOT" ] || exit 1 cd $SRC_DIR/build/linux $SRC_DIR/build/linux/autogen.sh NVCC=$CUDA_ROOT/bin/nvcc EXTRA_NVCCFLAGS= $SRC_DIR/build/linux/configure --with-install-type=prefix --with-cuda=$CUDA_ROOT --prefix=$PREFIX NVCCFLAGS="-ccbin $CC -I$PREFIX/include $EXTRA_NVCCFLAGS" CC=$CC CXX=$CXX CPPFLAGS="-I$PREFIX/include" # Clean, because we may be re-using this source tree when building # multiple variants of this conda package. make clean make -j $CPU_COUNT make -j $CPU_COUNT install-dev test -d $CUDA_ROOT/lib64 && LIBPATH="$CUDA_ROOT/lib64" || LIBPATH="$CUDA_ROOT/lib" case `uname` in Darwin*) cp -P $LIBPATH/libcudart.*.dylib $PREFIX/lib cp -P $LIBPATH/libcufft.*.dylib $PREFIX/lib ;; esac astra-toolbox-2.3.0/build/conda/libastra/linux_deb11_build_config.yaml000066400000000000000000000024661475635207100260230ustar00rootroot00000000000000cuda_dep_type: - monolithic - monolithic - monolithic - monolithic - monolithic - modular - modular - modular cudatoolkit: - 11.1 - 11.2 - 11.3 - 11.4 - 11.5 - 11.6 - 11.7 - 11.8 # Version ranges from the pytorch-cuda libcufft dependency: # 11.6_1: >=10.7.0.55,<10.7.2.50 # 11.7_5: >=10.7.2.50,<10.9.0.58 # 11.8_5: >=10.9.0.58,<11.0.0.21 libcufft_dep: - ">=10.3,<10.4" - ">=10.4,<10.5" - ">=10.4,<10.5" - ">=10.5,<10.6" - ">=10.6,<10.7" - ">=10.7,<10.9" - ">=10.7,<10.9" - ">=10.9,<11.0" cudacudart_dep: - ">=11.1,<11.2" - ">=11.2,<11.3" - ">=11.3,<11.4" - ">=11.4,<11.5" - ">=11.5,<11.6" - ">=11.6,<11.7" - ">=11.7,<11.8" - ">=11.8,<11.9" pytorchcuda_dep: - ">=11.1,<11.2" - ">=11.2,<11.3" - ">=11.3,<11.4" - ">=11.4,<11.5" - ">=11.5,<11.6" - ">=11.6,<11.7" - ">=11.7,<11.8" - ">=11.8,<11.9" cudatoolkit_dep: - ">=11.1,<11.1.74" - ">=11.2,<11.2.72" - ">=11.3,<11.4" - ">=11.4,<11.5" - ">=11.5,<11.6" - ">=11.6,<11.7" - ">=11.7,<11.8" - ">=11.8,<11.9" c_compiler_version: # [linux or win] - 9.3 # [linux or win] cxx_compiler_version: # [linux or win] - 9.3 # [linux or win] zip_keys: - cuda_dep_type - cudatoolkit - libcufft_dep - cudacudart_dep - pytorchcuda_dep - cudatoolkit_dep astra-toolbox-2.3.0/build/conda/libastra/linux_deb12_build_config.yaml000066400000000000000000000022401475635207100260120ustar00rootroot00000000000000cuda_dep_type: - modular cudatoolkit: - 12.0 - 12.1 - 12.2 - 12.3 - 12.4 - 12.5 - 12.6 - 12.8 # Starting at CUDA 12.5, the libcufft packages depend on cuda-version, # so our dependencies can be more relaxed. libcufft_dep: - ">=11.0.0,<11.0.2" - ">=11.0.2,<11.0.3" - ">=11.0.8,<11.0.9" - ">=11.0.11,<11.2.0" - ">=11.2.0,<11.2.3" - ">=11.2.3.18" - ">=11.2.6.28" - ">=11.3.3.41" cudacudart_dep: - ">=12.0,<12.1" - ">=12.1,<12.2" - ">=12.2,<12.3" - ">=12.3,<12.4" - ">=12.4,<12.5" - ">=12.5,<12.6" - ">=12.6,<12.7" - ">=12.8,<12.9" pytorchcuda_dep: - ">=12.0,<12.1" - ">=12.1,<12.2" - ">=12.2,<12.3" - ">=12.3,<12.4" - ">=12.4,<12.5" - ">=12.5,<12.6" - ">=12.6,<12.7" - ">=12.8,<12.9" cudatoolkit_dep: - ">=12.0,<12.1" - ">=12.1,<12.2" - ">=12.2,<12.3" - ">=12.3,<12.4" - ">=12.4,<12.5" - ">=12.5,<12.6" - ">=12.6,<12.7" - ">=12.8,<12.9" c_compiler_version: # [linux or win] - 11.2 # [linux or win] cxx_compiler_version: # [linux or win] - 11.2 # [linux or win] zip_keys: - cudatoolkit - libcufft_dep - cudacudart_dep - pytorchcuda_dep - cudatoolkit_dep astra-toolbox-2.3.0/build/conda/libastra/linux_deb9_build_config.yaml000066400000000000000000000005431475635207100257440ustar00rootroot00000000000000cuda_dep_type: - monolithic cudatoolkit: - 11.0 libcufft_dep: - ">=10.2,<10.3" cudacudart_dep: - ">=11.0,<11.1" pytorchcuda_dep: - ">=11.0,<11.1" cudatoolkit_dep: - ">=11.0.3,<11.1" c_compiler_version: # [linux or win] - 7.3 # [linux or win] cxx_compiler_version: # [linux or win] - 7.3 # [linux or win] astra-toolbox-2.3.0/build/conda/libastra/meta.yaml000066400000000000000000000051371475635207100221300ustar00rootroot00000000000000package: name: libastra version: '2.3.0' source: git_url: https://github.com/astra-toolbox/astra-toolbox.git git_tag: master build: number: 0 string: cuda_{{ cudatoolkit }}_{{ PKG_BUILDNUM }} # [win or linux] string: nocuda_{{ PKG_BUILDNUM }} # [osx] test: imports: - astra requires: - astra-toolbox requirements: build: - {{ compiler('c') }} # [win or linux] - {{ compiler('cxx') }} # [linux] - automake # [osx] - autoconf # [osx] - libtool # [osx] # libgcc-ng/libstdcxx-ng 9.3 introduce extra system requirements on glibc # For the older systems (debian8, debian9) we use g++ 5.4 and 7.3, resp. {% if linux and cxx_compiler_version in ['5.4', '7.3'] %} - libstdcxx-ng <9.3 # [linux] - libgcc-ng <9.3 # [linux] - libgomp <9.3 # [linux] {% endif %} host: {% if linux and cxx_compiler_version in ['5.4', '7.3'] %} - libstdcxx-ng <9.3 # [linux] - libgcc-ng <9.3 # [linux] - libgomp <9.3 # [linux] {% endif %} {% if cuda_dep_type == 'modular' %} - cuda-cudart {{ cudacudart_dep }} # [win or linux] # The cudart dll is (mistakenly?) in the *dev* package in win-64 before cuda-12.5 - cuda-cudart-dev {{ cudacudart_dep }} # [win] - libcufft {{ libcufft_dep }} # [win or linux] - libcufft-dev {{ libcufft_dep }} # [win] {% else %} # We explicitly specify the cudatoolkit dependency string in # conda_build_config.yaml , forgoing pin_compatible. # This is because the cudatoolkit version numbering for 11.0/11.1/11.2 is # non-chronological on the nvidia conda channel. - cudatoolkit {{ cudatoolkit_dep }} # [win or linux] {% endif %} run: {% if cuda_dep_type == 'modular' %} - cuda-cudart {{ cudacudart_dep }} # [win or linux] - cuda-cudart-dev {{ cudacudart_dep }} # [win] - libcufft {{ libcufft_dep }} # [win or linux] - libcufft-dev {{ libcufft_dep }} # [win] {% else %} - cudatoolkit {{ cudatoolkit_dep }} # [win or linux] {% endif %} run_constrained: - cuda-version {{ cudatoolkit }} # [win or linux] - pytorch-cuda {{ pytorchcuda_dep }} # [win or linux] {% if cuda_dep_type == 'modular' %} - cudatoolkit {{ cudatoolkit_dep }} # [win or linux] {% else %} - cuda-cudart {{ cudacudart_dep }} # [win or linux] - cuda-cudart-dev {{ cudacudart_dep }} # [win] - libcufft {{ libcufft_dep }} # [win or linux] - libcufft-dev {{ libcufft_dep }} # [win] {% endif %} about: home: https://www.astra-toolbox.com license: GPLv3 summary: 'The ASTRA Toolbox is a Python toolbox of high-performance GPU primitives for 2D and 3D tomography.' astra-toolbox-2.3.0/build/conda/libastra/win64_build_config.yaml000066400000000000000000000040721475635207100246520ustar00rootroot00000000000000cuda_dep_type: #- monolithic #- monolithic #- monolithic #- monolithic - monolithic #- monolithic #- monolithic #- modular #- modular - modular #- modular - modular #- modular #- modular - modular #- modular - modular cudatoolkit: #- 10.2 #- 11.0 #- 11.1 #- 11.2 - 11.3 #- 11.4 #- 11.5 #- 11.6 #- 11.7 - 11.8 #- 12.0 - 12.1 #- 12.2 #- 12.3 - 12.4 #- 12.5 - 12.8 # Starting at CUDA 12.5, the libcufft packages depend on cuda-version, # so our dependencies can be more relaxed. libcufft_dep: #- ">=10.1,<10.2" #- ">=10.2,<10.3" #- ">=10.3,<10.4" #- ">=10.4,<10.5" - ">=10.4,<10.5" #- ">=10.5,<10.6" #- ">=10.6,<10.7" #- ">=10.7,<10.9" #- ">=10.7,<10.9" - ">=10.9,<11.0" #- ">=11.0.0,<11.0.2" - ">=11.0.2,<11.0.3" #- ">=11.0.8,<11.0.9" #- ">=11.0.11,<11.2.0" - ">=11.2.0,<11.2.3" #- ">=11.2.3,<11.2.4" - ">=11.3.3.41" cudacudart_dep: #- ">=10.2,<10.3" #- ">=11.0,<11.1" #- ">=11.1,<11.2" #- ">=11.2,<11.3" - ">=11.3,<11.4" #- ">=11.4,<11.5" #- ">=11.5,<11.6" #- ">=11.6,<11.7" #- ">=11.7,<11.8" - ">=11.8,<11.9" #- ">=12.0,<12.1" - ">=12.1,<12.2" #- ">=12.2,<12.3" #- ">=12.3,<12.4" - ">=12.4,<12.5" #- ">=12.5,<12.6" - ">=12.8,<12.9" pytorchcuda_dep: #- ">=10.2,<10.3" #- ">=11.0,<11.1" #- ">=11.1,<11.2" #- ">=11.2,<11.3" - ">=11.3,<11.4" #- ">=11.4,<11.5" #- ">=11.5,<11.6" #- ">=11.6,<11.7" #- ">=11.7,<11.8" - ">=11.8,<11.9" #- ">=12.0,<12.1" - ">=12.1,<12.2" #- ">=12.2,<12.3" #- ">=12.3,<12.4" - ">=12.4,<12.5" #- ">=12.5,<12.6" - ">=12.8,<12.9" cudatoolkit_dep: #- ">=10.2,<10.3" #- ">=11.0.3,<11.1" #- ">=11.1,<11.1.74" #- ">=11.2,<11.2.72" - ">=11.3,<11.4" #- ">=11.4,<11.5" #- ">=11.5,<11.6" #- ">=11.6,<11.7" #- ">=11.7,<11.8" - ">=11.8,<11.9" #- ">=12.0,<12.1" - ">=12.1,<12.2" #- ">=12.2,<12.3" #- ">=12.3,<12.4" - ">=12.4,<12.5" #- ">=12.5,<12.6" - ">=12.8,<12.9" zip_keys: - cuda_dep_type - cudatoolkit - libcufft_dep - cudacudart_dep - pytorchcuda_dep - cudatoolkit_dep astra-toolbox-2.3.0/build/conda/release.sh000077500000000000000000000005771475635207100204770ustar00rootroot00000000000000#!/bin/bash set -e D=`mktemp -d` cp build.sh $D V=2.3.0 B=0 podman run --rm -v $D:/out:z astra-build-deb9 /bin/bash /out/build.sh $V $B deb9 full podman run --rm -v $D:/out:z astra-build-deb11 /bin/bash /out/build.sh $V $B deb11 full podman run --rm -v $D:/out:z astra-build-deb12 /bin/bash /out/build.sh $V $B deb12 full rm -f $D/build.sh mkdir -p pkgs mv $D/* pkgs rmdir $D astra-toolbox-2.3.0/build/containers/000077500000000000000000000000001475635207100175705ustar00rootroot00000000000000astra-toolbox-2.3.0/build/containers/podman/000077500000000000000000000000001475635207100210465ustar00rootroot00000000000000astra-toolbox-2.3.0/build/containers/podman/setup_debian11.sh000066400000000000000000000013011475635207100242010ustar00rootroot00000000000000#!/bin/bash set -e . util.sh setup_download $MINICONDA $CUDA111 $CUDA112 $CUDA113 $CUDA114 $CUDA115 $CUDA116 $CUDA117 $CUDA118 $CUDA120 $CUDA121 $CUDA122 ctr=$(buildah from debian:11) setup_base setup_conda setup_cuda $CUDA111 $CUDA112 $CUDA113 $CUDA114 $CUDA115 $CUDA116 $CUDA117 $CUDA118 setup_cache_conda cuda-cudart 11.6 11.7 11.8 setup_cache_conda libcufft 10.7.0 10.7.1 10.7.2 10.9.0 setup_cache_conda cudatoolkit 11.3 11.4 11.5 11.6 11.7 11.8 buildah run $ctr conda create -y -n prep -c nvidia --download-only "cudatoolkit=11.1.*,<11.1.74" buildah run $ctr conda create -y -n prep -c nvidia --download-only "cudatoolkit=11.2.*,<11.2.72" buildah commit $ctr astra-build-deb11 buildah rm $ctr astra-toolbox-2.3.0/build/containers/podman/setup_debian12.sh000066400000000000000000000007201475635207100242060ustar00rootroot00000000000000#!/bin/bash set -e . util.sh setup_download $MINICONDA $CUDA120 $CUDA121 $CUDA122 $CUDA123 $CUDA124 $CUDA125 $CUDA126 $CUDA128 ctr=$(buildah from debian:12) setup_base setup_conda setup_cuda $CUDA120 $CUDA121 $CUDA122 $CUDA123 $CUDA124 $CUDA125 $CUDA126 $CUDA128 setup_cache_conda cuda-cudart 12.0 12.1 12.2 12.3 12.4 12.5 12.6 12.8 setup_cache_conda libcufft 11.0.8 11.0.12 11.2.1 11.2.3 11.3.0 11.3.3 buildah commit $ctr astra-build-deb12 buildah rm $ctr astra-toolbox-2.3.0/build/containers/podman/setup_debian9.sh000066400000000000000000000004241475635207100241350ustar00rootroot00000000000000#!/bin/bash set -e . util.sh setup_download $MINICONDA $CUDA102 $CUDA110 ctr=$(buildah from debian:9) setup_fixup_debian9 setup_base setup_conda setup_cuda $CUDA102 $CUDA110 setup_cache_conda cudatoolkit 10.2 11.0 buildah commit $ctr astra-build-deb9 buildah rm $ctr astra-toolbox-2.3.0/build/containers/podman/setup_manylinux.sh000066400000000000000000000010701475635207100246440ustar00rootroot00000000000000#!/bin/bash set -e . util.sh setup_download $CUDA121 ctr=$(buildah from quay.io/pypa/manylinux2014_x86_64:latest) setup_cuda $CUDA121 buildah run $ctr manylinux-interpreters ensure cp39-cp39 cp310-cp310 cp311-cp311 cp312-cp312 setup_pip_install python3.9 setuptools wheel numpy six Cython scipy setup_pip_install python3.10 setuptools wheel numpy six Cython scipy setup_pip_install python3.11 setuptools wheel numpy six Cython scipy setup_pip_install python3.12 setuptools wheel numpy six Cython scipy buildah commit $ctr astra-build-manylinux buildah rm $ctr astra-toolbox-2.3.0/build/containers/podman/util.sh000066400000000000000000000114651475635207100223660ustar00rootroot00000000000000#!/bin/bash set -e MINICONDA=https://repo.anaconda.com/miniconda/Miniconda3-py312_24.7.1-0-Linux-x86_64.sh CUDA102=https://developer.download.nvidia.com/compute/cuda/10.2/Prod/local_installers/cuda_10.2.89_440.33.01_linux.run CUDA110=https://developer.download.nvidia.com/compute/cuda/11.0.3/local_installers/cuda_11.0.3_450.51.06_linux.run CUDA111=https://developer.download.nvidia.com/compute/cuda/11.1.1/local_installers/cuda_11.1.1_455.32.00_linux.run CUDA112=https://developer.download.nvidia.com/compute/cuda/11.2.2/local_installers/cuda_11.2.2_460.32.03_linux.run CUDA113=https://developer.download.nvidia.com/compute/cuda/11.3.1/local_installers/cuda_11.3.1_465.19.01_linux.run #CUDA114=https://developer.download.nvidia.com/compute/cuda/11.4.1/local_installers/cuda_11.4.1_470.57.02_linux.run CUDA114=https://developer.download.nvidia.com/compute/cuda/11.4.4/local_installers/cuda_11.4.4_470.82.01_linux.run #CUDA115=https://developer.download.nvidia.com/compute/cuda/11.5.1/local_installers/cuda_11.5.1_495.29.05_linux.run CUDA115=https://developer.download.nvidia.com/compute/cuda/11.5.2/local_installers/cuda_11.5.2_495.29.05_linux.run #CUDA116=https://developer.download.nvidia.com/compute/cuda/11.6.0/local_installers/cuda_11.6.0_510.39.01_linux.run CUDA116=https://developer.download.nvidia.com/compute/cuda/11.6.2/local_installers/cuda_11.6.2_510.47.03_linux.run #CUDA117=https://developer.download.nvidia.com/compute/cuda/11.7.0/local_installers/cuda_11.7.0_515.43.04_linux.run CUDA117=https://developer.download.nvidia.com/compute/cuda/11.7.1/local_installers/cuda_11.7.1_515.65.01_linux.run CUDA118=https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda_11.8.0_520.61.05_linux.run CUDA120=https://developer.download.nvidia.com/compute/cuda/12.0.1/local_installers/cuda_12.0.1_525.85.12_linux.run CUDA121=https://developer.download.nvidia.com/compute/cuda/12.1.1/local_installers/cuda_12.1.1_530.30.02_linux.run #CUDA122=https://developer.download.nvidia.com/compute/cuda/12.2.0/local_installers/cuda_12.2.0_535.54.03_linux.run CUDA122=https://developer.download.nvidia.com/compute/cuda/12.2.2/local_installers/cuda_12.2.2_535.104.05_linux.run CUDA123=https://developer.download.nvidia.com/compute/cuda/12.3.2/local_installers/cuda_12.3.2_545.23.08_linux.run CUDA124=https://developer.download.nvidia.com/compute/cuda/12.4.1/local_installers/cuda_12.4.1_550.54.15_linux.run #CUDA125=https://developer.download.nvidia.com/compute/cuda/12.5.0/local_installers/cuda_12.5.0_555.42.02_linux.run CUDA125=https://developer.download.nvidia.com/compute/cuda/12.5.1/local_installers/cuda_12.5.1_555.42.06_linux.run #CUDA126=https://developer.download.nvidia.com/compute/cuda/12.6.0/local_installers/cuda_12.6.0_560.28.03_linux.run #CUDA126=https://developer.download.nvidia.com/compute/cuda/12.6.1/local_installers/cuda_12.6.1_560.35.03_linux.run #CUDA126=https://developer.download.nvidia.com/compute/cuda/12.6.2/local_installers/cuda_12.6.2_560.35.03_linux.run CUDA126=https://developer.download.nvidia.com/compute/cuda/12.6.3/local_installers/cuda_12.6.3_560.35.05_linux.run CUDA128=https://developer.download.nvidia.com/compute/cuda/12.8.0/local_installers/cuda_12.8.0_570.86.10_linux.run setup_download() { mkdir -p sw chcon unconfined_u:object_r:container_file_t:s0 sw || /bin/true for F in $@; do [ -f sw/`basename $F` ] || (cd sw; wget $F) chcon unconfined_u:object_r:container_file_t:s0 sw/`basename $F` || /bin/true done } setup_fixup_debian9() { buildah run $ctr sed -i 's|deb.debian.org|archive.debian.org|g' /etc/apt/sources.list buildah run $ctr sed -i 's|security.debian.org|archive.debian.org|g' /etc/apt/sources.list buildah run $ctr sed -i '/stretch-updates/d' /etc/apt/sources.list } setup_base() { echo Setting up $ctr buildah config --env DEBIAN_FRONTEND=noninteractive $ctr buildah run $ctr apt-get update buildah run $ctr apt-get install -y perl-modules build-essential autoconf libtool automake libboost-dev git libxml2 buildah run $ctr apt-get install -y git-lfs || /bin/true } setup_conda() { echo Installing $(basename $MINICONDA) buildah run --volume `pwd`/sw:/sw:ro,z $ctr bash /sw/$(basename $MINICONDA) -b buildah config --env PATH=/root/miniconda3/bin:$(buildah run $ctr printenv PATH) $ctr buildah run $ctr conda install -y conda-build conda-verify buildah run $ctr conda config --set solver libmamba } setup_cuda() { for C in $@; do echo Installing $(basename $C) buildah run --volume `pwd`/sw:/sw:ro,z $ctr bash /sw/$(basename $C) --toolkit --silent done buildah run $ctr rm -f /usr/local/cuda } setup_cache_conda() { N=$1 shift for C in $@; do buildah run $ctr conda create -y -n prep -c nvidia --download-only $N=$C done } setup_pip_install() { PYTHON=$1 shift buildah run $ctr $PYTHON -m pip install --upgrade pip buildah run $ctr $PYTHON -m pip install $@ } astra-toolbox-2.3.0/build/containers/windows/000077500000000000000000000000001475635207100212625ustar00rootroot00000000000000astra-toolbox-2.3.0/build/containers/windows/provision-boost.ps1000066400000000000000000000004101475635207100250560ustar00rootroot00000000000000#curl.exe -L -o boost.exe https://sourceforge.net/projects/boost/files/boost-binaries/1.78.0/boost_1_78_0-msvc-14.1-64.exe/download #.\boost.exe /sp- /verysilent /suppressmsgboxes /norestart | more #del boost.exe choco install -y boost-msvc-14.1 --version=1.74.0 astra-toolbox-2.3.0/build/containers/windows/provision-buildtools.ps1000066400000000000000000000006561475635207100261240ustar00rootroot00000000000000choco install -y visualstudio2017community choco install -y visualstudio2017buildtools choco install -y visualstudio2017-workload-nativedesktop choco install -y visualstudio2017-workload-python choco install -y git choco install -y curl choco install -y unzip choco install -y windows-sdk-11-version-22H2-all --version=10.0.22621.2 curl.exe -L -o C:\Users\vagrant\vc_redist.x64.exe https://aka.ms/vs/17/release/vc_redist.x64.exe astra-toolbox-2.3.0/build/containers/windows/provision-chocolatey.ps1000066400000000000000000000001651475635207100260710ustar00rootroot00000000000000Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) astra-toolbox-2.3.0/build/containers/windows/provision-cuda.ps1000066400000000000000000000051241475635207100246530ustar00rootroot00000000000000$c = @( #'https://developer.download.nvidia.com/compute/cuda/10.2/Prod/local_installers/cuda_10.2.89_441.22_win10.exe' #'https://developer.download.nvidia.com/compute/cuda/10.2/Prod/patches/1/cuda_10.2.1_win10.exe' #'https://developer.download.nvidia.com/compute/cuda/10.2/Prod/patches/2/cuda_10.2.2_win10.exe' #'https://developer.download.nvidia.com/compute/cuda/11.0.3/local_installers/cuda_11.0.3_451.82_win10.exe' #'https://developer.download.nvidia.com/compute/cuda/11.1.1/local_installers/cuda_11.1.1_456.81_win10.exe' #'https://developer.download.nvidia.com/compute/cuda/11.2.2/local_installers/cuda_11.2.2_461.33_win10.exe' 'https://developer.download.nvidia.com/compute/cuda/11.3.1/local_installers/cuda_11.3.1_465.89_win10.exe' #'https://developer.download.nvidia.com/compute/cuda/11.4.4/local_installers/cuda_11.4.4_472.50_windows.exe' #'https://developer.download.nvidia.com/compute/cuda/11.5.2/local_installers/cuda_11.5.2_496.13_windows.exe' #'https://developer.download.nvidia.com/compute/cuda/11.6.2/local_installers/cuda_11.6.2_511.65_windows.exe' #'https://developer.download.nvidia.com/compute/cuda/11.7.1/local_installers/cuda_11.7.1_516.94_windows.exe' 'https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda_11.8.0_522.06_windows.exe' #'https://developer.download.nvidia.com/compute/cuda/12.0.1/local_installers/cuda_12.0.1_528.33_windows.exe' 'https://developer.download.nvidia.com/compute/cuda/12.1.1/local_installers/cuda_12.1.1_531.14_windows.exe' #'https://developer.download.nvidia.com/compute/cuda/12.2.2/local_installers/cuda_12.2.2_537.13_windows.exe' #'https://developer.download.nvidia.com/compute/cuda/12.3.2/local_installers/cuda_12.3.2_546.12_windows.exe' 'https://developer.download.nvidia.com/compute/cuda/12.4.1/local_installers/cuda_12.4.1_551.78_windows.exe' #'https://developer.download.nvidia.com/compute/cuda/12.5.1/local_installers/cuda_12.5.1_555.85_windows.exe' #'https://developer.download.nvidia.com/compute/cuda/12.6.1/local_installers/cuda_12.6.1_560.94_windows.exe' #'https://developer.download.nvidia.com/compute/cuda/12.6.2/local_installers/cuda_12.6.2_560.94_windows.exe' #'https://developer.download.nvidia.com/compute/cuda/12.6.3/local_installers/cuda_12.6.3_561.17_windows.exe' 'https://developer.download.nvidia.com/compute/cuda/12.8.0/local_installers/cuda_12.8.0_571.96_windows.exe' ) $c | %{ $fn = ([uri] $_).Segments[-1] $swfn = 'C:\vagrant\sw\' + $fn if (Test-Path $swfn) { echo ('Using local ' + $fn) copy $swfn cuda.exe } else { echo ('Downloading ' + $fn) curl.exe -L -o cuda.exe $_ } .\cuda.exe -s | more del cuda.exe } astra-toolbox-2.3.0/build/containers/windows/provision-python.ps1000066400000000000000000000003751475635207100252630ustar00rootroot00000000000000choco install -y python3 --version=3.12.2 choco install -y miniconda3 C:\python312\python -m pip install numpy scipy six cython setuptools C:\tools\miniconda3\shell\condabin\conda-hook.ps1 conda activate base conda install -y conda-build conda-verify astra-toolbox-2.3.0/build/linux/000077500000000000000000000000001475635207100165625ustar00rootroot00000000000000astra-toolbox-2.3.0/build/linux/Makefile.in000066400000000000000000000456121475635207100206370ustar00rootroot00000000000000install_type=@INSTALL_TYPE@ cuda=@HAVECUDA@ matlab=@HAVEMATLAB@ python=@HAVEPYTHON@ boostutf=@HAVEBOOSTUTF@ macos=@IS_MACOS@ MATLAB_ROOT=@MATLAB_ROOT@ octave=@HAVEOCTAVE@ MKDIR=mkdir -p CXX=@CXX@ LD=@CXX@ SHELL=@SHELL@ INSTALL_SH=$(SHELL) $(srcdir)/install-sh TARGETS=libastra.la ifeq ($(matlab),yes) TARGETS+=mex endif ifeq ($(python),yes) TARGETS+=py endif ifeq ($(octave),yes) TARGETS+=oct endif all: $(TARGETS) prefix=@prefix@ exec_prefix=@exec_prefix@ datarootdir=@datarootdir@ includedir=@includedir@ libdir=@libdir@ srcdir=@srcdir@ abs_top_builddir=@abs_top_builddir@ VPATH=@VPATH_SRCDIR@/../.. CPPFLAGS=@SAVED_CPPFLAGS@ CXXFLAGS=@SAVED_CXXFLAGS@ NVCCFLAGS=@SAVED_NVCCFLAGS@ LDFLAGS=@SAVED_LDFLAGS@ LIBS=@SAVED_LIBS@ CXXFLAGS+=-g -O3 -Wall --std=c++17 LDFLAGS+=-g CXXFLAGS+=@CXXFLAGS_OS@ LDFLAGS+=@LDFLAGS_OS@ BOOSTUTF_LIBS=@LIBS_BOOSTUTF@ ifeq ($(cuda),yes) CPPFLAGS += @CPPFLAGS_CUDA@ -DASTRA_CUDA NVCCFLAGS += @NVCCFLAGS_EXTRA@ @CPPFLAGS_CUDA@ -I$(srcdir)/../../include -DASTRA_CUDA --std c++17 LDFLAGS += @LDFLAGS_CUDA@ LIBS += -lcudart -lcufft NVCC = @NVCC@ endif ifeq ($(matlab),yes) # TODO: Do we also want -fopenmp for octave? CPPFLAGS+=-I$(MATLAB_ROOT)/extern/include -DMATLAB_MEX_FILE @CPPFLAGS_MATLAB@ ifeq ($(macos),no) CXXFLAGS+=-fopenmp LDFLAGS+=-fopenmp endif endif # MODLDFLAGS are the base LDFLAGS for matlab, octave, python modules MODLDFLAGS=$(LDFLAGS) -L$(abs_top_builddir)/.libs ifeq ($(install_type),module) MODLDFLAGS+=-Wl,-rpath,'\$$ORIGIN' endif ifeq ($(python),yes) PYTHON = @PYTHON@ PYINCDIR := $(shell $(PYTHON) -c 'from __future__ import print_function; from distutils.sysconfig import get_python_inc; print(get_python_inc())') PYLIBDIR := $(shell $(PYTHON) -c 'from __future__ import print_function; from distutils.sysconfig import get_config_var; print(get_config_var("LIBDIR"))') PYLIBVER = `basename $(PYINCDIR)` CPPFLAGS += -DASTRA_PYTHON -I$(PYINCDIR) PYCPPFLAGS := $(CPPFLAGS) PYCPPFLAGS += -I../include -I../lib/include PYCXXFLAGS := $(CXXFLAGS) # copy the current MODLDFLAGS to PYLDFLAGS, and then add flags for matlab/octave PYLDFLAGS := $(MODLDFLAGS) MODLDFLAGS += -L$(PYLIBDIR) MODPYLIBS=-l$(PYLIBVER) PYBUILDERENV=CXX="${CXX}" CC="${CXX}" CPPFLAGS="${PYCPPFLAGS}" LDFLAGS='${PYLDFLAGS}' CXXFLAGS="${PYCXXFLAGS}" CFLAGS="${PYCXXFLAGS}" PYBUILDERFLAGS=--astra_build_config_dir $(abs_top_builddir)/python/config --astra_build_cython_dir $(abs_top_builddir)/python/cython PYBUILDER_BUILD_FLAGS=--build-base=$(abs_top_builddir)/python/build PYTHON_WHEEL_PLATFORM = @PYTHON_WHEEL_PLATFORM@ endif # This is below where PYCPPFLAGS copies CPPFLAGS. The python code is built # from a different directory, so these relative includes would be wrong. CPPFLAGS+=-I$(srcdir)/../../include -I$(srcdir)/../../lib/include # Also enable -Wshadow except for the python extensions, since certain cython # versions generate code with much shadowing. CXXFLAGS += -Wshadow ifeq ($(matlab),yes) MEXFLAGS = @MEXFLAGS@ -cxx MEXLDFLAGS=\$$LDFLAGS $(MODLDFLAGS) MEXLIBS = -lut MEXSUFFIX = @MEXSUFFIX@ MEX = @MEX@ ifeq ($(cuda),yes) MEXFLAGS += -DASTRA_CUDA endif endif ifeq ($(octave),yes) OCTLDFLAGS:=$(MODLDFLAGS) CPPFLAGS += @OCTAVE_CPPFLAGS@ ifeq ($(cuda),yes) OCTFLAGS=-DASTRA_CUDA else OCTFLAGS= endif endif DEPDIR=.deps BASE_OBJECTS=\ src/Algorithm.lo \ src/ReconstructionAlgorithm2D.lo \ src/ReconstructionAlgorithm3D.lo \ src/ArtAlgorithm.lo \ src/AstraObjectFactory.lo \ src/AstraObjectManager.lo \ src/BackProjectionAlgorithm.lo \ src/CglsAlgorithm.lo \ src/CompositeGeometryManager.lo \ src/ConeProjectionGeometry3D.lo \ src/ConeVecProjectionGeometry3D.lo \ src/Config.lo \ src/Data3D.lo \ src/DataProjector.lo \ src/DataProjectorPolicies.lo \ src/FanFlatBeamLineKernelProjector2D.lo \ src/FanFlatBeamStripKernelProjector2D.lo \ src/FanFlatProjectionGeometry2D.lo \ src/FanFlatVecProjectionGeometry2D.lo \ src/Features.lo \ src/FilteredBackProjectionAlgorithm.lo \ src/Filters.lo \ src/Float32Data.lo \ src/Float32Data2D.lo \ src/Float32ProjectionData2D.lo \ src/Float32VolumeData2D.lo \ src/ForwardProjectionAlgorithm.lo \ src/Fourier.lo \ src/GeometryUtil2D.lo \ src/GeometryUtil3D.lo \ src/Globals.lo \ src/Logging.lo \ src/ParallelBeamBlobKernelProjector2D.lo \ src/ParallelBeamDistanceDrivenProjector2D.lo \ src/ParallelBeamLinearKernelProjector2D.lo \ src/ParallelBeamLineKernelProjector2D.lo \ src/ParallelBeamStripKernelProjector2D.lo \ src/ParallelProjectionGeometry2D.lo \ src/ParallelVecProjectionGeometry2D.lo \ src/ParallelProjectionGeometry3D.lo \ src/ParallelVecProjectionGeometry3D.lo \ src/PlatformDepSystemCode.lo \ src/PluginAlgorithmFactory.lo \ src/ProjectionGeometry2D.lo \ src/ProjectionGeometry2DFactory.lo \ src/ProjectionGeometry3D.lo \ src/ProjectionGeometry3DFactory.lo \ src/Projector2D.lo \ src/Projector3D.lo \ src/SartAlgorithm.lo \ src/SheppLogan.lo \ src/SirtAlgorithm.lo \ src/SparseMatrixProjectionGeometry2D.lo \ src/SparseMatrixProjector2D.lo \ src/SparseMatrix.lo \ src/Utilities.lo \ src/VolumeGeometry2D.lo \ src/VolumeGeometry3D.lo \ src/XMLConfig.lo \ src/XMLDocument.lo \ src/XMLNode.lo CUDA_CXX_OBJECTS=\ src/CudaProjector2D.lo \ src/CudaProjector3D.lo \ src/CudaReconstructionAlgorithm2D.lo \ src/CudaBackProjectionAlgorithm.lo \ src/CudaDartMaskAlgorithm.lo \ src/CudaDartMaskAlgorithm3D.lo \ src/CudaDataOperationAlgorithm.lo \ src/CudaRoiSelectAlgorithm.lo \ src/CudaDartSmoothingAlgorithm.lo \ src/CudaDartSmoothingAlgorithm3D.lo \ src/CudaFilteredBackProjectionAlgorithm.lo \ src/CudaForwardProjectionAlgorithm.lo \ src/CudaSartAlgorithm.lo \ src/CudaSirtAlgorithm.lo \ src/CudaCglsAlgorithm.lo \ src/CudaCglsAlgorithm3D.lo \ src/CudaEMAlgorithm.lo \ src/CudaFDKAlgorithm3D.lo \ src/CudaSirtAlgorithm3D.lo \ src/CudaBackProjectionAlgorithm3D.lo \ src/CudaForwardProjectionAlgorithm3D.lo CUDA_OBJECTS=\ cuda/2d/algo.lo \ cuda/2d/par_fp.lo \ cuda/2d/par_bp.lo \ cuda/2d/fan_fp.lo \ cuda/2d/fan_bp.lo \ cuda/2d/fbp.lo \ cuda/2d/sirt.lo \ cuda/2d/sart.lo \ cuda/2d/cgls.lo \ cuda/2d/em.lo \ cuda/2d/astra.lo \ cuda/2d/util.lo \ cuda/2d/arith.lo \ cuda/2d/fft.lo \ cuda/2d/darthelper.lo \ cuda/3d/darthelper3d.lo \ cuda/3d/algo3d.lo \ cuda/3d/cgls3d.lo \ cuda/3d/cone_fp.lo \ cuda/3d/cone_bp.lo \ cuda/3d/fdk.lo \ cuda/3d/par3d_fp.lo \ cuda/3d/par3d_bp.lo \ cuda/3d/sirt3d.lo \ cuda/3d/astra3d.lo \ cuda/3d/util3d.lo \ cuda/3d/arith3d.lo \ cuda/3d/mem3d.lo ALL_OBJECTS=$(BASE_OBJECTS) ifeq ($(cuda),yes) ALL_OBJECTS+=$(CUDA_CXX_OBJECTS) $(CUDA_OBJECTS) endif TEST_OBJECTS=\ tests/main.o \ tests/test_AstraObjectManager.o \ tests/test_Float32Data2D.o \ tests/test_VolumeGeometry2D.o \ tests/test_ParallelProjectionGeometry2D.o \ tests/test_FanFlatProjectionGeometry2D.o \ tests/test_Float32VolumeData2D.o \ tests/test_Float32ProjectionData2D.o \ tests/test_Fourier.o \ tests/test_XMLDocument.o MATLAB_CXX_OBJECTS=\ matlab/mex/mexHelpFunctions.o \ matlab/mex/mexCopyDataHelpFunctions.o \ matlab/mex/mexInitFunctions.o \ matlab/mex/mexDataManagerHelpFunctions.o MATLAB_MEX=\ matlab/mex/astra_mex_algorithm_c.$(MEXSUFFIX) \ matlab/mex/astra_mex_data2d_c.$(MEXSUFFIX) \ matlab/mex/astra_mex_c.$(MEXSUFFIX) \ matlab/mex/astra_mex_matrix_c.$(MEXSUFFIX) \ matlab/mex/astra_mex_projector_c.$(MEXSUFFIX) \ matlab/mex/astra_mex_projector3d_c.$(MEXSUFFIX) \ matlab/mex/astra_mex_log_c.$(MEXSUFFIX) \ matlab/mex/astra_mex_data3d_c.$(MEXSUFFIX) \ matlab/mex/astra_mex_direct_c.$(MEXSUFFIX) OCTAVE_CXX_OBJECTS=\ matlab/mex/octave_support.o OCTAVE_MEX=\ matlab/mex/astra_mex_algorithm_c.mex \ matlab/mex/astra_mex_data2d_c.mex \ matlab/mex/astra_mex_c.mex \ matlab/mex/astra_mex_matrix_c.mex \ matlab/mex/astra_mex_projector_c.mex \ matlab/mex/astra_mex_projector3d_c.mex \ matlab/mex/astra_mex_log_c.mex \ matlab/mex/astra_mex_data3d_c.mex \ matlab/mex/astra_mex_direct_c.mex ifeq ($(python),yes) MATLAB_MEX+=matlab/mex/astra_mex_plugin_c.$(MEXSUFFIX) OCTAVE_MEX+=matlab/mex/astra_mex_plugin_c.mex endif OBJECT_DIRS = src/ tests/ cuda/2d/ cuda/3d/ matlab/mex/ ./ DEPDIRS = $(addsuffix $(DEPDIR),$(OBJECT_DIRS)) -include $(wildcard $(addsuffix /*.d,$(DEPDIRS))) LIBDIRS = $(addsuffix .libs,./ src/ cuda/2d/ cuda/3d/) SONAME=$(shell . ${abs_top_builddir}/libastra.la; echo $$dlname) ifeq ($(matlab),yes) mex: $(MATLAB_MEX) %.$(MEXSUFFIX): %.o $(MATLAB_CXX_OBJECTS) libastra.la $(MEX) LDFLAGS="$(MEXLDFLAGS)" $(MEXFLAGS) $(LIBS) $(MEXLIBS) -lastra -output $* $*.o $(MATLAB_CXX_OBJECTS) ifeq ($(install_type),module) ifeq ($(macos),yes) @# tell macOS dynamic loader to look in mex directory for libastra.0.dylib @# CHECKME: some versions of otool return a two-line output for otool -DX? @# (xcode 8.2.1 / macos 10.11.6 ?) install_name_tool -change `otool -DX .libs/$(SONAME) | tail -n 1` @loader_path/$(SONAME) $@ endif endif ifeq ($(python),yes) matlab/mex/astra_mex_plugin_c.$(MEXSUFFIX): matlab/mex/astra_mex_plugin_c.o $(MATLAB_CXX_OBJECTS) libastra.la $(MEX) LDFLAGS="$(MEXLDFLAGS)" $(MEXFLAGS) $(LIBS) $(MEXLIBS) $(MODPYLIBS) -lastra -output matlab/mex/astra_mex_plugin_c $< $(MATLAB_CXX_OBJECTS) endif endif ifeq ($(python),yes) py: libastra.la $(MKDIR) python/build $(MKDIR) python/config # Note: setting CC to CXX is intentional. Python uses CC for compilation even if input is C++. cd $(srcdir)/../../python; $(PYBUILDERENV) \ $(PYTHON) builder.py $(PYBUILDERFLAGS) \ build $(PYBUILDER_BUILD_FLAGS) \ $(patsubst %,add_extra_lib --file=%,$(PYPKGDATA)) endif ifeq ($(octave),yes) oct: $(OCTAVE_MEX) %.mex: %.o $(MATLAB_CXX_OBJECTS) $(OCTAVE_CXX_OBJECTS) libastra.la mkoctfile --mex $(OCTFLAGS) $(OCTLDFLAGS) $(LIBS) -lastra --output $* $*.o $(MATLAB_CXX_OBJECTS) $(OCTAVE_CXX_OBJECTS) ifeq ($(python),yes) matlab/mex/astra_mex_plugin_c.mex: matlab/mex/astra_mex_plugin_c.o $(MATLAB_CXX_OBJECTS) $(OCTAVE_CXX_OBJECTS) libastra.la mkoctfile --mex $(OCTFLAGS) $(OCTLDFLAGS) $(LIBS) $(MODPYLIBS) -lastra --output matlab/mex/astra_mex_plugin_c $< $(MATLAB_CXX_OBJECTS) $(OCTAVE_CXX_OBJECTS) endif endif libastra.la: $(ALL_OBJECTS) ./libtool --mode=link --tag=CXX $(LD) -rpath $(libdir) -o $@ $(LDFLAGS) $(LIBS) $+ %.o: %.cpp $(MKDIR) $(*D)/$(DEPDIR) $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) -fPIC -DPIC $(CPPFLAGS) -c $(<) -o $*.o %.lo: %.cpp $(MKDIR) $(*D)/$(DEPDIR) ./libtool --mode=compile --tag=CXX $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o gen_static_libs := `./libtool --features | grep -q 'disable static' && echo no || echo yes` ifeq ($(cuda),yes) %.lo: %.cu @# Behave like libtool: compile both a PIC and a non-PIC object file @$(MKDIR) $(*D) @$(MKDIR) $(*D)/.libs @$(MKDIR) $(*D)/$(DEPDIR) $(NVCC) $(NVCCFLAGS) -c $(<) -Xcompiler -fPIC -DPIC -o $(*D)/.libs/$(*F).o ifeq ($(gen_static_libs),yes) @$(NVCC) $(NVCCFLAGS) -c $(<) -o $*.o >/dev/null 2>&1 endif @# Generate a .d file, and change the target name in it from .o to .lo @# CUDA 5.5's nvcc doesn't have the -MT option we would like to use. @$(NVCC) $(NVCCFLAGS) -M $(<) -odir $(*D) -o $(*D)/$(DEPDIR)/$(*F).d2 @sed '1s/\.o :/.lo :/' < $(*D)/$(DEPDIR)/$(*F).d2 > $(*D)/$(DEPDIR)/$(*F).d @rm -f $(*D)/$(DEPDIR)/$(*F).d2 @# Generate empty targets for all dependencies listed in the .d file. @# This mimics gcc's -MP option. @for x in `cat $(*D)/$(DEPDIR)/$(*F).d`; do if test a$$x != a: -a a$$x != a\\; then (echo; echo "$$x:") >> $(*D)/$(DEPDIR)/$(*F).d; fi; done @# Generate a fake libtool .lo file @echo "# $*.lo - a libtool object file" > $*.lo @echo "# Generated by" `./libtool --version | head -n 1` >> $*.lo @echo "#" >> $*.lo @echo "# Please DO NOT delete this file!" >> $*.lo @echo "# It is necessary for linking the library." >> $*.lo @echo >> $*.lo @echo "# Name of the PIC object." >> $*.lo @echo "pic_object='.libs/$(*F).o'" >> $*.lo @echo >> $*.lo @echo "# Name of the non-PIC object." >> $*.lo ifeq ($(gen_static_libs),yes) @echo "non_pic_object='$(*F).o'" >> $*.lo else @echo "non_pic_object=none" >> $*.lo endif @# Remove generated .linkinfo file @rm -f $(*F).linkinfo endif ifeq ($(boostutf),yes) test.bin: $(ALL_OBJECTS) $(TEST_OBJECTS) ./libtool --mode=link $(LD) -o $@ $(LDFLAGS) $+ $(LIBS) $(BOOSTUTF_LIBS) test: test.bin ./test.bin else test: @echo "Tests have been disabled by configure" endif clean: rm -f $(MATLAB_MEX) libastra.la rm -f $(addsuffix /*.lo,$(OBJECT_DIRS)) rm -f $(addsuffix /*.o,$(OBJECT_DIRS)) rm -f $(addsuffix /*.d,$(DEPDIRS)) rm -f $(addsuffix /*,$(LIBDIRS)) rm -f $(TEST_OBJECTS) test.bin rm -fr python/ distclean: clean rm -f $(srcdir)/config.guess $(srcdir)/config.sub $(srcdir)/ltmain.sh libtool $(srcdir)/install-sh rm -f config.log config.status rm -f $(srcdir)/aclocal.m4 rm -rf $(srcdir)/autom4te.cache rm -f $(srcdir)/configure Makefile install-libraries: libastra.la $(INSTALL_SH) -m 755 -d $(DESTDIR)$(libdir) ./libtool --mode=install $(INSTALL_SH) -m 644 libastra.la $(DESTDIR)$(libdir) ./libtool --mode=finish $(DESTDIR)$(libdir) # ------------------------ # INSTALLATION # ------------------------ ifeq ($(install_type),prefix) # libraries into @libdir@, python into site-packages, mex into @datadir@ install: install-libraries install-matlab install-python-site-packages install-octave PYPKGDATA= MATLABBASE=@datadir@/astra/matlab OCTAVEBASE=@datadir@/astra/octave endif ifeq ($(install_type),dir) # everything into @prefix@ install: install-libraries install-matlab install-python install-octave PYPKGDATA= MATLABBASE=@prefix@/matlab OCTAVEBASE=@prefix@/octave PYTHONBASE=@prefix@/python endif ifeq ($(install_type),module) # python into site-packages, mex into @datadir@ # library copied into python/mex directories # modules built with rpath=$ORIGIN install: install-matlab install-python-site-packages install-octave PYPKGDATA=$(abs_top_builddir)/.libs/$(SONAME) MATLABBASE=@prefix@/matlab OCTAVEBASE=@prefix@/octave install-matlab-so: libastra.la $(INSTALL_SH) -m 755 -d $(DESTDIR)$(MATLABBASE)/mex $(INSTALL_SH) -m 755 $(abs_top_builddir)/.libs/$(SONAME) $(DESTDIR)$(MATLABBASE)/mex install-octave-so: libastra.la $(INSTALL_SH) -m 755 -d $(DESTDIR)$(OCTAVEBASE)/mex $(INSTALL_SH) -m 755 $(abs_top_builddir)/.libs/$(SONAME) $(DESTDIR)$(OCTAVEBASE)/mex else install-matlab-so: install-octave-so: endif ifeq ($(python),yes) install-python: py # Note: setting CC to CXX is intentional. Python uses CC for compilation even if input is C++. cd $(srcdir)/../../python; $(PYBUILDERENV) \ $(PYTHON) builder.py $(PYBUILDERFLAGS) \ set_dist_dir --dist-dir=$(abs_top_builddir)/python/dist \ build $(PYBUILDER_BUILD_FLAGS) \ egg_info --egg-base=$(abs_top_builddir)/python \ $(patsubst %,add_extra_lib --file=%,$(PYPKGDATA)) \ install $(patsubst %,--root=%,$(DESTDIR)) --install-base=@prefix@/python --install-headers=@prefix@/python --install-purelib=@prefix@/python --install-platlib=@prefix@/python --install-scripts=@prefix@/python --install-data=@prefix@/python --old-and-unmanageable --no-compile # TODO: docs install-python-site-packages: py # Note: setting CC to CXX is intentional. Python uses CC for compilation even if input is C++. cd $(srcdir)/../../python; $(PYBUILDERENV) \ $(PYTHON) builder.py $(PYBUILDERFLAGS) \ set_dist_dir --dist-dir=$(abs_top_builddir)/python/dist \ build $(PYBUILDER_BUILD_FLAGS) \ egg_info --egg-base=$(abs_top_builddir)/python \ $(patsubst %,add_extra_lib --file=%,$(PYPKGDATA)) \ install $(patsubst %,--root=% --single-version-externally-managed,$(DESTDIR)) python-wheel: py # Note: setting CC to CXX is intentional. Python uses CC for compilation even if input is C++. cd $(srcdir)/../../python; $(PYBUILDERENV) \ $(PYTHON) builder.py $(PYBUILDERFLAGS) --astra_cuda_dependencies \ set_dist_dir --dist-dir=$(abs_top_builddir)/python/dist \ build $(PYBUILDER_BUILD_FLAGS) \ egg_info --egg-base=$(abs_top_builddir)/python \ $(patsubst %,add_extra_lib --file=%,$(PYPKGDATA)) \ bdist_wheel $(patsubst %,--plat-name %,$(PYTHON_WHEEL_PLATFORM)) else install-python-site-packages: install-python: endif ifeq ($(matlab),yes) install-matlab: $(MATLAB_MEX) install-matlab-so $(INSTALL_SH) -m 755 -d $(DESTDIR)$(MATLABBASE) $(INSTALL_SH) -m 755 -d $(DESTDIR)$(MATLABBASE)/mex $(INSTALL_SH) -m 755 -d $(DESTDIR)$(MATLABBASE)/tools $(INSTALL_SH) -m 755 -d $(DESTDIR)$(MATLABBASE)/algorithms $(INSTALL_SH) -m 755 $(MATLAB_MEX) $(DESTDIR)$(MATLABBASE)/mex $(INSTALL_SH) -m 644 $(srcdir)/../../matlab/tools/*.m $(DESTDIR)$(MATLABBASE)/tools for D in DART DART/tools DART/examples plot_geom plot_geom/private plot_geom/+draw plot_geom/+draw/private plot_geom/+parseargs plot_geom/+stlTools; do \ $(INSTALL_SH) -m 755 -d $(DESTDIR)$(MATLABBASE)/algorithms/$$D; \ $(INSTALL_SH) -m 644 $(srcdir)/../../matlab/algorithms/$$D/*.m $(DESTDIR)$(MATLABBASE)/algorithms/$$D; \ done # TODO: docs else install-matlab: endif ifeq ($(octave),yes) install-octave: $(OCTAVE_MEX) install-octave-so $(INSTALL_SH) -m 755 -d $(DESTDIR)$(OCTAVEBASE) $(INSTALL_SH) -m 755 -d $(DESTDIR)$(OCTAVEBASE)/mex $(INSTALL_SH) -m 755 -d $(DESTDIR)$(OCTAVEBASE)/tools $(INSTALL_SH) -m 755 $(OCTAVE_MEX) $(DESTDIR)$(OCTAVEBASE)/mex $(INSTALL_SH) -m 644 $(srcdir)/../../matlab/tools/*.m $(DESTDIR)$(OCTAVEBASE)/tools # TODO: docs else install-octave: endif ifeq ($(install_type),module) install-dev: @echo "install-dev is not compatible with the 'module' install type." @echo "Please use 'prefix' or 'dir' instead." install-headers: @echo "install-headers is not compatible with the 'module' install type." @echo "Please use 'prefix' or 'dir' instead." else install-headers: $(INSTALL_SH) -m 755 -d $(DESTDIR)$(includedir) $(INSTALL_SH) -m 755 -d $(DESTDIR)$(includedir)/astra $(INSTALL_SH) -m 755 -d $(DESTDIR)$(includedir)/astra/cuda $(INSTALL_SH) -m 755 -d $(DESTDIR)$(includedir)/astra/cuda/2d $(INSTALL_SH) -m 755 -d $(DESTDIR)$(includedir)/astra/cuda/3d $(INSTALL_SH) -m 644 $(srcdir)/../../include/astra/*.h $(DESTDIR)$(includedir)/astra $(INSTALL_SH) -m 644 $(srcdir)/../../include/astra/*.inl $(DESTDIR)$(includedir)/astra $(INSTALL_SH) -m 644 $(srcdir)/../../include/astra/cuda/2d/*.h $(DESTDIR)$(includedir)/astra/cuda/2d $(INSTALL_SH) -m 644 $(srcdir)/../../include/astra/cuda/3d/*.h $(DESTDIR)$(includedir)/astra/cuda/3d install-dev: install-libraries install-headers libastra.pc $(INSTALL_SH) -m 755 -d $(DESTDIR)$(libdir) $(INSTALL_SH) -m 755 -d $(DESTDIR)$(libdir)/pkgconfig/ $(INSTALL_SH) -m 644 libastra.pc $(DESTDIR)$(libdir)/pkgconfig/ endif Makefile libastra.pc: %: $(srcdir)/%.in config.status CONFIG_HEADERS= CONFIG_LINKS= CONFIG_FILES=$@ $(SHELL) ./config.status config.status: $(srcdir)/configure @echo "configure script has changed. Re-running it with last parameters" $(SHELL) ./config.status --recheck $(srcdir)/configure: $(srcdir)/configure.ac @echo "configure.ac has been changed. Regenerating configure script" cd $(srcdir) && $(SHELL) ./autogen.sh .PHONY: all mex test clean distclean install install-libraries py install-python-site-packages install-python install-matlab install-octave install-matlab-so install-octave-so install-headers install-dev python-wheel # don't remove intermediate files: .SECONDARY: # disable all implicit built-in rules .SUFFIXES: astra-toolbox-2.3.0/build/linux/README.txt000066400000000000000000000015441475635207100202640ustar00rootroot00000000000000Requirements: g++, boost, CUDA (driver+toolkit), Matlab and/or Python (2.7 or 3.x) cd build/linux ./autogen.sh # when building a git version ./configure --with-cuda=/usr/local/cuda \ --with-matlab=/usr/local/MATLAB/R2012a \ --with-python \ --prefix=/usr/local/astra make make install Add /usr/local/astra/lib to your LD_LIBRARY_PATH. Add /usr/local/astra/matlab and its subdirectories (tools, mex) to your matlab path. Add /usr/local/astra/python to your PYTHONPATH. NB: Each matlab version only supports a specific range of g++ versions. Despite this, if you have a newer g++ and if you get errors related to missing GLIBCXX_3.4.xx symbols, it is often possible to work around this requirement by deleting the version of libstdc++ supplied by matlab in MATLAB_PATH/bin/glnx86 or MATLAB_PATH/bin/glnxa64 (at your own risk). astra-toolbox-2.3.0/build/linux/acinclude.m4000066400000000000000000000106101475635207100207510ustar00rootroot00000000000000AC_DEFUN([ASTRA_CHECK_BOOST_UNIT_TEST_FRAMEWORK],[ BOOST_BACKUP_LIBS="$LIBS" LIBS="$LIBS $1" AC_LINK_IFELSE([AC_LANG_SOURCE([ #define BOOST_TEST_DYN_LINK #define BOOST_AUTO_TEST_MAIN #include #include #include ])],[$2],[$3]) LIBS="$BOOST_BACKUP_LIBS" unset BOOST_BACKUP_LIBS ]) dnl ASTRA_CHECK_MEX_SUFFIX(list-of-suffices, variable-to-set) AC_DEFUN([ASTRA_CHECK_MEX_SUFFIX],[ cat >conftest.cc <<_ACEOF extern "C" void mexFunction() { } _ACEOF $CXX -fPIC -c -o conftest.o conftest.cc ASTRA_RUN_LOGOUTPUT([$MEX -cxx -output conftest conftest.o]) for suffix in $1; do if test -f "conftest.$suffix"; then $2="$suffix" rm -f "conftest.$suffix" fi done rm -f conftest.cc conftest.o ]) dnl ASTRA_CHECK_MEX_OPTION(option, mex-suffix, action-if-supported, action-if-not-supported) dnl Check if an option is supported by mex. dnl We test if mex works by testing if it produces a mex file as output; dnl this is required since 'mex' is also a commonly installed LaTeX format AC_DEFUN([ASTRA_CHECK_MEX_OPTION],[ cat >conftest.cc <<_ACEOF extern "C" void mexFunction() { } _ACEOF $CXX -fPIC -c -o conftest.o conftest.cc ASTRA_RUN_LOGOUTPUT([$MEX $1 -cxx -output conftest conftest.o]) AS_IF([test -f "conftest.$2"],[ rm -f "conftest.$2" $3],[$4]) rm -f conftest.cc conftest.o ]) dnl ASTRA_RUN_STOREOUTPUT(command, output) AC_DEFUN([ASTRA_RUN_STOREOUTPUT],[{ AS_ECHO(["$as_me:${as_lineno-$LINENO}: $1"]) >&AS_MESSAGE_LOG_FD ( $1 ) >$2 2>&1 ac_status=$? cat $2 >&AS_MESSAGE_LOG_FD AS_ECHO(["$as_me:${as_lineno-$LINENO}: \$? = $ac_status"]) >&AS_MESSAGE_LOG_FD test $ac_status = 0; }]) dnl ASTRA_RUN_LOGOUTPUT(command) AC_DEFUN([ASTRA_RUN_LOGOUTPUT],[{ AS_ECHO(["$as_me:${as_lineno-$LINENO}: $1"]) >&AS_MESSAGE_LOG_FD ( $1 ) >&AS_MESSAGE_LOG_FD 2>&1 ac_status=$? AS_ECHO(["$as_me:${as_lineno-$LINENO}: \$? = $ac_status"]) >&AS_MESSAGE_LOG_FD test $ac_status = 0; }]) dnl ASTRA_TRY_PYTHON(code, action-if-ok, action-if-not-ok) AC_DEFUN([ASTRA_TRY_PYTHON],[ cat >conftest.py <<_ACEOF $1 _ACEOF ASTRA_RUN_LOGOUTPUT($PYTHON conftest.py) AS_IF([test $? = 0],[$2],[ AS_ECHO(["$as_me: failed program was:"]) >&AS_MESSAGE_LOG_FD sed 's/^/| /' conftest.py >&AS_MESSAGE_LOG_FD $3]) ]) dnl ASTRA_CHECK_NVCC(variable-to-set,cppflags-to-set) AC_DEFUN([ASTRA_CHECK_NVCC],[ cat >conftest.cu <<_ACEOF #include int main() { std::cout << "Test" << std::endl; return 0; } _ACEOF $1="yes" ASTRA_RUN_STOREOUTPUT([$NVCC -c -o conftest.o conftest.cu $NVCCFLAGS $$2],conftest.nvcc.out) || { $1="no" # Check if hack for gcc 4.4 helps if grep -q __builtin_stdarg_start conftest.nvcc.out; then AS_ECHO(["$as_me:${as_lineno-$LINENO}: Trying CUDA hack for gcc 4.4"]) >&AS_MESSAGE_LOG_FD NVCC_OPT="-Xcompiler -D__builtin_stdarg_start=__builtin_va_start" ASTRA_RUN_LOGOUTPUT([$NVCC -c -o conftest.o conftest.cu $NVCCFLAGS $$2 $NVCC_OPT]) && { $1="yes" $2="$$2 $NVCC_OPT" } fi } if test x$$1 = xno; then AS_ECHO(["$as_me: failed program was:"]) >&AS_MESSAGE_LOG_FD sed 's/^/| /' conftest.cu >&AS_MESSAGE_LOG_FD fi rm -f conftest.cu conftest.o conftest.nvcc.out ]) dnl ASTRA_FIND_NVCC_ARCHS(archs-to-try,cppflags-to-extend,output-list) dnl Architectures should be of the form 10,20,30,35, dnl and should be in order. The last accepted one will be used for PTX output. dnl All accepted ones will be used for cubin output. AC_DEFUN([ASTRA_FIND_NVCC_ARCHS],[ cat >conftest.cu <<_ACEOF #include int main() { std::cout << "Test" << std::endl; return 0; } _ACEOF NVCC_extra="" # Add -Wno-deprecated-gpu-targets option if supported ASTRA_RUN_LOGOUTPUT([$NVCC -c -o conftest.o conftest.cu -Wno-deprecated-gpu-targets $NVCCFLAGS $$2]) && { NVCC_extra="-Wno-deprecated-gpu-targets" } NVCC_lastarch="none" NVCC_list="" astra_save_IFS=$IFS IFS=, for arch in $1; do IFS=$astra_save_IFS NVCC_opt="-gencode=arch=compute_$arch,code=sm_$arch" $NVCC -c -o conftest.o conftest.cu $NVCCFLAGS $$2 $NVCC_opt >conftest.nvcc.out 2>&1 && { NVCC_lastarch=$arch NVCC_extra="$NVCC_extra $NVCC_opt" NVCC_list="${NVCC_list:+$NVCC_list, }$arch" } done IFS=$astra_save_IFS if test $NVCC_lastarch != none; then NVCC_extra="$NVCC_extra -gencode=arch=compute_${NVCC_lastarch},code=compute_${NVCC_lastarch}" $3="$NVCC_list" $2="$$2 $NVCC_extra" else $3="none" fi rm -f conftest.cu conftest.o conftest.nvcc.out ]) astra-toolbox-2.3.0/build/linux/autogen.sh000077500000000000000000000013621475635207100205650ustar00rootroot00000000000000#!/usr/bin/env bash aclocal if test $? -ne 0; then echo "Error running aclocal" exit 1 fi autoconf if test $? -ne 0; then echo "Error running autoconf" exit 1 fi case `uname` in Darwin*) test -x "`which glibtoolize 2>/dev/null`" && LIBTOOLIZEBIN=glibtoolize || LIBTOOLIZEBIN=libtoolize ;; *) LIBTOOLIZEBIN=libtoolize ;; esac $LIBTOOLIZEBIN --install --force > /dev/null 2>&1 if test $? -ne 0; then $LIBTOOLIZEBIN --force if test $? -ne 0; then echo "Error running libtoolize" exit 1 fi fi if test ! -e config.guess; then ln -s config.guess.dist config.guess fi if test ! -e config.sub; then ln -s config.sub.dist config.sub fi if test ! -e install-sh; then ln -s install-sh.dist install-sh fi echo "Done." astra-toolbox-2.3.0/build/linux/config.guess.dist000077500000000000000000001303611475635207100220500ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2013 Free Software Foundation, Inc. timestamp='2013-06-10' # This file is free software; you can redistribute it and/or modify it # under the terms of the 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 . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # # Please send patches with a ChangeLog entry to config-patches@gnu.org. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; or1k:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; or32:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: astra-toolbox-2.3.0/build/linux/config.sub.dist000077500000000000000000001053701475635207100215150ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2013 Free Software Foundation, Inc. timestamp='2013-08-10' # This file is free software; you can redistribute it and/or modify it # under the terms of the 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 . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches with a ChangeLog entry to config-patches@gnu.org. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 \ | or1k | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-* | ppc64p7-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or1k-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: astra-toolbox-2.3.0/build/linux/configure.ac000066400000000000000000000244631475635207100210610ustar00rootroot00000000000000dnl ----------------------------------------------------------------------- dnl Copyright: 2010-2022, imec Vision Lab, University of Antwerp dnl 2014-2022, CWI, Amsterdam dnl dnl Contact: astra@astra-toolbox.com dnl Website: http://www.astra-toolbox.com/ dnl dnl This file is part of the ASTRA Toolbox. dnl dnl dnl The ASTRA Toolbox is free software: you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation, either version 3 of the License, or dnl (at your option) any later version. dnl dnl The ASTRA Toolbox is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with the ASTRA Toolbox. If not, see . dnl dnl ----------------------------------------------------------------------- AC_INIT([astra],[2.3.0]) AC_CONFIG_SRCDIR([Makefile.in]) LT_INIT([disable-static]) SAVED_CPPFLAGS="$CPPFLAGS" SAVED_CXXFLAGS="$CXXFLAGS" SAVED_NVCCFLAGS="$NVCCFLAGS" SAVED_LDFLAGS="$LDFLAGS" SAVED_LIBS="$LIBS" AC_CANONICAL_BUILD AC_CANONICAL_HOST AC_PROG_CC AC_PROG_CXX AC_PROG_INSTALL AC_PROG_MAKE_SET AC_LANG([C++]) dnl Use iostream to check if the C++ compiler works AC_CHECK_HEADER(iostream, , AC_MSG_ERROR([No working c++ compiler found])) # boost-unit-test-framework AC_MSG_CHECKING([for boost-unit-test-framework]) ASTRA_CHECK_BOOST_UNIT_TEST_FRAMEWORK(-lboost_unit_test_framework-mt, BOOSTUTF=yes_mt, BOOSTUTF=no) HAVEBOOSTUTF=no if test x$BOOSTUTF = xno; then ASTRA_CHECK_BOOST_UNIT_TEST_FRAMEWORK(-lboost_unit_test_framework, BOOSTUTF=yes, BOOSTUTF=no) if test x$BOOSTUTF = xno; then AC_MSG_RESULT(no) else AC_MSG_RESULT([yes, libboost_unit_test_framework]) LIBS_BOOSTUTF="-lboost_unit_test_framework" HAVEBOOSTUTF=yes fi else AC_MSG_RESULT([yes, libboost_unit_test_framework-mt]) LIBS_BOOSTUTF="-lboost_unit_test_framework-mt" HAVEBOOSTUTF=yes fi AC_SUBST(HAVEBOOSTUTF) AC_SUBST(LIBS_BOOSTUTF) # nvcc, cuda AC_ARG_WITH(cuda, [[ --with-cuda=path path of CUDA (optional)]],,) if test x"$with_cuda" != xno; then NVCC_PATH=$PATH if test x"$with_cuda" != x -a x"$with_cuda" != xyes; then NVCC_PATH="$with_cuda/bin:$NVCC_PATH" fi AC_PATH_PROG([NVCC], [nvcc], [no], [$NVCC_PATH]) else NVCC=no fi HAVECUDA=no if test x"$NVCC" != xno; then HAVECUDA=yes BACKUP_CUDA_LDFLAGS="$LDFLAGS" if test x"$with_cuda" != x -a x"$with_cuda" != xyes; then test -d $with_cuda/lib64 && LDFLAGS_CUDA="-L$with_cuda/lib64" || LDFLAGS_CUDA="-L$with_cuda/lib" CPPFLAGS_CUDA="-I$with_cuda/include" LDFLAGS="$LDFLAGS $LDFLAGS_CUDA" fi AC_CHECK_LIB(cudart,cudaMalloc, ,HAVECUDA=no) AC_CHECK_LIB(cufft,cufftPlan1d, ,HAVECUDA=no) LDFLAGS="$BACKUP_CUDA_LDFLAGS" unset BACKUP_CUDA_LDFLAGS # TODO: check for cuda headers? AC_SUBST(NVCC) fi if test x"$HAVECUDA" = xyes; then AC_MSG_CHECKING([if nvcc works]) ASTRA_CHECK_NVCC(HAVECUDA,NVCCFLAGS_EXTRA) AC_MSG_RESULT($HAVECUDA) fi AC_ARG_WITH(cuda_compute, [[ --with-cuda-compute=archs comma separated list of CUDA compute models (optional)]],,) if test x"$HAVECUDA" = xyes; then AC_MSG_CHECKING([for nvcc archs]) dnl 10 11 12 13 20 21 30 32 35 37 50 52 53 60 61 62 70 72 75 80 86 dnl dnl Build cubin for range of platforms, and ptx for newest for forward compat. dnl Build cubin x.0 for older platforms, and all x.y for "recent" ones. dnl dnl Include 3.5 since CUDA 11.x doesn't support 3.0, but does support 3.5. dnl (All other CUDA toolkits from 7-10 start support at x.0 versions.) dnl Skip 7.2 cubin since that seems to be Jetson-only (and can use 7.0 cubin). dnl dnl From CUDA Programming Guide: dnl "Binary compatibility is guaranteed from one minor revision to the next one, but not from one minor revision to the previous one or across major revisions." dnl if test x"$with_cuda_compute" = x; then with_cuda_compute="20,30,35,50,60,70,75,80,86" fi ASTRA_FIND_NVCC_ARCHS([$with_cuda_compute],NVCCFLAGS_EXTRA,NVCCARCHS) AC_MSG_RESULT([$NVCCARCHS]) fi AC_ARG_VAR(NVCCFLAGS, [CUDA nvcc flags]) PKGCONFIG_CFLAGS_EXTRA= if test x"$HAVECUDA" = xyes; then PKGCONFIG_CFLAGS_EXTRA="-DASTRA_CUDA" fi AC_SUBST(PKGCONFIG_CFLAGS_EXTRA) AC_SUBST(HAVECUDA) AC_SUBST(LDFLAGS_CUDA) AC_SUBST(CPPFLAGS_CUDA) AC_SUBST(NVCCFLAGS_EXTRA) # mex, matlab AC_ARG_WITH(matlab, [[ --with-matlab=path path of Matlab (optional)]],,) MEX_PATH=$PATH HAVEMATLAB=no if test x"$with_matlab" != x; then MEX_PATH="$with_matlab/bin:$MEX_PATH" AC_PATH_PROG([MEX], [mex], [no], [$MEX_PATH]) if test x"$MEX" != xno; then HAVEMATLAB=yes # TODO: check platform of C compiler is same as mex AC_SUBST(MEX) MATLAB_ROOT="$with_matlab" AC_SUBST(MATLAB_ROOT) # TODO: maybe catch mex warnings ASTRA_CHECK_MEX_SUFFIX([mexa64 mexglx mexmaci64 mexmaci],[MEXSUFFIX]) if test x$MEXSUFFIX = x; then AC_MSG_FAILURE([Unable to determine matlab mex suffix]) HAVEMATLAB=no fi AC_SUBST(MEXSUFFIX) fi fi if test x"$HAVEMATLAB" = xyes; then AC_MSG_CHECKING([if mex requires the -R2017b option]) ASTRA_CHECK_MEX_OPTION([-R2017b],[$MEXSUFFIX],[ MEXFLAGS="-R2017b" # TODO: check if this is the best way of emulating -R2017b when using # CXX directly to compile mex files. CPPFLAGS_MATLAB="-DMATLAB_MEXCMD_RELEASE=700" AC_MSG_RESULT([yes]) ],[ MEXFLAGS= CPPFLAGS_MATLAB= AC_MSG_RESULT([no]) ]) fi AC_SUBST(HAVEMATLAB) AC_SUBST(MEXFLAGS) AC_SUBST(CPPFLAGS_MATLAB) # octave AC_ARG_ENABLE(octave, [[ --enable-octave enable Octave support]]) if test x"$enable_octave" = xyes; then AC_PATH_PROG([HAVEOCTAVE], [octave-config], [no], [$PATH]) AC_MSG_CHECKING([for octave]) if test x"HAVEOCTAVE" != xno -a $HAVEMATLAB = yes; then HAVEOCTAVE=no AC_MSG_RESULT([no (since Matlab support is enabled)]) else if test x"$HAVEOCTAVE" != xno; then OCTAVE_CPPFLAGS="-I`octave-config -p OCTINCLUDEDIR`" AC_SUBST(OCTAVE_CPPFLAGS) HAVEOCTAVE=yes fi AC_MSG_RESULT($HAVEOCTAVE) fi else HAVEOCTAVE=no fi AC_SUBST(HAVEOCTAVE) # python AC_ARG_WITH(python, [[ --with-python=path path of Python binary (optional)]],,) HAVEPYTHON=no if test x"$with_python" != x -a x"$with_python" != xno; then if test x"$with_python" = xyes; then PYTHON=python else PYTHON="$with_python" fi AC_MSG_CHECKING(for python) ASTRA_RUN_LOGOUTPUT(echo 'import sys' | $PYTHON -) if test $? -ne 0; then AC_MSG_ERROR(Python binary not found) fi AC_MSG_RESULT([$PYTHON]) HAVEPYTHON=yes AC_SUBST(PYTHON) AC_MSG_CHECKING(for numpy module) ASTRA_TRY_PYTHON([import numpy],,HAVEPYTHON=no) if test x$HAVEPYTHON = xno; then AC_MSG_RESULT(no) AC_MSG_ERROR(You need the numpy module to use the ASTRA toolbox in Python) fi AC_MSG_RESULT(yes) AC_MSG_CHECKING(for Cython module) ASTRA_TRY_PYTHON([import Cython],,HAVEPYTHON=no) if test x$HAVEPYTHON = xno; then AC_MSG_RESULT(no) AC_MSG_ERROR(You need the Cython module to use the ASTRA toolbox in Python) fi AC_MSG_RESULT(yes) AC_MSG_CHECKING(for scipy module) ASTRA_TRY_PYTHON([import scipy],,HAVEPYTHON=no) if test x$HAVEPYTHON = xno; then AC_MSG_RESULT(no) AC_MSG_ERROR(You need the scipy module to use the ASTRA toolbox in Python) fi AC_MSG_RESULT(yes) fi AC_SUBST(HAVEPYTHON) AC_ARG_WITH(python-wheel-platform, [[ --with-python-wheel-platform=platform platform name to use for .whl (optional)]],,) PYTHON_WHEEL_PLATFORM= AC_MSG_CHECKING(for platform name to use for Python wheel) if test x"$with_python_wheel_platform" != x -a x"$with_python_wheel_platform" != xno; then PYTHON_WHEEL_PLATFORM=$with_python_wheel_platform AC_MSG_RESULT($PYTHON_WHEEL_PLATFORM) else AC_MSG_RESULT(default) fi AC_SUBST(PYTHON_WHEEL_PLATFORM) #OS specific setup AC_CANONICAL_HOST case $host_os in darwin* ) CXXFLAGS_OS="-stdlib=libstdc++ -mmacosx-version-min=10.6" LDFLAGS_OS="-stdlib=libstdc++" IS_MACOS=yes ;; *) CXXFLAGS_OS="" LDFLAGS_OS="" IS_MACOS=no ;; esac AC_SUBST(CXXFLAGS_OS) AC_SUBST(LDFLAGS_OS) AC_SUBST(IS_MACOS) # For some reason, some older versions of autoconf produce a config.status # that disables all lines looking like VPATH=@srcdir@ # (More recent autoconf fixes the too broad matching there.) # We use a different variable name as a workaround. VPATH_SRCDIR="$srcdir" AC_SUBST(VPATH_SRCDIR) # Installation type AC_ARG_WITH(install-type, [[ --with-install-type=prefix|module|dir type of installation (default prefix)]],,with_install_type=prefix) INSTALL_TYPE=$with_install_type AC_SUBST(INSTALL_TYPE) # TODO: # Detection of tools: # libtool (how?) # options: # debugging/optimization/profiling flags AC_SUBST(SAVED_CPPFLAGS) AC_SUBST(SAVED_CXXFLAGS) AC_SUBST(SAVED_NVCCFLAGS) AC_SUBST(SAVED_LDFLAGS) AC_SUBST(SAVED_LIBS) AC_CONFIG_FILES([Makefile libastra.pc]) AC_OUTPUT echo echo "Summary of ASTRA Toolbox build options:" echo " CUDA : $HAVECUDA" echo " Matlab: $HAVEMATLAB" echo " Octave: $HAVEOCTAVE" echo " Python: $HAVEPYTHON" echo echo "Installation type: $with_install_type" case $with_install_type in prefix) echo " Library : "$(eval echo `eval echo $libdir`) if test $HAVEMATLAB = yes; then echo " Matlab files: "$(eval echo `eval echo $datadir`)"/astra/matlab" fi if test $HAVEOCTAVE = yes; then echo " Octave files: "$(eval echo `eval echo $datadir`)"/astra/octave" fi if test $HAVEPYTHON = yes; then echo " Python files to site-packages" fi ;; dir) echo " Library : "$(eval echo `eval echo $libdir`) if test $HAVEMATLAB = yes; then echo " Matlab files: $prefix/matlab" fi if test $HAVEOCTAVE = yes; then echo " Octave files: $prefix/octave" fi if test $HAVEPYTHON = yes; then echo " Python files: $prefix/python" fi ;; module) echo " Library into Matlab/Octave/Python module directories" if test $HAVEMATLAB = yes; then echo " Matlab files: $prefix/matlab" fi if test $HAVEOCTAVE = yes; then echo " Octave files: $prefix/octave" fi if test $HAVEPYTHON = yes; then echo " Python files to site-packages" fi esac astra-toolbox-2.3.0/build/linux/install-sh.dist000077500000000000000000000332561475635207100215410ustar00rootroot00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2011-01-19.21; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # 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 # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 # Protect names problematic for `test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for `test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for `test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: astra-toolbox-2.3.0/build/linux/libastra.pc.in000066400000000000000000000003431475635207100213140ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libastra Description: ASTRA Toolbox Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lastra Cflags: -I${includedir} @PKGCONFIG_CFLAGS_EXTRA@ astra-toolbox-2.3.0/build/msvc/000077500000000000000000000000001475635207100163735ustar00rootroot00000000000000astra-toolbox-2.3.0/build/msvc/astra_vc14.sln000066400000000000000000000253421475635207100210660ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "astra_vc14", "projects\astra_vc14.vcxproj", "{DABD9D82-609E-4C71-B1CA-A41B07495290}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "astra_mex", "projects\astra_mex", "{2076FB73-ECFE-4B1B-9A8C-E351C500FAAB}" ProjectSection(ProjectDependencies) = postProject {DABD9D82-609E-4C71-B1CA-A41B07495290} = {DABD9D82-609E-4C71-B1CA-A41B07495290} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "astra_mex", "projects\astra_mex_vc14.vcxproj", "{6FDF72C4-A855-4F1C-A401-6500040B5E28}" ProjectSection(ProjectDependencies) = postProject {DABD9D82-609E-4C71-B1CA-A41B07495290} = {DABD9D82-609E-4C71-B1CA-A41B07495290} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "astra_mex_algorithm", "projects\astra_mex_algorithm_vc14.vcxproj", "{CE5EF874-830C-4C10-8651-CCA2A34ED9E4}" ProjectSection(ProjectDependencies) = postProject {DABD9D82-609E-4C71-B1CA-A41B07495290} = {DABD9D82-609E-4C71-B1CA-A41B07495290} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "astra_mex_data2d", "projects\astra_mex_data2d_vc14.vcxproj", "{D2CDCDB3-7AD9-4853-8D87-BDB1DAD9C9C1}" ProjectSection(ProjectDependencies) = postProject {DABD9D82-609E-4C71-B1CA-A41B07495290} = {DABD9D82-609E-4C71-B1CA-A41B07495290} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "astra_mex_data3d", "projects\astra_mex_data3d_vc14.vcxproj", "{2A7084C6-62ED-4235-85F4-094C17689DEB}" ProjectSection(ProjectDependencies) = postProject {DABD9D82-609E-4C71-B1CA-A41B07495290} = {DABD9D82-609E-4C71-B1CA-A41B07495290} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "astra_mex_matrix", "projects\astra_mex_matrix_vc14.vcxproj", "{6BFA8857-37EB-4E43-A97C-B860E21599F5}" ProjectSection(ProjectDependencies) = postProject {DABD9D82-609E-4C71-B1CA-A41B07495290} = {DABD9D82-609E-4C71-B1CA-A41B07495290} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "astra_mex_projector", "projects\astra_mex_projector_vc14.vcxproj", "{85ECCF1D-C5F6-4E0E-A4F9-0DE7C0B916B2}" ProjectSection(ProjectDependencies) = postProject {DABD9D82-609E-4C71-B1CA-A41B07495290} = {DABD9D82-609E-4C71-B1CA-A41B07495290} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "astra_mex_projector3d", "projects\astra_mex_projector3d_vc14.vcxproj", "{CA85BDA0-9BDD-495E-B200-BFE863EB6318}" ProjectSection(ProjectDependencies) = postProject {DABD9D82-609E-4C71-B1CA-A41B07495290} = {DABD9D82-609E-4C71-B1CA-A41B07495290} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "astra_mex_log", "projects\astra_mex_log_vc14.vcxproj", "{88539382-66DB-4BBC-A48E-8B6B3CA6064F}" ProjectSection(ProjectDependencies) = postProject {DABD9D82-609E-4C71-B1CA-A41B07495290} = {DABD9D82-609E-4C71-B1CA-A41B07495290} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "astra_mex_direct", "projects\astra_mex_direct_vc14.vcxproj", "{47460476-912B-4313-8B10-BDF1D60A84C4}" ProjectSection(ProjectDependencies) = postProject {DABD9D82-609E-4C71-B1CA-A41B07495290} = {DABD9D82-609E-4C71-B1CA-A41B07495290} EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug_CUDA|x64 = Debug_CUDA|x64 Debug|x64 = Debug|x64 Release_CUDA|x64 = Release_CUDA|x64 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {DABD9D82-609E-4C71-B1CA-A41B07495290}.Debug_CUDA|x64.ActiveCfg = Debug_CUDA|x64 {DABD9D82-609E-4C71-B1CA-A41B07495290}.Debug_CUDA|x64.Build.0 = Debug_CUDA|x64 {DABD9D82-609E-4C71-B1CA-A41B07495290}.Debug|x64.ActiveCfg = Debug|x64 {DABD9D82-609E-4C71-B1CA-A41B07495290}.Debug|x64.Build.0 = Debug|x64 {DABD9D82-609E-4C71-B1CA-A41B07495290}.Release_CUDA|x64.ActiveCfg = Release_CUDA|x64 {DABD9D82-609E-4C71-B1CA-A41B07495290}.Release_CUDA|x64.Build.0 = Release_CUDA|x64 {DABD9D82-609E-4C71-B1CA-A41B07495290}.Release|x64.ActiveCfg = Release|x64 {DABD9D82-609E-4C71-B1CA-A41B07495290}.Release|x64.Build.0 = Release|x64 {6FDF72C4-A855-4F1C-A401-6500040B5E28}.Debug_CUDA|x64.ActiveCfg = Debug_CUDA|x64 {6FDF72C4-A855-4F1C-A401-6500040B5E28}.Debug_CUDA|x64.Build.0 = Debug_CUDA|x64 {6FDF72C4-A855-4F1C-A401-6500040B5E28}.Debug|x64.ActiveCfg = Debug|x64 {6FDF72C4-A855-4F1C-A401-6500040B5E28}.Debug|x64.Build.0 = Debug|x64 {6FDF72C4-A855-4F1C-A401-6500040B5E28}.Release_CUDA|x64.ActiveCfg = Release_CUDA|x64 {6FDF72C4-A855-4F1C-A401-6500040B5E28}.Release_CUDA|x64.Build.0 = Release_CUDA|x64 {6FDF72C4-A855-4F1C-A401-6500040B5E28}.Release|x64.ActiveCfg = Release|x64 {6FDF72C4-A855-4F1C-A401-6500040B5E28}.Release|x64.Build.0 = Release|x64 {CE5EF874-830C-4C10-8651-CCA2A34ED9E4}.Debug_CUDA|x64.ActiveCfg = Debug_CUDA|x64 {CE5EF874-830C-4C10-8651-CCA2A34ED9E4}.Debug_CUDA|x64.Build.0 = Debug_CUDA|x64 {CE5EF874-830C-4C10-8651-CCA2A34ED9E4}.Debug|x64.ActiveCfg = Debug|x64 {CE5EF874-830C-4C10-8651-CCA2A34ED9E4}.Debug|x64.Build.0 = Debug|x64 {CE5EF874-830C-4C10-8651-CCA2A34ED9E4}.Release_CUDA|x64.ActiveCfg = Release_CUDA|x64 {CE5EF874-830C-4C10-8651-CCA2A34ED9E4}.Release_CUDA|x64.Build.0 = Release_CUDA|x64 {CE5EF874-830C-4C10-8651-CCA2A34ED9E4}.Release|x64.ActiveCfg = Release|x64 {CE5EF874-830C-4C10-8651-CCA2A34ED9E4}.Release|x64.Build.0 = Release|x64 {D2CDCDB3-7AD9-4853-8D87-BDB1DAD9C9C1}.Debug_CUDA|x64.ActiveCfg = Debug_CUDA|x64 {D2CDCDB3-7AD9-4853-8D87-BDB1DAD9C9C1}.Debug_CUDA|x64.Build.0 = Debug_CUDA|x64 {D2CDCDB3-7AD9-4853-8D87-BDB1DAD9C9C1}.Debug|x64.ActiveCfg = Debug|x64 {D2CDCDB3-7AD9-4853-8D87-BDB1DAD9C9C1}.Debug|x64.Build.0 = Debug|x64 {D2CDCDB3-7AD9-4853-8D87-BDB1DAD9C9C1}.Release_CUDA|x64.ActiveCfg = Release_CUDA|x64 {D2CDCDB3-7AD9-4853-8D87-BDB1DAD9C9C1}.Release_CUDA|x64.Build.0 = Release_CUDA|x64 {D2CDCDB3-7AD9-4853-8D87-BDB1DAD9C9C1}.Release|x64.ActiveCfg = Release|x64 {D2CDCDB3-7AD9-4853-8D87-BDB1DAD9C9C1}.Release|x64.Build.0 = Release|x64 {2A7084C6-62ED-4235-85F4-094C17689DEB}.Debug_CUDA|x64.ActiveCfg = Debug_CUDA|x64 {2A7084C6-62ED-4235-85F4-094C17689DEB}.Debug_CUDA|x64.Build.0 = Debug_CUDA|x64 {2A7084C6-62ED-4235-85F4-094C17689DEB}.Debug|x64.ActiveCfg = Debug|x64 {2A7084C6-62ED-4235-85F4-094C17689DEB}.Debug|x64.Build.0 = Debug|x64 {2A7084C6-62ED-4235-85F4-094C17689DEB}.Release_CUDA|x64.ActiveCfg = Release_CUDA|x64 {2A7084C6-62ED-4235-85F4-094C17689DEB}.Release_CUDA|x64.Build.0 = Release_CUDA|x64 {2A7084C6-62ED-4235-85F4-094C17689DEB}.Release|x64.ActiveCfg = Release|x64 {2A7084C6-62ED-4235-85F4-094C17689DEB}.Release|x64.Build.0 = Release|x64 {6BFA8857-37EB-4E43-A97C-B860E21599F5}.Debug_CUDA|x64.ActiveCfg = Debug_CUDA|x64 {6BFA8857-37EB-4E43-A97C-B860E21599F5}.Debug_CUDA|x64.Build.0 = Debug_CUDA|x64 {6BFA8857-37EB-4E43-A97C-B860E21599F5}.Debug|x64.ActiveCfg = Debug|x64 {6BFA8857-37EB-4E43-A97C-B860E21599F5}.Debug|x64.Build.0 = Debug|x64 {6BFA8857-37EB-4E43-A97C-B860E21599F5}.Release_CUDA|x64.ActiveCfg = Release_CUDA|x64 {6BFA8857-37EB-4E43-A97C-B860E21599F5}.Release_CUDA|x64.Build.0 = Release_CUDA|x64 {6BFA8857-37EB-4E43-A97C-B860E21599F5}.Release|x64.ActiveCfg = Release|x64 {6BFA8857-37EB-4E43-A97C-B860E21599F5}.Release|x64.Build.0 = Release|x64 {85ECCF1D-C5F6-4E0E-A4F9-0DE7C0B916B2}.Debug_CUDA|x64.ActiveCfg = Debug_CUDA|x64 {85ECCF1D-C5F6-4E0E-A4F9-0DE7C0B916B2}.Debug_CUDA|x64.Build.0 = Debug_CUDA|x64 {85ECCF1D-C5F6-4E0E-A4F9-0DE7C0B916B2}.Debug|x64.ActiveCfg = Debug|x64 {85ECCF1D-C5F6-4E0E-A4F9-0DE7C0B916B2}.Debug|x64.Build.0 = Debug|x64 {85ECCF1D-C5F6-4E0E-A4F9-0DE7C0B916B2}.Release_CUDA|x64.ActiveCfg = Release_CUDA|x64 {85ECCF1D-C5F6-4E0E-A4F9-0DE7C0B916B2}.Release_CUDA|x64.Build.0 = Release_CUDA|x64 {85ECCF1D-C5F6-4E0E-A4F9-0DE7C0B916B2}.Release|x64.ActiveCfg = Release|x64 {85ECCF1D-C5F6-4E0E-A4F9-0DE7C0B916B2}.Release|x64.Build.0 = Release|x64 {CA85BDA0-9BDD-495E-B200-BFE863EB6318}.Debug_CUDA|x64.ActiveCfg = Debug_CUDA|x64 {CA85BDA0-9BDD-495E-B200-BFE863EB6318}.Debug_CUDA|x64.Build.0 = Debug_CUDA|x64 {CA85BDA0-9BDD-495E-B200-BFE863EB6318}.Debug|x64.ActiveCfg = Debug|x64 {CA85BDA0-9BDD-495E-B200-BFE863EB6318}.Debug|x64.Build.0 = Debug|x64 {CA85BDA0-9BDD-495E-B200-BFE863EB6318}.Release_CUDA|x64.ActiveCfg = Release_CUDA|x64 {CA85BDA0-9BDD-495E-B200-BFE863EB6318}.Release_CUDA|x64.Build.0 = Release_CUDA|x64 {CA85BDA0-9BDD-495E-B200-BFE863EB6318}.Release|x64.ActiveCfg = Release|x64 {CA85BDA0-9BDD-495E-B200-BFE863EB6318}.Release|x64.Build.0 = Release|x64 {88539382-66DB-4BBC-A48E-8B6B3CA6064F}.Debug_CUDA|x64.ActiveCfg = Debug_CUDA|x64 {88539382-66DB-4BBC-A48E-8B6B3CA6064F}.Debug_CUDA|x64.Build.0 = Debug_CUDA|x64 {88539382-66DB-4BBC-A48E-8B6B3CA6064F}.Debug|x64.ActiveCfg = Debug|x64 {88539382-66DB-4BBC-A48E-8B6B3CA6064F}.Debug|x64.Build.0 = Debug|x64 {88539382-66DB-4BBC-A48E-8B6B3CA6064F}.Release_CUDA|x64.ActiveCfg = Release_CUDA|x64 {88539382-66DB-4BBC-A48E-8B6B3CA6064F}.Release_CUDA|x64.Build.0 = Release_CUDA|x64 {88539382-66DB-4BBC-A48E-8B6B3CA6064F}.Release|x64.ActiveCfg = Release|x64 {88539382-66DB-4BBC-A48E-8B6B3CA6064F}.Release|x64.Build.0 = Release|x64 {47460476-912B-4313-8B10-BDF1D60A84C4}.Debug_CUDA|x64.ActiveCfg = Debug_CUDA|x64 {47460476-912B-4313-8B10-BDF1D60A84C4}.Debug_CUDA|x64.Build.0 = Debug_CUDA|x64 {47460476-912B-4313-8B10-BDF1D60A84C4}.Debug|x64.ActiveCfg = Debug|x64 {47460476-912B-4313-8B10-BDF1D60A84C4}.Debug|x64.Build.0 = Debug|x64 {47460476-912B-4313-8B10-BDF1D60A84C4}.Release_CUDA|x64.ActiveCfg = Release_CUDA|x64 {47460476-912B-4313-8B10-BDF1D60A84C4}.Release_CUDA|x64.Build.0 = Release_CUDA|x64 {47460476-912B-4313-8B10-BDF1D60A84C4}.Release|x64.ActiveCfg = Release|x64 {47460476-912B-4313-8B10-BDF1D60A84C4}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {6FDF72C4-A855-4F1C-A401-6500040B5E28} = {2076FB73-ECFE-4B1B-9A8C-E351C500FAAB} {CE5EF874-830C-4C10-8651-CCA2A34ED9E4} = {2076FB73-ECFE-4B1B-9A8C-E351C500FAAB} {D2CDCDB3-7AD9-4853-8D87-BDB1DAD9C9C1} = {2076FB73-ECFE-4B1B-9A8C-E351C500FAAB} {2A7084C6-62ED-4235-85F4-094C17689DEB} = {2076FB73-ECFE-4B1B-9A8C-E351C500FAAB} {6BFA8857-37EB-4E43-A97C-B860E21599F5} = {2076FB73-ECFE-4B1B-9A8C-E351C500FAAB} {85ECCF1D-C5F6-4E0E-A4F9-0DE7C0B916B2} = {2076FB73-ECFE-4B1B-9A8C-E351C500FAAB} {CA85BDA0-9BDD-495E-B200-BFE863EB6318} = {2076FB73-ECFE-4B1B-9A8C-E351C500FAAB} {88539382-66DB-4BBC-A48E-8B6B3CA6064F} = {2076FB73-ECFE-4B1B-9A8C-E351C500FAAB} {47460476-912B-4313-8B10-BDF1D60A84C4} = {2076FB73-ECFE-4B1B-9A8C-E351C500FAAB} EndGlobalSection EndGlobal astra-toolbox-2.3.0/build/msvc/build_clean.bat000066400000000000000000000002611475635207100213230ustar00rootroot00000000000000@echo off call "%~dp0build_env.bat" call "%B_VC%\vcvars64.bat" cd %~dp0 msbuild astra_vc14.sln /p:Configuration=Release_CUDA /p:Platform=x64 /t:clean /maxcpucount:20 pause astra-toolbox-2.3.0/build/msvc/build_env.bat000066400000000000000000000004661475635207100210400ustar00rootroot00000000000000set B_WP3=3.13.1 set B_WINPYTHON3=C:\Python313 set B_MATLAB_ROOT=C:\Users\Vagrant\MATLAB\R2023b set B_VC=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\ set B_VCREDIST=C:\Users\Vagrant\vc_redist.x64.exe set B_README_WP3=%B_WINPYTHON3%\Lib\site-packages set B_RELEASE=2.3.0 astra-toolbox-2.3.0/build/msvc/build_matlab.bat000066400000000000000000000003141475635207100215000ustar00rootroot00000000000000@echo off call "%~dp0build_env.bat" call "%B_VC%\vcvars64.bat" cd /D %~dp0 set MATLAB_ROOT=%B_MATLAB_ROOT% msbuild astra_vc14.sln /p:Configuration=Release_CUDA /p:Platform=x64 /maxcpucount:20 pause astra-toolbox-2.3.0/build/msvc/build_python3.bat000066400000000000000000000013061475635207100216460ustar00rootroot00000000000000@echo off cd /D %~dp0 cd ..\.. set R=%CD% call "%~dp0build_env.bat" call "%B_VC%\vcvars64.bat" cd /D %~dp0 msbuild astra_vc14.sln /p:Configuration=Release_CUDA /p:Platform=x64 /t:astra_vc14 /maxcpucount:20 cd /D %R% cd python rd /s /q build rd /s /q "%B_WINPYTHON3%\lib\site-packages\astra" set CL=/DASTRA_CUDA /DASTRA_PYTHON /std:c++17 set INCLUDE=%R%\include;%R%\lib\include;%CUDA_PATH%\include;%INCLUDE% copy ..\build\msvc\bin\x64\Release_CUDA\AstraCuda64.lib astra.lib %B_WINPYTHON3%\python builder.py build_ext --compiler=msvc add_extra_lib --file=..\build\msvc\bin\x64\Release_CUDA\AstraCuda64.dll;"%CUDA_PATH_V12_8%\bin\cudart64_12.dll";"%CUDA_PATH_V12_8%\bin\cufft64_11.dll" bdist_wheel pause astra-toolbox-2.3.0/build/msvc/build_release.bat000066400000000000000000000041601475635207100216630ustar00rootroot00000000000000@echo off cd /D %~dp0 cd ..\.. set R=%CD% call "%~dp0build_env.bat" cd /D %~dp0 rd /s /q release mkdir release cd release mkdir matlab mkdir python313 pause cd %R%\build\msvc\release\matlab mkdir astra-%B_RELEASE% cd astra-%B_RELEASE% xcopy /e /i %R%\samples\matlab samples xcopy /e /i %R%\matlab\algorithms algorithms xcopy /e /i %R%\matlab\tools tools copy %R%\NEWS.txt . copy %R%\README.txt . copy %R%\COPYING COPYING.txt copy %B_VCREDIST% . mkdir mex copy %R%\build\msvc\bin\x64\Release_CUDA\*.mexw64 mex copy %R%\build\msvc\bin\x64\Release_CUDA\AstraCuda64.dll mex copy %R%\build\msvc\bin\x64\Release_CUDA\AstraCuda64.lib mex copy "%CUDA_PATH_V12_8%\bin\cudart64_12.dll" mex copy "%CUDA_PATH_V12_8%\bin\cufft64_11.dll" mex pause rem ------------------------------------------------------------------- cd %R%\build\msvc\release\python313 mkdir astra-%B_RELEASE% cd astra-%B_RELEASE% xcopy /e /i %R%\samples\python samples copy %R%\NEWS.txt . copy %R%\COPYING COPYING.txt copy %B_VCREDIST% . copy %R%\python\dist\*.whl . ( echo ----------------------------------------------------------------------- echo This file is part of the ASTRA Toolbox echo. echo Copyright: 2010-2024, imec Vision Lab, University of Antwerp echo 2014-2024, CWI, Amsterdam echo https://visielab.uantwerpen.be/ and https://www.cwi.nl/ echo License: Open Source under GPLv3 echo Contact: astra@astra-toolbox.com echo Website: https://www.astra-toolbox.com/ echo ----------------------------------------------------------------------- echo. echo. echo This directory contains pre-built Python modules for the ASTRA Toolbox. echo. echo It has been built with python %B_WP3%, installed via Chocolatey. echo. echo To use it, run 'pip3 install astra_toolbox-%B_RELEASE%-cp312-cp312-win_amd64.whl' echo. echo Sample code can be found in the samples\ directory. ) > README.txt pause cd %R%\build\msvc\release %B_WINPYTHON3%\python -c "import shutil; shutil.make_archive('astra-%B_RELEASE%-matlab-win-x64', 'zip', 'matlab')" %B_WINPYTHON3%\python -c "import shutil; shutil.make_archive('astra-%B_RELEASE%-python313-win-x64', 'zip', 'python313')" pause astra-toolbox-2.3.0/build/msvc/build_setup.bat000066400000000000000000000002331475635207100214000ustar00rootroot00000000000000@echo off call "%~dp0build_env.bat" cd /D %~dp0 cd ..\.. set R=%CD% echo Removing bin directories rd /s /q "%R%\build\msvc\bin\x64\Release_CUDA" pause astra-toolbox-2.3.0/build/msvc/gen.py000066400000000000000000001055531475635207100175270ustar00rootroot00000000000000import sys import os import codecs vcppguid = "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942" # C++ project siguid = "2150E333-8FDC-42A3-9474-1A3956D46DE8" # project group # to generate a new uuid: # # import uuid # uuid.uuid4().__str__().upper() # see configure.ac CUDA_CC = { (9,0): "compute_30,sm_30;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_70,compute_70", (9,2): "compute_30,sm_30;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_70,compute_70", (10,0): "compute_30,sm_30;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_75,compute_75", (10,1): "compute_30,sm_30;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_75,compute_75", (10,2): "compute_30,sm_30;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_75,compute_75", (11,0): "compute_35,sm_35;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_80,compute_80", (11,1): "compute_35,sm_35;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_86,compute_86", (11,2): "compute_35,sm_35;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_86,compute_86", (11,3): "compute_35,sm_35;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_86,compute_86", (11,4): "compute_35,sm_35;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_86,compute_86", (11,5): "compute_35,sm_35;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_86,compute_86", (11,6): "compute_35,sm_35;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_86,compute_86", (11,7): "compute_35,sm_35;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_86,compute_86", (11,8): "compute_35,sm_35;compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_86,compute_86", (12,0): "compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_86,compute_86", (12,1): "compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_86,compute_86", (12,2): "compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_86,compute_86", (12,3): "compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_87,sm_87;compute_89,sm_89;compute_90,sm_90;compute_90,compute_90", (12,4): "compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_87,sm_87;compute_89,sm_89;compute_90,sm_90;compute_90,compute_90", (12,5): "compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_87,sm_87;compute_89,sm_89;compute_90,sm_90;compute_90,compute_90", (12,8): "compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_87,sm_87;compute_89,sm_89;compute_90,sm_90;compute_100,sm_100;compute_101,sm_101;compute_120,sm_120;compute_120,compute_120", } def create_mex_project(name, uuid14): return { "type": vcppguid, "name": name, "file14": name + "_vc14.vcxproj", "uuid14": uuid14, "files": [] } P_astra = { "type": vcppguid, "name": "astra_vc14", "file14": "astra_vc14.vcxproj", "uuid14": "DABD9D82-609E-4C71-B1CA-A41B07495290" } P0 = create_mex_project("astra_mex", "6FDF72C4-A855-4F1C-A401-6500040B5E28") P1 = create_mex_project("astra_mex_algorithm", "CE5EF874-830C-4C10-8651-CCA2A34ED9E4") P2 = create_mex_project("astra_mex_data2d", "D2CDCDB3-7AD9-4853-8D87-BDB1DAD9C9C1") P3 = create_mex_project("astra_mex_data3d", "2A7084C6-62ED-4235-85F4-094C17689DEB") P4 = create_mex_project("astra_mex_matrix", "6BFA8857-37EB-4E43-A97C-B860E21599F5") P5 = create_mex_project("astra_mex_projector", "85ECCF1D-C5F6-4E0E-A4F9-0DE7C0B916B2") P6 = create_mex_project("astra_mex_projector3d", "CA85BDA0-9BDD-495E-B200-BFE863EB6318") P7 = create_mex_project("astra_mex_log", "88539382-66DB-4BBC-A48E-8B6B3CA6064F") P8 = create_mex_project("astra_mex_direct", "47460476-912B-4313-8B10-BDF1D60A84C4") F_astra_mex = { "type": siguid, "name": "astra_mex", "file14": "astra_mex", "uuid14": "2076FB73-ECFE-4B1B-9A8C-E351C500FAAB", "entries": [ P0, P1, P2, P3, P4, P5, P6, P7, P8 ] } P0["files"] = [ "astra_mex_c.cpp", "mexHelpFunctions.cpp", "mexHelpFunctions.h", "mexInitFunctions.cpp", "mexInitFunctions.h", ] P1["files"] = [ "astra_mex_algorithm_c.cpp", "mexHelpFunctions.cpp", "mexHelpFunctions.h", "mexInitFunctions.cpp", "mexInitFunctions.h", ] P2["files"] = [ "astra_mex_data2d_c.cpp", "mexHelpFunctions.cpp", "mexHelpFunctions.h", "mexCopyDataHelpFunctions.cpp", "mexCopyDataHelpFunctions.h", "mexDataManagerHelpFunctions.cpp", "mexDataManagerHelpFunctions.h", "mexInitFunctions.cpp", "mexInitFunctions.h", ] P3["files"] = [ "astra_mex_data3d_c.cpp", "mexHelpFunctions.cpp", "mexHelpFunctions.h", "mexCopyDataHelpFunctions.cpp", "mexCopyDataHelpFunctions.h", "mexDataManagerHelpFunctions.cpp", "mexDataManagerHelpFunctions.h", "mexInitFunctions.cpp", "mexInitFunctions.h", ] P4["files"] = [ "astra_mex_matrix_c.cpp", "mexHelpFunctions.cpp", "mexHelpFunctions.h", "mexInitFunctions.cpp", "mexInitFunctions.h", ] P5["files"] = [ "astra_mex_projector_c.cpp", "mexHelpFunctions.cpp", "mexHelpFunctions.h", "mexInitFunctions.cpp", "mexInitFunctions.h", ] P6["files"] = [ "astra_mex_projector3d_c.cpp", "mexHelpFunctions.cpp", "mexHelpFunctions.h", "mexInitFunctions.cpp", "mexInitFunctions.h", ] P7["files"] = [ "astra_mex_log_c.cpp", "mexHelpFunctions.cpp", "mexHelpFunctions.h", "mexInitFunctions.cpp", "mexInitFunctions.h", ] P8["files"] = [ "astra_mex_direct_c.cpp", "mexHelpFunctions.cpp", "mexHelpFunctions.h", "mexCopyDataHelpFunctions.cpp", "mexCopyDataHelpFunctions.h", "mexDataManagerHelpFunctions.cpp", "mexDataManagerHelpFunctions.h", "mexInitFunctions.cpp", "mexInitFunctions.h", ] P_astra["filter_names"] = [ "Algorithms", "Data Structures", "Projectors", "CUDA", "Global & Other", "Geometries", "Algorithms\\headers", "Algorithms\\source", "Data Structures\\headers", "Data Structures\\source", "Global & Other\\headers", "Global & Other\\source", "Geometries\\headers", "Geometries\\source", "Projectors\\headers", "Projectors\\inline", "Projectors\\source", "CUDA\\astra headers", "CUDA\\astra source", "CUDA\\cuda headers", "CUDA\\cuda source", ] P_astra["filters"] = {} P_astra["filters"]["Algorithms"] = [ "262b0d17-774a-4cb1-b51a-b358d2d02791" ] P_astra["filters"]["Data Structures"] = [ "76d6d672-670b-4454-b3ab-10dc8f9b8710" ] P_astra["filters"]["Projectors"] = [ "77a581a9-60da-4265-97c0-80cdf97408c0" ] P_astra["filters"]["CUDA"] = [ "c1af0e56-5fcc-4e75-b5db-88eeb4148185" ] P_astra["filters"]["Global & Other"] = [ "72fbe846-10ef-4c52-88df-13bd66c4cbfc" ] P_astra["filters"]["Geometries"] = [ "7ef37c12-c98c-4dd6-938d-12f49279eae0" ] P_astra["filters"]["CUDA\\cuda source"] = [ "04a878ed-77b4-4525-9bc2-38ccd65282c5", "cuda\\2d\\algo.cu", "cuda\\2d\\arith.cu", "cuda\\2d\\astra.cu", "cuda\\2d\\cgls.cu", "cuda\\2d\\darthelper.cu", "cuda\\2d\\em.cu", "cuda\\2d\\fan_bp.cu", "cuda\\2d\\fan_fp.cu", "cuda\\2d\\fbp.cu", "cuda\\2d\\fft.cu", "cuda\\2d\\par_bp.cu", "cuda\\2d\\par_fp.cu", "cuda\\2d\\sart.cu", "cuda\\2d\\sirt.cu", "cuda\\2d\\util.cu", "cuda\\3d\\algo3d.cu", "cuda\\3d\\arith3d.cu", "cuda\\3d\\astra3d.cu", "cuda\\3d\\cgls3d.cu", "cuda\\3d\\cone_bp.cu", "cuda\\3d\\cone_fp.cu", "cuda\\3d\\darthelper3d.cu", "cuda\\3d\\fdk.cu", "cuda\\3d\\mem3d.cu", "cuda\\3d\\par3d_bp.cu", "cuda\\3d\\par3d_fp.cu", "cuda\\3d\\sirt3d.cu", "cuda\\3d\\util3d.cu", ] P_astra["filters"]["Algorithms\\source"] = [ "9df653ab-26c3-4bec-92a2-3dda22fda761", "src\\Algorithm.cpp", "src\\ArtAlgorithm.cpp", "src\\BackProjectionAlgorithm.cpp", "src\\CglsAlgorithm.cpp", "src\\FilteredBackProjectionAlgorithm.cpp", "src\\ForwardProjectionAlgorithm.cpp", "src\\PluginAlgorithmFactory.cpp", "src\\ReconstructionAlgorithm2D.cpp", "src\\ReconstructionAlgorithm3D.cpp", "src\\SartAlgorithm.cpp", "src\\SirtAlgorithm.cpp", ] P_astra["filters"]["Data Structures\\source"] = [ "95346487-8185-487b-a794-3e7fb5fcbd4c", "src\\Data3D.cpp", "src\\Float32Data.cpp", "src\\Float32Data2D.cpp", "src\\Float32ProjectionData2D.cpp", "src\\Float32VolumeData2D.cpp", "src\\SheppLogan.cpp", "src\\SparseMatrix.cpp", ] P_astra["filters"]["Global & Other\\source"] = [ "1546cb47-7e5b-42c2-b695-ef172024c14b", "src\\AstraObjectFactory.cpp", "src\\AstraObjectManager.cpp", "src\\CompositeGeometryManager.cpp", "src\\Config.cpp", "src\\Features.cpp", "src\\Filters.cpp", "src\\Fourier.cpp", "src\\Globals.cpp", "src\\Logging.cpp", "src\\PlatformDepSystemCode.cpp", "src\\Utilities.cpp", "src\\XMLConfig.cpp", "src\\XMLDocument.cpp", "src\\XMLNode.cpp", ] P_astra["filters"]["Geometries\\source"] = [ "dc27bff7-4256-4311-a131-47612a44af20", "src\\ConeProjectionGeometry3D.cpp", "src\\ConeVecProjectionGeometry3D.cpp", "src\\FanFlatProjectionGeometry2D.cpp", "src\\FanFlatVecProjectionGeometry2D.cpp", "src\\GeometryUtil2D.cpp", "src\\GeometryUtil3D.cpp", "src\\ParallelProjectionGeometry2D.cpp", "src\\ParallelProjectionGeometry3D.cpp", "src\\ParallelVecProjectionGeometry2D.cpp", "src\\ParallelVecProjectionGeometry3D.cpp", "src\\ProjectionGeometry2D.cpp", "src\\ProjectionGeometry2DFactory.cpp", "src\\ProjectionGeometry3D.cpp", "src\\ProjectionGeometry3DFactory.cpp", "src\\SparseMatrixProjectionGeometry2D.cpp", "src\\VolumeGeometry2D.cpp", "src\\VolumeGeometry3D.cpp", ] P_astra["filters"]["Projectors\\source"] = [ "2d60e3c8-7874-4cee-b139-991ac15e811d", "src\\DataProjector.cpp", "src\\DataProjectorPolicies.cpp", "src\\FanFlatBeamLineKernelProjector2D.cpp", "src\\FanFlatBeamStripKernelProjector2D.cpp", "src\\ParallelBeamBlobKernelProjector2D.cpp", "src\\ParallelBeamDistanceDrivenProjector2D.cpp", "src\\ParallelBeamLinearKernelProjector2D.cpp", "src\\ParallelBeamLineKernelProjector2D.cpp", "src\\ParallelBeamStripKernelProjector2D.cpp", "src\\Projector2D.cpp", "src\\Projector3D.cpp", "src\\SparseMatrixProjector2D.cpp", ] P_astra["filters"]["CUDA\\astra source"] = [ "bbef012e-598a-456f-90d8-416bdcb4221c", "src\\CudaBackProjectionAlgorithm.cpp", "src\\CudaBackProjectionAlgorithm3D.cpp", "src\\CudaCglsAlgorithm.cpp", "src\\CudaCglsAlgorithm3D.cpp", "src\\CudaDartMaskAlgorithm.cpp", "src\\CudaDartMaskAlgorithm3D.cpp", "src\\CudaDartSmoothingAlgorithm.cpp", "src\\CudaDartSmoothingAlgorithm3D.cpp", "src\\CudaDataOperationAlgorithm.cpp", "src\\CudaEMAlgorithm.cpp", "src\\CudaFDKAlgorithm3D.cpp", "src\\CudaFilteredBackProjectionAlgorithm.cpp", "src\\CudaForwardProjectionAlgorithm.cpp", "src\\CudaForwardProjectionAlgorithm3D.cpp", "src\\CudaProjector2D.cpp", "src\\CudaProjector3D.cpp", "src\\CudaReconstructionAlgorithm2D.cpp", "src\\CudaRoiSelectAlgorithm.cpp", "src\\CudaSartAlgorithm.cpp", "src\\CudaSirtAlgorithm.cpp", "src\\CudaSirtAlgorithm3D.cpp", ] P_astra["filters"]["CUDA\\cuda headers"] = [ "4e17872e-db7d-41bc-9760-fad1c253b583", "include\\astra\\cuda\\2d\\algo.h", "include\\astra\\cuda\\2d\\arith.h", "include\\astra\\cuda\\2d\\astra.h", "include\\astra\\cuda\\2d\\cgls.h", "include\\astra\\cuda\\2d\\darthelper.h", "include\\astra\\cuda\\2d\\dims.h", "include\\astra\\cuda\\2d\\em.h", "include\\astra\\cuda\\2d\\fan_bp.h", "include\\astra\\cuda\\2d\\fan_fp.h", "include\\astra\\cuda\\2d\\fbp.h", "include\\astra\\cuda\\2d\\fft.h", "include\\astra\\cuda\\2d\\par_bp.h", "include\\astra\\cuda\\2d\\par_fp.h", "include\\astra\\cuda\\2d\\sart.h", "include\\astra\\cuda\\2d\\sirt.h", "include\\astra\\cuda\\2d\\util.h", "include\\astra\\cuda\\3d\\algo3d.h", "include\\astra\\cuda\\3d\\arith3d.h", "include\\astra\\cuda\\3d\\astra3d.h", "include\\astra\\cuda\\3d\\cgls3d.h", "include\\astra\\cuda\\3d\\cone_bp.h", "include\\astra\\cuda\\3d\\cone_fp.h", "include\\astra\\cuda\\3d\\darthelper3d.h", "include\\astra\\cuda\\3d\\dims3d.h", "include\\astra\\cuda\\3d\\fdk.h", "include\\astra\\cuda\\3d\\mem3d.h", "include\\astra\\cuda\\3d\\par3d_bp.h", "include\\astra\\cuda\\3d\\par3d_fp.h", "include\\astra\\cuda\\3d\\sirt3d.h", "include\\astra\\cuda\\3d\\util3d.h", ] P_astra["filters"]["Algorithms\\headers"] = [ "a76ffd6d-3895-4365-b27e-fc9a72f2ed75", "include\\astra\\Algorithm.h", "include\\astra\\AlgorithmTypelist.h", "include\\astra\\ArtAlgorithm.h", "include\\astra\\BackProjectionAlgorithm.h", "include\\astra\\CglsAlgorithm.h", "include\\astra\\CudaBackProjectionAlgorithm.h", "include\\astra\\CudaBackProjectionAlgorithm3D.h", "include\\astra\\FilteredBackProjectionAlgorithm.h", "include\\astra\\ForwardProjectionAlgorithm.h", "include\\astra\\PluginAlgorithmFactory.h", "include\\astra\\ReconstructionAlgorithm2D.h", "include\\astra\\ReconstructionAlgorithm3D.h", "include\\astra\\SartAlgorithm.h", "include\\astra\\SirtAlgorithm.h", ] P_astra["filters"]["Data Structures\\headers"] = [ "444c44b0-6454-483a-be26-7cb9c8ab0b98", "include\\astra\\Data3D.h", "include\\astra\\Float32Data.h", "include\\astra\\Float32Data2D.h", "include\\astra\\Float32ProjectionData2D.h", "include\\astra\\Float32VolumeData2D.h", "include\\astra\\SheppLogan.h", "include\\astra\\SparseMatrix.h", ] P_astra["filters"]["Global & Other\\headers"] = [ "1c52efc8-a77e-4c72-b9be-f6429a87e6d7", "include\\astra\\AstraObjectFactory.h", "include\\astra\\AstraObjectManager.h", "include\\astra\\clog.h", "include\\astra\\CompositeGeometryManager.h", "include\\astra\\Config.h", "include\\astra\\Features.h", "include\\astra\\Filters.h", "include\\astra\\Fourier.h", "include\\astra\\Globals.h", "include\\astra\\Logging.h", "include\\astra\\PlatformDepSystemCode.h", "include\\astra\\Singleton.h", "include\\astra\\TypeList.h", "include\\astra\\Utilities.h", "include\\astra\\Vector3D.h", "include\\astra\\XMLConfig.h", "include\\astra\\XMLDocument.h", "include\\astra\\XMLNode.h", ] P_astra["filters"]["Geometries\\headers"] = [ "eddb31ba-0db7-4ab1-a490-36623aaf8901", "include\\astra\\ConeProjectionGeometry3D.h", "include\\astra\\ConeVecProjectionGeometry3D.h", "include\\astra\\FanFlatProjectionGeometry2D.h", "include\\astra\\FanFlatVecProjectionGeometry2D.h", "include\\astra\\GeometryUtil2D.h", "include\\astra\\GeometryUtil3D.h", "include\\astra\\ParallelProjectionGeometry2D.h", "include\\astra\\ParallelProjectionGeometry3D.h", "include\\astra\\ParallelVecProjectionGeometry2D.h", "include\\astra\\ParallelVecProjectionGeometry3D.h", "include\\astra\\ProjectionGeometry2D.h", "include\\astra\\ProjectionGeometry2DFactory.h", "include\\astra\\ProjectionGeometry3D.h", "include\\astra\\ProjectionGeometry3DFactory.h", "include\\astra\\SparseMatrixProjectionGeometry2D.h", "include\\astra\\VolumeGeometry2D.h", "include\\astra\\VolumeGeometry3D.h", ] P_astra["filters"]["Projectors\\headers"] = [ "91ae2cfd-6b45-46eb-ad99-2f16e5ce4b1e", "include\\astra\\DataProjector.h", "include\\astra\\DataProjectorPolicies.h", "include\\astra\\FanFlatBeamLineKernelProjector2D.h", "include\\astra\\FanFlatBeamStripKernelProjector2D.h", "include\\astra\\ParallelBeamBlobKernelProjector2D.h", "include\\astra\\ParallelBeamDistanceDrivenProjector2D.h", "include\\astra\\ParallelBeamLinearKernelProjector2D.h", "include\\astra\\ParallelBeamLineKernelProjector2D.h", "include\\astra\\ParallelBeamStripKernelProjector2D.h", "include\\astra\\Projector2D.h", "include\\astra\\Projector3D.h", "include\\astra\\ProjectorTypelist.h", "include\\astra\\SparseMatrixProjector2D.h", ] P_astra["filters"]["CUDA\\astra headers"] = [ "bd4e1f94-2f56-4db6-b946-20c29d65a351", "include\\astra\\CudaCglsAlgorithm.h", "include\\astra\\CudaCglsAlgorithm3D.h", "include\\astra\\CudaDartMaskAlgorithm.h", "include\\astra\\CudaDartMaskAlgorithm3D.h", "include\\astra\\CudaDartSmoothingAlgorithm.h", "include\\astra\\CudaDartSmoothingAlgorithm3D.h", "include\\astra\\CudaDataOperationAlgorithm.h", "include\\astra\\CudaEMAlgorithm.h", "include\\astra\\CudaFDKAlgorithm3D.h", "include\\astra\\CudaFilteredBackProjectionAlgorithm.h", "include\\astra\\CudaForwardProjectionAlgorithm.h", "include\\astra\\CudaForwardProjectionAlgorithm3D.h", "include\\astra\\CudaProjector2D.h", "include\\astra\\CudaProjector3D.h", "include\\astra\\CudaReconstructionAlgorithm2D.h", "include\\astra\\CudaRoiSelectAlgorithm.h", "include\\astra\\CudaSartAlgorithm.h", "include\\astra\\CudaSirtAlgorithm.h", "include\\astra\\CudaSirtAlgorithm3D.h", ] P_astra["filters"]["Projectors\\inline"] = [ "0daffd63-ba49-4a5f-8d7a-5322e0e74f22", "include\\astra\\DataProjectorPolicies.inl", "include\\astra\\FanFlatBeamLineKernelProjector2D.inl", "include\\astra\\FanFlatBeamStripKernelProjector2D.inl", "include\\astra\\ParallelBeamBlobKernelProjector2D.inl", "include\\astra\\ParallelBeamDistanceDrivenProjector2D.inl", "include\\astra\\ParallelBeamLinearKernelProjector2D.inl", "include\\astra\\ParallelBeamLineKernelProjector2D.inl", "include\\astra\\ParallelBeamStripKernelProjector2D.inl", "include\\astra\\SparseMatrixProjector2D.inl", ] P_astra["files"] = [] for f in P_astra["filters"]: P_astra["files"].extend(P_astra["filters"][f][1:]) P_astra["files"].sort() projects = [ P_astra, F_astra_mex, P0, P1, P2, P3, P4, P5, P6, P7, P8 ] bom = codecs.BOM_UTF8.decode("utf-8") class Configuration: def __init__(self, debug, cuda): self.debug = debug self.cuda = cuda def type(self): if self.debug: return "Debug" else: return "Release" def config(self): n = self.type() if self.cuda: n += "_CUDA" return n def platform(self): return "x64" def name(self): n = self.config() n += "|" n += self.platform() return n def target(self): n = "Astra" if self.cuda: n += "Cuda" n += "64" if self.debug: n += "D" return n configs = [ Configuration(a,b) for a in [ True, False ] for b in [ True, False ] ] def write_sln(): main_project = P_astra F = open("astra_vc14.sln", "w", encoding="utf-8") print(bom, file=F) print("Microsoft Visual Studio Solution File, Format Version 12.00", file=F) print("# Visual Studio 14", file=F) print("VisualStudioVersion = 14.0.25420.1", file=F) print("MinimumVisualStudioVersion = 10.0.40219.1", file=F) for p in projects: s = '''Project("{%s}") = "%s", "projects\\%s", "{%s}"''' % (p["type"], p["name"], p["file14"], p["uuid14"]) print(s, file=F) if "mex" in p["name"]: print("\tProjectSection(ProjectDependencies) = postProject", file=F) print("\t\t{%s} = {%s}" % (main_project["uuid14"], main_project["uuid14"]), file=F) print("\tEndProjectSection", file=F) print("EndProject", file=F) print("Global", file=F) print("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution", file=F) for c in configs: print("\t\t" + c.name() + " = " + c.name(), file=F) print("\tEndGlobalSection", file=F) print("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution", file=F) for p in projects: if "entries" in p: continue for c in configs: print("\t\t{" + p["uuid14"] + "}." + c.name() + ".ActiveCfg = " + c.name(), file=F) print("\t\t{" + p["uuid14"] + "}." + c.name() + ".Build.0 = " + c.name(), file=F) print("\tEndGlobalSection", file=F) print("\tGlobalSection(SolutionProperties) = preSolution", file=F) print("\t\tHideSolutionNode = FALSE", file=F) print("\tEndGlobalSection", file=F) print("\tGlobalSection(NestedProjects) = preSolution", file=F) for p in projects: if "entries" not in p: continue for e in p["entries"]: print("\t\t{" + e["uuid14"] + "} = {" + p["uuid14"] + "}", file=F) print("\tEndGlobalSection", file=F) print("EndGlobal", file=F) F.close() def write_project14_start(P, F): print(bom + '', file=F) print('', file=F) print(' ', file=F) for c in configs: print(' ', file=F) print(' ' + c.config() + '', file=F) print(' ' + c.platform() + '', file=F) print(' ', file=F) print(' ', file=F) print(' ', file=F) if 'mex' in P["name"]: print(' ' + P["name"] + '', file=F) print(' {' + P["uuid14"] + '}', file=F) if 'mex' in P["name"]: print(' astraMatlab', file=F) else: print(' ' + P["name"] + '', file=F) print(' 10.0.22621.0', file=F) print(' ', file=F) print(' ', file=F) for c in configs: print(''' ''' % (c.name(), ), file=F) print(' DynamicLibrary', file=F) if 'mex' not in P["name"]: if c.debug: print(' true', file=F) else: print(' false', file=F) print(' v141', file=F) if 'mex' not in P["name"]: if not c.debug: print(' true', file=F) print(' MultiByte', file=F) print(' ', file=F) print(' ', file=F) print(' ', file=F) if "mex" not in P["name"]: print(f' ', file=F) print(' ', file=F) for c in configs: print(''' ''' % (c.name(), ), file=F) print(''' ''', file=F) print(''' ''', file=F) print(' ', file=F) def write_project14_end(P, F): relpath = '..\\..\\..\\' if 'mex' in P["name"]: relpath += 'matlab\\mex\\' l = [ f for f in P["files"] if len(f) > 4 and f[-4:] == ".cpp" ] if l: print(' ', file=F) for f in l: if ("cuda" in f) or ("Cuda" in f): print(' ', file=F) for c in configs: if not c.cuda: print(''' true''' % (c.name(), ), file=F) print(' ', file=F) else: print(' ', file=F) print(' ', file=F) l = [ f for f in P["files"] if len(f) > 2 and f[-2:] == ".h" ] if l: print(' ', file=F) for f in l: print(' ', file=F) print(' ', file=F) l = [ f for f in P["files"] if len(f) > 3 and f[-3:] == ".cu" ] if l: print(' ', file=F) for f in l: print(' ', file=F) for c in configs: if not c.cuda: print(''' true''' % (c.name(), ), file=F) print(' ', file=F) print(' ', file=F) l = [ f for f in P["files"] if len(f) > 4 and f[-4:] == ".inl" ] if l: print(' ', file=F) for f in l: print(' ', file=F) print(' ', file=F) print(' ', file=F) print(' ', file=F) if "mex" not in P["name"]: print(f' ', file=F) print(' ', file=F) print('', end="", file=F) def write_main_project14(): P = P_astra; F = open(os.path.join("projects", P["file14"]), "w", encoding="utf-8") write_project14_start(P, F) for c in configs: print(''' ''' % (c.name(), ), file=F) print(' ..\\bin\\$(Platform)\\$(Configuration)\\', file=F) print(' $(OutDir)obj\\', file=F) print(' $(OutDir)obj\\', file=F) print(' $(SolutionDir)bin\\$(Platform)\\$(Configuration)\\obj\\', file=F) print(' .dll', file=F) print(' ' + c.target() + '', file=F) print(' true', file=F) print(' ', file=F) for c in configs: print(''' ''' % (c.name(), ), file=F) print(' ', file=F) if c.debug: print(' MultiThreadedDebugDLL', file=F) else: print(' MultiThreadedDLL', file=F) print(' Level3', file=F) print(' ..\\..\\..\\lib\\include;..\\..\\..\\include\\;%(AdditionalIncludeDirectories)', file=F) print(' true', file=F) if c.debug: print(' Disabled', file=F) else: print(' MaxSpeed', file=F) print(' true', file=F) print(' true', file=F) print(' AnySuitable', file=F) print(' Speed', file=F) d=' ' if c.cuda: d+="ASTRA_CUDA;" d+="__SSE2__;" d+="DLL_EXPORTS;_CRT_SECURE_NO_WARNINGS;" d+='%(PreprocessorDefinitions)' print(d, file=F) print(' true', file=F) print(' true', file=F) print(' stdcpp17', file=F) print(' ', file=F) print(' ', file=F) print(' true', file=F) if not c.debug: print(' true', file=F) print(' true', file=F) print(' ..\\bin\\' + c.platform() + '\\' + c.config() + '\\' + c.target() + '.dll', file=F) if c.cuda: print(' cudart.lib;cufft.lib;%(AdditionalDependencies)', file=F) l = ' '; l += '%(AdditionalLibraryDirectories)' if c.cuda: l += ';$(CudaToolkitLibDir)' l += '' print(l, file=F) print(' ', file=F) if c.cuda: print(' ', file=F) print(' 64', file=F) print(' true', file=F) print(f' {CUDA_CC[(CUDA_MAJOR,CUDA_MINOR)]}', file=F) print(' -std=c++17', file=F) print(' /std:c++17', file=F) print(' ', file=F) print(' ', file=F) write_project14_end(P, F) F.close() def write_mex_project14(P): F = open(os.path.join("projects", P["name"] + "_vc14.vcxproj"), "w", encoding="utf-8") write_project14_start(P, F) print(' ', file=F) print(' <_ProjectFileVersion>11.0.60610.1', file=F) print(' ', file=F) for c in configs: print(''' ''' % (c.name(), ), file=F) print(' ..\\bin\\$(Platform)\\$(Configuration)\\', file=F) print(' $(OutDir)obj\\$(ProjectName)\\', file=F) print(' $(ProjectName)_c', file=F) print(' .mexw64', file=F) print(' ', file=F) for c in configs: print(''' ''' % (c.name(), ), file=F) print(' ', file=F) if c.debug: print(' MultiThreadedDebugDLL', file=F) else: print(' MultiThreadedDLL', file=F) # print(' Level3', file=F) #print(' $(MATLAB_ROOT)\\extern\\include\\;..\\..\\lib\\include;..\\..\\include;%(AdditionalIncludeDirectories)', file=F) # FIXME: This CUDA_PATH shouldn't be necessary print(' $(MATLAB_ROOT)\\extern\\include\\;$(CUDA_PATH)\\include;..\\..\\..\\lib\\include;..\\..\\..\\include;%(AdditionalIncludeDirectories)', file=F) print(' true', file=F) if c.debug: print(' Disabled', file=F) else: print(' MaxSpeed', file=F) # print(' true', file=F) # print(' true', file=F) # print(' AnySuitable', file=F) # print(' Speed', file=F) d=' ' if c.cuda: d+="ASTRA_CUDA;" d+="__SSE2__;" d+="MATLAB_MEXCMD_RELEASE=700;" # d+="DLL_EXPORTS;_CRT_SECURE_NO_WARNINGS;" d+='%(PreprocessorDefinitions)' print(d, file=F) print(' stdcpp17', file=F) print(' true', file=F) # print(' true', file=F) # if c.debug: # EditAndContinue ?? print(' ', file=F) print(' ', file=F) # if not c.debug: # print(' true', file=F) # print(' true', file=F) print(' $(OutDir)$(ProjectName)_c.mexw64', file=F) print(' %s.lib;libmex.lib;libmx.lib;libut.lib;%%(AdditionalDependencies)' % (c.target(), ), file=F) l = ' '; l += '..\\bin\\x64\\' l += c.config() l += ';$(MATLAB_ROOT)\\extern\\lib\\win64\\microsoft' l += ';%(AdditionalLibraryDirectories)' l += '' print(l, file=F) print(' mex.def', file=F) print(' true', file=F) print(' ', file=F) print(' ', file=F) write_project14_end(P, F) F.close() def write_main_filters14(): P = P_astra F = open(os.path.join("projects", P["name"] + ".vcxproj.filters"), "w", encoding="utf-8") print(bom + '', file=F) print('', file=F) print(' ', file=F) for Filter in P_astra["filter_names"]: L = P_astra["filters"][Filter][1:] l = [ f for f in L if len(f) > 3 and f[-3:] == ".cu" ] for f in l: print(' ', file=F) print(' ' + Filter + '', file=F) print(' ', file=F) print(' ', file=F) print(' ', file=F) for Filter in P_astra["filter_names"]: L = P_astra["filters"][Filter][1:] l = [ f for f in L if len(f) > 4 and f[-4:] == ".cpp" ] for f in l: print(' ', file=F) print(' ' + Filter + '', file=F) print(' ', file=F) print(' ', file=F) print(' ', file=F) for Filter in P_astra["filter_names"]: L = P_astra["filters"][Filter][1:] l = [ f for f in L if len(f) > 2 and f[-2:] == ".h" ] for f in l: print(' ', file=F) print(' ' + Filter + '', file=F) print(' ', file=F) print(' ', file=F) print(' ', file=F) for Filter in P_astra["filter_names"]: L = P_astra["filters"][Filter][1:] l = [ f for f in L if len(f) > 4 and f[-4:] == ".inl" ] for f in l: print(' ', file=F) print(' ' + Filter + '', file=F) print(' ', file=F) print(' ', file=F) print(' ', file=F) for f in P["filter_names"]: print(' ', file=F) print(' {' + P["filters"][f][0] + '}', file=F) print(' ', file=F) print(' ', file=F) print('', end="", file=F) F.close() def parse_cuda_version(ver): return [ int(x) for x in ver.split('.') ] def check_cuda_version(ver): try: major, minor = parse_cuda_version(ver) if major >= 9 and minor >= 0: return True except: pass return False if (len(sys.argv) != 2) or not check_cuda_version(sys.argv[1]): print("Usage: python gen.py [10.2|11.0|...]", file=sys.stderr) sys.exit(1) CUDA_MAJOR, CUDA_MINOR = parse_cuda_version(sys.argv[1]) try: open("../../src/AstraObjectManager.cpp", "r") except IOError: print("Run gen.py from the build/msvc directory", file=sys.stderr) sys.exit(1) # Change directory to main dir os.makedirs("projects", exist_ok=True) write_sln() write_main_project14() write_main_filters14() write_mex_project14(P0) write_mex_project14(P1) write_mex_project14(P2) write_mex_project14(P3) write_mex_project14(P4) write_mex_project14(P5) write_mex_project14(P6) write_mex_project14(P7) write_mex_project14(P8) astra-toolbox-2.3.0/build/msvc/projects/000077500000000000000000000000001475635207100202245ustar00rootroot00000000000000astra-toolbox-2.3.0/build/msvc/projects/astra_mex_algorithm_vc14.vcxproj000066400000000000000000000235161475635207100265360ustar00rootroot00000000000000 Debug_CUDA x64 Debug x64 Release_CUDA x64 Release x64 astra_mex_algorithm {CE5EF874-830C-4C10-8651-CCA2A34ED9E4} astraMatlab 10.0.22621.0 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 <_ProjectFileVersion>11.0.60610.1 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true astra-toolbox-2.3.0/build/msvc/projects/astra_mex_data2d_vc14.vcxproj000066400000000000000000000241761475635207100257120ustar00rootroot00000000000000 Debug_CUDA x64 Debug x64 Release_CUDA x64 Release x64 astra_mex_data2d {D2CDCDB3-7AD9-4853-8D87-BDB1DAD9C9C1} astraMatlab 10.0.22621.0 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 <_ProjectFileVersion>11.0.60610.1 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true astra-toolbox-2.3.0/build/msvc/projects/astra_mex_data3d_vc14.vcxproj000066400000000000000000000241761475635207100257130ustar00rootroot00000000000000 Debug_CUDA x64 Debug x64 Release_CUDA x64 Release x64 astra_mex_data3d {2A7084C6-62ED-4235-85F4-094C17689DEB} astraMatlab 10.0.22621.0 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 <_ProjectFileVersion>11.0.60610.1 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true astra-toolbox-2.3.0/build/msvc/projects/astra_mex_direct_vc14.vcxproj000066400000000000000000000241761475635207100260250ustar00rootroot00000000000000 Debug_CUDA x64 Debug x64 Release_CUDA x64 Release x64 astra_mex_direct {47460476-912B-4313-8B10-BDF1D60A84C4} astraMatlab 10.0.22621.0 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 <_ProjectFileVersion>11.0.60610.1 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true astra-toolbox-2.3.0/build/msvc/projects/astra_mex_log_vc14.vcxproj000066400000000000000000000235021475635207100253240ustar00rootroot00000000000000 Debug_CUDA x64 Debug x64 Release_CUDA x64 Release x64 astra_mex_log {88539382-66DB-4BBC-A48E-8B6B3CA6064F} astraMatlab 10.0.22621.0 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 <_ProjectFileVersion>11.0.60610.1 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true astra-toolbox-2.3.0/build/msvc/projects/astra_mex_matrix_vc14.vcxproj000066400000000000000000000235101475635207100260460ustar00rootroot00000000000000 Debug_CUDA x64 Debug x64 Release_CUDA x64 Release x64 astra_mex_matrix {6BFA8857-37EB-4E43-A97C-B860E21599F5} astraMatlab 10.0.22621.0 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 <_ProjectFileVersion>11.0.60610.1 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true astra-toolbox-2.3.0/build/msvc/projects/astra_mex_projector3d_vc14.vcxproj000066400000000000000000000235221475635207100270030ustar00rootroot00000000000000 Debug_CUDA x64 Debug x64 Release_CUDA x64 Release x64 astra_mex_projector3d {CA85BDA0-9BDD-495E-B200-BFE863EB6318} astraMatlab 10.0.22621.0 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 <_ProjectFileVersion>11.0.60610.1 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true astra-toolbox-2.3.0/build/msvc/projects/astra_mex_projector_vc14.vcxproj000066400000000000000000000235161475635207100265570ustar00rootroot00000000000000 Debug_CUDA x64 Debug x64 Release_CUDA x64 Release x64 astra_mex_projector {85ECCF1D-C5F6-4E0E-A4F9-0DE7C0B916B2} astraMatlab 10.0.22621.0 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 <_ProjectFileVersion>11.0.60610.1 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true astra-toolbox-2.3.0/build/msvc/projects/astra_mex_vc14.vcxproj000066400000000000000000000234721475635207100244710ustar00rootroot00000000000000 Debug_CUDA x64 Debug x64 Release_CUDA x64 Release x64 astra_mex {6FDF72C4-A855-4F1C-A401-6500040B5E28} astraMatlab 10.0.22621.0 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 DynamicLibrary v141 <_ProjectFileVersion>11.0.60610.1 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\$(ProjectName)\ $(ProjectName)_c .mexw64 MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDebugDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true Disabled __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64D.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Debug;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed ASTRA_CUDA;__SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 AstraCuda64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release_CUDA;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true MultiThreadedDLL $(MATLAB_ROOT)\extern\include\;$(CUDA_PATH)\include;..\..\..\lib\include;..\..\..\include;%(AdditionalIncludeDirectories) true MaxSpeed __SSE2__;MATLAB_MEXCMD_RELEASE=700;%(PreprocessorDefinitions) stdcpp17 true $(OutDir)$(ProjectName)_c.mexw64 Astra64.lib;libmex.lib;libmx.lib;libut.lib;%(AdditionalDependencies) ..\bin\x64\Release;$(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) mex.def true astra-toolbox-2.3.0/build/msvc/projects/astra_vc14.vcxproj000066400000000000000000001154171475635207100236210ustar00rootroot00000000000000 Debug_CUDA x64 Debug x64 Release_CUDA x64 Release x64 {DABD9D82-609E-4C71-B1CA-A41B07495290} astra_vc14 10.0.22621.0 DynamicLibrary true v141 MultiByte DynamicLibrary true v141 MultiByte DynamicLibrary false v141 true MultiByte DynamicLibrary false v141 true MultiByte ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\ $(OutDir)obj\ $(SolutionDir)bin\$(Platform)\$(Configuration)\obj\ .dll AstraCuda64D true ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\ $(OutDir)obj\ $(SolutionDir)bin\$(Platform)\$(Configuration)\obj\ .dll Astra64D true ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\ $(OutDir)obj\ $(SolutionDir)bin\$(Platform)\$(Configuration)\obj\ .dll AstraCuda64 true ..\bin\$(Platform)\$(Configuration)\ $(OutDir)obj\ $(OutDir)obj\ $(SolutionDir)bin\$(Platform)\$(Configuration)\obj\ .dll Astra64 true MultiThreadedDebugDLL Level3 ..\..\..\lib\include;..\..\..\include\;%(AdditionalIncludeDirectories) true Disabled ASTRA_CUDA;__SSE2__;DLL_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true true stdcpp17 true ..\bin\x64\Debug_CUDA\AstraCuda64D.dll cudart.lib;cufft.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories);$(CudaToolkitLibDir) 64 true compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_87,sm_87;compute_89,sm_89;compute_90,sm_90;compute_100,sm_100;compute_101,sm_101;compute_120,sm_120;compute_120,compute_120 -std=c++17 /std:c++17 MultiThreadedDebugDLL Level3 ..\..\..\lib\include;..\..\..\include\;%(AdditionalIncludeDirectories) true Disabled __SSE2__;DLL_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true true stdcpp17 true ..\bin\x64\Debug\Astra64D.dll %(AdditionalLibraryDirectories) MultiThreadedDLL Level3 ..\..\..\lib\include;..\..\..\include\;%(AdditionalIncludeDirectories) true MaxSpeed true true AnySuitable Speed ASTRA_CUDA;__SSE2__;DLL_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true true stdcpp17 true true true ..\bin\x64\Release_CUDA\AstraCuda64.dll cudart.lib;cufft.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories);$(CudaToolkitLibDir) 64 true compute_50,sm_50;compute_60,sm_60;compute_70,sm_70;compute_75,sm_75;compute_80,sm_80;compute_86,sm_86;compute_87,sm_87;compute_89,sm_89;compute_90,sm_90;compute_100,sm_100;compute_101,sm_101;compute_120,sm_120;compute_120,compute_120 -std=c++17 /std:c++17 MultiThreadedDLL Level3 ..\..\..\lib\include;..\..\..\include\;%(AdditionalIncludeDirectories) true MaxSpeed true true AnySuitable Speed __SSE2__;DLL_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true true stdcpp17 true true true ..\bin\x64\Release\Astra64.dll %(AdditionalLibraryDirectories) true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true astra-toolbox-2.3.0/build/msvc/projects/astra_vc14.vcxproj.filters000066400000000000000000001005371475635207100252650ustar00rootroot00000000000000 CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source CUDA\cuda source Algorithms\source Algorithms\source Algorithms\source Algorithms\source Algorithms\source Algorithms\source Algorithms\source Algorithms\source Algorithms\source Algorithms\source Algorithms\source Data Structures\source Data Structures\source Data Structures\source Data Structures\source Data Structures\source Data Structures\source Data Structures\source Global & Other\source Global & Other\source Global & Other\source Global & Other\source Global & Other\source Global & Other\source Global & Other\source Global & Other\source Global & Other\source Global & Other\source Global & Other\source Global & Other\source Global & Other\source Global & Other\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Geometries\source Projectors\source Projectors\source Projectors\source Projectors\source Projectors\source Projectors\source Projectors\source Projectors\source Projectors\source Projectors\source Projectors\source Projectors\source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source CUDA\astra source Algorithms\headers Algorithms\headers Algorithms\headers Algorithms\headers Algorithms\headers Algorithms\headers Algorithms\headers Algorithms\headers Algorithms\headers Algorithms\headers Algorithms\headers Algorithms\headers Algorithms\headers Algorithms\headers Data Structures\headers Data Structures\headers Data Structures\headers Data Structures\headers Data Structures\headers Data Structures\headers Data Structures\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Global & Other\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Geometries\headers Projectors\headers Projectors\headers Projectors\headers Projectors\headers Projectors\headers Projectors\headers Projectors\headers Projectors\headers Projectors\headers Projectors\headers Projectors\headers Projectors\headers Projectors\headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\astra headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers CUDA\cuda headers Projectors\inline Projectors\inline Projectors\inline Projectors\inline Projectors\inline Projectors\inline Projectors\inline Projectors\inline Projectors\inline {262b0d17-774a-4cb1-b51a-b358d2d02791} {76d6d672-670b-4454-b3ab-10dc8f9b8710} {77a581a9-60da-4265-97c0-80cdf97408c0} {c1af0e56-5fcc-4e75-b5db-88eeb4148185} {72fbe846-10ef-4c52-88df-13bd66c4cbfc} {7ef37c12-c98c-4dd6-938d-12f49279eae0} {a76ffd6d-3895-4365-b27e-fc9a72f2ed75} {9df653ab-26c3-4bec-92a2-3dda22fda761} {444c44b0-6454-483a-be26-7cb9c8ab0b98} {95346487-8185-487b-a794-3e7fb5fcbd4c} {1c52efc8-a77e-4c72-b9be-f6429a87e6d7} {1546cb47-7e5b-42c2-b695-ef172024c14b} {eddb31ba-0db7-4ab1-a490-36623aaf8901} {dc27bff7-4256-4311-a131-47612a44af20} {91ae2cfd-6b45-46eb-ad99-2f16e5ce4b1e} {0daffd63-ba49-4a5f-8d7a-5322e0e74f22} {2d60e3c8-7874-4cee-b139-991ac15e811d} {bd4e1f94-2f56-4db6-b946-20c29d65a351} {bbef012e-598a-456f-90d8-416bdcb4221c} {4e17872e-db7d-41bc-9760-fad1c253b583} {04a878ed-77b4-4525-9bc2-38ccd65282c5} astra-toolbox-2.3.0/build/msvc/projects/mex.def000066400000000000000000000000231475635207100214700ustar00rootroot00000000000000EXPORTS mexFunctionastra-toolbox-2.3.0/build/pypi/000077500000000000000000000000001475635207100164045ustar00rootroot00000000000000astra-toolbox-2.3.0/build/pypi/build.sh000077500000000000000000000021111475635207100200350ustar00rootroot00000000000000#!/bin/sh set -e BRANCH=master URL=https://github.com/astra-toolbox/astra-toolbox echo "Cloning from ${URL}" echo " branch: ${BRANCH}" cd /root git clone --depth 1 --branch ${BRANCH} ${URL} cd astra-toolbox/build/linux ./autogen.sh ./configure --with-python=python3.9 --with-cuda=/usr/local/cuda-12.1 --with-install-type=module --with-python-wheel-platform=manylinux2014_x86_64 make -j 20 all python-wheel cp python/dist/*.whl /out make clean ./configure --with-python=python3.10 --with-cuda=/usr/local/cuda-12.1 --with-install-type=module --with-python-wheel-platform=manylinux2014_x86_64 make -j 20 all python-wheel cp python/dist/*.whl /out make clean ./configure --with-python=python3.11 --with-cuda=/usr/local/cuda-12.1 --with-install-type=module --with-python-wheel-platform=manylinux2014_x86_64 make -j 20 all python-wheel cp python/dist/*.whl /out make clean ./configure --with-python=python3.12 --with-cuda=/usr/local/cuda-12.1 --with-install-type=module --with-python-wheel-platform=manylinux2014_x86_64 make -j 20 all python-wheel cp python/dist/*.whl /out make clean astra-toolbox-2.3.0/build/pypi/release.sh000077500000000000000000000002671475635207100203700ustar00rootroot00000000000000#!/bin/bash set -e D=`mktemp -d` cp build.sh $D podman run --rm -v $D:/out:z astra-build-manylinux /bin/bash /out/build.sh rm -f $D/build.sh mkdir -p pkgs mv $D/* pkgs rmdir $D astra-toolbox-2.3.0/cuda/000077500000000000000000000000001475635207100152405ustar00rootroot00000000000000astra-toolbox-2.3.0/cuda/2d/000077500000000000000000000000001475635207100155455ustar00rootroot00000000000000astra-toolbox-2.3.0/cuda/2d/algo.cu000066400000000000000000000164371475635207100170330ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/algo.h" #include "astra/cuda/2d/par_fp.h" #include "astra/cuda/2d/fan_fp.h" #include "astra/cuda/2d/par_bp.h" #include "astra/cuda/2d/fan_bp.h" #include "astra/cuda/2d/util.h" #include "astra/cuda/2d/arith.h" #include "astra/cuda/2d/astra.h" #include namespace astraCUDA { ReconAlgo::ReconAlgo() { parProjs = 0; fanProjs = 0; useVolumeMask = false; useSinogramMask = false; D_maskData = 0; D_smaskData = 0; D_sinoData = 0; D_volumeData = 0; useMinConstraint = false; useMaxConstraint = false; freeGPUMemory = false; } ReconAlgo::~ReconAlgo() { reset(); } void ReconAlgo::reset() { delete[] parProjs; delete[] fanProjs; if (freeGPUMemory) { cudaFree(D_maskData); cudaFree(D_smaskData); cudaFree(D_sinoData); cudaFree(D_volumeData); } parProjs = 0; fanProjs = 0; useVolumeMask = false; useSinogramMask = false; D_maskData = 0; D_smaskData = 0; D_sinoData = 0; D_volumeData = 0; useMinConstraint = false; useMaxConstraint = false; freeGPUMemory = false; } bool ReconAlgo::setGPUIndex(int iGPUIndex) { if (iGPUIndex != -1) { cudaSetDevice(iGPUIndex); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } return true; } bool ReconAlgo::enableVolumeMask() { useVolumeMask = true; return true; } bool ReconAlgo::enableSinogramMask() { useSinogramMask = true; return true; } bool ReconAlgo::setGeometry(const astra::CVolumeGeometry2D* pVolGeom, const astra::CProjectionGeometry2D* pProjGeom) { bool ok; ok = convertAstraGeometry_dims(pVolGeom, pProjGeom, dims); if (!ok) return false; delete[] parProjs; parProjs = 0; delete[] fanProjs; fanProjs = 0; fProjectorScale = 1.0f; ok = convertAstraGeometry(pVolGeom, pProjGeom, parProjs, fanProjs, fProjectorScale); if (!ok) return false; return true; } bool ReconAlgo::setSuperSampling(int raysPerDet, int raysPerPixelDim) { if (raysPerDet <= 0 || raysPerPixelDim <= 0) return false; dims.iRaysPerDet = raysPerDet; dims.iRaysPerPixelDim = raysPerPixelDim; return true; } bool ReconAlgo::setVolumeMask(float* _D_maskData, unsigned int _maskPitch) { assert(useVolumeMask); D_maskData = _D_maskData; maskPitch = _maskPitch; return true; } bool ReconAlgo::setSinogramMask(float* _D_smaskData, unsigned int _smaskPitch) { assert(useSinogramMask); D_smaskData = _D_smaskData; smaskPitch = _smaskPitch; return true; } bool ReconAlgo::setBuffers(float* _D_volumeData, unsigned int _volumePitch, float* _D_projData, unsigned int _projPitch) { D_volumeData = _D_volumeData; volumePitch = _volumePitch; D_sinoData = _D_projData; sinoPitch = _projPitch; return true; } bool ReconAlgo::setMinConstraint(float fMin) { fMinConstraint = fMin; useMinConstraint = true; return true; } bool ReconAlgo::setMaxConstraint(float fMax) { fMaxConstraint = fMax; useMaxConstraint = true; return true; } bool ReconAlgo::allocateBuffers() { bool ok; ok = allocateVolumeData(D_volumeData, volumePitch, dims); if (!ok) return false; ok = allocateProjectionData(D_sinoData, sinoPitch, dims); if (!ok) { cudaFree(D_volumeData); D_volumeData = 0; return false; } if (useVolumeMask) { ok = allocateVolumeData(D_maskData, maskPitch, dims); if (!ok) { cudaFree(D_volumeData); cudaFree(D_sinoData); D_volumeData = 0; D_sinoData = 0; return false; } } if (useSinogramMask) { ok = allocateProjectionData(D_smaskData, smaskPitch, dims); if (!ok) { cudaFree(D_volumeData); cudaFree(D_sinoData); cudaFree(D_maskData); D_volumeData = 0; D_sinoData = 0; D_maskData = 0; return false; } } freeGPUMemory = true; return true; } bool ReconAlgo::copyDataToGPU(const float* pfSinogram, unsigned int iSinogramPitch, const float* pfReconstruction, unsigned int iReconstructionPitch, const float* pfVolMask, unsigned int iVolMaskPitch, const float* pfSinoMask, unsigned int iSinoMaskPitch) { if (!pfSinogram) return false; if (!pfReconstruction) return false; bool ok = copySinogramToDevice(pfSinogram, iSinogramPitch, dims, D_sinoData, sinoPitch); if (!ok) return false; ok = copyVolumeToDevice(pfReconstruction, iReconstructionPitch, dims, D_volumeData, volumePitch); if (!ok) return false; if (useVolumeMask) { if (!pfVolMask) return false; ok = copyVolumeToDevice(pfVolMask, iVolMaskPitch, dims, D_maskData, maskPitch); if (!ok) return false; } if (useSinogramMask) { if (!pfSinoMask) return false; ok = copySinogramToDevice(pfSinoMask, iSinoMaskPitch, dims, D_smaskData, smaskPitch); if (!ok) return false; } return true; } bool ReconAlgo::getReconstruction(float* pfReconstruction, unsigned int iReconstructionPitch) const { bool ok = copyVolumeFromDevice(pfReconstruction, iReconstructionPitch, dims, D_volumeData, volumePitch); if (!ok) return false; return true; } bool ReconAlgo::callFP(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, float outputScale) { if (parProjs) { assert(!fanProjs); return FP(D_volumeData, volumePitch, D_projData, projPitch, dims, parProjs, fProjectorScale * outputScale); } else { assert(fanProjs); return FanFP(D_volumeData, volumePitch, D_projData, projPitch, dims, fanProjs, fProjectorScale * outputScale); } } bool ReconAlgo::callBP(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, float outputScale) { if (parProjs) { assert(!fanProjs); return BP(D_volumeData, volumePitch, D_projData, projPitch, dims, parProjs, fProjectorScale * outputScale); } else { assert(fanProjs); return FanBP(D_volumeData, volumePitch, D_projData, projPitch, dims, fanProjs, fProjectorScale * outputScale); } } } astra-toolbox-2.3.0/cuda/2d/arith.cu000066400000000000000000000531541475635207100172150ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/util.h" #include "astra/cuda/2d/arith.h" #include namespace astraCUDA { struct opAddScaled { __device__ void operator()(float& out, const float in, const float inp) { out += in * inp; } }; struct opScaleAndAdd { __device__ void operator()(float& out, const float in, const float inp) { out = in + out * inp; } }; struct opAddMulScaled { __device__ void operator()(float& out, const float in1, const float in2, const float inp) { out += in1 * in2 * inp; } }; struct opAddMul { __device__ void operator()(float& out, const float in1, const float in2) { out += in1 * in2; } }; struct opAdd { __device__ void operator()(float& out, const float in) { out += in; } }; struct opAdd2 { __device__ void operator()(float& out, const float in1, const float in2) { out += in1 + in2; } }; struct opMul { __device__ void operator()(float& out, const float in) { out *= in; } }; struct opDiv { __device__ void operator()(float& out, const float in) { if (in > 0.000001f) // out is assumed to be positive out /= in; else out = 0.0f; } }; struct opMul2 { __device__ void operator()(float& out, const float in1, const float in2) { out *= in1 * in2; } }; struct opDividedBy { __device__ void operator()(float& out, const float in) { if (out > 0.000001f) // out is assumed to be positive out = in / out; else out = 0.0f; } }; struct opInvert { __device__ void operator()(float& out) { if (out > 0.000001f) // out is assumed to be positive out = 1 / out; else out = 0.0f; } }; struct opSet { __device__ void operator()(float& out, const float inp) { out = inp; } }; struct opClampMin { __device__ void operator()(float& out, const float inp) { if (out < inp) out = inp; } }; struct opClampMax { __device__ void operator()(float& out, const float inp) { if (out > inp) out = inp; } }; struct opClampMinMask { __device__ void operator()(float& out, const float in) { if (out < in) out = in; } }; struct opClampMaxMask { __device__ void operator()(float& out, const float in) { if (out > in) out = in; } }; struct opSetMaskedValues { __device__ void operator()(float& out, const float in, const float inp) { if (!in) out = inp; } }; struct opSegmentAndMask { __device__ void operator()(float& out1, float& out2, const float inp1, const float inp2) { if (out1 >= inp1) { out1 = inp2; out2 = 0.0f; } } }; struct opMulMask { __device__ void operator()(float& out, const float mask, const float in) { if (mask > 0.0f) { out *= in; } } }; template __global__ void devtoD(float* pfOut, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut[off]); off += pitch; y++; } } template __global__ void devFtoD(float* pfOut, float fParam, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut[off], fParam); off += pitch; y++; } } template __global__ void devFFtoDD(float* pfOut1, float* pfOut2, float fParam1, float fParam2, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut1[off], pfOut2[off], fParam1, fParam2); off += pitch; y++; } } template __global__ void devDtoD(float* pfOut, const float* pfIn, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut[off], pfIn[off]); off += pitch; y++; } } template __global__ void devDFtoD(float* pfOut, const float* pfIn, float fParam, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut[off], pfIn[off], fParam); off += pitch; y++; } } template __global__ void devDDtoD(float* pfOut, const float* pfIn1, const float* pfIn2, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut[off], pfIn1[off], pfIn2[off]); off += pitch; y++; } } template __global__ void devDDFtoD(float* pfOut, const float* pfIn1, const float* pfIn2, float fParam, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut[off], pfIn1[off], pfIn2[off], fParam); off += pitch; y++; } } template bool processVolCopy(float* out, const SDimensions& dims) { float* D_out; size_t width = dims.iVolWidth; unsigned int pitch; bool ok = true; ok &= allocateVolumeData(D_out, pitch, dims); if (!ok) return false; ok &= copyVolumeToDevice(out, width, dims, D_out, pitch); ok &= processVol(D_out, pitch, dims); ok &= copyVolumeFromDevice(out, width, dims, D_out, pitch); cudaFree(D_out); return ok; } template bool processVolCopy(float* out, float param, const SDimensions& dims) { float* D_out; size_t width = dims.iVolWidth; unsigned int pitch; bool ok = true; ok &= allocateVolumeData(D_out, pitch, dims); if (!ok) return false; ok &= copyVolumeToDevice(out, width, dims, D_out, pitch); ok &= processVol(D_out, param, pitch, dims); ok &= copyVolumeFromDevice(out, width, dims, D_out, pitch); cudaFree(D_out); return ok; } template bool processVolCopy(float* out1, float* out2, float param1, float param2, const SDimensions& dims) { float* D_out1; float* D_out2; size_t width = dims.iVolWidth; unsigned int pitch; bool ok = true; ok &= allocateVolumeData(D_out1, pitch, dims); if (!ok) return false; ok &= allocateVolumeData(D_out2, pitch, dims); if (!ok) { cudaFree(D_out1); return false; } ok &= copyVolumeToDevice(out1, width, dims, D_out1, pitch); ok &= copyVolumeToDevice(out2, width, dims, D_out2, pitch); ok &= processVol(D_out1, D_out2, param1, param2, pitch, dims); ok &= copyVolumeFromDevice(out1, width, dims, D_out1, pitch); ok &= copyVolumeFromDevice(out2, width, dims, D_out2, pitch); cudaFree(D_out1); cudaFree(D_out2); return ok; } template bool processVolCopy(float* out, const float* in, const SDimensions& dims) { float* D_out; float* D_in; size_t width = dims.iVolWidth; unsigned int pitch; bool ok = true; ok &= allocateVolumeData(D_out, pitch, dims); if (!ok) return false; ok &= allocateVolumeData(D_in, pitch, dims); if (!ok) { cudaFree(D_out); return false; } ok &= copyVolumeToDevice(out, width, dims, D_out, pitch); ok &= copyVolumeToDevice(in, width, dims, D_in, pitch); ok &= processVol(D_out, D_in, pitch, dims); ok &= copyVolumeFromDevice(out, width, dims, D_out, pitch); cudaFree(D_out); cudaFree(D_in); return ok; } template bool processVolCopy(float* out, const float* in, float param, const SDimensions& dims) { float* D_out; float* D_in; size_t width = dims.iVolWidth; unsigned int pitch; bool ok = true; ok &= allocateVolumeData(D_out, pitch, dims); if (!ok) return false; ok &= allocateVolumeData(D_in, pitch, dims); if (!ok) { cudaFree(D_out); return false; } ok &= copyVolumeToDevice(out, width, dims, D_out, pitch); ok &= copyVolumeToDevice(in, width, dims, D_in, pitch); ok &= processVol(D_out, D_in, param, pitch, dims); ok &= copyVolumeFromDevice(out, width, dims, D_out, pitch); cudaFree(D_out); cudaFree(D_in); return ok; } template bool processVolCopy(float* out, const float* in1, const float* in2, const SDimensions& dims) { float* D_out; float* D_in1; float* D_in2; size_t width = dims.iVolWidth; unsigned int pitch; bool ok = true; ok &= allocateVolumeData(D_out, pitch, dims); if (!ok) return false; ok &= allocateVolumeData(D_in1, pitch, dims); if (!ok) { cudaFree(D_out); return false; } ok &= allocateVolumeData(D_in2, pitch, dims); if (!ok) { cudaFree(D_out); cudaFree(D_in1); return false; } ok &= copyVolumeToDevice(out, width, dims, D_out, pitch); ok &= copyVolumeToDevice(in1, width, dims, D_in1, pitch); ok &= copyVolumeToDevice(in2, width, dims, D_in2, pitch); ok &= processVol(D_out, D_in1, D_in2, pitch, dims); ok &= copyVolumeFromDevice(out, width, dims, D_out, pitch); cudaFree(D_out); cudaFree(D_in1); cudaFree(D_in2); return ok; } template bool processVolCopy(float* out, const float* in1, const float* in2, float param, const SDimensions& dims) { float* D_out; float* D_in1; float* D_in2; size_t width = dims.iVolWidth; unsigned int pitch; bool ok = true; ok &= allocateVolumeData(D_out, pitch, dims); if (!ok) return false; ok &= allocateVolumeData(D_in1, pitch, dims); if (!ok) { cudaFree(D_out); return false; } ok &= allocateVolumeData(D_in2, pitch, dims); if (!ok) { cudaFree(D_out); cudaFree(D_in1); return false; } ok &= copyVolumeToDevice(out, width, dims, D_out, pitch); ok &= copyVolumeToDevice(in1, width, dims, D_in1, pitch); ok &= copyVolumeToDevice(in2, width, dims, D_in2, pitch); ok &= processVol(D_out, D_in1, D_in2, param, pitch, dims); ok &= copyVolumeFromDevice(out, width, dims, D_out, pitch); cudaFree(D_out); cudaFree(D_in1); cudaFree(D_in2); return ok; } template bool processData(float* pfOut, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((width+15)/16, (height+511)/512); devtoD<<>>(pfOut, pitch, width, height); return stream.syncIfSync(__FUNCTION__); } template bool processData(float* pfOut, float fParam, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((width+15)/16, (height+15)/16); devFtoD<<>>(pfOut, fParam, pitch, width, height); return stream.syncIfSync(__FUNCTION__); } template bool processData(float* pfOut1, float* pfOut2, float fParam1, float fParam2, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((width+15)/16, (height+15)/16); devFFtoDD<<>>(pfOut1, pfOut2, fParam1, fParam2, pitch, width, height); return stream.syncIfSync(__FUNCTION__); } template bool processData(float* pfOut, const float* pfIn, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((width+15)/16, (height+15)/16); devDtoD<<>>(pfOut, pfIn, pitch, width, height); return stream.syncIfSync(__FUNCTION__); } template bool processData(float* pfOut, const float* pfIn, float fParam, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((width+15)/16, (height+15)/16); devDFtoD<<>>(pfOut, pfIn, fParam, pitch, width, height); return stream.syncIfSync(__FUNCTION__); } template bool processData(float* pfOut, const float* pfIn1, const float* pfIn2, float fParam, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((width+15)/16, (height+15)/16); devDDFtoD<<>>(pfOut, pfIn1, pfIn2, fParam, pitch, width, height); return stream.syncIfSync(__FUNCTION__); } template bool processData(float* pfOut, const float* pfIn1, const float* pfIn2, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((width+15)/16, (height+15)/16); devDDtoD<<>>(pfOut, pfIn1, pfIn2, pitch, width, height); return stream.syncIfSync(__FUNCTION__); } template bool processVol(float* out, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out, pitch, dims.iVolWidth, dims.iVolHeight, _stream); } template bool processVol(float* out, float param, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out, param, pitch, dims.iVolWidth, dims.iVolHeight, _stream); } template bool processVol(float* out1, float* out2, float param1, float param2, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out1, out2, param1, param2, pitch, dims.iVolWidth, dims.iVolHeight, _stream); } template bool processVol(float* out, const float* in, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out, in, pitch, dims.iVolWidth, dims.iVolHeight, _stream); } template bool processVol(float* out, const float* in, float param, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out, in, param, pitch, dims.iVolWidth, dims.iVolHeight, _stream); } template bool processVol(float* out, const float* in1, const float* in2, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out, in1, in2, pitch, dims.iVolWidth, dims.iVolHeight, _stream); } template bool processVol(float* out, const float* in1, const float* in2, float param, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out, in2, in2, param, pitch, dims.iVolWidth, dims.iVolHeight, _stream); } template bool processSino(float* out, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out, pitch, dims.iProjDets, dims.iProjAngles, _stream); } template bool processSino(float* out, float param, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out, param, pitch, dims.iProjDets, dims.iProjAngles, _stream); } template bool processSino(float* out1, float* out2, float param1, float param2, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out1, out2, param1, param2, pitch, dims.iProjDets, dims.iProjAngles, _stream); } template bool processSino(float* out, const float* in, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out, in, pitch, dims.iProjDets, dims.iProjAngles, _stream); } template bool processSino(float* out, const float* in, float param, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out, in, param, pitch, dims.iProjDets, dims.iProjAngles, _stream); } template bool processSino(float* out, const float* in1, const float* in2, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out, in1, in2, pitch, dims.iProjDets, dims.iProjAngles, _stream); } template bool processSino(float* out, const float* in1, const float* in2, float param, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return processData(out, in2, in2, param, pitch, dims.iProjDets, dims.iProjAngles, _stream); } #define INST_DFtoD(name) \ template bool processVolCopy(float* out, const float* in, float param, const SDimensions& dims); \ template bool processVol(float* out, const float* in, float param, unsigned int pitch, const SDimensions& dims, std::optional _stream); \ template bool processSino(float* out, const float* in, float param, unsigned int pitch, const SDimensions& dims, std::optional _stream); #define INST_DtoD(name) \ template bool processVolCopy(float* out, const float* in, const SDimensions& dims); \ template bool processVol(float* out, const float* in, unsigned int pitch, const SDimensions& dims, std::optional _stream); \ template bool processSino(float* out, const float* in, unsigned int pitch, const SDimensions& dims, std::optional _stream); #define INST_DDtoD(name) \ template bool processVolCopy(float* out, const float* in1, const float* in2, const SDimensions& dims); \ template bool processVol(float* out, const float* in1, const float* in2, unsigned int pitch, const SDimensions& dims, std::optional _stream); \ template bool processSino(float* out, const float* in1, const float* in2, unsigned int pitch, const SDimensions& dims, std::optional _stream); #define INST_DDFtoD(name) \ template bool processVolCopy(float* out, const float* in1, const float* in2, float fParam, const SDimensions& dims); \ template bool processVol(float* out, const float* in1, const float* in2, float fParam, unsigned int pitch, const SDimensions& dims, std::optional _stream); \ template bool processSino(float* out, const float* in1, const float* in2, float fParam, unsigned int pitch, const SDimensions& dims, std::optional _stream); #define INST_toD(name) \ template bool processVolCopy(float* out, const SDimensions& dims); \ template bool processVol(float* out, unsigned int pitch, const SDimensions& dims, std::optional _stream); \ template bool processSino(float* out, unsigned int pitch, const SDimensions& dims, std::optional _stream); #define INST_FtoD(name) \ template bool processVolCopy(float* out, float param, const SDimensions& dims); \ template bool processVol(float* out, float param, unsigned int pitch, const SDimensions& dims, std::optional _stream); \ template bool processSino(float* out, float param, unsigned int pitch, const SDimensions& dims, std::optional _stream); #define INST_FFtoDD(name) \ template bool processVolCopy(float* out1, float* out2, float fParam1, float fParam2, const SDimensions& dims); \ template bool processVol(float* out1, float* out2, float fParam1, float fParam2, unsigned int pitch, const SDimensions& dims, std::optional _stream); \ template bool processSino(float* out1, float* out2, float fParam1, float fParam2, unsigned int pitch, const SDimensions& dims, std::optional _stream); INST_DFtoD(opAddScaled) INST_DFtoD(opScaleAndAdd) INST_DDFtoD(opAddMulScaled) INST_DDtoD(opAddMul) INST_DDtoD(opMul2) INST_DDtoD(opAdd2) INST_DtoD(opMul) INST_DDtoD(opMulMask) INST_DtoD(opAdd) INST_DtoD(opDividedBy) INST_toD(opInvert) INST_FtoD(opSet) INST_FtoD(opMul) INST_DtoD(opDiv) INST_DFtoD(opMulMask) INST_FtoD(opAdd) INST_FtoD(opClampMin) INST_FtoD(opClampMax) INST_DtoD(opClampMinMask) INST_DtoD(opClampMaxMask) // PDART-specific: INST_DFtoD(opSetMaskedValues) INST_FFtoDD(opSegmentAndMask) } astra-toolbox-2.3.0/cuda/2d/astra.cu000066400000000000000000000331651475635207100172200ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/util.h" #include "astra/cuda/2d/par_fp.h" #include "astra/cuda/2d/fan_fp.h" #include "astra/cuda/2d/par_bp.h" #include "astra/cuda/2d/fan_bp.h" #include "astra/cuda/2d/arith.h" #include "astra/cuda/2d/astra.h" #include "astra/cuda/2d/fft.h" // For fan beam FBP weighting #include "astra/cuda/3d/fdk.h" #include "astra/GeometryUtil2D.h" #include "astra/VolumeGeometry2D.h" #include "astra/ParallelProjectionGeometry2D.h" #include "astra/ParallelVecProjectionGeometry2D.h" #include "astra/FanFlatProjectionGeometry2D.h" #include "astra/FanFlatVecProjectionGeometry2D.h" #include "astra/Logging.h" #include #include #include #include using namespace astraCUDA; using namespace std; namespace astra { enum CUDAProjectionType { PROJ_PARALLEL, PROJ_FAN }; BPalgo::BPalgo() { } BPalgo::~BPalgo() { } bool BPalgo::init() { return true; } bool BPalgo::iterate(unsigned int) { // TODO: This zeroVolume makes an earlier memcpy of D_volumeData redundant zeroVolumeData(D_volumeData, volumePitch, dims); callBP(D_volumeData, volumePitch, D_sinoData, sinoPitch, 1.0f); return true; } float BPalgo::computeDiffNorm() { float *D_projData; unsigned int projPitch; allocateProjectionData(D_projData, projPitch, dims); duplicateProjectionData(D_projData, D_sinoData, sinoPitch, dims); callFP(D_volumeData, volumePitch, D_projData, projPitch, -1.0f); float s = dotProduct2D(D_projData, projPitch, dims.iProjDets, dims.iProjAngles); cudaFree(D_projData); return sqrt(s); } bool astraCudaFP(const float* pfVolume, float* pfSinogram, unsigned int iVolWidth, unsigned int iVolHeight, unsigned int iProjAngles, unsigned int iProjDets, const SParProjection *pAngles, unsigned int iDetSuperSampling, float fOutputScale, int iGPUIndex) { SDimensions dims; if (iProjAngles == 0 || iProjDets == 0 || pAngles == 0) return false; dims.iProjAngles = iProjAngles; dims.iProjDets = iProjDets; if (iDetSuperSampling == 0) return false; dims.iRaysPerDet = iDetSuperSampling; if (iVolWidth <= 0 || iVolHeight <= 0) return false; dims.iVolWidth = iVolWidth; dims.iVolHeight = iVolHeight; if (iGPUIndex != -1) { cudaSetDevice(iGPUIndex); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } bool ok; float* D_volumeData; unsigned int volumePitch; ok = allocateVolumeData(D_volumeData, volumePitch, dims); if (!ok) return false; float* D_sinoData; unsigned int sinoPitch; ok = allocateProjectionData(D_sinoData, sinoPitch, dims); if (!ok) { cudaFree(D_volumeData); return false; } ok = copyVolumeToDevice(pfVolume, dims.iVolWidth, dims, D_volumeData, volumePitch); if (!ok) { cudaFree(D_volumeData); cudaFree(D_sinoData); return false; } zeroProjectionData(D_sinoData, sinoPitch, dims); ok = FP(D_volumeData, volumePitch, D_sinoData, sinoPitch, dims, pAngles, fOutputScale); if (!ok) { cudaFree(D_volumeData); cudaFree(D_sinoData); return false; } ok = copySinogramFromDevice(pfSinogram, dims.iProjDets, dims, D_sinoData, sinoPitch); if (!ok) { cudaFree(D_volumeData); cudaFree(D_sinoData); return false; } cudaFree(D_volumeData); cudaFree(D_sinoData); return true; } bool astraCudaFanFP(const float* pfVolume, float* pfSinogram, unsigned int iVolWidth, unsigned int iVolHeight, unsigned int iProjAngles, unsigned int iProjDets, const SFanProjection *pAngles, unsigned int iDetSuperSampling, float fOutputScale, int iGPUIndex) { SDimensions dims; if (iProjAngles == 0 || iProjDets == 0 || pAngles == 0) return false; dims.iProjAngles = iProjAngles; dims.iProjDets = iProjDets; if (iDetSuperSampling == 0) return false; dims.iRaysPerDet = iDetSuperSampling; if (iVolWidth <= 0 || iVolHeight <= 0) return false; dims.iVolWidth = iVolWidth; dims.iVolHeight = iVolHeight; if (iGPUIndex != -1) { cudaSetDevice(iGPUIndex); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } bool ok; float* D_volumeData; unsigned int volumePitch; ok = allocateVolumeData(D_volumeData, volumePitch, dims); if (!ok) return false; float* D_sinoData; unsigned int sinoPitch; ok = allocateProjectionData(D_sinoData, sinoPitch, dims); if (!ok) { cudaFree(D_volumeData); return false; } ok = copyVolumeToDevice(pfVolume, dims.iVolWidth, dims, D_volumeData, volumePitch); if (!ok) { cudaFree(D_volumeData); cudaFree(D_sinoData); return false; } zeroProjectionData(D_sinoData, sinoPitch, dims); ok = FanFP(D_volumeData, volumePitch, D_sinoData, sinoPitch, dims, pAngles, fOutputScale); if (!ok) { cudaFree(D_volumeData); cudaFree(D_sinoData); return false; } ok = copySinogramFromDevice(pfSinogram, dims.iProjDets, dims, D_sinoData, sinoPitch); if (!ok) { cudaFree(D_volumeData); cudaFree(D_sinoData); return false; } cudaFree(D_volumeData); cudaFree(D_sinoData); return true; } // adjust pProjs to normalize volume geometry template static bool convertAstraGeometry_internal(const CVolumeGeometry2D* pVolGeom, unsigned int iProjectionAngleCount, ProjectionT*& pProjs, float& fOutputScale) { // TODO: Make EPS relative const float EPS = 0.00001f; // Check if pixels are square if (abs(pVolGeom->getPixelLengthX() - pVolGeom->getPixelLengthY()) > EPS) return false; float dx = -(pVolGeom->getWindowMinX() + pVolGeom->getWindowMaxX()) / 2; float dy = -(pVolGeom->getWindowMinY() + pVolGeom->getWindowMaxY()) / 2; float factor = 1.0f / pVolGeom->getPixelLengthX(); for (int i = 0; i < iProjectionAngleCount; ++i) { // CHECKME: Order of scaling and translation pProjs[i].translate(dx, dy); pProjs[i].scale(factor); } // CHECKME: Check factor // NB: Only valid for square pixels fOutputScale *= pVolGeom->getPixelLengthX(); return true; } bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom, const CParallelProjectionGeometry2D* pProjGeom, SParProjection*& pProjs, float& fOutputScale) { assert(pVolGeom); assert(pProjGeom); assert(pProjGeom->getProjectionAngles()); int nth = pProjGeom->getProjectionAngleCount(); pProjs = genParProjections(nth, pProjGeom->getDetectorCount(), pProjGeom->getDetectorWidth(), pProjGeom->getProjectionAngles(), 0); bool ok; fOutputScale = 1.0f; ok = convertAstraGeometry_internal(pVolGeom, nth, pProjs, fOutputScale); if (!ok) { delete[] pProjs; pProjs = 0; } return ok; } bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom, const CParallelVecProjectionGeometry2D* pProjGeom, SParProjection*& pProjs, float& fOutputScale) { assert(pVolGeom); assert(pProjGeom); assert(pProjGeom->getProjectionVectors()); int nth = pProjGeom->getProjectionAngleCount(); pProjs = new SParProjection[nth]; for (int i = 0; i < nth; ++i) { pProjs[i] = pProjGeom->getProjectionVectors()[i]; } bool ok; fOutputScale = 1.0f; ok = convertAstraGeometry_internal(pVolGeom, nth, pProjs, fOutputScale); if (!ok) { delete[] pProjs; pProjs = 0; } return ok; } bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom, const CFanFlatProjectionGeometry2D* pProjGeom, astraCUDA::SFanProjection*& pProjs, float& outputScale) { assert(pVolGeom); assert(pProjGeom); assert(pProjGeom->getProjectionAngles()); // TODO: Make EPS relative const float EPS = 0.00001f; int nth = pProjGeom->getProjectionAngleCount(); // Check if pixels are square if (abs(pVolGeom->getPixelLengthX() - pVolGeom->getPixelLengthY()) > EPS) return false; // TODO: Deprecate this. // if (pProjGeom->getExtraDetectorOffset()) // return false; float fOriginSourceDistance = pProjGeom->getOriginSourceDistance(); float fOriginDetectorDistance = pProjGeom->getOriginDetectorDistance(); float fDetSize = pProjGeom->getDetectorWidth(); const float *pfAngles = pProjGeom->getProjectionAngles(); pProjs = genFanProjections(nth, pProjGeom->getDetectorCount(), fOriginSourceDistance, fOriginDetectorDistance, fDetSize, pfAngles); convertAstraGeometry_internal(pVolGeom, nth, pProjs, outputScale); return true; } bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom, const CFanFlatVecProjectionGeometry2D* pProjGeom, astraCUDA::SFanProjection*& pProjs, float& outputScale) { assert(pVolGeom); assert(pProjGeom); assert(pProjGeom->getProjectionVectors()); // TODO: Make EPS relative const float EPS = 0.00001f; int nx = pVolGeom->getGridColCount(); int ny = pVolGeom->getGridRowCount(); int nth = pProjGeom->getProjectionAngleCount(); // Check if pixels are square if (abs(pVolGeom->getPixelLengthX() - pVolGeom->getPixelLengthY()) > EPS) return false; pProjs = new SFanProjection[nth]; // Copy vectors for (int i = 0; i < nth; ++i) pProjs[i] = pProjGeom->getProjectionVectors()[i]; convertAstraGeometry_internal(pVolGeom, nth, pProjs, outputScale); return true; } bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom, const CProjectionGeometry2D* pProjGeom, astraCUDA::SParProjection*& pParProjs, astraCUDA::SFanProjection*& pFanProjs, float& outputScale) { const CParallelProjectionGeometry2D* parProjGeom = dynamic_cast(pProjGeom); const CParallelVecProjectionGeometry2D* parVecProjGeom = dynamic_cast(pProjGeom); const CFanFlatProjectionGeometry2D* fanProjGeom = dynamic_cast(pProjGeom); const CFanFlatVecProjectionGeometry2D* fanVecProjGeom = dynamic_cast(pProjGeom); bool ok = false; if (parProjGeom) { ok = convertAstraGeometry(pVolGeom, parProjGeom, pParProjs, outputScale); } else if (parVecProjGeom) { ok = convertAstraGeometry(pVolGeom, parVecProjGeom, pParProjs, outputScale); } else if (fanProjGeom) { ok = convertAstraGeometry(pVolGeom, fanProjGeom, pFanProjs, outputScale); } else if (fanVecProjGeom) { ok = convertAstraGeometry(pVolGeom, fanVecProjGeom, pFanProjs, outputScale); } else { ok = false; } return ok; } bool convertAstraGeometry_dims(const CVolumeGeometry2D* pVolGeom, const CProjectionGeometry2D* pProjGeom, SDimensions& dims) { dims.iVolWidth = pVolGeom->getGridColCount(); dims.iVolHeight = pVolGeom->getGridRowCount(); dims.iProjAngles = pProjGeom->getProjectionAngleCount(); dims.iProjDets = pProjGeom->getDetectorCount(); dims.iRaysPerDet = 1; dims.iRaysPerPixelDim = 1; return true; } } namespace astraCUDA { _AstraExport std::string getCudaDeviceString(int device) { char buf[1024]; cudaError_t err; if (device == -1) { err = cudaGetDevice(&device); if (err != cudaSuccess) { return "Error getting current GPU index"; } } cudaDeviceProp prop; err = cudaGetDeviceProperties(&prop, device); if (err != cudaSuccess) { snprintf(buf, 1024, "GPU #%d: Invalid device (%d): %s", device, err, cudaGetErrorString(err)); return buf; } long mem = prop.totalGlobalMem / (1024*1024); snprintf(buf, 1024, "GPU #%d: %s, with %ldMB, CUDA compute capability %d.%d", device, prop.name, mem, prop.major, prop.minor); return buf; } _AstraExport bool setGPUIndex(int iGPUIndex) { if (iGPUIndex != -1) { cudaError_t err = cudaSetDevice(iGPUIndex); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } return true; } _AstraExport size_t availableGPUMemory() { size_t free, total; cudaError_t err = cudaMemGetInfo(&free, &total); if (err != cudaSuccess) return 0; return free; } } astra-toolbox-2.3.0/cuda/2d/cgls.cu000066400000000000000000000115371475635207100170350ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/cgls.h" #include "astra/cuda/2d/util.h" #include "astra/cuda/2d/arith.h" #include #include namespace astraCUDA { CGLS::CGLS() : ReconAlgo() { D_z = 0; D_p = 0; D_r = 0; D_w = 0; sliceInitialized = false; } CGLS::~CGLS() { reset(); } void CGLS::reset() { cudaFree(D_z); cudaFree(D_p); cudaFree(D_r); cudaFree(D_w); D_z = 0; D_p = 0; D_r = 0; D_w = 0; ReconAlgo::reset(); } bool CGLS::init() { // Lifetime of z: within an iteration allocateVolumeData(D_z, zPitch, dims); // Lifetime of p: full algorithm allocateVolumeData(D_p, pPitch, dims); // Lifetime of r: full algorithm allocateProjectionData(D_r, rPitch, dims); // Lifetime of w: within an iteration allocateProjectionData(D_w, wPitch, dims); // TODO: check if allocations succeeded return true; } bool CGLS::setBuffers(float* _D_volumeData, unsigned int _volumePitch, float* _D_projData, unsigned int _projPitch) { bool ok = ReconAlgo::setBuffers(_D_volumeData, _volumePitch, _D_projData, _projPitch); if (!ok) return false; sliceInitialized = false; return true; } bool CGLS::copyDataToGPU(const float* pfSinogram, unsigned int iSinogramPitch, const float* pfReconstruction, unsigned int iReconstructionPitch, const float* pfVolMask, unsigned int iVolMaskPitch, const float* pfSinoMask, unsigned int iSinoMaskPitch) { sliceInitialized = false; return ReconAlgo::copyDataToGPU(pfSinogram, iSinogramPitch, pfReconstruction, iReconstructionPitch, pfVolMask, iVolMaskPitch, pfSinoMask, iSinoMaskPitch); } bool CGLS::iterate(unsigned int iterations) { if (!sliceInitialized) { // copy sinogram duplicateProjectionData(D_r, D_sinoData, sinoPitch, dims); // r = sino - A*x if (useVolumeMask) { // Use z as temporary storage here since it is unused duplicateVolumeData(D_z, D_volumeData, volumePitch, dims); processVol(D_z, D_maskData, zPitch, dims); callFP(D_z, zPitch, D_r, rPitch, -1.0f); } else { callFP(D_volumeData, volumePitch, D_r, rPitch, -1.0f); } // p = A'*r zeroVolumeData(D_p, pPitch, dims); callBP(D_p, pPitch, D_r, rPitch, 1.0f); if (useVolumeMask) processVol(D_p, D_maskData, pPitch, dims); gamma = dotProduct2D(D_p, pPitch, dims.iVolWidth, dims.iVolHeight); sliceInitialized = true; } // iteration for (unsigned int iter = 0; iter < iterations && !astra::shouldAbort(); ++iter) { // w = A*p zeroProjectionData(D_w, wPitch, dims); callFP(D_p, pPitch, D_w, wPitch, 1.0f); // alpha = gamma / float ww = dotProduct2D(D_w, wPitch, dims.iProjDets, dims.iProjAngles); float alpha = gamma / ww; // x += alpha*p processVol(D_volumeData, D_p, alpha, volumePitch, dims); // r -= alpha*w processSino(D_r, D_w, -alpha, rPitch, dims); // z = A'*r zeroVolumeData(D_z, zPitch, dims); callBP(D_z, zPitch, D_r, rPitch, 1.0f); if (useVolumeMask) processVol(D_z, D_maskData, zPitch, dims); float beta = 1.0f / gamma; gamma = dotProduct2D(D_z, zPitch, dims.iVolWidth, dims.iVolHeight); beta *= gamma; // p = z + beta*p processVol(D_p, D_z, beta, pPitch, dims); } return true; } float CGLS::computeDiffNorm() { // We can use w and z as temporary storage here since they're not // used outside of iterations. // copy sinogram to w duplicateProjectionData(D_w, D_sinoData, sinoPitch, dims); // do FP, subtracting projection from sinogram if (useVolumeMask) { duplicateVolumeData(D_z, D_volumeData, volumePitch, dims); processVol(D_z, D_maskData, zPitch, dims); callFP(D_z, zPitch, D_w, wPitch, -1.0f); } else { callFP(D_volumeData, volumePitch, D_w, wPitch, -1.0f); } // compute norm of D_w float s = dotProduct2D(D_w, wPitch, dims.iProjDets, dims.iProjAngles); return sqrt(s); } } astra-toolbox-2.3.0/cuda/2d/darthelper.cu000066400000000000000000000252331475635207100202350ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/util.h" #include "astra/cuda/2d/darthelper.h" #include namespace astraCUDA { // CUDA function for the selection of ROI __global__ void devRoiSelect(float* in, float radius, unsigned int pitch, unsigned int width, unsigned int height) { float x = (float)(threadIdx.x + 16*blockIdx.x); float y = (float)(threadIdx.y + 16*blockIdx.y); float w = (width-1.0f)*0.5f; float h = (height-1.0f)*0.5f; if ((x-w)*(x-w) + (y-h)*(y-h) > radius * radius * 0.25f) { float* d = (float*)in; unsigned int o = y*pitch+x; d[o] = 0.0f; } } void roiSelect(float* out, float radius, unsigned int width, unsigned int height) { float* D_data; unsigned int pitch; // We abuse dims here... SDimensions dims; dims.iVolWidth = width; dims.iVolHeight = height; allocateVolumeData(D_data, pitch, dims); copyVolumeToDevice(out, width, dims, D_data, pitch); dim3 blockSize(16,16); dim3 gridSize((width+15)/16, (height+15)/16); devRoiSelect<<>>(D_data, radius, pitch, width, height); copyVolumeFromDevice(out, width, dims, D_data, pitch); cudaFree(D_data); } // CUDA function for the masking of DART with a radius == 1 __global__ void devDartMask(float* mask, const float* in, unsigned int conn, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; unsigned int y = threadIdx.y + 16*blockIdx.y; // Sacrifice the border pixels to simplify the implementation. if (x > 0 && x < width - 1 && y > 0 && y < height - 1) { float* d = (float*)in; float* m = (float*)mask; unsigned int o2 = y*pitch+x; // On this row. unsigned int o1 = o2 - pitch; // On previous row. unsigned int o3 = o2 + pitch; // On next row. if ((conn == 8 && // 8-connected (d[o1 - 1] != d[o2] || d[o1] != d[o2] || d[o1 + 1] != d[o2] || d[o2 - 1] != d[o2] || d[o2 + 1] != d[o2] || d[o3 - 1] != d[o2] || d[o3] != d[o2] || d[o3 + 1] != d[o2] )) || (conn == 4 && // 4-connected ( d[o1] != d[o2] || d[o2 - 1] != d[o2] || d[o3 + 1] != d[o2] || d[o3] != d[o2] ))) { m[o2] = 1.0f; } } } // CUDA function for the masking of DART with a radius > 1 __global__ void devDartMaskRadius(float* mask, const float* in, unsigned int conn, unsigned int radius, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; unsigned int y = threadIdx.y + 16*blockIdx.y; // Sacrifice the border pixels to simplify the implementation. if (x > radius-1 && x < width - radius && y > radius-1 && y < height - radius) { float* d = (float*)in; float* m = (float*)mask; int r = radius; // o2: index of the current center pixel int o2 = y*pitch+x; if (conn == 8) // 8-connected { for (int row = -r; row <= r; row++) { int o1 = (y+row)*pitch+x; for (int col = -r; col <= r; col++) { if (d[o1 + col] != d[o2]) {m[o2] = 1.0f; return;} } } } else if (conn == 4) // 4-connected { // horizontal unsigned int o1 = y*pitch+x; for (int col = -r; col <= r; col++) { if (d[o1 + col] != d[o2]) {m[o2] = 1.0f; return;} } // vertical for (int row = -r; row <= r; row++) { unsigned int o1 = (y+row)*pitch+x; if (d[o1] != d[o2]) {m[o2] = 1.0f; return;} } } } } // CUDA function for the masking of ADART with a radius == 1 __global__ void devADartMask(float* mask, const float* in, unsigned int conn, unsigned int threshold, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; unsigned int y = threadIdx.y + 16*blockIdx.y; // Sacrifice the border pixels to simplify the implementation. if (x > 0 && x < width - 1 && y > 0 && y < height - 1) { float* d = (float*)in; float* m = (float*)mask; unsigned int o2 = y*pitch+x; // On this row. unsigned int o1 = o2 - pitch; // On previous row. unsigned int o3 = o2 + pitch; // On next row. if (conn == 8) { if (d[o1 - 1] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} if (d[o1 ] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} if (d[o1 + 1] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} if (d[o2 - 1] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} if (d[o2 + 1] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} if (d[o3 - 1] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} if (d[o3 ] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} if (d[o3 + 1] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} } else if (conn == 4) { if (d[o1 ] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} if (d[o2 - 1] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} if (d[o2 + 1] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} if (d[o3 ] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} } } } // CUDA function for the masking of ADART with a radius > 1 __global__ void devADartMaskRadius(float* mask, const float* in, unsigned int conn, unsigned int radius, unsigned int threshold, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; unsigned int y = threadIdx.y + 16*blockIdx.y; // Sacrifice the border pixels to simplify the implementation. if (x > radius-1 && x < width - radius && y > radius-1 && y < height - radius) { float* d = (float*)in; float* m = (float*)mask; int r = radius; unsigned int o2 = y*pitch+x; // On this row. if (conn == 8) { for (int row = -r; row <= r; row++) { unsigned int o1 = (y+row)*pitch+x; for (int col = -r; col <= r; col++) { if (d[o1+col] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} } } } else if (conn == 4) { // horizontal for (int col = -r; col <= r; col++) { if (d[o2+col] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} } // vertical for (int row = -r; row <= r; row++) { unsigned int o1 = (y+row)*pitch+x; if (d[o1] != d[o2] && --threshold == 0) {m[o2] = 1.0f; return;} } } } } void dartMask(float* mask, const float* segmentation, unsigned int conn, unsigned int radius, unsigned int threshold, unsigned int width, unsigned int height) { float* D_segmentationData; float* D_maskData; unsigned int pitch; // We abuse dims here... SDimensions dims; dims.iVolWidth = width; dims.iVolHeight = height; allocateVolumeData(D_segmentationData, pitch, dims); copyVolumeToDevice(segmentation, width, dims, D_segmentationData, pitch); allocateVolumeData(D_maskData, pitch, dims); zeroVolumeData(D_maskData, pitch, dims); dim3 blockSize(16,16); dim3 gridSize((width+15)/16, (height+15)/16); if (threshold == 1 && radius == 1) devDartMask<<>>(D_maskData, D_segmentationData, conn, pitch, width, height); else if (threshold > 1 && radius == 1) devADartMask<<>>(D_maskData, D_segmentationData, conn, threshold, pitch, width, height); else if (threshold == 1 && radius > 1) devDartMaskRadius<<>>(D_maskData, D_segmentationData, conn, radius, pitch, width, height); else devADartMaskRadius<<>>(D_maskData, D_segmentationData, conn, radius, threshold, pitch, width, height); copyVolumeFromDevice(mask, width, dims, D_maskData, pitch); cudaFree(D_segmentationData); cudaFree(D_maskData); } __global__ void devDartSmoothingRadius(float* out, const float* in, float b, unsigned int radius, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; unsigned int y = threadIdx.y + 16*blockIdx.y; // Sacrifice the border pixels to simplify the implementation. if (x > radius-1 && x < width - radius && y > radius-1 && y < height - radius) { float* d = (float*)in; float* m = (float*)out; unsigned int o2 = y*pitch+x; int r = radius; float count = 4*r*(r+1); float res = -d[o2]; for (int row = -r; row <= r; row++) { unsigned int o1 = (y+row)*pitch+x; for (int col = -r; col <= r; col++) { res += d[o1+col]; } } res *= b / count; res += (1.0f-b) * d[o2]; m[o2] = res; } } __global__ void devDartSmoothing(float* out, const float* in, float b, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; unsigned int y = threadIdx.y + 16*blockIdx.y; // Sacrifice the border pixels to simplify the implementation. if (x > 0 && x < width - 1 && y > 0 && y < height - 1) { float* d = (float*)in; float* m = (float*)out; unsigned int o2 = y*pitch+x; // On this row. unsigned int o1 = o2 - pitch; // On previous row. unsigned int o3 = o2 + pitch; // On next row. m[o2] = (1.0f-b) * d[o2] + b * 0.125f * (d[o1 - 1] + d[o1] + d[o1 + 1] + d[o2 - 1] + d[o2 + 1] + d[o3 - 1] + d[o3] + d[o3 + 1]); } } void dartSmoothing(float* out, const float* in, float b, unsigned int radius, unsigned int width, unsigned int height) { float* D_inData; float* D_outData; unsigned int pitch; // We abuse dims here... SDimensions dims; dims.iVolWidth = width; dims.iVolHeight = height; allocateVolumeData(D_inData, pitch, dims); copyVolumeToDevice(in, width, dims, D_inData, pitch); allocateVolumeData(D_outData, pitch, dims); zeroVolumeData(D_outData, pitch, dims); dim3 blockSize(16,16); dim3 gridSize((width+15)/16, (height+15)/16); if (radius == 1) devDartSmoothing<<>>(D_outData, D_inData, b, pitch, width, height); else devDartSmoothingRadius<<>>(D_outData, D_inData, b, radius, pitch, width, height); copyVolumeFromDevice(out, width, dims, D_outData, pitch); cudaFree(D_outData); cudaFree(D_inData); } } astra-toolbox-2.3.0/cuda/2d/em.cu000066400000000000000000000074671475635207100165150ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/em.h" #include "astra/cuda/2d/util.h" #include "astra/cuda/2d/arith.h" #include #include namespace astraCUDA { // TODO: ensure non-negativity somewhere?? EM::EM() { D_projData = 0; D_tmpData = 0; D_pixelWeight = 0; } EM::~EM() { reset(); } void EM::reset() { cudaFree(D_projData); cudaFree(D_tmpData); cudaFree(D_pixelWeight); D_projData = 0; D_tmpData = 0; D_pixelWeight = 0; ReconAlgo::reset(); } bool EM::init() { allocateVolumeData(D_pixelWeight, pixelPitch, dims); zeroVolumeData(D_pixelWeight, pixelPitch, dims); allocateVolumeData(D_tmpData, tmpPitch, dims); zeroVolumeData(D_tmpData, tmpPitch, dims); allocateProjectionData(D_projData, projPitch, dims); zeroProjectionData(D_projData, projPitch, dims); // We can't precompute pixelWeights when using a volume mask #if 0 if (!useVolumeMask) #endif precomputeWeights(); // TODO: check if allocations succeeded return true; } bool EM::precomputeWeights() { zeroVolumeData(D_pixelWeight, pixelPitch, dims); #if 0 if (useSinogramMask) { callBP(D_pixelWeight, pixelPitch, D_smaskData, smaskPitch); } else #endif { processSino(D_projData, 1.0f, projPitch, dims); callBP(D_pixelWeight, pixelPitch, D_projData, projPitch, 1.0f); } processVol(D_pixelWeight, pixelPitch, dims); #if 0 if (useVolumeMask) { // scale pixel weights with mask to zero out masked pixels processVol(D_pixelWeight, D_maskData, pixelPitch, dims); } #endif return true; } bool EM::iterate(unsigned int iterations) { #if 0 if (useVolumeMask) precomputeWeights(); #endif // iteration for (unsigned int iter = 0; iter < iterations && !astra::shouldAbort(); ++iter) { // Do FP of volumeData zeroProjectionData(D_projData, projPitch, dims); callFP(D_volumeData, volumePitch, D_projData, projPitch, 1.0f); // Divide sinogram by FP (into projData) processSino(D_projData, D_sinoData, projPitch, dims); // Do BP of projData into tmpData zeroVolumeData(D_tmpData, tmpPitch, dims); callBP(D_tmpData, tmpPitch, D_projData, projPitch, 1.0f); // Multiply volumeData with tmpData divided by pixel weights processVol(D_volumeData, D_tmpData, D_pixelWeight, pixelPitch, dims); } return true; } float EM::computeDiffNorm() { // copy sinogram to projection data duplicateProjectionData(D_projData, D_sinoData, sinoPitch, dims); // do FP, subtracting projection from sinogram if (useVolumeMask) { duplicateVolumeData(D_tmpData, D_volumeData, volumePitch, dims); processVol(D_tmpData, D_maskData, tmpPitch, dims); callFP(D_tmpData, tmpPitch, D_projData, projPitch, -1.0f); } else { callFP(D_volumeData, volumePitch, D_projData, projPitch, -1.0f); } // compute norm of D_projData float s = dotProduct2D(D_projData, projPitch, dims.iProjDets, dims.iProjAngles); return sqrt(s); } } astra-toolbox-2.3.0/cuda/2d/fan_bp.cu000066400000000000000000000325621475635207100173330ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/util.h" #include "astra/cuda/2d/arith.h" #include #include #include namespace astraCUDA { const unsigned int g_anglesPerBlock = 16; const unsigned int g_blockSliceSize = 32; const unsigned int g_blockSlices = 16; const unsigned int g_MaxAngles = 2560; struct DevFanParams { float fNumC; float fNumX; float fNumY; float fDenC; float fDenX; float fDenY; }; __constant__ DevFanParams gC_C[g_MaxAngles]; template __global__ void devFanBP(float* D_volData, unsigned int volPitch, cudaTextureObject_t tex, unsigned int startAngle, const SDimensions dims, float fOutputScale) { const int relX = threadIdx.x; const int relY = threadIdx.y; int endAngle = startAngle + g_anglesPerBlock; if (endAngle > dims.iProjAngles) endAngle = dims.iProjAngles; const int X = blockIdx.x * g_blockSlices + relX; const int Y = blockIdx.y * g_blockSliceSize + relY; if (X >= dims.iVolWidth || Y >= dims.iVolHeight) return; const float fX = ( X - 0.5f*dims.iVolWidth + 0.5f ); const float fY = - ( Y - 0.5f*dims.iVolHeight + 0.5f ); float* volData = (float*)D_volData; float fVal = 0.0f; float fA = startAngle + 0.5f; for (int angle = startAngle; angle < endAngle; ++angle) { const float fNumC = gC_C[angle].fNumC; const float fNumX = gC_C[angle].fNumX; const float fNumY = gC_C[angle].fNumY; const float fDenX = gC_C[angle].fDenX; const float fDenY = gC_C[angle].fDenY; const float fNum = fNumC + fNumX * fX + fNumY * fY; const float fDen = (FBPWEIGHT ? 1.0 : gC_C[angle].fDenC) + fDenX * fX + fDenY * fY; // Scale factor is the approximate number of rays traversing this pixel, // given by the inverse size of a detector pixel scaled by the magnification // factor of this pixel. // Magnification factor is || u (d-s) || / || u (x-s) || const float fr = __fdividef(1.0f, fDen); const float fT = fNum * fr; fVal += tex2D(tex, fT, fA) * (FBPWEIGHT ? fr * fr : fr); fA += 1.0f; } volData[Y*volPitch+X] += fVal * fOutputScale; } // supersampling version __global__ void devFanBP_SS(float* D_volData, unsigned int volPitch, cudaTextureObject_t tex, unsigned int startAngle, const SDimensions dims, float fOutputScale) { const int relX = threadIdx.x; const int relY = threadIdx.y; int endAngle = startAngle + g_anglesPerBlock; if (endAngle > dims.iProjAngles) endAngle = dims.iProjAngles; const int X = blockIdx.x * g_blockSlices + relX; const int Y = blockIdx.y * g_blockSliceSize + relY; if (X >= dims.iVolWidth || Y >= dims.iVolHeight) return; const float fXb = ( X - 0.5f*dims.iVolWidth + 0.5f - 0.5f + 0.5f/dims.iRaysPerPixelDim); const float fYb = - ( Y - 0.5f*dims.iVolHeight + 0.5f - 0.5f + 0.5f/dims.iRaysPerPixelDim); const float fSubStep = 1.0f/dims.iRaysPerPixelDim; float* volData = (float*)D_volData; fOutputScale /= (dims.iRaysPerPixelDim * dims.iRaysPerPixelDim); float fVal = 0.0f; float fA = startAngle + 0.5f; for (int angle = startAngle; angle < endAngle; ++angle) { const float fNumC = gC_C[angle].fNumC; const float fNumX = gC_C[angle].fNumX; const float fNumY = gC_C[angle].fNumY; const float fDenC = gC_C[angle].fDenC; const float fDenX = gC_C[angle].fDenX; const float fDenY = gC_C[angle].fDenY; // TODO: Optimize these loops... float fX = fXb; for (int iSubX = 0; iSubX < dims.iRaysPerPixelDim; ++iSubX) { float fY = fYb; for (int iSubY = 0; iSubY < dims.iRaysPerPixelDim; ++iSubY) { const float fNum = fNumC + fNumX * fX + fNumY * fY; const float fDen = fDenC + fDenX * fX + fDenY * fY; const float fr = __fdividef(1.0f, fDen); const float fT = fNum * fr; fVal += tex2D(tex, fT, fA) * fr; fY -= fSubStep; } fX += fSubStep; } fA += 1.0f; } volData[Y*volPitch+X] += fVal * fOutputScale; } // BP specifically for SART. // It includes (free) weighting with voxel weight. // It assumes the proj texture is set up _without_ padding, unlike regular BP. __global__ void devFanBP_SART(float* D_volData, unsigned int volPitch, cudaTextureObject_t tex, const SDimensions dims, float fOutputScale) { const int relX = threadIdx.x; const int relY = threadIdx.y; const int X = blockIdx.x * g_blockSlices + relX; const int Y = blockIdx.y * g_blockSliceSize + relY; if (X >= dims.iVolWidth || Y >= dims.iVolHeight) return; const float fX = ( X - 0.5f*dims.iVolWidth + 0.5f ); const float fY = - ( Y - 0.5f*dims.iVolHeight + 0.5f ); float* volData = (float*)D_volData; const float fNumC = gC_C[0].fNumC; const float fNumX = gC_C[0].fNumX; const float fNumY = gC_C[0].fNumY; const float fDenC = gC_C[0].fDenC; const float fDenX = gC_C[0].fDenX; const float fDenY = gC_C[0].fDenY; const float fNum = fNumC + fNumX * fX + fNumY * fY; const float fDen = fDenC + fDenX * fX + fDenY * fY; const float fr = __fdividef(1.0f, fDen); const float fT = fNum * fr; // NB: The scale constant in devBP is cancelled out by the SART weighting const float fVal = tex2D(tex, fT, 0.5f); volData[Y*volPitch+X] += fVal * fOutputScale; } struct Vec2 { double x; double y; Vec2(double x_, double y_) : x(x_), y(y_) { } Vec2 operator+(const Vec2 &b) const { return Vec2(x + b.x, y + b.y); } Vec2 operator-(const Vec2 &b) const { return Vec2(x - b.x, y - b.y); } Vec2 operator-() const { return Vec2(-x, -y); } double norm() const { return sqrt(x*x + y*y); } }; double det2(const Vec2 &a, const Vec2 &b) { return a.x * b.y - a.y * b.x; } using TransferConstantsBuffer = TransferConstantsBuffer_t; bool transferConstants(const SFanProjection* angles, unsigned int iProjAngles, bool FBP, TransferConstantsBuffer& buf, cudaStream_t stream) { DevFanParams *p = &(std::get<0>(buf.d))[0]; bool ok = checkCuda(cudaStreamWaitEvent(stream, buf.event, 0), "transferConstants wait"); // We need three values in the kernel: // projected coordinates of pixels on the detector: // || x (s-d) || + ||s d|| / || u (s-x) || // ray density weighting factor for the adjoint // || u (s-d) || / ( |u| * || u (s-x) || ) // fan-beam FBP weighting factor // ( || u s || / || u (s-x) || ) ^ 2 for (unsigned int i = 0; i < iProjAngles; ++i) { Vec2 u(angles[i].fDetUX, angles[i].fDetUY); Vec2 s(angles[i].fSrcX, angles[i].fSrcY); Vec2 d(angles[i].fDetSX, angles[i].fDetSY); double fScale; if (!FBP) { // goal: 1/fDen = || u (s-d) || / ( |u| * || u (s-x) || ) // fDen = ( |u| * || u (s-x) || ) / || u (s-d) || // i.e. scale = |u| / || u (s-d) || fScale = u.norm() / det2(u, s-d); } else { // goal: 1/fDen = || u s || / || u (s-x) || // fDen = || u (s-x) || / || u s || // i.e., scale = 1 / || u s || fScale = 1.0 / det2(u, s); } p[i].fNumC = fScale * det2(s,d); p[i].fNumX = fScale * (s-d).y; p[i].fNumY = -fScale * (s-d).x; p[i].fDenC = fScale * det2(u, s); // == 1.0 for FBP p[i].fDenX = fScale * u.y; p[i].fDenY = -fScale * u.x; } ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_C, p, iProjAngles*sizeof(DevFanParams), 0, cudaMemcpyHostToDevice, stream), "transferConstants transfer"); ok &= checkCuda(cudaEventRecord(buf.event, stream), "transferConstants event"); return ok; } bool FanBP_internal(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SFanProjection* angles, float fOutputScale, cudaStream_t stream) { assert(dims.iProjAngles <= g_MaxAngles); cudaTextureObject_t D_texObj; if (!createTextureObjectPitch2D(D_projData, D_texObj, projPitch, dims.iProjDets, dims.iProjAngles)) return false; dim3 dimBlock(g_blockSlices, g_blockSliceSize); dim3 dimGrid((dims.iVolWidth+g_blockSlices-1)/g_blockSlices, (dims.iVolHeight+g_blockSliceSize-1)/g_blockSliceSize); for (unsigned int i = 0; i < dims.iProjAngles; i += g_anglesPerBlock) { if (dims.iRaysPerPixelDim > 1) devFanBP_SS<<>>(D_volumeData, volumePitch, D_texObj, i, dims, fOutputScale); else devFanBP<<>>(D_volumeData, volumePitch, D_texObj, i, dims, fOutputScale); } bool ok = checkCuda(cudaStreamSynchronize(stream), "FanBP"); cudaDestroyTextureObject(D_texObj); return ok; } bool FanBP_FBPWeighted_internal(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SFanProjection* angles, float fOutputScale, cudaStream_t stream) { assert(dims.iProjAngles <= g_MaxAngles); cudaTextureObject_t D_texObj; if (!createTextureObjectPitch2D(D_projData, D_texObj, projPitch, dims.iProjDets, dims.iProjAngles)) return false; dim3 dimBlock(g_blockSlices, g_blockSliceSize); dim3 dimGrid((dims.iVolWidth+g_blockSlices-1)/g_blockSlices, (dims.iVolHeight+g_blockSliceSize-1)/g_blockSliceSize); for (unsigned int i = 0; i < dims.iProjAngles; i += g_anglesPerBlock) { devFanBP<<>>(D_volumeData, volumePitch, D_texObj, i, dims, fOutputScale); } bool ok = checkCuda(cudaStreamSynchronize(stream), "FanBP_FBPWeighted"); cudaDestroyTextureObject(D_texObj); return ok; } // D_projData is a pointer to one padded sinogram line bool FanBP_SART(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, unsigned int angle, const SDimensions& dims, const SFanProjection* angles, float fOutputScale) { TransferConstantsBuffer tcbuf(1); cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "FanBP_SART stream")) return false; // only one angle cudaTextureObject_t D_texObj; if (!createTextureObjectPitch2D(D_projData, D_texObj, projPitch, dims.iProjDets, 1, cudaAddressModeClamp)) { cudaStreamDestroy(stream); return false; } bool ok = transferConstants(angles + angle, 1, false, tcbuf, stream); if (!ok) { cudaStreamDestroy(stream); cudaDestroyTextureObject(D_texObj); return false; } dim3 dimBlock(g_blockSlices, g_blockSliceSize); dim3 dimGrid((dims.iVolWidth+g_blockSlices-1)/g_blockSlices, (dims.iVolHeight+g_blockSliceSize-1)/g_blockSliceSize); devFanBP_SART<<>>(D_volumeData, volumePitch, D_texObj, dims, fOutputScale); ok = checkCuda(cudaStreamSynchronize(stream), "FanBP_SART"); cudaStreamDestroy(stream); cudaDestroyTextureObject(D_texObj); return ok; } bool FanBP(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SFanProjection* angles, float fOutputScale) { TransferConstantsBuffer tcbuf(g_MaxAngles); cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "FanBP stream")) return false; bool ok = true; for (unsigned int iAngle = 0; iAngle < dims.iProjAngles; iAngle += g_MaxAngles) { SDimensions subdims = dims; unsigned int iEndAngle = iAngle + g_MaxAngles; if (iEndAngle >= dims.iProjAngles) iEndAngle = dims.iProjAngles; subdims.iProjAngles = iEndAngle - iAngle; ok &= transferConstants(angles, dims.iProjAngles, false, tcbuf, stream); if (!ok) break; ok &= FanBP_internal(D_volumeData, volumePitch, D_projData + iAngle * projPitch, projPitch, subdims, angles + iAngle, fOutputScale, stream); if (!ok) break; } ok &= checkCuda(cudaStreamSynchronize(stream), "fan_bp"); cudaStreamDestroy(stream); return ok; } bool FanBP_FBPWeighted(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SFanProjection* angles, float fOutputScale) { TransferConstantsBuffer tcbuf(g_MaxAngles); cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "FanBP_FBPWeighted stream")) return false; bool ok = true; for (unsigned int iAngle = 0; iAngle < dims.iProjAngles; iAngle += g_MaxAngles) { SDimensions subdims = dims; unsigned int iEndAngle = iAngle + g_MaxAngles; if (iEndAngle >= dims.iProjAngles) iEndAngle = dims.iProjAngles; subdims.iProjAngles = iEndAngle - iAngle; ok = transferConstants(angles, dims.iProjAngles, true, tcbuf, stream); if (!ok) break; ok = FanBP_FBPWeighted_internal(D_volumeData, volumePitch, D_projData + iAngle * projPitch, projPitch, subdims, angles + iAngle, fOutputScale, stream); if (!ok) break; } ok &= checkCuda(cudaStreamSynchronize(stream), "fan_bp_fbpweighted"); cudaStreamDestroy(stream); return ok; } } astra-toolbox-2.3.0/cuda/2d/fan_fp.cu000066400000000000000000000216411475635207100173330ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/util.h" #include "astra/cuda/2d/arith.h" #include #include #include namespace astraCUDA { static const unsigned g_MaxAngles = 2560; __constant__ float gC_SrcX[g_MaxAngles]; __constant__ float gC_SrcY[g_MaxAngles]; __constant__ float gC_DetSX[g_MaxAngles]; __constant__ float gC_DetSY[g_MaxAngles]; __constant__ float gC_DetUX[g_MaxAngles]; __constant__ float gC_DetUY[g_MaxAngles]; // optimization parameters static const unsigned int g_anglesPerBlock = 16; static const unsigned int g_detBlockSize = 32; static const unsigned int g_blockSlices = 64; // projection for angles that are roughly horizontal // (detector roughly vertical) __global__ void FanFPhorizontal(float* D_projData, unsigned int projPitch, cudaTextureObject_t tex, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions dims, float outputScale) { float* projData = (float*)D_projData; const int relDet = threadIdx.x; const int relAngle = threadIdx.y; const int angle = startAngle + blockIdx.x * g_anglesPerBlock + relAngle; if (angle >= endAngle) return; const int detector = blockIdx.y * g_detBlockSize + relDet; if (detector < 0 || detector >= dims.iProjDets) return; const float fSrcX = gC_SrcX[angle]; const float fSrcY = gC_SrcY[angle]; const float fDetSX = gC_DetSX[angle]; const float fDetSY = gC_DetSY[angle]; const float fDetUX = gC_DetUX[angle]; const float fDetUY = gC_DetUY[angle]; float fVal = 0.0f; const float fdx = fabsf(fDetSX + detector*fDetUX + 0.5f - fSrcX); const float fdy = fabsf(fDetSY + detector*fDetUY + 0.5f - fSrcY); if (fdy > fdx) return; for (int iSubT = 0; iSubT < dims.iRaysPerDet; ++iSubT) { const float fDet = detector + (0.5f + iSubT) / dims.iRaysPerDet; const float fDetX = fDetSX + fDet * fDetUX; const float fDetY = fDetSY + fDet * fDetUY; // ray: y = alpha * x + beta const float alpha = (fSrcY - fDetY) / (fSrcX - fDetX); const float beta = fSrcY - alpha * fSrcX; const float fDistCorr = sqrt(alpha*alpha+1.0f) * outputScale / dims.iRaysPerDet; // intersect ray with first slice float fY = -alpha * (startSlice - 0.5f*dims.iVolWidth + 0.5f) - beta + 0.5f*dims.iVolHeight - 0.5f + 0.5f; float fX = startSlice + 0.5f; int endSlice = startSlice + g_blockSlices; if (endSlice > dims.iVolWidth) endSlice = dims.iVolWidth; float fV = 0.0f; for (int slice = startSlice; slice < endSlice; ++slice) { fV += tex2D(tex, fX, fY); fY -= alpha; fX += 1.0f; } fVal += fV * fDistCorr; } projData[angle*projPitch+detector] += fVal; } // projection for angles that are roughly vertical // (detector roughly horizontal) __global__ void FanFPvertical(float* D_projData, unsigned int projPitch, cudaTextureObject_t tex, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions dims, float outputScale) { const int relDet = threadIdx.x; const int relAngle = threadIdx.y; const int angle = startAngle + blockIdx.x * g_anglesPerBlock + relAngle; if (angle >= endAngle) return; const int detector = blockIdx.y * g_detBlockSize + relDet; if (detector < 0 || detector >= dims.iProjDets) return; float* projData = (float*)D_projData; const float fSrcX = gC_SrcX[angle]; const float fSrcY = gC_SrcY[angle]; const float fDetSX = gC_DetSX[angle]; const float fDetSY = gC_DetSY[angle]; const float fDetUX = gC_DetUX[angle]; const float fDetUY = gC_DetUY[angle]; float fVal = 0.0f; const float fdx = fabsf(fDetSX + detector*fDetUX + 0.5f - fSrcX); const float fdy = fabsf(fDetSY + detector*fDetUY + 0.5f - fSrcY); if (fdy <= fdx) return; for (int iSubT = 0; iSubT < dims.iRaysPerDet; ++iSubT) { const float fDet = detector + (0.5f + iSubT) / dims.iRaysPerDet /*- gC_angle_offset[angle]*/; const float fDetX = fDetSX + fDet * fDetUX; const float fDetY = fDetSY + fDet * fDetUY; // ray: x = alpha * y + beta const float alpha = (fSrcX - fDetX) / (fSrcY - fDetY); const float beta = fSrcX - alpha * fSrcY; const float fDistCorr = sqrt(alpha*alpha+1) * outputScale / dims.iRaysPerDet; // intersect ray with first slice float fX = -alpha * (startSlice - 0.5f*dims.iVolHeight + 0.5f) + beta + 0.5f*dims.iVolWidth - 0.5f + 0.5f; float fY = startSlice + 0.5f; int endSlice = startSlice + g_blockSlices; if (endSlice > dims.iVolHeight) endSlice = dims.iVolHeight; float fV = 0.0f; for (int slice = startSlice; slice < endSlice; ++slice) { fV += tex2D(tex, fX, fY); fX -= alpha; fY += 1.0f; } fVal += fV * fDistCorr; } projData[angle*projPitch+detector] += fVal; } using TransferConstantsBuffer = TransferConstantsBuffer_t; static bool transferConstants(const SFanProjection *projs, unsigned int nth, TransferConstantsBuffer& buf, cudaStream_t stream) { // transfer angles to constant memory float* tmp = &(std::get<0>(buf.d))[0]; bool ok = checkCuda(cudaStreamWaitEvent(stream, buf.event, 0), "transferConstants wait"); #define TRANSFER_TO_CONSTANT(name) do { for (unsigned int i = 0; i < nth; ++i) tmp[i] = projs[i].f##name ; ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_##name, tmp, nth*sizeof(float), 0, cudaMemcpyHostToDevice, stream), "transferConstants transfer"); } while (0) TRANSFER_TO_CONSTANT(SrcX); TRANSFER_TO_CONSTANT(SrcY); TRANSFER_TO_CONSTANT(DetSX); TRANSFER_TO_CONSTANT(DetSY); TRANSFER_TO_CONSTANT(DetUX); TRANSFER_TO_CONSTANT(DetUY); #undef TRANSFER_TO_CONSTANT ok &= checkCuda(cudaEventRecord(buf.event, stream), "transferConstants event"); return ok; } bool FanFP_internal(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SFanProjection* angles, float outputScale, cudaStream_t stream) { assert(dims.iProjAngles <= g_MaxAngles); cudaArray* D_dataArray; cudaTextureObject_t D_texObj; if (!createArrayAndTextureObject2D(D_volumeData, D_dataArray, D_texObj, volumePitch, dims.iVolWidth, dims.iVolHeight)) return false; dim3 dimBlock(g_detBlockSize, g_anglesPerBlock); // region size, angles const unsigned int g_blockSliceSize = g_detBlockSize; unsigned int blockStart = 0; unsigned int blockEnd = dims.iProjAngles; dim3 dimGrid((blockEnd-blockStart+g_anglesPerBlock-1)/g_anglesPerBlock, (dims.iProjDets+g_blockSliceSize-1)/g_blockSliceSize); // angle blocks, regions for (unsigned int i = 0; i < dims.iVolWidth; i += g_blockSlices) FanFPhorizontal<<>>(D_projData, projPitch, D_texObj, i, blockStart, blockEnd, dims, outputScale); for (unsigned int i = 0; i < dims.iVolHeight; i += g_blockSlices) FanFPvertical<<>>(D_projData, projPitch, D_texObj, i, blockStart, blockEnd, dims, outputScale); bool ok = true; ok &= checkCuda(cudaStreamSynchronize(stream), "fan_fp"); cudaFreeArray(D_dataArray); cudaDestroyTextureObject(D_texObj); return ok; } bool FanFP(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SFanProjection* angles, float outputScale) { TransferConstantsBuffer tcbuf(g_MaxAngles); cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "FanFP")) return false; bool ok = true; for (unsigned int iAngle = 0; iAngle < dims.iProjAngles; iAngle += g_MaxAngles) { SDimensions subdims = dims; unsigned int iEndAngle = iAngle + g_MaxAngles; if (iEndAngle >= dims.iProjAngles) iEndAngle = dims.iProjAngles; subdims.iProjAngles = iEndAngle - iAngle; ok &= transferConstants(angles + iAngle, subdims.iProjAngles, tcbuf, stream); if (!ok) break; ok &= FanFP_internal(D_volumeData, volumePitch, D_projData + iAngle * projPitch, projPitch, subdims, angles + iAngle, outputScale, stream); if (!ok) break; } ok &= checkCuda(cudaStreamSynchronize(stream), "fan_fp"); cudaStreamDestroy(stream); return ok; } } astra-toolbox-2.3.0/cuda/2d/fbp.cu000066400000000000000000000120251475635207100166450ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/fbp.h" #include "astra/cuda/2d/fft.h" #include "astra/cuda/2d/par_bp.h" #include "astra/cuda/2d/fan_bp.h" #include "astra/cuda/2d/util.h" // For fan-beam preweighting #include "astra/cuda/3d/fdk.h" #include "astra/Logging.h" #include "astra/Filters.h" #include namespace astraCUDA { // static int FBP::calcFourierFilterSize(int _iDetectorCount) { int iFFTRealDetCount = astra::calcNextPowerOfTwo(2 * _iDetectorCount); int iFreqBinCount = astra::calcFFTFourierSize(iFFTRealDetCount); // CHECKME: Matlab makes this at least 64. Do we also need to? return iFreqBinCount; } FBP::FBP() : ReconAlgo() { D_filter = 0; m_bShortScan = false; fReconstructionScale = 1.0f; } FBP::~FBP() { reset(); } void FBP::reset() { if (D_filter) { freeComplexOnDevice((cufftComplex *)D_filter); D_filter = 0; } m_bShortScan = false; fReconstructionScale = 1.0f; } bool FBP::init() { return true; } bool FBP::setReconstructionScale(float fScale) { fReconstructionScale = fScale; return true; } bool FBP::setFilter(const astra::SFilterConfig &_cfg) { if (D_filter) { freeComplexOnDevice((cufftComplex*)D_filter); D_filter = 0; } cufftComplex *f; bool ok = prepareCuFFTFilter(_cfg, f, m_bSingleFilter, dims.iProjAngles, dims.iProjDets); D_filter = (void *)f; return ok; } bool FBP::iterate(unsigned int iterations) { zeroVolumeData(D_volumeData, volumePitch, dims); bool ok = false; float fFanDetSize = 0.0f; if (fanProjs) { // Call FDK_PreWeight to handle fan beam geometry. We treat // this as a cone beam setup of a single slice: // TODO: TOffsets affects this preweighting... // TODO: We take the fan parameters from the last projection here // without checking if they're the same in all projections float *pfAngles = new float[dims.iProjAngles]; float fOriginSource, fOriginDetector, fOffset; for (unsigned int i = 0; i < dims.iProjAngles; ++i) { bool ok = astra::getFanParameters(fanProjs[i], dims.iProjDets, pfAngles[i], fOriginSource, fOriginDetector, fFanDetSize, fOffset); if (!ok) { ASTRA_ERROR("FBP_CUDA: Failed to extract circular fan beam parameters from fan beam geometry"); return false; } } // We create a fake cudaPitchedPtr cudaPitchedPtr tmp; tmp.ptr = D_sinoData; tmp.pitch = sinoPitch * sizeof(float); tmp.xsize = dims.iProjDets; tmp.ysize = dims.iProjAngles; // and a fake Dimensions3D astraCUDA3d::SDimensions3D dims3d; dims3d.iVolX = dims.iVolWidth; dims3d.iVolY = dims.iVolHeight; dims3d.iVolZ = 1; dims3d.iProjAngles = dims.iProjAngles; dims3d.iProjU = dims.iProjDets; dims3d.iProjV = 1; astraCUDA3d::FDK_PreWeight(tmp, fOriginSource, fOriginDetector, 0.0f, fFanDetSize, 1.0f, m_bShortScan, dims3d, pfAngles); } else { // TODO: How should different detector pixel size in different // projections be handled? } if (D_filter) { int iPaddedSize = astra::calcNextPowerOfTwo(2 * dims.iProjDets); int iFourierSize = astra::calcFFTFourierSize(iPaddedSize); cufftComplex * D_pcFourier = NULL; allocateComplexOnDevice(dims.iProjAngles, iFourierSize, &D_pcFourier); runCudaFFT(dims.iProjAngles, D_sinoData, sinoPitch, dims.iProjDets, iPaddedSize, D_pcFourier); applyFilter(dims.iProjAngles, iFourierSize, D_pcFourier, (cufftComplex*)D_filter, m_bSingleFilter); runCudaIFFT(dims.iProjAngles, D_pcFourier, D_sinoData, sinoPitch, dims.iProjDets, iPaddedSize); freeComplexOnDevice(D_pcFourier); } if (fanProjs) { ok = FanBP_FBPWeighted(D_volumeData, volumePitch, D_sinoData, sinoPitch, dims, fanProjs, fProjectorScale * fReconstructionScale); } else { // scale by number of angles. For the fan-beam case, this is already // handled by FDK_PreWeight float fOutputScale = (M_PI / 2.0f) / (float)dims.iProjAngles; ok = BP(D_volumeData, volumePitch, D_sinoData, sinoPitch, dims, parProjs, fOutputScale * fProjectorScale * fReconstructionScale); } if(!ok) { return false; } return true; } } astra-toolbox-2.3.0/cuda/2d/fft.cu000066400000000000000000000450551475635207100166660ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/fft.h" #include "astra/cuda/2d/util.h" #include "astra/Logging.h" #include "astra/Fourier.h" #include #include using namespace astra; namespace astraCUDA { bool checkCufft(cufftResult err, const char *msg) { if (err != CUFFT_SUCCESS) { ASTRA_ERROR("%s: CUFFT error %d.", msg, err); return false; } else { return true; } } __global__ static void applyFilter_kernel(int _iProjectionCount, int _iFreqBinCount, cufftComplex * _pSinogram, cufftComplex * _pFilter) { int iIndex = threadIdx.x + blockIdx.x * blockDim.x; int iProjectionIndex = iIndex / _iFreqBinCount; if(iProjectionIndex >= _iProjectionCount) { return; } float fA = _pSinogram[iIndex].x; float fB = _pSinogram[iIndex].y; float fC = _pFilter[iIndex].x; float fD = _pFilter[iIndex].y; _pSinogram[iIndex].x = fA * fC - fB * fD; _pSinogram[iIndex].y = fA * fD + fC * fB; } __global__ static void applyFilter_singleFilter_kernel(int _iProjectionCount, int _iFreqBinCount, cufftComplex * _pSinogram, cufftComplex * _pFilter) { int iIndex = threadIdx.x + blockIdx.x * blockDim.x; int iProjectionIndex = iIndex / _iFreqBinCount; int iFilterIndex = iIndex % _iFreqBinCount; if(iProjectionIndex >= _iProjectionCount) { return; } float fA = _pSinogram[iIndex].x; float fB = _pSinogram[iIndex].y; float fC = _pFilter[iFilterIndex].x; float fD = _pFilter[iFilterIndex].y; _pSinogram[iIndex].x = fA * fC - fB * fD; _pSinogram[iIndex].y = fA * fD + fC * fB; } __global__ static void rescaleInverseFourier_kernel(int _iProjectionCount, int _iDetectorCount, float* _pfInFourierOutput) { int iIndex = threadIdx.x + blockIdx.x * blockDim.x; int iProjectionIndex = iIndex / _iDetectorCount; int iDetectorIndex = iIndex % _iDetectorCount; if(iProjectionIndex >= _iProjectionCount) { return; } _pfInFourierOutput[iProjectionIndex * _iDetectorCount + iDetectorIndex] /= (float)_iDetectorCount; } bool rescaleInverseFourier(int _iProjectionCount, int _iDetectorCount, float * _pfInFourierOutput, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; const int iBlockSize = 256; int iElementCount = _iProjectionCount * _iDetectorCount; int iBlockCount = (iElementCount + iBlockSize - 1) / iBlockSize; rescaleInverseFourier_kernel<<< iBlockCount, iBlockSize, 0, stream() >>>(_iProjectionCount, _iDetectorCount, _pfInFourierOutput); return stream.syncIfSync("rescaleInverseFourier"); } bool applyFilter(int _iProjectionCount, int _iFreqBinCount, cufftComplex * _pSinogram, cufftComplex * _pFilter, bool singleFilter, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; const int iBlockSize = 256; int iElementCount = _iProjectionCount * _iFreqBinCount; int iBlockCount = (iElementCount + iBlockSize - 1) / iBlockSize; if (singleFilter) { applyFilter_singleFilter_kernel<<< iBlockCount, iBlockSize, 0, stream() >>>(_iProjectionCount, _iFreqBinCount, _pSinogram, _pFilter); } else { applyFilter_kernel<<< iBlockCount, iBlockSize, 0, stream() >>>(_iProjectionCount, _iFreqBinCount, _pSinogram, _pFilter); } return stream.syncIfSync("applyFilter"); } static bool invokeCudaFFT(int _iProjectionCount, int _iDetectorCount, const float * _pfDevSource, cufftComplex * _pDevTargetComplex, cudaStream_t stream) { cufftHandle plan; if (!checkCufft(cufftPlan1d(&plan, _iDetectorCount, CUFFT_R2C, _iProjectionCount), "invokeCudaFFT plan")) { return false; } if (!checkCufft(cufftSetStream(plan, stream), "invokeCudaFFT plan stream")) { cufftDestroy(plan); return false; } if (!checkCufft(cufftExecR2C(plan, (cufftReal *)_pfDevSource, _pDevTargetComplex), "invokeCudaFFT exec")) { cufftDestroy(plan); return false; } if (!checkCuda(cudaStreamSynchronize(stream), "invokeCudaFFT sync")) { cufftDestroy(plan); return false; } cufftDestroy(plan); return true; } static bool invokeCudaIFFT(int _iProjectionCount, int _iDetectorCount, const cufftComplex * _pDevSourceComplex, float * _pfDevTarget, cudaStream_t stream) { cufftHandle plan; if (!checkCufft(cufftPlan1d(&plan, _iDetectorCount, CUFFT_C2R, _iProjectionCount), "invokeCudaIFFT plan")) { return false; } if (!checkCufft(cufftSetStream(plan, stream), "invokeCudaIFFT plan stream")) { cufftDestroy(plan); return false; } // Getting rid of the const qualifier is due to cufft API issue? if (!checkCufft(cufftExecC2R(plan, (cufftComplex *)_pDevSourceComplex, (cufftReal *)_pfDevTarget), "invokeCudaIFFT exec")) { cufftDestroy(plan); return false; } if (!checkCuda(cudaStreamSynchronize(stream), "invokeCudaIFFT sync")) { cufftDestroy(plan); return false; } cufftDestroy(plan); return true; } bool allocateComplexOnDevice(int _iProjectionCount, int _iDetectorCount, cufftComplex ** _ppDevComplex) { size_t bufferSize = sizeof(cufftComplex) * _iProjectionCount * _iDetectorCount; return checkCuda(cudaMalloc((void **)_ppDevComplex, bufferSize), "fft allocateComplexOnDevice"); } bool freeComplexOnDevice(cufftComplex * _pDevComplex) { return checkCuda(cudaFree(_pDevComplex), "fft freeComplexOnDevice"); } bool uploadComplexArrayToDevice(int _iProjectionCount, int _iDetectorCount, cufftComplex * _pHostComplexSource, cufftComplex * _pDevComplexTarget, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; size_t memSize = sizeof(cufftComplex) * _iProjectionCount * _iDetectorCount; bool ok = checkCuda(cudaMemcpyAsync(_pDevComplexTarget, _pHostComplexSource, memSize, cudaMemcpyHostToDevice, stream()), "fft uploadComplexArrayToDevice"); ok &= stream.syncIfSync("fft uploadComplexArrayToDevice"); return ok; } bool runCudaFFT(int _iProjectionCount, const float * D_pfSource, int _iSourcePitch, int _iProjDets, int _iPaddedSize, cufftComplex * D_pcTarget, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; float * D_pfPaddedSource = NULL; size_t bufferMemSize = sizeof(float) * _iProjectionCount * _iPaddedSize; if (!checkCuda(cudaMalloc((void **)&D_pfPaddedSource, bufferMemSize), "runCudaFFT malloc")) { return false; } if (!checkCuda(cudaMemsetAsync(D_pfPaddedSource, 0, bufferMemSize, stream()), "runCudaFFT memset")) { cudaFree(D_pfPaddedSource); return false; } // pitched memcpy 2D to handle both source pitch and target padding if (!checkCuda(cudaMemcpy2DAsync(D_pfPaddedSource, _iPaddedSize*sizeof(float), D_pfSource, _iSourcePitch*sizeof(float), _iProjDets*sizeof(float), _iProjectionCount, cudaMemcpyDeviceToDevice, stream()), "runCudaFFT memcpy")) { cudaFree(D_pfPaddedSource); return false; } if (!invokeCudaFFT(_iProjectionCount, _iPaddedSize, D_pfPaddedSource, D_pcTarget, stream())) { cudaFree(D_pfPaddedSource); return false; } if (!stream.sync("runCudaFFT sync")) { cudaFree(D_pfPaddedSource); return false; } cudaFree(D_pfPaddedSource); return true; } bool runCudaIFFT(int _iProjectionCount, const cufftComplex *D_pcSource, float * D_pfTarget, int _iTargetPitch, int _iProjDets, int _iPaddedSize, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; float * D_pfPaddedTarget = NULL; size_t bufferMemSize = sizeof(float) * _iProjectionCount * _iPaddedSize; if (!checkCuda(cudaMalloc((void **)&D_pfPaddedTarget, bufferMemSize), "runCudaIFFT malloc")) { return false; } if (!invokeCudaIFFT(_iProjectionCount, _iPaddedSize, D_pcSource, D_pfPaddedTarget, stream())) { cudaFree(D_pfPaddedTarget); return false; } rescaleInverseFourier(_iProjectionCount, _iPaddedSize, D_pfPaddedTarget, stream()); if (!checkCuda(cudaMemsetAsync(D_pfTarget, 0, sizeof(float) * _iProjectionCount * _iTargetPitch, stream()), "runCudaIFFT memset")) { cudaFree(D_pfPaddedTarget); return false; } // pitched memcpy 2D to handle both source padding and target pitch if (!checkCuda(cudaMemcpy2DAsync(D_pfTarget, _iTargetPitch*sizeof(float), D_pfPaddedTarget, _iPaddedSize*sizeof(float), _iProjDets*sizeof(float), _iProjectionCount, cudaMemcpyDeviceToDevice, stream()), "runCudaIFFT memcpy")) { cudaFree(D_pfPaddedTarget); return false; } if (!stream.sync("runCudaIFFT sync")) { cudaFree(D_pfPaddedTarget); return false; } cudaFree(D_pfPaddedTarget); return true; } void genIdenFilter(int _iProjectionCount, cufftComplex * _pFilter, int _iFFTRealDetectorCount, int _iFFTFourierDetectorCount) { for(int iProjectionIndex = 0; iProjectionIndex < _iProjectionCount; iProjectionIndex++) { for(int iDetectorIndex = 0; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { int iIndex = iDetectorIndex + iProjectionIndex * _iFFTFourierDetectorCount; _pFilter[iIndex].x = 1.0f; _pFilter[iIndex].y = 0.0f; } } } void genCuFFTFilter(const SFilterConfig &_cfg, int _iProjectionCount, cufftComplex * _pFilter, int _iFFTRealDetectorCount, int _iFFTFourierDetectorCount) { float * pfFilt = astra::genFilter(_cfg, _iFFTRealDetectorCount, _iFFTFourierDetectorCount); for(int iDetectorIndex = 0; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fFilterValue = pfFilt[iDetectorIndex]; for(int iProjectionIndex = 0; iProjectionIndex < _iProjectionCount; iProjectionIndex++) { int iIndex = iDetectorIndex + iProjectionIndex * _iFFTFourierDetectorCount; _pFilter[iIndex].x = fFilterValue; _pFilter[iIndex].y = 0.0f; } } delete[] pfFilt; } bool prepareCuFFTFilter(const SFilterConfig &cfg, cufftComplex *&D_filter, bool &singleFilter, int iProjectionCount, int iDetectorCount, std::optional _stream) { D_filter = nullptr; singleFilter = false; StreamHelper stream(_stream); if (!stream) return false; if (cfg.m_eType == astra::FILTER_NONE) return true; if (cfg.m_eType != astra::FILTER_SINOGRAM && cfg.m_eType != astra::FILTER_RSINOGRAM) singleFilter = true; int filterRows; if (singleFilter) filterRows = 1; else filterRows = iProjectionCount; int iPaddedDetCount = calcNextPowerOfTwo(2 * iDetectorCount); int iHalfFFTSize = astra::calcFFTFourierSize(iPaddedDetCount); //int iFFTRealDetCount = astra::calcNextPowerOfTwo(2 * dims.iProjDets); //int iFreqBinCount = astra::calcFFTFourierSize(iFFTRealDetCount); size_t filterSize = (size_t)filterRows * iHalfFFTSize; if (!allocateComplexOnDevice(filterRows, iHalfFFTSize, &D_filter)) { D_filter = nullptr; return false; } std::vector hostFilter(filterSize); switch(cfg.m_eType) { case astra::FILTER_NONE: // handled above break; case astra::FILTER_RAMLAK: case astra::FILTER_SHEPPLOGAN: case astra::FILTER_COSINE: case astra::FILTER_HAMMING: case astra::FILTER_HANN: case astra::FILTER_TUKEY: case astra::FILTER_LANCZOS: case astra::FILTER_TRIANGULAR: case astra::FILTER_GAUSSIAN: case astra::FILTER_BARTLETTHANN: case astra::FILTER_BLACKMAN: case astra::FILTER_NUTTALL: case astra::FILTER_BLACKMANHARRIS: case astra::FILTER_BLACKMANNUTTALL: case astra::FILTER_FLATTOP: case astra::FILTER_KAISER: case astra::FILTER_PARZEN: { genCuFFTFilter(cfg, filterRows, &hostFilter[0], iPaddedDetCount, iHalfFFTSize); bool ok = uploadComplexArrayToDevice(filterRows, iHalfFFTSize, &hostFilter[0], D_filter, stream()); ok &= stream.syncIfSync("prepareCuFFTFilter upload"); if (!ok) { cudaFree(D_filter); D_filter = nullptr; return false; } break; } case astra::FILTER_PROJECTION: { // make sure the offered filter has the correct size assert(cfg.m_iCustomFilterWidth == iHalfFFTSize); assert(cfg.m_iCustomFilterHeight == 1); for (int i = 0; i < iHalfFFTSize; ++i) { float fValue = cfg.m_pfCustomFilter[i]; for (int j = 0; j < filterRows; ++j) { hostFilter[i + j * iHalfFFTSize].x = fValue; hostFilter[i + j * iHalfFFTSize].y = 0.0f; } } bool ok = uploadComplexArrayToDevice(filterRows, iHalfFFTSize, &hostFilter[0], D_filter, stream()); ok &= stream.syncIfSync("prepareCuFFTFilter upload"); if (!ok) { cudaFree(D_filter); D_filter = nullptr; return false; } break; } case astra::FILTER_SINOGRAM: { // make sure the offered filter has the correct size assert(cfg.m_iCustomFilterWidth == iHalfFFTSize); assert(cfg.m_iCustomFilterHeight == iProjectionCount); assert(filterRows == iProjectionCount); for (int i = 0; i < iHalfFFTSize; ++i) { for (int j = 0; j < filterRows; ++j) { float fValue = cfg.m_pfCustomFilter[i + j * iHalfFFTSize]; hostFilter[i + j * iHalfFFTSize].x = fValue; hostFilter[i + j * iHalfFFTSize].y = 0.0f; } } bool ok = uploadComplexArrayToDevice(filterRows, iHalfFFTSize, &hostFilter[0], D_filter, stream()); ok &= stream.syncIfSync("prepareCuFFTFilter upload"); if (!ok) { cudaFree(D_filter); D_filter = nullptr; return false; } break; } case astra::FILTER_RPROJECTION: { size_t iSpatialFilterSize = filterRows * iPaddedDetCount; std::vector hostSpatialFilter(iSpatialFilterSize); int iUsedFilterWidth = min(cfg.m_iCustomFilterWidth, iPaddedDetCount); int iStartFilterIndex = (cfg.m_iCustomFilterWidth - iUsedFilterWidth) / 2; int iMaxFilterIndex = iStartFilterIndex + iUsedFilterWidth; int iFilterShiftSize = cfg.m_iCustomFilterWidth / 2; for (int iDetectorIndex = iStartFilterIndex; iDetectorIndex < iMaxFilterIndex; iDetectorIndex++) { int iFFTInFilterIndex = (iDetectorIndex + iPaddedDetCount - iFilterShiftSize) % iPaddedDetCount; float fValue = cfg.m_pfCustomFilter[iDetectorIndex]; for (int iProjectionIndex = 0; iProjectionIndex < filterRows; iProjectionIndex++) { hostSpatialFilter[iFFTInFilterIndex + iProjectionIndex * iPaddedDetCount] = fValue; } } float* D_spatialFilter = NULL; if (!checkCuda(cudaMalloc((void **)&D_spatialFilter, sizeof(float) * iSpatialFilterSize), "prepareCuFFTFilter malloc")) { cudaFree(D_filter); D_filter = nullptr; return false; } if (!checkCuda(cudaMemcpy(D_spatialFilter, &hostSpatialFilter[0], sizeof(float) * iSpatialFilterSize, cudaMemcpyHostToDevice), "prepareCuFFTFilter memcpy")) { cudaFree(D_filter); D_filter = nullptr; return false; } bool ok = runCudaFFT(filterRows, D_spatialFilter, iPaddedDetCount, iPaddedDetCount, iPaddedDetCount, D_filter, stream()); // need to synchronize here for the cudaFree ok &= stream.sync("prepareCuFFTFilter FFT"); cudaFree(D_spatialFilter); if (!ok) { cudaFree(D_filter); D_filter = nullptr; return false; } break; } case astra::FILTER_RSINOGRAM: { size_t iSpatialFilterSize = filterRows * iPaddedDetCount; std::vector hostSpatialFilter(iSpatialFilterSize); int iUsedFilterWidth = min(cfg.m_iCustomFilterWidth, iPaddedDetCount); int iStartFilterIndex = (cfg.m_iCustomFilterWidth - iUsedFilterWidth) / 2; int iMaxFilterIndex = iStartFilterIndex + iUsedFilterWidth; int iFilterShiftSize = cfg.m_iCustomFilterWidth / 2; for(int iDetectorIndex = iStartFilterIndex; iDetectorIndex < iMaxFilterIndex; iDetectorIndex++) { int iFFTInFilterIndex = (iDetectorIndex + iPaddedDetCount - iFilterShiftSize) % iPaddedDetCount; for(int iProjectionIndex = 0; iProjectionIndex < iProjectionCount; iProjectionIndex++) { float fValue = cfg.m_pfCustomFilter[iDetectorIndex + iProjectionIndex * cfg.m_iCustomFilterWidth]; hostSpatialFilter[iFFTInFilterIndex + iProjectionIndex * iPaddedDetCount] = fValue; } } float* D_spatialFilter = NULL; if (!checkCuda(cudaMalloc((void **)&D_spatialFilter, sizeof(float) * iSpatialFilterSize), "prepareCuFFTFilter malloc")) { cudaFree(D_filter); D_filter = nullptr; return false; } if (!checkCuda(cudaMemcpy(D_spatialFilter, &hostSpatialFilter[0], sizeof(float) * iSpatialFilterSize, cudaMemcpyHostToDevice), "prepareCuFFTFilter memcpy")) { cudaFree(D_filter); D_filter = nullptr; return false; } bool ok = runCudaFFT(filterRows, D_spatialFilter, iPaddedDetCount, iPaddedDetCount, iPaddedDetCount, D_filter, stream()); // need to synchronize here for the cudaFree ok &= stream.sync("prepareCuFFTFilter FFT"); cudaFree(D_spatialFilter); if (!ok) { cudaFree(D_filter); D_filter = nullptr; return false; } break; } default: { ASTRA_ERROR("FBP::setFilter: Unknown filter type requested"); return false; } } return true; } } astra-toolbox-2.3.0/cuda/2d/par_bp.cu000066400000000000000000000252141475635207100173450ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/util.h" #include "astra/cuda/2d/arith.h" #include #include #include namespace astraCUDA { const unsigned int g_anglesPerBlock = 16; const unsigned int g_blockSliceSize = 32; const unsigned int g_blockSlices = 16; const unsigned int g_MaxAngles = 2560; __constant__ float gC_angle_scaled_sin[g_MaxAngles]; __constant__ float gC_angle_scaled_cos[g_MaxAngles]; __constant__ float gC_angle_offset[g_MaxAngles]; __constant__ float gC_angle_scale[g_MaxAngles]; // TODO: Templated version with/without scale? (Or only the global outputscale) __global__ void devBP(float* D_volData, unsigned int volPitch, cudaTextureObject_t tex, unsigned int startAngle, const SDimensions dims, float fOutputScale) { const int relX = threadIdx.x; const int relY = threadIdx.y; int endAngle = startAngle + g_anglesPerBlock; if (endAngle > dims.iProjAngles) endAngle = dims.iProjAngles; const int X = blockIdx.x * g_blockSlices + relX; const int Y = blockIdx.y * g_blockSliceSize + relY; if (X >= dims.iVolWidth || Y >= dims.iVolHeight) return; const float fX = ( X - 0.5f*dims.iVolWidth + 0.5f ); const float fY = ( Y - 0.5f*dims.iVolHeight + 0.5f ); float* volData = (float*)D_volData; float fVal = 0.0f; float fA = startAngle + 0.5f; for (int angle = startAngle; angle < endAngle; ++angle) { const float scaled_cos_theta = gC_angle_scaled_cos[angle]; const float scaled_sin_theta = gC_angle_scaled_sin[angle]; const float TOffset = gC_angle_offset[angle]; const float scale = gC_angle_scale[angle]; const float fT = fX * scaled_cos_theta - fY * scaled_sin_theta + TOffset; fVal += tex2D(tex, fT, fA) * scale; fA += 1.0f; } volData[Y*volPitch+X] += fVal * fOutputScale; } // supersampling version __global__ void devBP_SS(float* D_volData, unsigned int volPitch, cudaTextureObject_t tex, unsigned int startAngle, const SDimensions dims, float fOutputScale) { const int relX = threadIdx.x; const int relY = threadIdx.y; int endAngle = startAngle + g_anglesPerBlock; if (endAngle > dims.iProjAngles) endAngle = dims.iProjAngles; const int X = blockIdx.x * g_blockSlices + relX; const int Y = blockIdx.y * g_blockSliceSize + relY; if (X >= dims.iVolWidth || Y >= dims.iVolHeight) return; const float fX = ( X - 0.5f*dims.iVolWidth + 0.5f - 0.5f + 0.5f/dims.iRaysPerPixelDim); const float fY = ( Y - 0.5f*dims.iVolHeight + 0.5f - 0.5f + 0.5f/dims.iRaysPerPixelDim); const float fSubStep = 1.0f/(dims.iRaysPerPixelDim); // * dims.fDetScale); float* volData = (float*)D_volData; float fVal = 0.0f; float fA = startAngle + 0.5f; fOutputScale /= (dims.iRaysPerPixelDim * dims.iRaysPerPixelDim); for (int angle = startAngle; angle < endAngle; ++angle) { const float cos_theta = gC_angle_scaled_cos[angle]; const float sin_theta = gC_angle_scaled_sin[angle]; const float TOffset = gC_angle_offset[angle]; const float scale = gC_angle_scale[angle]; float fT = fX * cos_theta - fY * sin_theta + TOffset; for (int iSubX = 0; iSubX < dims.iRaysPerPixelDim; ++iSubX) { float fTy = fT; fT += fSubStep * cos_theta; for (int iSubY = 0; iSubY < dims.iRaysPerPixelDim; ++iSubY) { fVal += tex2D(tex, fTy, fA) * scale; fTy -= fSubStep * sin_theta; } } fA += 1.0f; } volData[Y*volPitch+X] += fVal * fOutputScale; } __global__ void devBP_SART(float* D_volData, unsigned int volPitch, cudaTextureObject_t tex, float offset, float angle_sin, float angle_cos, const SDimensions dims, float fOutputScale) { const int relX = threadIdx.x; const int relY = threadIdx.y; const int X = blockIdx.x * g_blockSlices + relX; const int Y = blockIdx.y * g_blockSliceSize + relY; if (X >= dims.iVolWidth || Y >= dims.iVolHeight) return; const float fX = ( X - 0.5f*dims.iVolWidth + 0.5f ); const float fY = ( Y - 0.5f*dims.iVolHeight + 0.5f ); const float fT = fX * angle_cos - fY * angle_sin + offset; const float fVal = tex2D(tex, fT, 0.5f); // NB: The 'scale' constant in devBP is cancelled out by the SART weighting D_volData[Y*volPitch+X] += fVal * fOutputScale; } using TransferConstantsBuffer = TransferConstantsBuffer_t; static bool transferConstants(const SParProjection *angles, unsigned int nth, TransferConstantsBuffer& buf, cudaStream_t stream) { float* angle_scaled_sin = &(std::get<0>(buf.d))[0]; float* angle_scaled_cos = &(std::get<1>(buf.d))[0]; float* angle_offset = &(std::get<2>(buf.d))[0]; float* angle_scale = &(std::get<3>(buf.d))[0]; bool ok = checkCuda(cudaStreamWaitEvent(stream, buf.event, 0), "transferConstants wait"); for (unsigned int i = 0; i < nth; ++i) { double d = angles[i].fDetUX * angles[i].fRayY - angles[i].fDetUY * angles[i].fRayX; angle_scaled_cos[i] = angles[i].fRayY / d; angle_scaled_sin[i] = -angles[i].fRayX / d; angle_offset[i] = (angles[i].fDetSY * angles[i].fRayX - angles[i].fDetSX * angles[i].fRayY) / d; angle_scale[i] = sqrt(angles[i].fRayX * angles[i].fRayX + angles[i].fRayY * angles[i].fRayY) / abs(d); } //fprintf(stderr, "outputscale in BP_internal: %f, %f\n", fOutputScale, angle_scale[0]); //fprintf(stderr, "ray in BP_internal: %f,%f (length %f)\n", angles[0].fRayX, angles[0].fRayY, sqrt(angles[0].fRayX * angles[0].fRayX + angles[0].fRayY * angles[0].fRayY)); ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_angle_scaled_sin, angle_scaled_sin, nth*sizeof(float), 0, cudaMemcpyHostToDevice, stream), "transferConstants transfer 1"); ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_angle_scaled_cos, angle_scaled_cos, nth*sizeof(float), 0, cudaMemcpyHostToDevice, stream), "transferConstants transfer 2"); ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_angle_offset, angle_offset, nth*sizeof(float), 0, cudaMemcpyHostToDevice, stream), "transferConstants transfer 3"); ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_angle_scale, angle_scale, nth*sizeof(float), 0, cudaMemcpyHostToDevice, stream), "transferConstants transfer 4"); ok &= checkCuda(cudaEventRecord(buf.event, stream), "transferConstants event"); return ok; } bool BP_internal(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SParProjection* angles, float fOutputScale, cudaStream_t stream) { assert(dims.iProjAngles <= g_MaxAngles); cudaTextureObject_t D_texObj; if (!createTextureObjectPitch2D(D_projData, D_texObj, projPitch, dims.iProjDets, dims.iProjAngles)) return false; dim3 dimBlock(g_blockSlices, g_blockSliceSize); dim3 dimGrid((dims.iVolWidth+g_blockSlices-1)/g_blockSlices, (dims.iVolHeight+g_blockSliceSize-1)/g_blockSliceSize); for (unsigned int i = 0; i < dims.iProjAngles; i += g_anglesPerBlock) { if (dims.iRaysPerPixelDim > 1) devBP_SS<<>>(D_volumeData, volumePitch, D_texObj, i, dims, fOutputScale); else devBP<<>>(D_volumeData, volumePitch, D_texObj, i, dims, fOutputScale); } bool ok = checkCuda(cudaStreamSynchronize(stream), "par_bp"); cudaDestroyTextureObject(D_texObj); return ok; } bool BP(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SParProjection* angles, float fOutputScale) { TransferConstantsBuffer tcbuf(g_MaxAngles); cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "BP stream")) return false; bool ok = true; for (unsigned int iAngle = 0; iAngle < dims.iProjAngles; iAngle += g_MaxAngles) { SDimensions subdims = dims; unsigned int iEndAngle = iAngle + g_MaxAngles; if (iEndAngle >= dims.iProjAngles) iEndAngle = dims.iProjAngles; subdims.iProjAngles = iEndAngle - iAngle; ok &= transferConstants(angles + iAngle, subdims.iProjAngles, tcbuf, stream); if (!ok) break; ok &= BP_internal(D_volumeData, volumePitch, D_projData + iAngle * projPitch, projPitch, subdims, angles + iAngle, fOutputScale, stream); if (!ok) break; } ok &= checkCuda(cudaStreamSynchronize(stream), "par_bp"); cudaStreamDestroy(stream); return ok; } bool BP_SART(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, unsigned int angle, const SDimensions& dims, const SParProjection* angles, float fOutputScale) { // Only one angle. // We need to Clamp to the border pixels instead of to zero, because // SART weights with ray length. cudaTextureObject_t D_texObj; if (!createTextureObjectPitch2D(D_projData, D_texObj, projPitch, dims.iProjDets, 1, cudaAddressModeClamp)) return false; double d = angles[angle].fDetUX * angles[angle].fRayY - angles[angle].fDetUY * angles[angle].fRayX; float angle_scaled_cos = angles[angle].fRayY / d; float angle_scaled_sin = -angles[angle].fRayX / d; // TODO: Check signs float angle_offset = (angles[angle].fDetSY * angles[angle].fRayX - angles[angle].fDetSX * angles[angle].fRayY) / d; // NB: The adjoint scaling factor from regular BP is cancelled out by the SART weighting //fOutputScale *= sqrt(angles[angle].fRayX * angles[angle].fRayX + angles[angle].fRayY * angles[angle].fRayY) / abs(d); dim3 dimBlock(g_blockSlices, g_blockSliceSize); dim3 dimGrid((dims.iVolWidth+g_blockSlices-1)/g_blockSlices, (dims.iVolHeight+g_blockSliceSize-1)/g_blockSliceSize); cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "BP_SART stream")) { cudaDestroyTextureObject(D_texObj); return false; } devBP_SART<<>>(D_volumeData, volumePitch, D_texObj, angle_offset, angle_scaled_sin, angle_scaled_cos, dims, fOutputScale); bool ok = checkCuda(cudaStreamSynchronize(stream), "BP_SART"); cudaStreamDestroy(stream); cudaDestroyTextureObject(D_texObj); return ok; } } astra-toolbox-2.3.0/cuda/2d/par_fp.cu000066400000000000000000000253671475635207100173620ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/util.h" #include "astra/cuda/2d/arith.h" #include #include #include #include namespace astraCUDA { static const unsigned g_MaxAngles = 2560; __constant__ float gC_angle[g_MaxAngles]; __constant__ float gC_angle_offset[g_MaxAngles]; __constant__ float gC_angle_detsize[g_MaxAngles]; // optimization parameters static const unsigned int g_anglesPerBlock = 16; static const unsigned int g_detBlockSize = 32; static const unsigned int g_blockSlices = 64; // projection for angles that are roughly horizontal // (detector roughly vertical) __global__ void FPhorizontal_simple(float* D_projData, unsigned int projPitch, cudaTextureObject_t tex, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions dims, float outputScale) { const int relDet = threadIdx.x; const int relAngle = threadIdx.y; int angle = startAngle + blockIdx.x * g_anglesPerBlock + relAngle; if (angle >= endAngle) return; const float theta = gC_angle[angle]; const float cos_theta = __cosf(theta); const float sin_theta = __sinf(theta); // compute start detector for this block/angle: const int detRegion = blockIdx.y; const int detector = detRegion * g_detBlockSize + relDet; // Now project the part of the ray to angle,detector through // slices startSlice to startSlice+g_blockSlices-1 if (detector < 0 || detector >= dims.iProjDets) return; const float fDetStep = -gC_angle_detsize[angle] / sin_theta; float fSliceStep = cos_theta / sin_theta; float fDistCorr; if (sin_theta > 0.0f) fDistCorr = outputScale / sin_theta; else fDistCorr = -outputScale / sin_theta; float fVal = 0.0f; // project detector on slice float fP = (detector - 0.5f*dims.iProjDets + 0.5f - gC_angle_offset[angle]) * fDetStep + (startSlice - 0.5f*dims.iVolWidth + 0.5f) * fSliceStep + 0.5f*dims.iVolHeight - 0.5f + 0.5f; float fS = startSlice + 0.5f; int endSlice = startSlice + g_blockSlices; if (endSlice > dims.iVolWidth) endSlice = dims.iVolWidth; if (dims.iRaysPerDet > 1) { fP += (-0.5f*dims.iRaysPerDet + 0.5f)/dims.iRaysPerDet * fDetStep; const float fSubDetStep = fDetStep / dims.iRaysPerDet; fDistCorr /= dims.iRaysPerDet; fSliceStep -= dims.iRaysPerDet * fSubDetStep; for (int slice = startSlice; slice < endSlice; ++slice) { for (int iSubT = 0; iSubT < dims.iRaysPerDet; ++iSubT) { fVal += tex2D(tex, fS, fP); fP += fSubDetStep; } fP += fSliceStep; fS += 1.0f; } } else { for (int slice = startSlice; slice < endSlice; ++slice) { fVal += tex2D(tex, fS, fP); fP += fSliceStep; fS += 1.0f; } } D_projData[angle*projPitch+detector] += fVal * fDistCorr; } // projection for angles that are roughly vertical // (detector roughly horizontal) __global__ void FPvertical_simple(float* D_projData, unsigned int projPitch, cudaTextureObject_t tex, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions dims, float outputScale) { const int relDet = threadIdx.x; const int relAngle = threadIdx.y; int angle = startAngle + blockIdx.x * g_anglesPerBlock + relAngle; if (angle >= endAngle) return; const float theta = gC_angle[angle]; const float cos_theta = __cosf(theta); const float sin_theta = __sinf(theta); // compute start detector for this block/angle: const int detRegion = blockIdx.y; const int detector = detRegion * g_detBlockSize + relDet; // Now project the part of the ray to angle,detector through // slices startSlice to startSlice+g_blockSlices-1 if (detector < 0 || detector >= dims.iProjDets) return; const float fDetStep = gC_angle_detsize[angle] / cos_theta; float fSliceStep = sin_theta / cos_theta; float fDistCorr; if (cos_theta < 0.0f) fDistCorr = -outputScale / cos_theta; else fDistCorr = outputScale / cos_theta; float fVal = 0.0f; float fP = (detector - 0.5f*dims.iProjDets + 0.5f - gC_angle_offset[angle]) * fDetStep + (startSlice - 0.5f*dims.iVolHeight + 0.5f) * fSliceStep + 0.5f*dims.iVolWidth - 0.5f + 0.5f; float fS = startSlice+0.5f; int endSlice = startSlice + g_blockSlices; if (endSlice > dims.iVolHeight) endSlice = dims.iVolHeight; if (dims.iRaysPerDet > 1) { fP += (-0.5f*dims.iRaysPerDet + 0.5f)/dims.iRaysPerDet * fDetStep; const float fSubDetStep = fDetStep / dims.iRaysPerDet; fDistCorr /= dims.iRaysPerDet; fSliceStep -= dims.iRaysPerDet * fSubDetStep; for (int slice = startSlice; slice < endSlice; ++slice) { for (int iSubT = 0; iSubT < dims.iRaysPerDet; ++iSubT) { fVal += tex2D(tex, fP, fS); fP += fSubDetStep; } fP += fSliceStep; fS += 1.0f; } } else { for (int slice = startSlice; slice < endSlice; ++slice) { fVal += tex2D(tex, fP, fS); fP += fSliceStep; fS += 1.0f; } } D_projData[angle*projPitch+detector] += fVal * fDistCorr; } // Coordinates of center of detector pixel number t: // x = (t - 0.5*nDets + 0.5 - fOffset) * fSize * cos(fAngle) // y = - (t - 0.5*nDets + 0.5 - fOffset) * fSize * sin(fAngle) using TransferConstantsBuffer = TransferConstantsBuffer_t; static bool transferConstants(const SParProjection *projs, unsigned int nth, unsigned int ndets, TransferConstantsBuffer& buf, cudaStream_t stream) { float *angles = &(std::get<0>(buf.d))[0]; float *offsets = &(std::get<1>(buf.d))[0]; float *detsizes = &(std::get<2>(buf.d))[0]; bool ok = checkCuda(cudaStreamWaitEvent(stream, buf.event, 0), "transferConstants wait"); for (int i = 0; i < nth; ++i) getParParameters(projs[i], ndets, angles[i], detsizes[i], offsets[i]); ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_angle, angles, nth*sizeof(float), 0, cudaMemcpyHostToDevice, stream), "transferConstants angles"); ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_angle_offset, offsets, nth*sizeof(float), 0, cudaMemcpyHostToDevice, stream), "transferConstants offsets"); ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_angle_detsize, detsizes, nth*sizeof(float), 0, cudaMemcpyHostToDevice, stream), "transferConstants detsizes"); ok &= checkCuda(cudaEventRecord(buf.event, stream), "transferConstants event"); return ok; } bool FP_simple_internal(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SParProjection* angles, float outputScale, cudaStream_t stream) { assert(dims.iProjAngles <= g_MaxAngles); assert(angles); cudaArray* D_dataArray; cudaTextureObject_t D_texObj; if (!createArrayAndTextureObject2D(D_volumeData, D_dataArray, D_texObj, volumePitch, dims.iVolWidth, dims.iVolHeight, stream)) return false; dim3 dimBlock(g_detBlockSize, g_anglesPerBlock); // detector block size, angles // Run over all angles, grouping them into groups of the same // orientation (roughly horizontal vs. roughly vertical). // Start a stream of grids for each such group. // TODO: Check if it's worth it to store this info instead // of recomputing it every FP. unsigned int blockStart = 0; unsigned int blockEnd = 0; bool blockVertical = false; for (unsigned int a = 0; a <= dims.iProjAngles; ++a) { bool vertical = false; // TODO: Having <= instead of < below causes a 5% speedup. // Maybe we should detect corner cases and put them in the optimal // group of angles. if (a != dims.iProjAngles) vertical = (fabsf(angles[a].fRayX) <= fabsf(angles[a].fRayY)); if (a == dims.iProjAngles || vertical != blockVertical) { // block done blockEnd = a; if (blockStart != blockEnd) { dim3 dimGrid((blockEnd-blockStart+g_anglesPerBlock-1)/g_anglesPerBlock, (dims.iProjDets+g_detBlockSize-1)/g_detBlockSize); // angle blocks, detector blocks //printf("angle block: %d to %d, %d\n", blockStart, blockEnd, blockVertical); if (!blockVertical) for (unsigned int i = 0; i < dims.iVolWidth; i += g_blockSlices) FPhorizontal_simple<<>>(D_projData, projPitch, D_texObj, i, blockStart, blockEnd, dims, outputScale); else for (unsigned int i = 0; i < dims.iVolHeight; i += g_blockSlices) FPvertical_simple<<>>(D_projData, projPitch, D_texObj, i, blockStart, blockEnd, dims, outputScale); } blockVertical = vertical; blockStart = a; } } bool ok = checkCuda(cudaStreamSynchronize(stream), "par_fp"); cudaFreeArray(D_dataArray); cudaDestroyTextureObject(D_texObj); return ok; } bool FP_simple(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SParProjection* angles, float outputScale) { TransferConstantsBuffer tcbuf(g_MaxAngles); cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "FP stream")) return false; bool ok = true; for (unsigned int iAngle = 0; iAngle < dims.iProjAngles; iAngle += g_MaxAngles) { SDimensions subdims = dims; unsigned int iEndAngle = iAngle + g_MaxAngles; if (iEndAngle >= dims.iProjAngles) iEndAngle = dims.iProjAngles; subdims.iProjAngles = iEndAngle - iAngle; ok &= transferConstants(angles + iAngle, subdims.iProjAngles, dims.iProjDets, tcbuf, stream); if (!ok) break; ok &= FP_simple_internal(D_volumeData, volumePitch, D_projData + iAngle * projPitch, projPitch, subdims, angles + iAngle, outputScale, stream); if (!ok) break; } ok &= checkCuda(cudaStreamSynchronize(stream), "par_fp"); cudaStreamDestroy(stream); return ok; } bool FP(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SParProjection* angles, float outputScale) { return FP_simple(D_volumeData, volumePitch, D_projData, projPitch, dims, angles, outputScale); } } astra-toolbox-2.3.0/cuda/2d/sart.cu000066400000000000000000000165701475635207100170600ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/sart.h" #include "astra/cuda/2d/util.h" #include "astra/cuda/2d/arith.h" #include "astra/cuda/2d/fan_fp.h" #include "astra/cuda/2d/fan_bp.h" #include "astra/cuda/2d/par_fp.h" #include "astra/cuda/2d/par_bp.h" #include #include namespace astraCUDA { // FIXME: Remove these functions. (Outdated) __global__ void devMUL_SART(float* pfOut, const float* pfIn, unsigned int pitch, unsigned int width) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; pfOut[x] *= pfIn[x]; } bool MUL_SART(float* pfOut, const float* pfIn, unsigned int pitch, unsigned int width) { dim3 blockSize(16,16); dim3 gridSize((width+15)/16, 1); cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "MUL_SART stream")) return false; devMUL_SART<<>>(pfOut, pfIn, pitch, width); bool ok = checkCuda(cudaStreamSynchronize(stream), "MUL_SART"); cudaStreamDestroy(stream); return ok; } SART::SART() : ReconAlgo() { D_projData = 0; D_tmpData = 0; D_lineWeight = 0; projectionOrder = 0; projectionCount = 0; iteration = 0; customOrder = false; fRelaxation = 1.0f; } SART::~SART() { reset(); } void SART::reset() { cudaFree(D_projData); cudaFree(D_tmpData); cudaFree(D_lineWeight); D_projData = 0; D_tmpData = 0; D_lineWeight = 0; useVolumeMask = false; useSinogramMask = false; if (projectionOrder != NULL) delete[] projectionOrder; projectionOrder = 0; projectionCount = 0; iteration = 0; customOrder = false; fRelaxation = 1.0f; ReconAlgo::reset(); } bool SART::init() { if (useVolumeMask) { allocateVolumeData(D_tmpData, tmpPitch, dims); zeroVolumeData(D_tmpData, tmpPitch, dims); } // NB: Non-standard dimensions SDimensions linedims = dims; linedims.iProjAngles = 1; allocateProjectionData(D_projData, projPitch, linedims); zeroProjectionData(D_projData, projPitch, linedims); allocateProjectionData(D_lineWeight, linePitch, dims); zeroProjectionData(D_lineWeight, linePitch, dims); // We can't precompute lineWeights when using a mask if (!useVolumeMask) precomputeWeights(); // TODO: check if allocations succeeded return true; } bool SART::setProjectionOrder(int* _projectionOrder, int _projectionCount) { customOrder = true; projectionCount = _projectionCount; projectionOrder = new int[projectionCount]; for (int i = 0; i < projectionCount; i++) { projectionOrder[i] = _projectionOrder[i]; } return true; } bool SART::precomputeWeights() { zeroProjectionData(D_lineWeight, linePitch, dims); if (useVolumeMask) { callFP(D_maskData, maskPitch, D_lineWeight, linePitch, 1.0f); } else { // Allocate tmpData temporarily allocateVolumeData(D_tmpData, tmpPitch, dims); zeroVolumeData(D_tmpData, tmpPitch, dims); processVol(D_tmpData, 1.0f, tmpPitch, dims); callFP(D_tmpData, tmpPitch, D_lineWeight, linePitch, 1.0f); cudaFree(D_tmpData); D_tmpData = 0; } processSino(D_lineWeight, linePitch, dims); return true; } bool SART::iterate(unsigned int iterations) { if (useVolumeMask) precomputeWeights(); // iteration for (unsigned int iter = 0; iter < iterations && !astra::shouldAbort(); ++iter) { int angle; if (customOrder) { angle = projectionOrder[iteration % projectionCount]; } else { angle = iteration % dims.iProjAngles; } // copy one line of sinogram to projection data // NB: Non-standard dimensions SDimensions linedims = dims; linedims.iProjAngles = 1; duplicateProjectionData(D_projData, D_sinoData + angle*sinoPitch, sinoPitch, linedims); // do FP, subtracting projection from sinogram if (useVolumeMask) { duplicateVolumeData(D_tmpData, D_volumeData, volumePitch, dims); processVol(D_tmpData, D_maskData, tmpPitch, dims); callFP_SART(D_tmpData, tmpPitch, D_projData, projPitch, angle, -1.0f); } else { callFP_SART(D_volumeData, volumePitch, D_projData, projPitch, angle, -1.0f); } MUL_SART(D_projData, D_lineWeight + angle*linePitch, projPitch, dims.iProjDets); if (useVolumeMask) { // BP, mask, and add back // TODO: Try putting the masking directly in the BP zeroVolumeData(D_tmpData, tmpPitch, dims); callBP_SART(D_tmpData, tmpPitch, D_projData, projPitch, angle, fRelaxation); processVol(D_volumeData, D_maskData, D_tmpData, volumePitch, dims); } else { callBP_SART(D_volumeData, volumePitch, D_projData, projPitch, angle, fRelaxation); } if (useMinConstraint) processVol(D_volumeData, fMinConstraint, volumePitch, dims); if (useMaxConstraint) processVol(D_volumeData, fMaxConstraint, volumePitch, dims); iteration++; } return true; } float SART::computeDiffNorm() { unsigned int pPitch; float *D_p; allocateProjectionData(D_p, pPitch, dims); // copy sinogram to D_p duplicateProjectionData(D_p, D_sinoData, sinoPitch, dims); // do FP, subtracting projection from sinogram if (useVolumeMask) { duplicateVolumeData(D_tmpData, D_volumeData, volumePitch, dims); processVol(D_tmpData, D_maskData, tmpPitch, dims); callFP(D_tmpData, tmpPitch, D_p, pPitch, -1.0f); } else { callFP(D_volumeData, volumePitch, D_p, pPitch, -1.0f); } // compute norm of D_p float s = dotProduct2D(D_p, pPitch, dims.iProjDets, dims.iProjAngles); cudaFree(D_p); return sqrt(s); } bool SART::callFP_SART(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, unsigned int angle, float outputScale) { SDimensions d = dims; d.iProjAngles = 1; if (parProjs) { assert(!fanProjs); return FP(D_volumeData, volumePitch, D_projData, projPitch, d, &parProjs[angle], outputScale * fProjectorScale); } else { assert(fanProjs); return FanFP(D_volumeData, volumePitch, D_projData, projPitch, d, &fanProjs[angle], outputScale * fProjectorScale); } } bool SART::callBP_SART(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, unsigned int angle, float outputScale) { // NB: No fProjectorScale here, as that it is cancelled out in the SART weighting if (parProjs) { assert(!fanProjs); return BP_SART(D_volumeData, volumePitch, D_projData, projPitch, angle, dims, parProjs, outputScale); } else { assert(fanProjs); return FanBP_SART(D_volumeData, volumePitch, D_projData, projPitch, angle, dims, fanProjs, outputScale); } } } astra-toolbox-2.3.0/cuda/2d/sirt.cu000066400000000000000000000176571475635207100170770ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/sirt.h" #include "astra/cuda/2d/util.h" #include "astra/cuda/2d/arith.h" #include #include namespace astraCUDA { SIRT::SIRT() : ReconAlgo() { D_projData = 0; D_tmpData = 0; D_lineWeight = 0; D_pixelWeight = 0; D_minMaskData = 0; D_maxMaskData = 0; fRelaxation = 1.0f; freeMinMaxMasks = false; } SIRT::~SIRT() { reset(); } void SIRT::reset() { cudaFree(D_projData); cudaFree(D_tmpData); cudaFree(D_lineWeight); cudaFree(D_pixelWeight); if (freeMinMaxMasks) { cudaFree(D_minMaskData); cudaFree(D_maxMaskData); } D_projData = 0; D_tmpData = 0; D_lineWeight = 0; D_pixelWeight = 0; freeMinMaxMasks = false; D_minMaskData = 0; D_maxMaskData = 0; useVolumeMask = false; useSinogramMask = false; fRelaxation = 1.0f; ReconAlgo::reset(); } bool SIRT::init() { allocateVolumeData(D_pixelWeight, pixelPitch, dims); zeroVolumeData(D_pixelWeight, pixelPitch, dims); allocateVolumeData(D_tmpData, tmpPitch, dims); zeroVolumeData(D_tmpData, tmpPitch, dims); allocateProjectionData(D_projData, projPitch, dims); zeroProjectionData(D_projData, projPitch, dims); allocateProjectionData(D_lineWeight, linePitch, dims); zeroProjectionData(D_lineWeight, linePitch, dims); // We can't precompute lineWeights and pixelWeights when using a mask if (!useVolumeMask && !useSinogramMask) precomputeWeights(); // TODO: check if allocations succeeded return true; } bool SIRT::precomputeWeights() { zeroProjectionData(D_lineWeight, linePitch, dims); if (useVolumeMask) { callFP(D_maskData, maskPitch, D_lineWeight, linePitch, 1.0f); } else { processVol(D_tmpData, 1.0f, tmpPitch, dims); callFP(D_tmpData, tmpPitch, D_lineWeight, linePitch, 1.0f); } processSino(D_lineWeight, linePitch, dims); if (useSinogramMask) { // scale line weights with sinogram mask to zero out masked sinogram pixels processSino(D_lineWeight, D_smaskData, linePitch, dims); } zeroVolumeData(D_pixelWeight, pixelPitch, dims); if (useSinogramMask) { callBP(D_pixelWeight, pixelPitch, D_smaskData, smaskPitch, 1.0f); } else { processSino(D_projData, 1.0f, projPitch, dims); callBP(D_pixelWeight, pixelPitch, D_projData, projPitch, 1.0f); } processVol(D_pixelWeight, pixelPitch, dims); if (useVolumeMask) { // scale pixel weights with mask to zero out masked pixels processVol(D_pixelWeight, D_maskData, pixelPitch, dims); } // Also fold the relaxation factor into pixel weights processVol(D_pixelWeight, fRelaxation, pixelPitch, dims); return true; } bool SIRT::doSlabCorrections() { // This function compensates for effectively infinitely large slab-like // objects of finite thickness 1 in a parallel beam geometry. // Each ray through the object has an intersection of length d/cos(alpha). // The length of the ray actually intersecting the reconstruction volume is // given by D_lineWeight. By dividing by 1/cos(alpha) and multiplying by the // lineweights, we correct for this missing attenuation outside of the // reconstruction volume, assuming the object is homogeneous. // This effectively scales the output values by assuming the thickness d // is 1 unit. // This function in its current implementation only works if there are no masks. // In this case, init() will also have already called precomputeWeights(), // so we can use D_lineWeight. if (useVolumeMask || useSinogramMask) return false; // Parallel-beam only if (!parProjs) return false; // multiply by line weights processSino(D_sinoData, D_lineWeight, projPitch, dims); SDimensions subdims = dims; subdims.iProjAngles = 1; // divide by 1/cos(angle) // ...but limit the correction to -80/+80 degrees. float bound = cosf(1.3963f); float* t = (float*)D_sinoData; for (int i = 0; i < dims.iProjAngles; ++i) { float angle, detsize, offset; getParParameters(parProjs[i], dims.iProjDets, angle, detsize, offset); float f = fabs(cosf(angle)); if (f < bound) f = bound; processSino(t, f, sinoPitch, subdims); t += sinoPitch; } return true; } bool SIRT::setMinMaxMasks(float* D_minMaskData_, float* D_maxMaskData_, unsigned int iPitch) { D_minMaskData = D_minMaskData_; D_maxMaskData = D_maxMaskData_; minMaskPitch = iPitch; maxMaskPitch = iPitch; freeMinMaxMasks = false; return true; } bool SIRT::uploadMinMaxMasks(const float* pfMinMaskData, const float* pfMaxMaskData, unsigned int iPitch) { freeMinMaxMasks = true; bool ok = true; if (pfMinMaskData) { allocateVolumeData(D_minMaskData, minMaskPitch, dims); ok = copyVolumeToDevice(pfMinMaskData, iPitch, dims, D_minMaskData, minMaskPitch); } if (!ok) return false; if (pfMaxMaskData) { allocateVolumeData(D_maxMaskData, maxMaskPitch, dims); ok = copyVolumeToDevice(pfMaxMaskData, iPitch, dims, D_maxMaskData, maxMaskPitch); } if (!ok) return false; return true; } bool SIRT::iterate(unsigned int iterations) { if (useVolumeMask || useSinogramMask) precomputeWeights(); // iteration for (unsigned int iter = 0; iter < iterations && !astra::shouldAbort(); ++iter) { // copy sinogram to projection data duplicateProjectionData(D_projData, D_sinoData, projPitch, dims); // do FP, subtracting projection from sinogram if (useVolumeMask) { duplicateVolumeData(D_tmpData, D_volumeData, volumePitch, dims); processVol(D_tmpData, D_maskData, tmpPitch, dims); callFP(D_tmpData, tmpPitch, D_projData, projPitch, -1.0f); } else { callFP(D_volumeData, volumePitch, D_projData, projPitch, -1.0f); } processSino(D_projData, D_lineWeight, projPitch, dims); zeroVolumeData(D_tmpData, tmpPitch, dims); callBP(D_tmpData, tmpPitch, D_projData, projPitch, 1.0f); // pixel weights also contain the volume mask and relaxation factor processVol(D_volumeData, D_pixelWeight, D_tmpData, volumePitch, dims); if (useMinConstraint) processVol(D_volumeData, fMinConstraint, volumePitch, dims); if (useMaxConstraint) processVol(D_volumeData, fMaxConstraint, volumePitch, dims); if (D_minMaskData) processVol(D_volumeData, D_minMaskData, volumePitch, dims); if (D_maxMaskData) processVol(D_volumeData, D_maxMaskData, volumePitch, dims); } return true; } float SIRT::computeDiffNorm() { // copy sinogram to projection data duplicateProjectionData(D_projData, D_sinoData, projPitch, dims); // do FP, subtracting projection from sinogram if (useVolumeMask) { duplicateVolumeData(D_tmpData, D_volumeData, volumePitch, dims); processVol(D_tmpData, D_maskData, tmpPitch, dims); callFP(D_tmpData, tmpPitch, D_projData, projPitch, -1.0f); } else { callFP(D_volumeData, volumePitch, D_projData, projPitch, -1.0f); } // compute norm of D_projData float s = dotProduct2D(D_projData, projPitch, dims.iProjDets, dims.iProjAngles); return sqrt(s); } } astra-toolbox-2.3.0/cuda/2d/util.cu000066400000000000000000000260641475635207100170630ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/2d/util.h" #include "astra/Logging.h" #include #include namespace astraCUDA { bool copyVolumeToDevice(const float* in_data, unsigned int in_pitch, const SDimensions& dims, float* outD_data, unsigned int out_pitch) { size_t width = dims.iVolWidth; size_t height = dims.iVolHeight; return checkCuda(cudaMemcpy2D(outD_data, sizeof(float)*out_pitch, in_data, sizeof(float)*in_pitch, sizeof(float)*width, height, cudaMemcpyHostToDevice), "copyVolumeToDevice"); } bool copyVolumeFromDevice(float* out_data, unsigned int out_pitch, const SDimensions& dims, float* inD_data, unsigned int in_pitch) { size_t width = dims.iVolWidth; size_t height = dims.iVolHeight; return checkCuda(cudaMemcpy2D(out_data, sizeof(float)*out_pitch, inD_data, sizeof(float)*in_pitch, sizeof(float)*width, height, cudaMemcpyDeviceToHost), "copyVolumeFromDevice"); } bool copySinogramFromDevice(float* out_data, unsigned int out_pitch, const SDimensions& dims, float* inD_data, unsigned int in_pitch) { size_t width = dims.iProjDets; size_t height = dims.iProjAngles; return checkCuda(cudaMemcpy2D(out_data, sizeof(float)*out_pitch, inD_data, sizeof(float)*in_pitch, sizeof(float)*width, height, cudaMemcpyDeviceToHost), "copySinogramFromDevice"); } bool copySinogramToDevice(const float* in_data, unsigned int in_pitch, const SDimensions& dims, float* outD_data, unsigned int out_pitch) { size_t width = dims.iProjDets; size_t height = dims.iProjAngles; return checkCuda(cudaMemcpy2D(outD_data, sizeof(float)*out_pitch, in_data, sizeof(float)*in_pitch, sizeof(float)*width, height, cudaMemcpyHostToDevice), "copySinogramToDevice"); } bool allocateVolume(float*& ptr, unsigned int width, unsigned int height, unsigned int& pitch) { size_t p; if (!checkCuda(cudaMallocPitch((void**)&ptr, &p, sizeof(float)*width, height), "allocateVolume")) { ASTRA_ERROR("Failed to allocate %dx%d GPU buffer", width, height); return false; } assert(p % sizeof(float) == 0); pitch = p / sizeof(float); return true; } bool zeroVolume(float* data, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; if (!checkCuda(cudaMemset2DAsync(data, sizeof(float)*pitch, 0, sizeof(float)*width, height, stream()), "zeroVolume")) return false; return stream.syncIfSync("zeroVolume sync"); } bool allocateVolumeData(float*& D_ptr, unsigned int& pitch, const SDimensions& dims) { return allocateVolume(D_ptr, dims.iVolWidth, dims.iVolHeight, pitch); } bool allocateProjectionData(float*& D_ptr, unsigned int& pitch, const SDimensions& dims) { return allocateVolume(D_ptr, dims.iProjDets, dims.iProjAngles, pitch); } bool zeroVolumeData(float* D_ptr, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return zeroVolume(D_ptr, pitch, dims.iVolWidth, dims.iVolHeight, _stream); } bool zeroProjectionData(float* D_ptr, unsigned int pitch, const SDimensions& dims, std::optional _stream) { return zeroVolume(D_ptr, pitch, dims.iProjDets, dims.iProjAngles, _stream); } bool duplicateVolumeData(float* D_dst, float* D_src, unsigned int pitch, const SDimensions& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; if (!checkCuda(cudaMemcpy2DAsync(D_dst, sizeof(float)*pitch, D_src, sizeof(float)*pitch, sizeof(float)*dims.iVolWidth, dims.iVolHeight, cudaMemcpyDeviceToDevice, stream()), "duplicateVolumeData")) return false; return stream.syncIfSync("duplicateVolumeData sync"); } bool duplicateProjectionData(float* D_dst, float* D_src, unsigned int pitch, const SDimensions& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; if (!checkCuda(cudaMemcpy2DAsync(D_dst, sizeof(float)*pitch, D_src, sizeof(float)*pitch, sizeof(float)*dims.iProjDets, dims.iProjAngles, cudaMemcpyDeviceToDevice, stream()), "duplicateProjectionData")) return false; return stream.syncIfSync("duplicateVolumeData sync"); } bool createArrayAndTextureObject2D(float* data, cudaArray*& dataArray, cudaTextureObject_t& texObj, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; // TODO: For very small sizes (roughly <=512x128) with few angles (<=180) // not using an array is more efficient. cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat); dataArray = 0; if (!checkCuda(cudaMallocArray(&dataArray, &channelDesc, width, height), "createTextureObject2D malloc")) return false; cudaResourceDesc resDesc; memset(&resDesc, 0, sizeof(resDesc)); resDesc.resType = cudaResourceTypeArray; resDesc.res.array.array = dataArray; cudaTextureDesc texDesc; memset(&texDesc, 0, sizeof(texDesc)); texDesc.addressMode[0] = cudaAddressModeBorder; texDesc.addressMode[1] = cudaAddressModeBorder; texDesc.filterMode = cudaFilterModeLinear; texDesc.readMode = cudaReadModeElementType; texDesc.normalizedCoords = 0; texObj = 0; if (!checkCuda(cudaCreateTextureObject(&texObj, &resDesc, &texDesc, NULL), "createTextureObject2D")) { cudaFreeArray(dataArray); return false; } bool ok = checkCuda(cudaMemcpy2DToArrayAsync(dataArray, 0, 0, data, pitch*sizeof(float), width*sizeof(float), height, cudaMemcpyDeviceToDevice, stream()), "createTextureObject2D memcpy"); ok &= stream.syncIfSync("createArrayAndTextureObject2D sync"); if (!ok) { cudaFreeArray(dataArray); return false; } return true; } bool createTextureObjectPitch2D(float* D_data, cudaTextureObject_t& texObj, unsigned int pitch, unsigned int width, unsigned int height, cudaTextureAddressMode mode) { cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat); cudaResourceDesc resDesc; memset(&resDesc, 0, sizeof(resDesc)); resDesc.resType = cudaResourceTypePitch2D; resDesc.res.pitch2D.devPtr = (void*)D_data; resDesc.res.pitch2D.desc = channelDesc; resDesc.res.pitch2D.width = width; resDesc.res.pitch2D.height = height; resDesc.res.pitch2D.pitchInBytes = sizeof(float)*pitch; cudaTextureDesc texDesc; memset(&texDesc, 0, sizeof(texDesc)); texDesc.addressMode[0] = mode; texDesc.addressMode[1] = mode; texDesc.filterMode = cudaFilterModeLinear; texDesc.readMode = cudaReadModeElementType; texDesc.normalizedCoords = 0; texObj = 0; return checkCuda(cudaCreateTextureObject(&texObj, &resDesc, &texDesc, NULL), "createTextureObjectPitch2D"); } template __global__ void reduce1D(float *g_idata, float *g_odata, unsigned int n) { extern __shared__ float sdata[]; unsigned int tid = threadIdx.x; unsigned int i = blockIdx.x*(blockSize*2) + tid; unsigned int gridSize = blockSize*gridDim.x; sdata[tid] = 0; while (i < n) { sdata[tid] += g_idata[i]; i += gridSize; } __syncthreads(); if (blockSize >= 512) { if (tid < 256) { sdata[tid] += sdata[tid + 256]; } __syncthreads(); } if (blockSize >= 256) { if (tid < 128) { sdata[tid] += sdata[tid + 128]; } __syncthreads(); } if (blockSize >= 128) { if (tid < 64) { sdata[tid] += sdata[tid + 64]; } __syncthreads(); } if (tid < 32) { volatile float* smem = sdata; if (blockSize >= 64) smem[tid] += smem[tid + 32]; if (blockSize >= 32) smem[tid] += smem[tid + 16]; if (blockSize >= 16) smem[tid] += smem[tid + 8]; if (blockSize >= 8) smem[tid] += smem[tid + 4]; if (blockSize >= 4) smem[tid] += smem[tid + 2]; if (blockSize >= 2) smem[tid] += smem[tid + 1]; } if (tid == 0) g_odata[blockIdx.x] = sdata[0]; } __global__ void reduce2D(float *g_idata, float *g_odata, unsigned int pitch, unsigned int nx, unsigned int ny) { extern __shared__ float sdata[]; const unsigned int tidx = threadIdx.x; const unsigned int tidy = threadIdx.y; const unsigned int tid = tidy * 16 + tidx; unsigned int x = blockIdx.x*16 + tidx; unsigned int y = blockIdx.y*16 + tidy; sdata[tid] = 0; if (x < nx) { while (y < ny) { sdata[tid] += (g_idata[pitch*y+x] * g_idata[pitch*y+x]); y += 16 * gridDim.y; } } __syncthreads(); if (tid < 128) sdata[tid] += sdata[tid + 128]; __syncthreads(); if (tid < 64) sdata[tid] += sdata[tid + 64]; __syncthreads(); if (tid < 32) { // 32 is warp size volatile float* smem = sdata; smem[tid] += smem[tid + 32]; smem[tid] += smem[tid + 16]; smem[tid] += smem[tid + 8]; smem[tid] += smem[tid + 4]; smem[tid] += smem[tid + 2]; smem[tid] += smem[tid + 1]; } if (tid == 0) g_odata[blockIdx.y * gridDim.x + blockIdx.x] = sdata[0]; } float dotProduct2D(float* D_data, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; unsigned int bx = (width + 15) / 16; unsigned int by = (height + 127) / 128; unsigned int shared_mem2 = sizeof(float) * 16 * 16; dim3 dimBlock2(16, 16); dim3 dimGrid2(bx, by); float* D_buf; cudaMalloc(&D_buf, sizeof(float) * (bx * by + 1) ); float* D_res = D_buf + (bx*by); // Step 1: reduce 2D from image to a single vector, taking sum of squares reduce2D<<< dimGrid2, dimBlock2, shared_mem2, stream()>>>(D_data, D_buf, pitch, width, height); // Step 2: reduce 1D: add up elements in vector if (bx * by > 512) reduce1D<512><<< 1, 512, sizeof(float)*512, stream()>>>(D_buf, D_res, bx*by); else if (bx * by > 128) reduce1D<128><<< 1, 128, sizeof(float)*128, stream()>>>(D_buf, D_res, bx*by); else if (bx * by > 32) reduce1D<32><<< 1, 32, sizeof(float)*32*2, stream()>>>(D_buf, D_res, bx*by); else if (bx * by > 8) reduce1D<8><<< 1, 8, sizeof(float)*8*2, stream()>>>(D_buf, D_res, bx*by); else reduce1D<1><<< 1, 1, sizeof(float)*1*2, stream()>>>(D_buf, D_res, bx*by); float x; cudaMemcpyAsync(&x, D_res, 4, cudaMemcpyDeviceToHost, stream()); stream.sync("dotProduct2D"); cudaFree(D_buf); return x; } bool checkCuda(cudaError_t err, const char *msg) { if (err != cudaSuccess) { ASTRA_ERROR("%s: CUDA error %d: %s.", msg, err, cudaGetErrorString(err)); return false; } else { return true; } } } astra-toolbox-2.3.0/cuda/3d/000077500000000000000000000000001475635207100155465ustar00rootroot00000000000000astra-toolbox-2.3.0/cuda/3d/algo3d.cu000066400000000000000000000054261475635207100172570ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/algo3d.h" #include "astra/cuda/3d/cone_fp.h" #include "astra/cuda/3d/cone_bp.h" #include "astra/cuda/3d/par3d_fp.h" #include "astra/cuda/3d/par3d_bp.h" #include namespace astraCUDA3d { ReconAlgo3D::ReconAlgo3D() { coneProjs = 0; par3DProjs = 0; } ReconAlgo3D::~ReconAlgo3D() { reset(); } void ReconAlgo3D::reset() { delete[] coneProjs; coneProjs = 0; delete[] par3DProjs; par3DProjs = 0; } bool ReconAlgo3D::setConeGeometry(const SDimensions3D& _dims, const SConeProjection* _angles, const SProjectorParams3D& _params) { dims = _dims; params = _params; coneProjs = new SConeProjection[dims.iProjAngles]; par3DProjs = 0; memcpy(coneProjs, _angles, sizeof(coneProjs[0]) * dims.iProjAngles); return true; } bool ReconAlgo3D::setPar3DGeometry(const SDimensions3D& _dims, const SPar3DProjection* _angles, const SProjectorParams3D& _params) { dims = _dims; params = _params; par3DProjs = new SPar3DProjection[dims.iProjAngles]; coneProjs = 0; memcpy(par3DProjs, _angles, sizeof(par3DProjs[0]) * dims.iProjAngles); return true; } bool ReconAlgo3D::callFP(cudaPitchedPtr& D_volumeData, cudaPitchedPtr& D_projData, float outputScale) { SProjectorParams3D p = params; p.fOutputScale *= outputScale; if (coneProjs) { return ConeFP(D_volumeData, D_projData, dims, coneProjs, p); } else { return Par3DFP(D_volumeData, D_projData, dims, par3DProjs, p); } } bool ReconAlgo3D::callBP(cudaPitchedPtr& D_volumeData, cudaPitchedPtr& D_projData, float outputScale) { SProjectorParams3D p = params; p.fOutputScale *= outputScale; if (coneProjs) { return ConeBP(D_volumeData, D_projData, dims, coneProjs, p); } else { return Par3DBP(D_volumeData, D_projData, dims, par3DProjs, p); } } } astra-toolbox-2.3.0/cuda/3d/arith3d.cu000066400000000000000000000376331475635207100174510ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/util3d.h" #include "astra/cuda/3d/arith3d.h" #include "astra/cuda/2d/util.h" #include namespace astraCUDA3d { struct opAddScaled { __device__ void operator()(float& out, const float in, const float inp) { out += in * inp; } }; struct opScaleAndAdd { __device__ void operator()(float& out, const float in, const float inp) { out = in + out * inp; } }; struct opAddMulScaled { __device__ void operator()(float& out, const float in1, const float in2, const float inp) { out += in1 * in2 * inp; } }; struct opAddMul { __device__ void operator()(float& out, const float in1, const float in2) { out += in1 * in2; } }; struct opAdd { __device__ void operator()(float& out, const float in) { out += in; } }; struct opMul { __device__ void operator()(float& out, const float in) { out *= in; } }; struct opMul2 { __device__ void operator()(float& out, const float in1, const float in2) { out *= in1 * in2; } }; struct opDividedBy { __device__ void operator()(float& out, const float in) { if (out > 0.000001f) // out is assumed to be positive out = in / out; else out = 0.0f; } }; struct opInvert { __device__ void operator()(float& out) { if (out > 0.000001f) // out is assumed to be positive out = 1 / out; else out = 0.0f; } }; struct opSet { __device__ void operator()(float& out, const float inp) { out = inp; } }; struct opClampMin { __device__ void operator()(float& out, const float inp) { if (out < inp) out = inp; } }; struct opClampMax { __device__ void operator()(float& out, const float inp) { if (out > inp) out = inp; } }; template __global__ void devtoD(float* pfOut, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut[off]); off += pitch; y++; } } template __global__ void devFtoD(float* pfOut, float fParam, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut[off], fParam); off += pitch; y++; } } template __global__ void devDtoD(float* pfOut, const float* pfIn, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut[off], pfIn[off]); off += pitch; y++; } } template __global__ void devDFtoD(float* pfOut, const float* pfIn, float fParam, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut[off], pfIn[off], fParam); off += pitch; y++; } } template __global__ void devDDtoD(float* pfOut, const float* pfIn1, const float* pfIn2, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut[off], pfIn1[off], pfIn2[off]); off += pitch; y++; } } template __global__ void devDDFtoD(float* pfOut, const float* pfIn1, const float* pfIn2, float fParam, unsigned int pitch, unsigned int width, unsigned int height) { unsigned int x = threadIdx.x + 16*blockIdx.x; if (x >= width) return; unsigned int y = (threadIdx.y + 16*blockIdx.y)*repeat; unsigned int off = y*pitch+x; for (unsigned int i = 0; i < repeat; ++i) { if (y >= height) break; op()(pfOut[off], pfIn1[off], pfIn2[off], fParam); off += pitch; y++; } } template bool processVol3D(cudaPitchedPtr& out, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((dims.iVolX+15)/16, (dims.iVolY+511)/512); float *pfOut = (float*)out.ptr; unsigned int step = out.pitch/sizeof(float) * dims.iVolY; for (unsigned int i = 0; i < dims.iVolZ; ++i) { devtoD<<>>(pfOut, out.pitch/sizeof(float), dims.iVolX, dims.iVolY); pfOut += step; } return stream.syncIfSync(__FUNCTION__); } template bool processVol3D(cudaPitchedPtr& out, float fParam, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((dims.iVolX+15)/16, (dims.iVolY+511)/512); float *pfOut = (float*)out.ptr; unsigned int step = out.pitch/sizeof(float) * dims.iVolY; for (unsigned int i = 0; i < dims.iVolZ; ++i) { devFtoD<<>>(pfOut, fParam, out.pitch/sizeof(float), dims.iVolX, dims.iVolY); pfOut += step; } return stream.syncIfSync(__FUNCTION__); } template bool processVol3D(cudaPitchedPtr& out, const cudaPitchedPtr& in, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((dims.iVolX+15)/16, (dims.iVolY+511)/512); float *pfOut = (float*)out.ptr; const float *pfIn = (const float*)in.ptr; unsigned int step = out.pitch/sizeof(float) * dims.iVolY; for (unsigned int i = 0; i < dims.iVolZ; ++i) { devDtoD<<>>(pfOut, pfIn, out.pitch/sizeof(float), dims.iVolX, dims.iVolY); pfOut += step; pfIn += step; } return stream.syncIfSync(__FUNCTION__); } template bool processVol3D(cudaPitchedPtr& out, const cudaPitchedPtr& in, float fParam, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((dims.iVolX+15)/16, (dims.iVolY+511)/512); float *pfOut = (float*)out.ptr; const float *pfIn = (const float*)in.ptr; unsigned int step = out.pitch/sizeof(float) * dims.iVolY; for (unsigned int i = 0; i < dims.iVolZ; ++i) { devDFtoD<<>>(pfOut, pfIn, fParam, out.pitch/sizeof(float), dims.iVolX, dims.iVolY); pfOut += step; pfIn += step; } return stream.syncIfSync(__FUNCTION__); } template bool processVol3D(cudaPitchedPtr& out, const cudaPitchedPtr& in1, const cudaPitchedPtr& in2, float fParam, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((dims.iVolX+15)/16, (dims.iVolY+511)/512); float *pfOut = (float*)out.ptr; const float *pfIn1 = (const float*)in1.ptr; const float *pfIn2 = (const float*)in2.ptr; unsigned int step = out.pitch/sizeof(float) * dims.iVolY; for (unsigned int i = 0; i < dims.iVolZ; ++i) { devDDFtoD<<>>(pfOut, pfIn1, pfIn2, fParam, out.pitch/sizeof(float), dims.iVolX, dims.iVolY); pfOut += step; pfIn1 += step; pfIn2 += step; } return stream.syncIfSync(__FUNCTION__); } template bool processVol3D(cudaPitchedPtr& out, const cudaPitchedPtr& in1, const cudaPitchedPtr& in2, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((dims.iVolX+15)/16, (dims.iVolY+511)/512); float *pfOut = (float*)out.ptr; const float *pfIn1 = (const float*)in1.ptr; const float *pfIn2 = (const float*)in2.ptr; unsigned int step = out.pitch/sizeof(float) * dims.iVolY; for (unsigned int i = 0; i < dims.iVolZ; ++i) { devDDtoD<<>>(pfOut, pfIn1, pfIn2, out.pitch/sizeof(float), dims.iVolX, dims.iVolY); pfOut += step; pfIn1 += step; pfIn2 += step; } return stream.syncIfSync(__FUNCTION__); } template bool processSino3D(cudaPitchedPtr& out, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((dims.iProjU+15)/16, (dims.iProjAngles+511)/512); float *pfOut = (float*)out.ptr; unsigned int step = out.pitch/sizeof(float) * dims.iProjAngles; for (unsigned int i = 0; i < dims.iProjV; ++i) { devtoD<<>>(pfOut, out.pitch/sizeof(float), dims.iProjU, dims.iProjAngles); pfOut += step; } return stream.syncIfSync(__FUNCTION__); } template bool processSino3D(cudaPitchedPtr& out, float fParam, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((dims.iProjU+15)/16, (dims.iProjAngles+511)/512); float *pfOut = (float*)out.ptr; unsigned int step = out.pitch/sizeof(float) * dims.iProjAngles; for (unsigned int i = 0; i < dims.iProjV; ++i) { devFtoD<<>>(pfOut, fParam, out.pitch/sizeof(float), dims.iProjU, dims.iProjAngles); pfOut += step; } return stream.syncIfSync(__FUNCTION__); } template bool processSino3D(cudaPitchedPtr& out, const cudaPitchedPtr& in, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((dims.iProjU+15)/16, (dims.iProjAngles+511)/512); float *pfOut = (float*)out.ptr; const float *pfIn = (const float*)in.ptr; unsigned int step = out.pitch/sizeof(float) * dims.iProjAngles; for (unsigned int i = 0; i < dims.iProjV; ++i) { devDtoD<<>>(pfOut, pfIn, out.pitch/sizeof(float), dims.iProjU, dims.iProjAngles); pfOut += step; pfIn += step; } return stream.syncIfSync(__FUNCTION__); } template bool processSino3D(cudaPitchedPtr& out, const cudaPitchedPtr& in, float fParam, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((dims.iProjU+15)/16, (dims.iProjAngles+511)/512); float *pfOut = (float*)out.ptr; const float *pfIn = (const float*)in.ptr; unsigned int step = out.pitch/sizeof(float) * dims.iProjAngles; for (unsigned int i = 0; i < dims.iProjV; ++i) { devDFtoD<<>>(pfOut, pfIn, fParam, out.pitch/sizeof(float), dims.iProjU, dims.iProjAngles); pfOut += step; pfIn += step; } return stream.syncIfSync(__FUNCTION__); } template bool processSino3D(cudaPitchedPtr& out, const cudaPitchedPtr& in1, const cudaPitchedPtr& in2, float fParam, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((dims.iProjU+15)/16, (dims.iProjAngles+511)/512); float *pfOut = (float*)out.ptr; const float *pfIn1 = (const float*)in1.ptr; const float *pfIn2 = (const float*)in2.ptr; unsigned int step = out.pitch/sizeof(float) * dims.iProjAngles; for (unsigned int i = 0; i < dims.iProjV; ++i) { devDDFtoD<<>>(pfOut, pfIn1, pfIn2, fParam, out.pitch/sizeof(float), dims.iProjU, dims.iProjAngles); pfOut += step; pfIn1 += step; pfIn2 += step; } return stream.syncIfSync(__FUNCTION__); } template bool processSino3D(cudaPitchedPtr& out, const cudaPitchedPtr& in1, const cudaPitchedPtr& in2, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; dim3 blockSize(16,16); dim3 gridSize((dims.iProjU+15)/16, (dims.iProjAngles+511)/512); float *pfOut = (float*)out.ptr; const float *pfIn1 = (const float*)in1.ptr; const float *pfIn2 = (const float*)in2.ptr; unsigned int step = out.pitch/sizeof(float) * dims.iProjAngles; for (unsigned int i = 0; i < dims.iProjV; ++i) { devDDtoD<<>>(pfOut, pfIn1, pfIn2, out.pitch/sizeof(float), dims.iProjU, dims.iProjAngles); pfOut += step; pfIn1 += step; pfIn2 += step; } return stream.syncIfSync(__FUNCTION__); } #define INST_DFtoD(name) \ template bool processVol3D(cudaPitchedPtr& out, const cudaPitchedPtr& in, float fParam, const SDimensions3D& dims, std::optional _stream); \ template bool processSino3D(cudaPitchedPtr& out, const cudaPitchedPtr& in, float fParam, const SDimensions3D& dims, std::optional _stream); #define INST_DtoD(name) \ template bool processVol3D(cudaPitchedPtr& out, const cudaPitchedPtr& in, const SDimensions3D& dims, std::optional _stream); \ template bool processSino3D(cudaPitchedPtr& out, const cudaPitchedPtr& in, const SDimensions3D& dims, std::optional _stream); #define INST_DDtoD(name) \ template bool processVol3D(cudaPitchedPtr& out, const cudaPitchedPtr& in1, const cudaPitchedPtr& in2, const SDimensions3D& dims, std::optional _stream); \ template bool processSino3D(cudaPitchedPtr& out, const cudaPitchedPtr& in1, const cudaPitchedPtr& in2, const SDimensions3D& dims, std::optional _stream); #define INST_DDFtoD(name) \ template bool processVol3D(cudaPitchedPtr& out, const cudaPitchedPtr& in1, const cudaPitchedPtr& in2, float fParam, const SDimensions3D& dims, std::optional _stream); \ template bool processSino3D(cudaPitchedPtr& out, const cudaPitchedPtr& in1, const cudaPitchedPtr& in2, float fParam, const SDimensions3D& dims, std::optional _stream); #define INST_toD(name) \ template bool processVol3D(cudaPitchedPtr& out, const SDimensions3D& dims, std::optional _stream); \ template bool processSino3D(cudaPitchedPtr& out, const SDimensions3D& dims, std::optional _stream); #define INST_FtoD(name) \ template bool processVol3D(cudaPitchedPtr& out, float fParam, const SDimensions3D& dims, std::optional _stream); \ template bool processSino3D(cudaPitchedPtr& out, float fParam, const SDimensions3D& dims, std::optional _stream); INST_DFtoD(opAddScaled) INST_DFtoD(opScaleAndAdd) INST_DDFtoD(opAddMulScaled) INST_DDtoD(opAddMul) INST_DDtoD(opMul2) INST_DtoD(opMul) INST_DtoD(opAdd) INST_DtoD(opDividedBy) INST_toD(opInvert) INST_FtoD(opMul) INST_FtoD(opSet) INST_FtoD(opClampMin) INST_FtoD(opClampMax) } astra-toolbox-2.3.0/cuda/3d/astra3d.cu000066400000000000000000000733421475635207100174510ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/cgls3d.h" #include "astra/cuda/3d/sirt3d.h" #include "astra/cuda/3d/util3d.h" #include "astra/cuda/3d/cone_fp.h" #include "astra/cuda/3d/cone_bp.h" #include "astra/cuda/3d/par3d_fp.h" #include "astra/cuda/3d/par3d_bp.h" #include "astra/cuda/3d/fdk.h" #include "astra/cuda/3d/arith3d.h" #include "astra/cuda/3d/astra3d.h" #include "astra/cuda/3d/mem3d.h" #include "astra/ParallelProjectionGeometry3D.h" #include "astra/ParallelVecProjectionGeometry3D.h" #include "astra/ConeProjectionGeometry3D.h" #include "astra/ConeVecProjectionGeometry3D.h" #include "astra/VolumeGeometry3D.h" #include "astra/Data3D.h" #include "astra/Logging.h" #include #include #include using namespace astraCUDA3d; namespace astra { enum CUDAProjectionType3d { PROJ_PARALLEL, PROJ_CONE }; // adjust pProjs to normalize volume geometry template static bool convertAstraGeometry_internal(const CVolumeGeometry3D* pVolGeom, std::vector& projs, SProjectorParams3D& params) { assert(pVolGeom); float dx = -(pVolGeom->getWindowMinX() + pVolGeom->getWindowMaxX()) / 2; float dy = -(pVolGeom->getWindowMinY() + pVolGeom->getWindowMaxY()) / 2; float dz = -(pVolGeom->getWindowMinZ() + pVolGeom->getWindowMaxZ()) / 2; float fx = 1.0f / pVolGeom->getPixelLengthX(); float fy = 1.0f / pVolGeom->getPixelLengthY(); float fz = 1.0f / pVolGeom->getPixelLengthZ(); for (size_t i = 0; i < projs.size(); ++i) { projs[i].translate(dx, dy, dz); projs[i].scale(fx, fy, fz); } params.fVolScaleX = pVolGeom->getPixelLengthX(); params.fVolScaleY = pVolGeom->getPixelLengthY(); params.fVolScaleZ = pVolGeom->getPixelLengthZ(); return true; } bool convertAstraGeometry_dims(const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom, SDimensions3D& dims) { dims.iVolX = pVolGeom->getGridColCount(); dims.iVolY = pVolGeom->getGridRowCount(); dims.iVolZ = pVolGeom->getGridSliceCount(); dims.iProjAngles = pProjGeom->getProjectionCount(); dims.iProjU = pProjGeom->getDetectorColCount(); dims.iProjV = pProjGeom->getDetectorRowCount(); if (dims.iVolX <= 0 || dims.iVolX <= 0 || dims.iVolX <= 0) return false; if (dims.iProjAngles <= 0 || dims.iProjU <= 0 || dims.iProjV <= 0) return false; return true; } bool convertAstraGeometry(const CVolumeGeometry3D* pVolGeom, const CParallelProjectionGeometry3D* pProjGeom, std::vector& projs, SProjectorParams3D& params) { assert(pVolGeom); assert(pProjGeom); assert(pProjGeom->getProjectionAngles()); int nth = pProjGeom->getProjectionCount(); projs = genPar3DProjections(nth, pProjGeom->getDetectorColCount(), pProjGeom->getDetectorRowCount(), pProjGeom->getDetectorSpacingX(), pProjGeom->getDetectorSpacingY(), pProjGeom->getProjectionAngles()); bool ok; ok = convertAstraGeometry_internal(pVolGeom, projs, params); return ok; } bool convertAstraGeometry(const CVolumeGeometry3D* pVolGeom, const CParallelVecProjectionGeometry3D* pProjGeom, std::vector& projs, SProjectorParams3D& params) { assert(pVolGeom); assert(pProjGeom); assert(pProjGeom->getProjectionVectors()); int nth = pProjGeom->getProjectionCount(); projs.resize(nth); for (int i = 0; i < nth; ++i) projs[i] = pProjGeom->getProjectionVectors()[i]; bool ok; ok = convertAstraGeometry_internal(pVolGeom, projs, params); return ok; } bool convertAstraGeometry(const CVolumeGeometry3D* pVolGeom, const CConeProjectionGeometry3D* pProjGeom, std::vector& projs, SProjectorParams3D& params) { assert(pVolGeom); assert(pProjGeom); assert(pProjGeom->getProjectionAngles()); int nth = pProjGeom->getProjectionCount(); projs = genConeProjections(nth, pProjGeom->getDetectorColCount(), pProjGeom->getDetectorRowCount(), pProjGeom->getOriginSourceDistance(), pProjGeom->getOriginDetectorDistance(), pProjGeom->getDetectorSpacingX(), pProjGeom->getDetectorSpacingY(), pProjGeom->getProjectionAngles()); bool ok; ok = convertAstraGeometry_internal(pVolGeom, projs, params); return ok; } bool convertAstraGeometry(const CVolumeGeometry3D* pVolGeom, const CConeVecProjectionGeometry3D* pProjGeom, std::vector& projs, SProjectorParams3D& params) { assert(pVolGeom); assert(pProjGeom); assert(pProjGeom->getProjectionVectors()); int nth = pProjGeom->getProjectionCount(); projs.resize(nth); for (int i = 0; i < nth; ++i) projs[i] = pProjGeom->getProjectionVectors()[i]; bool ok; ok = convertAstraGeometry_internal(pVolGeom, projs, params); return ok; } Geometry3DParameters convertAstraGeometry(const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom, SProjectorParams3D& params) { const CConeProjectionGeometry3D* conegeom = dynamic_cast(pProjGeom); const CParallelProjectionGeometry3D* par3dgeom = dynamic_cast(pProjGeom); const CParallelVecProjectionGeometry3D* parvec3dgeom = dynamic_cast(pProjGeom); const CConeVecProjectionGeometry3D* conevec3dgeom = dynamic_cast(pProjGeom); bool ok; if (conegeom || conevec3dgeom) { std::vector coneProjs; if (conegeom) ok = convertAstraGeometry(pVolGeom, conegeom, coneProjs, params); else ok = convertAstraGeometry(pVolGeom, conevec3dgeom, coneProjs, params); if (ok) return Geometry3DParameters::variant_t(std::move(coneProjs)); else return Geometry3DParameters::variant_t(); } else if (par3dgeom || parvec3dgeom) { std::vector parProjs; if (par3dgeom) ok = convertAstraGeometry(pVolGeom, par3dgeom, parProjs, params); else ok = convertAstraGeometry(pVolGeom, parvec3dgeom, parProjs, params); if (ok) return Geometry3DParameters::variant_t(std::move(parProjs)); else return Geometry3DParameters::variant_t(); } else { ok = false; } return Geometry3DParameters::variant_t(); } class AstraSIRT3d_internal { public: SDimensions3D dims; SProjectorParams3D params; float* angles; float fOriginSourceDistance; float fOriginDetectorDistance; float fRelaxation; Geometry3DParameters projs; bool initialized; bool setStartReconstruction; bool useVolumeMask; bool useSinogramMask; // Input/output cudaPitchedPtr D_projData; cudaPitchedPtr D_volumeData; cudaPitchedPtr D_maskData; cudaPitchedPtr D_smaskData; SIRT sirt; }; AstraSIRT3d::AstraSIRT3d() { pData = new AstraSIRT3d_internal(); pData->angles = 0; pData->D_projData.ptr = 0; pData->D_volumeData.ptr = 0; pData->D_maskData.ptr = 0; pData->D_smaskData.ptr = 0; pData->dims.iVolX = 0; pData->dims.iVolY = 0; pData->dims.iVolZ = 0; pData->dims.iProjAngles = 0; pData->dims.iProjU = 0; pData->dims.iProjV = 0; pData->fRelaxation = 1.0f; pData->initialized = false; pData->setStartReconstruction = false; pData->useVolumeMask = false; pData->useSinogramMask = false; } AstraSIRT3d::~AstraSIRT3d() { delete[] pData->angles; pData->angles = 0; cudaFree(pData->D_projData.ptr); pData->D_projData.ptr = 0; cudaFree(pData->D_volumeData.ptr); pData->D_volumeData.ptr = 0; cudaFree(pData->D_maskData.ptr); pData->D_maskData.ptr = 0; cudaFree(pData->D_smaskData.ptr); pData->D_smaskData.ptr = 0; delete pData; pData = 0; } bool AstraSIRT3d::setGeometry(const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom) { if (pData->initialized) return false; bool ok = convertAstraGeometry_dims(pVolGeom, pProjGeom, pData->dims); if (!ok) return false; pData->projs = convertAstraGeometry(pVolGeom, pProjGeom, pData->params); if (!pData->projs.isValid()) return false; return true; } bool AstraSIRT3d::enableSuperSampling(unsigned int iVoxelSuperSampling, unsigned int iDetectorSuperSampling) { if (pData->initialized) return false; if (iVoxelSuperSampling == 0 || iDetectorSuperSampling == 0) return false; pData->params.iRaysPerVoxelDim = iVoxelSuperSampling; pData->params.iRaysPerDetDim = iDetectorSuperSampling; return true; } bool AstraSIRT3d::setRelaxation(float r) { if (pData->initialized) return false; pData->fRelaxation = r; return true; } bool AstraSIRT3d::enableVolumeMask() { if (pData->initialized) return false; bool ok = pData->sirt.enableVolumeMask(); pData->useVolumeMask = ok; return ok; } bool AstraSIRT3d::enableSinogramMask() { if (pData->initialized) return false; bool ok = pData->sirt.enableSinogramMask(); pData->useSinogramMask = ok; return ok; } bool AstraSIRT3d::setGPUIndex(int index) { if (index != -1) { cudaSetDevice(index); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } return true; } bool AstraSIRT3d::init() { if (pData->initialized) return false; if (pData->dims.iVolX == 0 || pData->dims.iProjAngles == 0) return false; bool ok; if (pData->projs.isParallel()) ok = pData->sirt.setPar3DGeometry(pData->dims, pData->projs.getParallel(), pData->params); else if (pData->projs.isCone()) ok = pData->sirt.setConeGeometry(pData->dims, pData->projs.getCone(), pData->params); else ok = false; if (!ok) return false; pData->sirt.setRelaxation(pData->fRelaxation); ok = pData->sirt.init(); if (!ok) return false; pData->D_volumeData = allocateVolumeData(pData->dims); ok = pData->D_volumeData.ptr; if (!ok) return false; pData->D_projData = allocateProjectionData(pData->dims); ok = pData->D_projData.ptr; if (!ok) { cudaFree(pData->D_volumeData.ptr); pData->D_volumeData.ptr = 0; return false; } if (pData->useVolumeMask) { pData->D_maskData = allocateVolumeData(pData->dims); ok = pData->D_maskData.ptr; if (!ok) { cudaFree(pData->D_volumeData.ptr); cudaFree(pData->D_projData.ptr); pData->D_volumeData.ptr = 0; pData->D_projData.ptr = 0; return false; } } if (pData->useSinogramMask) { pData->D_smaskData = allocateProjectionData(pData->dims); ok = pData->D_smaskData.ptr; if (!ok) { cudaFree(pData->D_volumeData.ptr); cudaFree(pData->D_projData.ptr); cudaFree(pData->D_maskData.ptr); pData->D_volumeData.ptr = 0; pData->D_projData.ptr = 0; pData->D_maskData.ptr = 0; return false; } } pData->initialized = true; return true; } bool AstraSIRT3d::setMinConstraint(float fMin) { if (!pData->initialized) return false; return pData->sirt.setMinConstraint(fMin); } bool AstraSIRT3d::setMaxConstraint(float fMax) { if (!pData->initialized) return false; return pData->sirt.setMaxConstraint(fMax); } bool AstraSIRT3d::setSinogram(const float* pfSinogram, unsigned int iSinogramPitch) { if (!pData->initialized) return false; if (!pfSinogram) return false; bool ok = copyProjectionsToDevice(pfSinogram, pData->D_projData, pData->dims, iSinogramPitch); if (!ok) return false; ok = pData->sirt.setBuffers(pData->D_volumeData, pData->D_projData); if (!ok) return false; pData->setStartReconstruction = false; return true; } bool AstraSIRT3d::setVolumeMask(const float* pfMask, unsigned int iMaskPitch) { if (!pData->initialized) return false; if (!pData->useVolumeMask) return false; if (!pfMask) return false; bool ok = copyVolumeToDevice(pfMask, pData->D_maskData, pData->dims, iMaskPitch); if (!ok) return false; ok = pData->sirt.setVolumeMask(pData->D_maskData); if (!ok) return false; return true; } bool AstraSIRT3d::setSinogramMask(const float* pfMask, unsigned int iMaskPitch) { if (!pData->initialized) return false; if (!pData->useSinogramMask) return false; if (!pfMask) return false; bool ok = copyProjectionsToDevice(pfMask, pData->D_smaskData, pData->dims, iMaskPitch); if (!ok) return false; ok = pData->sirt.setSinogramMask(pData->D_smaskData); if (!ok) return false; return true; } bool AstraSIRT3d::setStartReconstruction(const float* pfReconstruction, unsigned int iReconstructionPitch) { if (!pData->initialized) return false; if (!pfReconstruction) return false; bool ok = copyVolumeToDevice(pfReconstruction, pData->D_volumeData, pData->dims, iReconstructionPitch); if (!ok) return false; pData->setStartReconstruction = true; return true; } bool AstraSIRT3d::iterate(unsigned int iIterations) { if (!pData->initialized) return false; if (!pData->setStartReconstruction) zeroVolumeData(pData->D_volumeData, pData->dims); bool ok = pData->sirt.iterate(iIterations); if (!ok) return false; return true; } bool AstraSIRT3d::getReconstruction(float* pfReconstruction, unsigned int iReconstructionPitch) const { if (!pData->initialized) return false; bool ok = copyVolumeFromDevice(pfReconstruction, pData->D_volumeData, pData->dims, iReconstructionPitch); if (!ok) return false; return true; } float AstraSIRT3d::computeDiffNorm() { if (!pData->initialized) return 0.0f; // FIXME: Error? return pData->sirt.computeDiffNorm(); } class AstraCGLS3d_internal { public: SDimensions3D dims; SProjectorParams3D params; float* angles; float fOriginSourceDistance; float fOriginDetectorDistance; Geometry3DParameters projs; bool initialized; bool setStartReconstruction; bool useVolumeMask; bool useSinogramMask; // Input/output cudaPitchedPtr D_projData; cudaPitchedPtr D_volumeData; cudaPitchedPtr D_maskData; cudaPitchedPtr D_smaskData; CGLS cgls; }; AstraCGLS3d::AstraCGLS3d() { pData = new AstraCGLS3d_internal(); pData->angles = 0; pData->D_projData.ptr = 0; pData->D_volumeData.ptr = 0; pData->D_maskData.ptr = 0; pData->D_smaskData.ptr = 0; pData->dims.iVolX = 0; pData->dims.iVolY = 0; pData->dims.iVolZ = 0; pData->dims.iProjAngles = 0; pData->dims.iProjU = 0; pData->dims.iProjV = 0; pData->initialized = false; pData->setStartReconstruction = false; pData->useVolumeMask = false; pData->useSinogramMask = false; } AstraCGLS3d::~AstraCGLS3d() { delete[] pData->angles; pData->angles = 0; cudaFree(pData->D_projData.ptr); pData->D_projData.ptr = 0; cudaFree(pData->D_volumeData.ptr); pData->D_volumeData.ptr = 0; cudaFree(pData->D_maskData.ptr); pData->D_maskData.ptr = 0; cudaFree(pData->D_smaskData.ptr); pData->D_smaskData.ptr = 0; delete pData; pData = 0; } bool AstraCGLS3d::setGeometry(const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom) { if (pData->initialized) return false; bool ok = convertAstraGeometry_dims(pVolGeom, pProjGeom, pData->dims); if (!ok) return false; pData->projs = convertAstraGeometry(pVolGeom, pProjGeom, pData->params); if (!pData->projs.isValid()) return false; return true; } bool AstraCGLS3d::enableSuperSampling(unsigned int iVoxelSuperSampling, unsigned int iDetectorSuperSampling) { if (pData->initialized) return false; if (iVoxelSuperSampling == 0 || iDetectorSuperSampling == 0) return false; pData->params.iRaysPerVoxelDim = iVoxelSuperSampling; pData->params.iRaysPerDetDim = iDetectorSuperSampling; return true; } bool AstraCGLS3d::enableVolumeMask() { if (pData->initialized) return false; bool ok = pData->cgls.enableVolumeMask(); pData->useVolumeMask = ok; return ok; } #if 0 bool AstraCGLS3d::enableSinogramMask() { if (pData->initialized) return false; bool ok = pData->cgls.enableSinogramMask(); pData->useSinogramMask = ok; return ok; } #endif bool AstraCGLS3d::setGPUIndex(int index) { if (index != -1) { cudaSetDevice(index); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } return true; } bool AstraCGLS3d::init() { if (pData->initialized) return false; if (pData->dims.iVolX == 0 || pData->dims.iProjAngles == 0) return false; bool ok; if (pData->projs.isParallel()) ok = pData->cgls.setPar3DGeometry(pData->dims, pData->projs.getParallel(), pData->params); else if (pData->projs.isCone()) ok = pData->cgls.setConeGeometry(pData->dims, pData->projs.getCone(), pData->params); else ok = false; if (!ok) return false; ok = pData->cgls.init(); if (!ok) return false; pData->D_volumeData = allocateVolumeData(pData->dims); ok = pData->D_volumeData.ptr; if (!ok) return false; pData->D_projData = allocateProjectionData(pData->dims); ok = pData->D_projData.ptr; if (!ok) { cudaFree(pData->D_volumeData.ptr); pData->D_volumeData.ptr = 0; return false; } if (pData->useVolumeMask) { pData->D_maskData = allocateVolumeData(pData->dims); ok = pData->D_maskData.ptr; if (!ok) { cudaFree(pData->D_volumeData.ptr); cudaFree(pData->D_projData.ptr); pData->D_volumeData.ptr = 0; pData->D_projData.ptr = 0; return false; } } if (pData->useSinogramMask) { pData->D_smaskData = allocateProjectionData(pData->dims); ok = pData->D_smaskData.ptr; if (!ok) { cudaFree(pData->D_volumeData.ptr); cudaFree(pData->D_projData.ptr); cudaFree(pData->D_maskData.ptr); pData->D_volumeData.ptr = 0; pData->D_projData.ptr = 0; pData->D_maskData.ptr = 0; return false; } } pData->initialized = true; return true; } #if 0 bool AstraCGLS3d::setMinConstraint(float fMin) { if (!pData->initialized) return false; return pData->cgls.setMinConstraint(fMin); } bool AstraCGLS3d::setMaxConstraint(float fMax) { if (!pData->initialized) return false; return pData->cgls.setMaxConstraint(fMax); } #endif bool AstraCGLS3d::setSinogram(const float* pfSinogram, unsigned int iSinogramPitch) { if (!pData->initialized) return false; if (!pfSinogram) return false; bool ok = copyProjectionsToDevice(pfSinogram, pData->D_projData, pData->dims, iSinogramPitch); if (!ok) return false; ok = pData->cgls.setBuffers(pData->D_volumeData, pData->D_projData); if (!ok) return false; pData->setStartReconstruction = false; return true; } bool AstraCGLS3d::setVolumeMask(const float* pfMask, unsigned int iMaskPitch) { if (!pData->initialized) return false; if (!pData->useVolumeMask) return false; if (!pfMask) return false; bool ok = copyVolumeToDevice(pfMask, pData->D_maskData, pData->dims, iMaskPitch); if (!ok) return false; ok = pData->cgls.setVolumeMask(pData->D_maskData); if (!ok) return false; return true; } #if 0 bool AstraCGLS3d::setSinogramMask(const float* pfMask, unsigned int iMaskPitch) { if (!pData->initialized) return false; if (!pData->useSinogramMask) return false; if (!pfMask) return false; bool ok = copyProjectionsToDevice(pfMask, pData->D_smaskData, pData->dims, iMaskPitch); if (!ok) return false; ok = pData->cgls.setSinogramMask(pData->D_smaskData); if (!ok) return false; return true; } #endif bool AstraCGLS3d::setStartReconstruction(const float* pfReconstruction, unsigned int iReconstructionPitch) { if (!pData->initialized) return false; if (!pfReconstruction) return false; bool ok = copyVolumeToDevice(pfReconstruction, pData->D_volumeData, pData->dims, iReconstructionPitch); if (!ok) return false; pData->setStartReconstruction = true; return true; } bool AstraCGLS3d::iterate(unsigned int iIterations) { if (!pData->initialized) return false; if (!pData->setStartReconstruction) zeroVolumeData(pData->D_volumeData, pData->dims); bool ok = pData->cgls.iterate(iIterations); if (!ok) return false; return true; } bool AstraCGLS3d::getReconstruction(float* pfReconstruction, unsigned int iReconstructionPitch) const { if (!pData->initialized) return false; bool ok = copyVolumeFromDevice(pfReconstruction, pData->D_volumeData, pData->dims, iReconstructionPitch); if (!ok) return false; return true; } float AstraCGLS3d::computeDiffNorm() { if (!pData->initialized) return 0.0f; // FIXME: Error? return pData->cgls.computeDiffNorm(); } bool astraCudaFP(const float* pfVolume, float* pfProjections, const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom, int iGPUIndex, int iDetectorSuperSampling, Cuda3DProjectionKernel projKernel) { SDimensions3D dims; SProjectorParams3D params; params.iRaysPerDetDim = iDetectorSuperSampling; bool ok = convertAstraGeometry_dims(pVolGeom, pProjGeom, dims); if (!ok) return false; if (iDetectorSuperSampling == 0) return false; Geometry3DParameters projs = convertAstraGeometry(pVolGeom, pProjGeom, params); if (iGPUIndex != -1) { cudaSetDevice(iGPUIndex); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } cudaPitchedPtr D_volumeData = allocateVolumeData(dims); ok = D_volumeData.ptr; if (!ok) return false; cudaPitchedPtr D_projData = allocateProjectionData(dims); ok = D_projData.ptr; if (!ok) { cudaFree(D_volumeData.ptr); return false; } ok &= copyVolumeToDevice(pfVolume, D_volumeData, dims, dims.iVolX); ok &= zeroProjectionData(D_projData, dims); if (!ok) { cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return false; } if (projs.isParallel()) { switch (projKernel) { case ker3d_default: ok &= Par3DFP(D_volumeData, D_projData, dims, projs.getParallel(), params); break; case ker3d_sum_square_weights: ok &= Par3DFP_SumSqW(D_volumeData, D_projData, dims, projs.getParallel(), params); break; default: ok = false; } } else if (projs.isCone()) { switch (projKernel) { case ker3d_default: ok &= ConeFP(D_volumeData, D_projData, dims, projs.getCone(), params); break; default: ok = false; } } else { ok = false; } ok &= copyProjectionsFromDevice(pfProjections, D_projData, dims, dims.iProjU); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return ok; } bool astraCudaBP(float* pfVolume, const float* pfProjections, const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom, int iGPUIndex, int iVoxelSuperSampling) { SDimensions3D dims; SProjectorParams3D params; params.iRaysPerVoxelDim = iVoxelSuperSampling; bool ok = convertAstraGeometry_dims(pVolGeom, pProjGeom, dims); if (!ok) return false; Geometry3DParameters projs = convertAstraGeometry(pVolGeom, pProjGeom, params); if (!projs.isValid()) return false; if (iGPUIndex != -1) { cudaSetDevice(iGPUIndex); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } cudaPitchedPtr D_volumeData = allocateVolumeData(dims); ok = D_volumeData.ptr; if (!ok) return false; cudaPitchedPtr D_projData = allocateProjectionData(dims); ok = D_projData.ptr; if (!ok) { cudaFree(D_volumeData.ptr); return false; } ok &= copyProjectionsToDevice(pfProjections, D_projData, dims, dims.iProjU); ok &= zeroVolumeData(D_volumeData, dims); if (!ok) { cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return false; } if (projs.isParallel()) ok &= Par3DBP(D_volumeData, D_projData, dims, projs.getParallel(), params); else if (projs.isCone()) ok &= ConeBP(D_volumeData, D_projData, dims, projs.getCone(), params); else ok = false; ok &= copyVolumeFromDevice(pfVolume, D_volumeData, dims, dims.iVolX); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return ok; } // This computes the column weights, divides by them, and adds the // result to the current volume. This is both more expensive and more // GPU memory intensive than the regular BP, but allows saving system RAM. bool astraCudaBP_SIRTWeighted(float* pfVolume, const float* pfProjections, const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom, int iGPUIndex, int iVoxelSuperSampling) { SDimensions3D dims; SProjectorParams3D params; params.iRaysPerVoxelDim = iVoxelSuperSampling; bool ok = convertAstraGeometry_dims(pVolGeom, pProjGeom, dims); if (!ok) return false; Geometry3DParameters projs = convertAstraGeometry(pVolGeom, pProjGeom, params); if (!projs.isValid()) return false; if (iGPUIndex != -1) { cudaSetDevice(iGPUIndex); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } cudaPitchedPtr D_pixelWeight = allocateVolumeData(dims); ok = D_pixelWeight.ptr; if (!ok) return false; cudaPitchedPtr D_volumeData = allocateVolumeData(dims); ok = D_volumeData.ptr; if (!ok) { cudaFree(D_pixelWeight.ptr); return false; } cudaPitchedPtr D_projData = allocateProjectionData(dims); ok = D_projData.ptr; if (!ok) { cudaFree(D_pixelWeight.ptr); cudaFree(D_volumeData.ptr); return false; } // Compute weights ok &= zeroVolumeData(D_pixelWeight, dims); ok &= processSino3D(D_projData, 1.0f, dims); if (projs.isParallel()) ok &= Par3DBP(D_pixelWeight, D_projData, dims, projs.getParallel(), params); else if (projs.isCone()) ok &= ConeBP(D_pixelWeight, D_projData, dims, projs.getCone(), params); else ok = false; ok &= processVol3D(D_pixelWeight, dims); if (!ok) { cudaFree(D_pixelWeight.ptr); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return false; } ok &= copyProjectionsToDevice(pfProjections, D_projData, dims, dims.iProjU); ok &= zeroVolumeData(D_volumeData, dims); // Do BP into D_volumeData if (projs.isParallel()) ok &= Par3DBP(D_volumeData, D_projData, dims, projs.getParallel(), params); else if (projs.isCone()) ok &= ConeBP(D_volumeData, D_projData, dims, projs.getCone(), params); else ok = false; // Multiply with weights ok &= processVol3D(D_volumeData, D_pixelWeight, dims); // Upload previous iterate to D_pixelWeight... ok &= copyVolumeToDevice(pfVolume, D_pixelWeight, dims, dims.iVolX); if (!ok) { cudaFree(D_pixelWeight.ptr); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return false; } // ...and add it to the weighted BP ok &= processVol3D(D_volumeData, D_pixelWeight, dims); // Then copy the result back ok &= copyVolumeFromDevice(pfVolume, D_volumeData, dims, dims.iVolX); cudaFree(D_pixelWeight.ptr); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return ok; } _AstraExport bool uploadMultipleProjections(CFloat32ProjectionData3D *proj, const float *data, unsigned int y_min, unsigned int y_max) { assert(proj->getStorage()->isGPU()); CDataGPU *storage = dynamic_cast(proj->getStorage()); astraCUDA3d::MemHandle3D hnd = storage->getHandle(); astraCUDA3d::SDimensions3D dims1; dims1.iProjU = proj->getDetectorColCount(); dims1.iProjV = proj->getDetectorRowCount(); dims1.iProjAngles = y_max - y_min + 1; cudaPitchedPtr D_proj = allocateProjectionData(dims1); bool ok = copyProjectionsToDevice(data, D_proj, dims1); if (!ok) { ASTRA_ERROR("Failed to upload projection to GPU"); return false; } astraCUDA3d::MemHandle3D hnd1 = astraCUDA3d::wrapHandle( (float *)D_proj.ptr, dims1.iProjU, dims1.iProjAngles, dims1.iProjV, D_proj.pitch / sizeof(float)); astraCUDA3d::SSubDimensions3D subdims; subdims.nx = dims1.iProjU; subdims.ny = proj->getAngleCount(); subdims.nz = dims1.iProjV; subdims.pitch = D_proj.pitch / sizeof(float); // FIXME: Pitch for wrong obj! subdims.subnx = dims1.iProjU; subdims.subny = dims1.iProjAngles; subdims.subnz = dims1.iProjV; subdims.subx = 0; subdims.suby = y_min; subdims.subz = 0; ok = astraCUDA3d::copyIntoArray(hnd, hnd1, subdims); if (!ok) { ASTRA_ERROR("Failed to copy projection into 3d data"); return false; } cudaFree(D_proj.ptr); return true; } } astra-toolbox-2.3.0/cuda/3d/cgls3d.cu000066400000000000000000000122511475635207100172570ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/cgls3d.h" #include "astra/cuda/3d/util3d.h" #include "astra/cuda/3d/arith3d.h" #include "astra/cuda/3d/cone_fp.h" #include #include namespace astraCUDA3d { CGLS::CGLS() : ReconAlgo3D() { D_maskData.ptr = 0; D_smaskData.ptr = 0; D_sinoData.ptr = 0; D_volumeData.ptr = 0; D_r.ptr = 0; D_w.ptr = 0; D_z.ptr = 0; D_p.ptr = 0; useVolumeMask = false; useSinogramMask = false; } CGLS::~CGLS() { reset(); } void CGLS::reset() { cudaFree(D_r.ptr); cudaFree(D_w.ptr); cudaFree(D_z.ptr); cudaFree(D_p.ptr); D_maskData.ptr = 0; D_smaskData.ptr = 0; D_sinoData.ptr = 0; D_volumeData.ptr = 0; D_r.ptr = 0; D_w.ptr = 0; D_z.ptr = 0; D_p.ptr = 0; useVolumeMask = false; useSinogramMask = false; sliceInitialized = false; ReconAlgo3D::reset(); } bool CGLS::enableVolumeMask() { useVolumeMask = true; return true; } bool CGLS::enableSinogramMask() { useSinogramMask = true; return true; } bool CGLS::init() { D_z = allocateVolumeData(dims); D_p = allocateVolumeData(dims); D_r = allocateProjectionData(dims); D_w = allocateProjectionData(dims); // TODO: check if allocations succeeded return true; } bool CGLS::setVolumeMask(cudaPitchedPtr& _D_maskData) { assert(useVolumeMask); D_maskData = _D_maskData; return true; } bool CGLS::setSinogramMask(cudaPitchedPtr& _D_smaskData) { return false; #if 0 // TODO: Implement this assert(useSinogramMask); D_smaskData = _D_smaskData; return true; #endif } bool CGLS::setBuffers(cudaPitchedPtr& _D_volumeData, cudaPitchedPtr& _D_projData) { D_volumeData = _D_volumeData; D_sinoData = _D_projData; sliceInitialized = false; return true; } bool CGLS::iterate(unsigned int iterations) { if (!sliceInitialized) { // copy sinogram duplicateProjectionData(D_r, D_sinoData, dims); // r = sino - A*x if (useVolumeMask) { duplicateVolumeData(D_z, D_volumeData, dims); processVol3D(D_z, D_maskData, dims); callFP(D_z, D_r, -1.0f); } else { callFP(D_volumeData, D_r, -1.0f); } // p = A'*r zeroVolumeData(D_p, dims); callBP(D_p, D_r, 1.0f); if (useVolumeMask) processVol3D(D_p, D_maskData, dims); gamma = dotProduct3D(D_p, dims.iVolX, dims.iVolY, dims.iVolZ); sliceInitialized = true; } // iteration for (unsigned int iter = 0; iter < iterations && !astra::shouldAbort(); ++iter) { // w = A*p zeroProjectionData(D_w, dims); callFP(D_p, D_w, 1.0f); // alpha = gamma / float ww = dotProduct3D(D_w, dims.iProjU, dims.iProjAngles, dims.iProjV); float alpha = gamma / ww; // x += alpha*p processVol3D(D_volumeData, D_p, alpha, dims); // r -= alpha*w processSino3D(D_r, D_w, -alpha, dims); // z = A'*r zeroVolumeData(D_z, dims); callBP(D_z, D_r, 1.0f); if (useVolumeMask) processVol3D(D_z, D_maskData, dims); float beta = 1.0f / gamma; gamma = dotProduct3D(D_z, dims.iVolX, dims.iVolY, dims.iVolZ); beta *= gamma; // p = z + beta*p processVol3D(D_p, D_z, beta, dims); } return true; } float CGLS::computeDiffNorm() { // We can use w and z as temporary storage here since they're not // used outside of iterations. // copy sinogram to w duplicateProjectionData(D_w, D_sinoData, dims); // do FP, subtracting projection from sinogram if (useVolumeMask) { duplicateVolumeData(D_z, D_volumeData, dims); processVol3D(D_z, D_maskData, dims); callFP(D_z, D_w, -1.0f); } else { callFP(D_volumeData, D_w, -1.0f); } float s = dotProduct3D(D_w, dims.iProjU, dims.iProjAngles, dims.iProjV); return sqrt(s); } bool doCGLS(cudaPitchedPtr& D_volumeData, cudaPitchedPtr& D_sinoData, cudaPitchedPtr& D_maskData, const SDimensions3D& dims, const SConeProjection* angles, unsigned int iterations) { CGLS cgls; bool ok = true; ok &= cgls.setConeGeometry(dims, angles, SProjectorParams3D()); if (D_maskData.ptr) ok &= cgls.enableVolumeMask(); if (!ok) return false; ok = cgls.init(); if (!ok) return false; if (D_maskData.ptr) ok &= cgls.setVolumeMask(D_maskData); ok &= cgls.setBuffers(D_volumeData, D_sinoData); if (!ok) return false; ok = cgls.iterate(iterations); return ok; } } astra-toolbox-2.3.0/cuda/3d/cone_bp.cu000066400000000000000000000314051475635207100175070ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/util3d.h" #include "astra/cuda/3d/dims3d.h" #include #include #include namespace astraCUDA3d { static const unsigned int g_volBlockZ = 6; static const unsigned int g_anglesPerBlock = 32; static const unsigned int g_volBlockX = 16; static const unsigned int g_volBlockY = 32; static const unsigned g_MaxAngles = 1024; struct DevConeParams { float4 fNumU; float4 fNumV; float4 fDen; }; __constant__ DevConeParams gC_C[g_MaxAngles]; //__launch_bounds__(32*16, 4) template __global__ void dev_cone_BP(void* D_volData, unsigned int volPitch, cudaTextureObject_t tex, int startAngle, int angleOffset, const astraCUDA3d::SDimensions3D dims, float fOutputScale) { float* volData = (float*)D_volData; int endAngle = startAngle + g_anglesPerBlock; if (endAngle > dims.iProjAngles - angleOffset) endAngle = dims.iProjAngles - angleOffset; // threadIdx: x = rel x // y = rel y // blockIdx: x = x + y // y = z const int X = blockIdx.x % ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockX + threadIdx.x; const int Y = blockIdx.x / ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockY + threadIdx.y; if (X >= dims.iVolX) return; if (Y >= dims.iVolY) return; const int startZ = blockIdx.y * g_volBlockZ; const float fX = X - 0.5f*dims.iVolX + 0.5f; const float fY = Y - 0.5f*dims.iVolY + 0.5f; const float fZ = startZ - 0.5f*dims.iVolZ + 0.5f; float Z[ZSIZE]; for(int i=0; i < ZSIZE; i++) Z[i] = 0.0f; { float fAngle = startAngle + angleOffset + 0.5f; for (int angle = startAngle; angle < endAngle; ++angle, fAngle += 1.0f) { float4 fCu = gC_C[angle].fNumU; float4 fCv = gC_C[angle].fNumV; float4 fCd = gC_C[angle].fDen; float fUNum = fCu.w + fX * fCu.x + fY * fCu.y + fZ * fCu.z; float fVNum = fCv.w + fX * fCv.x + fY * fCv.y + fZ * fCv.z; float fDen = ((KERNEL == ker3d_fdk_weighting) ? 1.0f : fCd.w) + fX * fCd.x + fY * fCd.y + fZ * fCd.z; float fU,fV, fr; for (int idx = 0; idx < ZSIZE; idx++) { fr = __fdividef(1.0f, fDen); fU = fUNum * fr; fV = fVNum * fr; float fVal = tex3D(tex, fU, fAngle, fV); if (KERNEL == ker3d_2d_weighting) Z[idx] += fr*fVal; else Z[idx] += fr*fr*fVal; fUNum += fCu.z; fVNum += fCv.z; fDen += fCd.z; } } } int endZ = ZSIZE; if (endZ > dims.iVolZ - startZ) endZ = dims.iVolZ - startZ; for(int i=0; i < endZ; i++) volData[(size_t)((startZ+i)*dims.iVolY+Y)*volPitch+X] += Z[i] * fOutputScale; } //End kernel // supersampling version __global__ void dev_cone_BP_SS(void* D_volData, unsigned int volPitch, cudaTextureObject_t tex, int startAngle, int angleOffset, const SDimensions3D dims, int iRaysPerVoxelDim, float fOutputScale) { float* volData = (float*)D_volData; int endAngle = startAngle + g_anglesPerBlock; if (endAngle > dims.iProjAngles - angleOffset) endAngle = dims.iProjAngles - angleOffset; // threadIdx: x = rel x // y = rel y // blockIdx: x = x + y // y = z // TO TRY: precompute part of detector intersection formulas in shared mem? // TO TRY: inner loop over z, gather ray values in shared mem const int X = blockIdx.x % ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockX + threadIdx.x; const int Y = blockIdx.x / ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockY + threadIdx.y; if (X >= dims.iVolX) return; if (Y >= dims.iVolY) return; const int startZ = blockIdx.y * g_volBlockZ; int endZ = startZ + g_volBlockZ; if (endZ > dims.iVolZ) endZ = dims.iVolZ; float fX = X - 0.5f*dims.iVolX + 0.5f - 0.5f + 0.5f/iRaysPerVoxelDim; float fY = Y - 0.5f*dims.iVolY + 0.5f - 0.5f + 0.5f/iRaysPerVoxelDim; float fZ = startZ - 0.5f*dims.iVolZ + 0.5f - 0.5f + 0.5f/iRaysPerVoxelDim; const float fSubStep = 1.0f/iRaysPerVoxelDim; fOutputScale /= (iRaysPerVoxelDim*iRaysPerVoxelDim*iRaysPerVoxelDim); for (int Z = startZ; Z < endZ; ++Z, fZ += 1.0f) { float fVal = 0.0f; float fAngle = startAngle + angleOffset + 0.5f; for (int angle = startAngle; angle < endAngle; ++angle, fAngle += 1.0f) { float4 fCu = gC_C[angle].fNumU; float4 fCv = gC_C[angle].fNumV; float4 fCd = gC_C[angle].fDen; float fXs = fX; for (int iSubX = 0; iSubX < iRaysPerVoxelDim; ++iSubX) { float fYs = fY; for (int iSubY = 0; iSubY < iRaysPerVoxelDim; ++iSubY) { float fZs = fZ; for (int iSubZ = 0; iSubZ < iRaysPerVoxelDim; ++iSubZ) { const float fUNum = fCu.w + fXs * fCu.x + fYs * fCu.y + fZs * fCu.z; const float fVNum = fCv.w + fXs * fCv.x + fYs * fCv.y + fZs * fCv.z; const float fDen = fCd.w + fXs * fCd.x + fYs * fCd.y + fZs * fCd.z; const float fr = __fdividef(1.0f, fDen); const float fU = fUNum * fr; const float fV = fVNum * fr; fVal += tex3D(tex, fU, fAngle, fV) * fr * fr; fZs += fSubStep; } fYs += fSubStep; } fXs += fSubStep; } } volData[(size_t)(Z*dims.iVolY+Y)*volPitch+X] += fVal * fOutputScale; } } using TransferConstantsBuffer = TransferConstantsBuffer_t; bool transferConstants(const SConeProjection* angles, unsigned int iProjAngles, const SProjectorParams3D& params, TransferConstantsBuffer& buf, cudaStream_t stream) { DevConeParams *p = &(std::get<0>(buf.d))[0]; // We need three things in the kernel: // projected coordinates of pixels on the detector: // u: || (x-s) v (s-d) || / || u v (s-x) || // v: -|| u (x-s) (s-d) || / || u v (s-x) || // ray density weighting factor for the adjoint // || u v (s-d) ||^2 / ( |cross(u,v)| * || u v (s-x) ||^2 ) // FDK weighting factor // ( || u v s || / || u v (s-x) || ) ^ 2 // Since u and v are ratios with the same denominator, we have // a degree of freedom to scale the denominator. We use that to make // the square of the denominator equal to the relevant weighting factor. // We use an event to assure that the previous transferConstants has completed before // re-using the buffer. (Even if it is very unlikely that it hasn't.) bool ok = checkCuda(cudaStreamWaitEvent(stream, buf.event, 0), "transferConstants wait"); for (unsigned int i = 0; i < iProjAngles; ++i) { Vec3 u(angles[i].fDetUX, angles[i].fDetUY, angles[i].fDetUZ); Vec3 v(angles[i].fDetVX, angles[i].fDetVY, angles[i].fDetVZ); Vec3 s(angles[i].fSrcX, angles[i].fSrcY, angles[i].fSrcZ); Vec3 d(angles[i].fDetSX, angles[i].fDetSY, angles[i].fDetSZ); double fScale; if (params.projKernel == ker3d_fdk_weighting) { // goal: 1/fDen = || u v s || / || u v (s-x) || // fDen = || u v (s-x) || / || u v s || // i.e., scale = 1 / || u v s || fScale = 1.0 / det3(u, v, s); } else if (params.projKernel == ker3d_2d_weighting) { // We set the weights here to approximate the adjoint // of a 2d fanbeam kernel. To be used when only // operating on a single slice. // fDen = |cross(u,e_v)| || u v (s-x) || / || u v (s-d) || Vec3 ev(0, 0, 1); fScale = scaled_cross3(u,ev,Vec3(params.fVolScaleX,params.fVolScaleY,params.fVolScaleZ)).norm() / det3(u, v, s-d); } else { // goal: 1/fDen^2 = || u v (s-d) ||^2 / ( |cross(u,v)| * || u v (s-x) ||^2 ) // fDen = ( sqrt(|cross(u,v)|) * || u v (s-x) || ) / || u v (s-d) || // i.e. scale = sqrt(|cross(u,v)|) / || u v (s-d) || // NB: for cross(u,v) we invert the volume scaling (for the voxel // size normalization) to get the proper dimensions for // the scaling of the adjoint fScale = sqrt(scaled_cross3(u,v,Vec3(params.fVolScaleX,params.fVolScaleY,params.fVolScaleZ)).norm()) / det3(u, v, s-d); } p[i].fNumU.w = fScale * det3(s,v,d); p[i].fNumU.x = fScale * det3x(v,s-d); p[i].fNumU.y = fScale * det3y(v,s-d); p[i].fNumU.z = fScale * det3z(v,s-d); p[i].fNumV.w = -fScale * det3(s,u,d); p[i].fNumV.x = -fScale * det3x(u,s-d); p[i].fNumV.y = -fScale * det3y(u,s-d); p[i].fNumV.z = -fScale * det3z(u,s-d); p[i].fDen.w = fScale * det3(u, v, s); // == 1.0 for FDK p[i].fDen.x = -fScale * det3x(u, v); p[i].fDen.y = -fScale * det3y(u, v); p[i].fDen.z = -fScale * det3z(u, v); } ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_C, p, iProjAngles*sizeof(DevConeParams), 0, cudaMemcpyHostToDevice, stream), "transferConstants transfer"); ok &= checkCuda(cudaEventRecord(buf.event, stream), "transferConstants event"); return ok; } bool ConeBP_Array(cudaPitchedPtr D_volumeData, cudaArray *D_projArray, const SDimensions3D& dims, const SConeProjection* angles, const SProjectorParams3D& params) { TransferConstantsBuffer tcbuf(g_MaxAngles); cudaTextureObject_t D_texObj; if (!createTextureObject3D(D_projArray, D_texObj)) return false; cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "ConeBP_Array stream")) { cudaDestroyTextureObject(D_texObj); return false; } float fOutputScale; if (params.projKernel == ker3d_fdk_weighting) { // NB: assuming cube voxels here fOutputScale = params.fOutputScale / (params.fVolScaleX); } else { fOutputScale = params.fOutputScale * (params.fVolScaleX * params.fVolScaleY * params.fVolScaleZ); } bool ok = true; for (unsigned int th = 0; th < dims.iProjAngles; th += g_MaxAngles) { unsigned int angleCount = g_MaxAngles; if (th + angleCount > dims.iProjAngles) angleCount = dims.iProjAngles - th; ok = transferConstants(angles, angleCount, params, tcbuf, stream); if (!ok) break; dim3 dimBlock(g_volBlockX, g_volBlockY); dim3 dimGrid(((dims.iVolX/1+g_volBlockX-1)/(g_volBlockX))*((dims.iVolY/1+1*g_volBlockY-1)/(1*g_volBlockY)), (dims.iVolZ+g_volBlockZ-1)/g_volBlockZ); // timeval t; // tic(t); for (unsigned int i = 0; i < angleCount; i += g_anglesPerBlock) { // printf("Calling BP: %d, %dx%d, %dx%d to %p\n", i, dimBlock.x, dimBlock.y, dimGrid.x, dimGrid.y, (void*)D_volumeData.ptr); if (params.projKernel == ker3d_fdk_weighting) { if (dims.iVolZ == 1) { dev_cone_BP<<>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), D_texObj, i, th, dims, fOutputScale); } else { dev_cone_BP<<>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), D_texObj, i, th, dims, fOutputScale); } } else if (params.projKernel == ker3d_2d_weighting) { dev_cone_BP<<>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), D_texObj, i, th, dims, fOutputScale); } else if (params.iRaysPerVoxelDim == 1) { if (dims.iVolZ == 1) { dev_cone_BP<<>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), D_texObj, i, th, dims, fOutputScale); } else { dev_cone_BP<<>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), D_texObj, i, th, dims, fOutputScale); } } else dev_cone_BP_SS<<>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), D_texObj, i, th, dims, params.iRaysPerVoxelDim, fOutputScale); } angles = angles + angleCount; // printf("%f\n", toc(t)); } ok = checkCuda(cudaStreamSynchronize(stream), "ConeBP sync"); cudaDestroyTextureObject(D_texObj); cudaStreamDestroy(stream); return ok; } bool ConeBP(cudaPitchedPtr D_volumeData, cudaPitchedPtr D_projData, const SDimensions3D& dims, const SConeProjection* angles, const SProjectorParams3D& params) { // transfer projections to array cudaArray* cuArray = allocateProjectionArray(dims); if (!cuArray) return false; if (!transferProjectionsToArray(D_projData, cuArray, dims)) { cudaFreeArray(cuArray); return false; } bool ret = ConeBP_Array(D_volumeData, cuArray, dims, angles, params); cudaFreeArray(cuArray); return ret; } } astra-toolbox-2.3.0/cuda/3d/cone_fp.cu000066400000000000000000000451711475635207100175200ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/util3d.h" #include "astra/cuda/3d/dims3d.h" #include #include #include namespace astraCUDA3d { static const unsigned int g_anglesPerBlock = 4; // thickness of the slices we're splitting the volume up into static const unsigned int g_blockSlices = 4; static const unsigned int g_detBlockU = 32; static const unsigned int g_detBlockV = 32; static const unsigned g_MaxAngles = 1024; __constant__ float gC_SrcX[g_MaxAngles]; __constant__ float gC_SrcY[g_MaxAngles]; __constant__ float gC_SrcZ[g_MaxAngles]; __constant__ float gC_DetSX[g_MaxAngles]; __constant__ float gC_DetSY[g_MaxAngles]; __constant__ float gC_DetSZ[g_MaxAngles]; __constant__ float gC_DetUX[g_MaxAngles]; __constant__ float gC_DetUY[g_MaxAngles]; __constant__ float gC_DetUZ[g_MaxAngles]; __constant__ float gC_DetVX[g_MaxAngles]; __constant__ float gC_DetVY[g_MaxAngles]; __constant__ float gC_DetVZ[g_MaxAngles]; // x=0, y=1, z=2 struct DIR_X { __device__ float nSlices(const SDimensions3D& dims) const { return dims.iVolX; } __device__ float nDim1(const SDimensions3D& dims) const { return dims.iVolY; } __device__ float nDim2(const SDimensions3D& dims) const { return dims.iVolZ; } __device__ float c0(float x, float y, float z) const { return x; } __device__ float c1(float x, float y, float z) const { return y; } __device__ float c2(float x, float y, float z) const { return z; } __device__ float tex(cudaTextureObject_t tex, float f0, float f1, float f2) const { return tex3D(tex, f0, f1, f2); } __device__ float x(float f0, float f1, float f2) const { return f0; } __device__ float y(float f0, float f1, float f2) const { return f1; } __device__ float z(float f0, float f1, float f2) const { return f2; } }; // y=0, x=1, z=2 struct DIR_Y { __device__ float nSlices(const SDimensions3D& dims) const { return dims.iVolY; } __device__ float nDim1(const SDimensions3D& dims) const { return dims.iVolX; } __device__ float nDim2(const SDimensions3D& dims) const { return dims.iVolZ; } __device__ float c0(float x, float y, float z) const { return y; } __device__ float c1(float x, float y, float z) const { return x; } __device__ float c2(float x, float y, float z) const { return z; } __device__ float tex(cudaTextureObject_t tex, float f0, float f1, float f2) const { return tex3D(tex, f1, f0, f2); } __device__ float x(float f0, float f1, float f2) const { return f1; } __device__ float y(float f0, float f1, float f2) const { return f0; } __device__ float z(float f0, float f1, float f2) const { return f2; } }; // z=0, x=1, y=2 struct DIR_Z { __device__ float nSlices(const SDimensions3D& dims) const { return dims.iVolZ; } __device__ float nDim1(const SDimensions3D& dims) const { return dims.iVolX; } __device__ float nDim2(const SDimensions3D& dims) const { return dims.iVolY; } __device__ float c0(float x, float y, float z) const { return z; } __device__ float c1(float x, float y, float z) const { return x; } __device__ float c2(float x, float y, float z) const { return y; } __device__ float tex(cudaTextureObject_t tex, float f0, float f1, float f2) const { return tex3D(tex, f1, f2, f0); } __device__ float x(float f0, float f1, float f2) const { return f1; } __device__ float y(float f0, float f1, float f2) const { return f2; } __device__ float z(float f0, float f1, float f2) const { return f0; } }; struct SCALE_CUBE { float fOutputScale; __device__ float scale(float a1, float a2) const { return sqrt(a1*a1+a2*a2+1.0f) * fOutputScale; } }; struct SCALE_NONCUBE { float fScale1; float fScale2; float fOutputScale; __device__ float scale(float a1, float a2) const { return sqrt(a1*a1*fScale1+a2*a2*fScale2+1.0f) * fOutputScale; } }; using TransferConstantsBuffer = TransferConstantsBuffer_t; bool transferConstants(const SConeProjection* angles, unsigned int iProjAngles, TransferConstantsBuffer& buf, cudaStream_t stream) { float* tmp = &(std::get<0>(buf.d))[0]; // We use an event to assure that the previous transferConstants has completed before // re-using the buffer. (Even if it is very unlikely that it hasn't.) bool ok = checkCuda(cudaStreamWaitEvent(stream, buf.event, 0), "transferConstants wait"); #define TRANSFER_TO_CONSTANT(name) do { for (unsigned int i = 0; i < iProjAngles; ++i) tmp[i] = angles[i].f##name ; ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_##name, tmp, iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice, stream), "transferConstants transfer"); } while (0) TRANSFER_TO_CONSTANT(SrcX); TRANSFER_TO_CONSTANT(SrcY); TRANSFER_TO_CONSTANT(SrcZ); TRANSFER_TO_CONSTANT(DetSX); TRANSFER_TO_CONSTANT(DetSY); TRANSFER_TO_CONSTANT(DetSZ); TRANSFER_TO_CONSTANT(DetUX); TRANSFER_TO_CONSTANT(DetUY); TRANSFER_TO_CONSTANT(DetUZ); TRANSFER_TO_CONSTANT(DetVX); TRANSFER_TO_CONSTANT(DetVY); TRANSFER_TO_CONSTANT(DetVZ); #undef TRANSFER_TO_CONSTANT ok &= checkCuda(cudaEventRecord(buf.event, stream), "transferConstants event"); return ok; } // threadIdx: x = ??? detector (u?) // y = relative angle // blockIdx: x = ??? detector (u+v?) // y = angle block template __global__ void cone_FP_t(float* D_projData, unsigned int projPitch, cudaTextureObject_t tex, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions3D dims, SCALE sc) { COORD c; int angle = startAngle + blockIdx.y * g_anglesPerBlock + threadIdx.y; if (angle >= endAngle) return; const float fSrcX = gC_SrcX[angle]; const float fSrcY = gC_SrcY[angle]; const float fSrcZ = gC_SrcZ[angle]; const float fDetUX = gC_DetUX[angle]; const float fDetUY = gC_DetUY[angle]; const float fDetUZ = gC_DetUZ[angle]; const float fDetVX = gC_DetVX[angle]; const float fDetVY = gC_DetVY[angle]; const float fDetVZ = gC_DetVZ[angle]; const float fDetSX = gC_DetSX[angle] + 0.5f * fDetUX + 0.5f * fDetVX; const float fDetSY = gC_DetSY[angle] + 0.5f * fDetUY + 0.5f * fDetVY; const float fDetSZ = gC_DetSZ[angle] + 0.5f * fDetUZ + 0.5f * fDetVZ; const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x; if (detectorU >= dims.iProjU) return; const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV; int endDetectorV = startDetectorV + g_detBlockV; if (endDetectorV > dims.iProjV) endDetectorV = dims.iProjV; int endSlice = startSlice + g_blockSlices; if (endSlice > c.nSlices(dims)) endSlice = c.nSlices(dims); for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV) { /* Trace ray from Src to (detectorU,detectorV) from */ /* X = startSlice to X = endSlice */ const float fDetX = fDetSX + detectorU*fDetUX + detectorV*fDetVX; const float fDetY = fDetSY + detectorU*fDetUY + detectorV*fDetVY; const float fDetZ = fDetSZ + detectorU*fDetUZ + detectorV*fDetVZ; /* (x) ( 1) ( 0) */ /* ray: (y) = (ay) * x + (by) */ /* (z) (az) (bz) */ const float a1 = (c.c1(fSrcX,fSrcY,fSrcZ) - c.c1(fDetX,fDetY,fDetZ)) / (c.c0(fSrcX,fSrcY,fSrcZ) - c.c0(fDetX,fDetY,fDetZ)); const float a2 = (c.c2(fSrcX,fSrcY,fSrcZ) - c.c2(fDetX,fDetY,fDetZ)) / (c.c0(fSrcX,fSrcY,fSrcZ) - c.c0(fDetX,fDetY,fDetZ)); const float b1 = c.c1(fSrcX,fSrcY,fSrcZ) - a1 * c.c0(fSrcX,fSrcY,fSrcZ); const float b2 = c.c2(fSrcX,fSrcY,fSrcZ) - a2 * c.c0(fSrcX,fSrcY,fSrcZ); const float fDistCorr = sc.scale(a1, a2); float fVal = 0.0f; float f0 = startSlice + 0.5f; float f1 = a1 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b1 + 0.5f*c.nDim1(dims) - 0.5f + 0.5f; float f2 = a2 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b2 + 0.5f*c.nDim2(dims) - 0.5f + 0.5f; for (int s = startSlice; s < endSlice; ++s) { fVal += c.tex(tex, f0, f1, f2); f0 += 1.0f; f1 += a1; f2 += a2; } fVal *= fDistCorr; D_projData[(size_t)(detectorV*dims.iProjAngles+angle)*projPitch+detectorU] += fVal; } } template __global__ void cone_FP_SS_t(float* D_projData, unsigned int projPitch, cudaTextureObject_t tex, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions3D dims, int iRaysPerDetDim, SCALE_NONCUBE sc) { COORD c; int angle = startAngle + blockIdx.y * g_anglesPerBlock + threadIdx.y; if (angle >= endAngle) return; const float fSrcX = gC_SrcX[angle]; const float fSrcY = gC_SrcY[angle]; const float fSrcZ = gC_SrcZ[angle]; const float fDetUX = gC_DetUX[angle]; const float fDetUY = gC_DetUY[angle]; const float fDetUZ = gC_DetUZ[angle]; const float fDetVX = gC_DetVX[angle]; const float fDetVY = gC_DetVY[angle]; const float fDetVZ = gC_DetVZ[angle]; const float fDetSX = gC_DetSX[angle] + 0.5f * fDetUX + 0.5f * fDetVX; const float fDetSY = gC_DetSY[angle] + 0.5f * fDetUY + 0.5f * fDetVY; const float fDetSZ = gC_DetSZ[angle] + 0.5f * fDetUZ + 0.5f * fDetVZ; const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x; if (detectorU >= dims.iProjU) return; const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV; int endDetectorV = startDetectorV + g_detBlockV; if (endDetectorV > dims.iProjV) endDetectorV = dims.iProjV; int endSlice = startSlice + g_blockSlices; if (endSlice > c.nSlices(dims)) endSlice = c.nSlices(dims); const float fSubStep = 1.0f/iRaysPerDetDim; for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV) { /* Trace ray from Src to (detectorU,detectorV) from */ /* X = startSlice to X = endSlice */ float fV = 0.0f; float fdU = detectorU - 0.5f + 0.5f*fSubStep; for (int iSubU = 0; iSubU < iRaysPerDetDim; ++iSubU, fdU+=fSubStep) { float fdV = detectorV - 0.5f + 0.5f*fSubStep; for (int iSubV = 0; iSubV < iRaysPerDetDim; ++iSubV, fdV+=fSubStep) { const float fDetX = fDetSX + fdU*fDetUX + fdV*fDetVX; const float fDetY = fDetSY + fdU*fDetUY + fdV*fDetVY; const float fDetZ = fDetSZ + fdU*fDetUZ + fdV*fDetVZ; /* (x) ( 1) ( 0) */ /* ray: (y) = (ay) * x + (by) */ /* (z) (az) (bz) */ const float a1 = (c.c1(fSrcX,fSrcY,fSrcZ) - c.c1(fDetX,fDetY,fDetZ)) / (c.c0(fSrcX,fSrcY,fSrcZ) - c.c0(fDetX,fDetY,fDetZ)); const float a2 = (c.c2(fSrcX,fSrcY,fSrcZ) - c.c2(fDetX,fDetY,fDetZ)) / (c.c0(fSrcX,fSrcY,fSrcZ) - c.c0(fDetX,fDetY,fDetZ)); const float b1 = c.c1(fSrcX,fSrcY,fSrcZ) - a1 * c.c0(fSrcX,fSrcY,fSrcZ); const float b2 = c.c2(fSrcX,fSrcY,fSrcZ) - a2 * c.c0(fSrcX,fSrcY,fSrcZ); const float fDistCorr = sc.scale(a1, a2); float fVal = 0.0f; float f0 = startSlice + 0.5f; float f1 = a1 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b1 + 0.5f*c.nDim1(dims) - 0.5f + 0.5f; float f2 = a2 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b2 + 0.5f*c.nDim2(dims) - 0.5f + 0.5f; for (int s = startSlice; s < endSlice; ++s) { fVal += c.tex(tex, f0, f1, f2); f0 += 1.0f; f1 += a1; f2 += a2; } fVal *= fDistCorr; fV += fVal; } } D_projData[(size_t)(detectorV*dims.iProjAngles+angle)*projPitch+detectorU] += fV / (iRaysPerDetDim * iRaysPerDetDim); } } bool ConeFP_Array_internal(cudaPitchedPtr D_projData, cudaTextureObject_t D_texObj, const SDimensions3D& dims, unsigned int angleCount, const SConeProjection* angles, const SProjectorParams3D& params, cudaStream_t stream) { dim3 dimBlock(g_detBlockU, g_anglesPerBlock); // region size, angles // Run over all angles, grouping them into groups of the same // orientation (roughly horizontal vs. roughly vertical). // Start a stream of grids for each such group. unsigned int blockStart = 0; unsigned int blockEnd = 0; int blockDirection = 0; bool cube = true; if (abs(params.fVolScaleX / params.fVolScaleY - 1.0) > 0.00001) cube = false; if (abs(params.fVolScaleX / params.fVolScaleZ - 1.0) > 0.00001) cube = false; SCALE_CUBE scube; scube.fOutputScale = params.fOutputScale * params.fVolScaleX; SCALE_NONCUBE snoncubeX; float fS1 = params.fVolScaleY / params.fVolScaleX; snoncubeX.fScale1 = fS1 * fS1; float fS2 = params.fVolScaleZ / params.fVolScaleX; snoncubeX.fScale2 = fS2 * fS2; snoncubeX.fOutputScale = params.fOutputScale * params.fVolScaleX; SCALE_NONCUBE snoncubeY; fS1 = params.fVolScaleX / params.fVolScaleY; snoncubeY.fScale1 = fS1 * fS1; fS2 = params.fVolScaleZ / params.fVolScaleY; snoncubeY.fScale2 = fS2 * fS2; snoncubeY.fOutputScale = params.fOutputScale * params.fVolScaleY; SCALE_NONCUBE snoncubeZ; fS1 = params.fVolScaleX / params.fVolScaleZ; snoncubeZ.fScale1 = fS1 * fS1; fS2 = params.fVolScaleY / params.fVolScaleZ; snoncubeZ.fScale2 = fS2 * fS2; snoncubeZ.fOutputScale = params.fOutputScale * params.fVolScaleZ; // timeval t; // tic(t); for (unsigned int a = 0; a <= angleCount; ++a) { int dir = -1; if (a != angleCount) { float dX = fabsf(angles[a].fSrcX - (angles[a].fDetSX + dims.iProjU*angles[a].fDetUX*0.5f + dims.iProjV*angles[a].fDetVX*0.5f)); float dY = fabsf(angles[a].fSrcY - (angles[a].fDetSY + dims.iProjU*angles[a].fDetUY*0.5f + dims.iProjV*angles[a].fDetVY*0.5f)); float dZ = fabsf(angles[a].fSrcZ - (angles[a].fDetSZ + dims.iProjU*angles[a].fDetUZ*0.5f + dims.iProjV*angles[a].fDetVZ*0.5f)); if (dX >= dY && dX >= dZ) dir = 0; else if (dY >= dX && dY >= dZ) dir = 1; else dir = 2; } if (a == angleCount || dir != blockDirection) { // block done blockEnd = a; if (blockStart != blockEnd) { dim3 dimGrid( ((dims.iProjU+g_detBlockU-1)/g_detBlockU)*((dims.iProjV+g_detBlockV-1)/g_detBlockV), (blockEnd-blockStart+g_anglesPerBlock-1)/g_anglesPerBlock); // printf("angle block: %d to %d, %d (%dx%d, %dx%d)\n", blockStart, blockEnd, blockDirection, dimGrid.x, dimGrid.y, dimBlock.x, dimBlock.y); if (blockDirection == 0) { for (unsigned int i = 0; i < dims.iVolX; i += g_blockSlices) if (params.iRaysPerDetDim == 1) if (cube) cone_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj, i, blockStart, blockEnd, dims, scube); else cone_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj, i, blockStart, blockEnd, dims, snoncubeX); else cone_FP_SS_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj, i, blockStart, blockEnd, dims, params.iRaysPerDetDim, snoncubeX); } else if (blockDirection == 1) { for (unsigned int i = 0; i < dims.iVolY; i += g_blockSlices) if (params.iRaysPerDetDim == 1) if (cube) cone_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj, i, blockStart, blockEnd, dims, scube); else cone_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj, i, blockStart, blockEnd, dims, snoncubeY); else cone_FP_SS_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj, i, blockStart, blockEnd, dims, params.iRaysPerDetDim, snoncubeY); } else if (blockDirection == 2) { for (unsigned int i = 0; i < dims.iVolZ; i += g_blockSlices) if (params.iRaysPerDetDim == 1) if (cube) cone_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj, i, blockStart, blockEnd, dims, scube); else cone_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj, i, blockStart, blockEnd, dims, snoncubeZ); else cone_FP_SS_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj, i, blockStart, blockEnd, dims, params.iRaysPerDetDim, snoncubeZ); } } blockDirection = dir; blockStart = a; } } // printf("%f\n", toc(t)); return true; } bool ConeFP(cudaPitchedPtr D_volumeData, cudaPitchedPtr D_projData, const SDimensions3D& dims, const SConeProjection* angles, const SProjectorParams3D& params) { TransferConstantsBuffer tcbuf(g_MaxAngles); cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "ConeFP stream")) return false; // transfer volume to array cudaArray* cuArray = allocateVolumeArray(dims); if (!cuArray) { cudaStreamDestroy(stream); return false; } cudaTextureObject_t D_texObj; if (!createTextureObject3D(cuArray, D_texObj)) { cudaStreamDestroy(stream); cudaFreeArray(cuArray); return false; } if (!transferVolumeToArray(D_volumeData, cuArray, dims, stream)) { cudaDestroyTextureObject(D_texObj); cudaStreamDestroy(stream); cudaFreeArray(cuArray); return false; } bool ok = true; for (unsigned int iAngle = 0; iAngle < dims.iProjAngles; iAngle += g_MaxAngles) { unsigned int iEndAngle = iAngle + g_MaxAngles; if (iEndAngle >= dims.iProjAngles) iEndAngle = dims.iProjAngles; ok = transferConstants(angles + iAngle, iEndAngle - iAngle, tcbuf, stream); if (!ok) break; cudaPitchedPtr D_subprojData = D_projData; D_subprojData.ptr = (char*)D_projData.ptr + iAngle * D_projData.pitch; ok = ConeFP_Array_internal(D_subprojData, D_texObj, dims, iEndAngle - iAngle, angles + iAngle, params, stream); if (!ok) break; } ok &= checkCuda(cudaStreamSynchronize(stream), "ConeFP sync"); cudaDestroyTextureObject(D_texObj); cudaFreeArray(cuArray); cudaStreamDestroy(stream); return ok; } } astra-toolbox-2.3.0/cuda/3d/darthelper3d.cu000066400000000000000000000167361475635207100204750ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/util3d.h" #include "astra/cuda/3d/dims3d.h" #include "astra/cuda/3d/darthelper3d.h" #include namespace astraCUDA3d { // ------------------------------------------------------------------------------------------------------------------------------------------------------------------- __global__ void devDartSmoothing(cudaPitchedPtr out, cudaPitchedPtr in, float b, SDimensions3D dims) { unsigned int x = threadIdx.x + 16*blockIdx.x; unsigned int y = threadIdx.y + 16*blockIdx.y; // Sacrifice the border pixels to simplify the implementation. if (x > 0 && x < dims.iVolX - 1 && y > 0 && y < dims.iVolY - 1) { float* d = (float*)in.ptr; float* m = (float*)out.ptr; unsigned int index; unsigned int p = (out.pitch >> 2); for (unsigned int z = 0; z <= dims.iVolZ-1; z++) { float res = 0.0f; // bottom slice if (z > 0) { index = ((z-1)*dims.iVolY + y) * p + x; res += d[index-p-1] + d[index-p] + d[index-p+1] + d[index -1] + d[index ] + d[index +1] + d[index+p-1] + d[index+p] + d[index+p+1]; } // top slice if (z < dims.iVolZ-1) { index = ((z+1)*dims.iVolY + y) * p + x; res += d[index-p-1] + d[index-p] + d[index-p+1] + d[index -1] + d[index ] + d[index +1] + d[index+p-1] + d[index+p] + d[index+p+1]; } // same slice index = (z*dims.iVolY + y) * p + x; res += d[index-p-1] + d[index-p] + d[index-p+1] + d[index -1] + d[index +1] + d[index+p-1] + d[index+p] + d[index+p+1]; // result m[index] = (1.0f-b) * d[index] + b * 0.038461538f * res; } } } // ------------------------------------------------------------------------------------------------------------------------------------------------------------------- void dartSmoothing(float* out, const float* in, float b, unsigned int radius, SDimensions3D dims) { cudaPitchedPtr D_inData; D_inData = allocateVolumeData(dims); copyVolumeToDevice(in, D_inData, dims); cudaPitchedPtr D_outData; D_outData = allocateVolumeData(dims); copyVolumeToDevice(out, D_outData, dims); dim3 blockSize(16,16); dim3 gridSize((dims.iVolX+15)/16, (dims.iVolY+15)/16); devDartSmoothing<<>>(D_outData, D_inData, b, dims); copyVolumeFromDevice(out, D_outData, dims); cudaFree(D_outData.ptr); cudaFree(D_inData.ptr); } // ------------------------------------------------------------------------------------------------------------------------------------------------------------------- // CUDA function for the masking of DART with a radius == 1 __global__ void devDartMasking(cudaPitchedPtr mask, cudaPitchedPtr in, unsigned int conn, SDimensions3D dims) { unsigned int x = threadIdx.x + 16*blockIdx.x; unsigned int y = threadIdx.y + 16*blockIdx.y; // Sacrifice the border pixels to simplify the implementation. if (x > 0 && x < dims.iVolX - 1 && y > 0 && y < dims.iVolY - 1) { float* d = (float*)in.ptr; float* m = (float*)mask.ptr; unsigned int index; unsigned int p = (in.pitch >> 2); for (unsigned int z = 0; z <= dims.iVolZ-1; z++) { unsigned int o2 = (z*dims.iVolY + y) * p + x; m[o2] = 0.0f; // bottom slice if (z > 0) { index = ((z-1)*dims.iVolY + y) * p + x; if ((conn == 26 && (d[index-p-1] != d[o2] || d[index-p] != d[o2] || d[index-p+1] != d[o2] || d[index -1] != d[o2] || d[index ] != d[o2] || d[index +1] != d[o2] || d[index+p-1] != d[o2] || d[index+p] != d[o2] || d[index+p+1] != d[o2] )) || (conn == 6 && d[index] != d[o2])) { m[o2] = 1.0f; continue; } } // top slice if (z < dims.iVolZ-1) { index = ((z+1)*dims.iVolY + y) * p + x; if ((conn == 26 && (d[index-p-1] != d[o2] || d[index-p] != d[o2] || d[index-p+1] != d[o2] || d[index -1] != d[o2] || d[index ] != d[o2] || d[index +1] != d[o2] || d[index+p-1] != d[o2] || d[index+p] != d[o2] || d[index+p+1] != d[o2] )) || (conn == 6 && d[index] != d[o2])) { m[o2] = 1.0f; continue; } } // other slices index = (z*dims.iVolY + y) * p + x; if ((conn == 26 && (d[index-p-1] != d[o2] || d[index-p] != d[o2] || d[index-p+1] != d[o2] || d[index -1] != d[o2] || d[index +1] != d[o2] || d[index+p-1] != d[o2] || d[index+p] != d[o2] || d[index+p+1] != d[o2] )) || (conn == 6 && ( d[index-p] != d[o2] || d[index -1] != d[o2] || d[index +1] != d[o2] || d[index+p] != d[o2] ))) { m[o2] = 1.0f; continue; } } } } // ------------------------------------------------------------------------------------------------------------------------------------------------------------------- void dartMasking(float* mask, const float* segmentation, unsigned int conn, unsigned int radius, unsigned int threshold, SDimensions3D dims) { cudaPitchedPtr D_maskData; D_maskData = allocateVolumeData(dims); copyVolumeToDevice(mask, D_maskData, dims); cudaPitchedPtr D_segmentationData; D_segmentationData = allocateVolumeData(dims); copyVolumeToDevice(segmentation, D_segmentationData, dims); dim3 blockSize(16,16); dim3 gridSize((dims.iVolX+15)/16, (dims.iVolY+15)/16); if (threshold == 1 && radius == 1) devDartMasking<<>>(D_maskData, D_segmentationData, conn, dims); //else if (threshold > 1 && radius == 1) // devADartMask<<>>(D_maskData, D_segmentationData, conn, threshold, pitch, width, height, 1, 1); //else if (threshold == 1 && radius > 1) // devDartMaskRadius<<>>(D_maskData, D_segmentationData, conn, radius, pitch, width, height, 1, 1); //else // devADartMaskRadius<<>>(D_maskData, D_segmentationData, conn, radius, threshold, pitch, width, height, 1, 1); copyVolumeFromDevice(mask, D_maskData, dims); cudaFree(D_maskData.ptr); cudaFree(D_segmentationData.ptr); } // ------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool setGPUIndex(int iGPUIndex) { if (iGPUIndex != -1) { cudaSetDevice(iGPUIndex); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } return true; } } astra-toolbox-2.3.0/cuda/3d/fdk.cu000066400000000000000000000354771475635207100166630ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/util3d.h" #include "astra/cuda/3d/dims3d.h" #include "astra/cuda/3d/arith3d.h" #include "astra/cuda/3d/cone_bp.h" #include "astra/cuda/2d/fft.h" #include "astra/Logging.h" #include #include #include #include namespace astraCUDA3d { static const unsigned int g_anglesPerWeightBlock = 16; static const unsigned int g_detBlockU = 32; static const unsigned int g_detBlockV = 32; static const unsigned g_MaxAngles = 12000; __constant__ float gC_angle[g_MaxAngles]; bool checkCufft(cufftResult err, const char *msg) { if (err != CUFFT_SUCCESS) { ASTRA_ERROR("%s: CUFFT error %d.", msg, err); return false; } else { return true; } } // TODO: To support non-cube voxels, preweighting needs per-view // parameters. NB: Need to properly take into account the // anisotropic volume normalization done for that too. __global__ void devFDK_preweight(void* D_projData, unsigned int projPitch, unsigned int startAngle, unsigned int endAngle, float fSrcOrigin, float fDetOrigin, float fZShift, float fDetUSize, float fDetVSize, const SDimensions3D dims) { float* projData = (float*)D_projData; int angle = startAngle + blockIdx.y * g_anglesPerWeightBlock + threadIdx.y; if (angle >= endAngle) return; const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x; const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV; int endDetectorV = startDetectorV + g_detBlockV; if (endDetectorV > dims.iProjV) endDetectorV = dims.iProjV; // We need the length of the central ray and the length of the ray(s) to // our detector pixel(s). const float fCentralRayLength = fSrcOrigin + fDetOrigin; const float fU = (detectorU - 0.5f*dims.iProjU + 0.5f) * fDetUSize; const float fT = fCentralRayLength * fCentralRayLength + fU * fU; float fV = (startDetectorV - 0.5f*dims.iProjV + 0.5f) * fDetVSize + fZShift; // Contributions to the weighting factors: // fCentralRayLength / fRayLength : the main FDK preweighting factor // fSrcOrigin / (fDetUSize * fCentralRayLength) // : to adjust the filter to the det width // pi / (2 * iProjAngles) : scaling of the integral over angles const float fW2 = fCentralRayLength / (fDetUSize * fSrcOrigin); const float fW = fCentralRayLength * fW2 * (M_PI / 2.0f) / (float)dims.iProjAngles; for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV) { const float fRayLength = sqrtf(fT + fV * fV); const float fWeight = fW / fRayLength; projData[(detectorV*dims.iProjAngles+angle)*projPitch+detectorU] *= fWeight; fV += fDetVSize; } } __global__ void devFDK_ParkerWeight(void* D_projData, unsigned int projPitch, unsigned int startAngle, unsigned int endAngle, float fSrcOrigin, float fDetOrigin, float fDetUSize, float fCentralFanAngle, float fScale, const SDimensions3D dims) { float* projData = (float*)D_projData; int angle = startAngle + blockIdx.y * g_anglesPerWeightBlock + threadIdx.y; if (angle >= endAngle) return; const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x; const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV; int endDetectorV = startDetectorV + g_detBlockV; if (endDetectorV > dims.iProjV) endDetectorV = dims.iProjV; // We need the length of the central ray and the length of the projection // of our ray onto the central slice const float fCentralRayLength = fSrcOrigin + fDetOrigin; // TODO: Detector pixel size const float fU = (detectorU - 0.5f*dims.iProjU + 0.5f) * fDetUSize; const float fGamma = atanf(fU / fCentralRayLength); float fBeta = gC_angle[angle]; // compute the weight depending on the location in the central fan's radon // space float fWeight; if (fBeta <= 0.0f) { fWeight = 0.0f; } else if (fBeta <= 2.0f*(fCentralFanAngle + fGamma)) { fWeight = sinf((M_PI / 4.0f) * fBeta / (fCentralFanAngle + fGamma)); fWeight *= fWeight; } else if (fBeta <= M_PI + 2*fGamma) { fWeight = 1.0f; } else if (fBeta <= M_PI + 2*fCentralFanAngle) { fWeight = sinf((M_PI / 4.0f) * (M_PI + 2.0f*fCentralFanAngle - fBeta) / (fCentralFanAngle - fGamma)); fWeight *= fWeight; } else { fWeight = 0.0f; } fWeight *= fScale; for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV) { projData[(detectorV*dims.iProjAngles+angle)*projPitch+detectorU] *= fWeight; } } // Perform the FDK pre-weighting and filtering bool FDK_PreWeight(cudaPitchedPtr D_projData, float fSrcOrigin, float fDetOrigin, float fZShift, float fDetUSize, float fDetVSize, bool bShortScan, const SDimensions3D& dims, const float* angles) { // The pre-weighting factor for a ray is the cosine of the angle between // the central line and the ray. cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "FDK_PreWeight stream")) return false; dim3 dimBlock(g_detBlockU, g_anglesPerWeightBlock); dim3 dimGrid( ((dims.iProjU+g_detBlockU-1)/g_detBlockU)*((dims.iProjV+g_detBlockV-1)/g_detBlockV), (dims.iProjAngles+g_anglesPerWeightBlock-1)/g_anglesPerWeightBlock); int projPitch = D_projData.pitch/sizeof(float); devFDK_preweight<<>>(D_projData.ptr, projPitch, 0, dims.iProjAngles, fSrcOrigin, fDetOrigin, fZShift, fDetUSize, fDetVSize, dims); if (bShortScan && dims.iProjAngles > 1) { ASTRA_DEBUG("Doing Parker weighting"); // We do short-scan Parker weighting // First, determine (in a very basic way) the interval that's // been scanned. We assume angles[0] is one of the endpoints of the // range. float fdA = angles[1] - angles[0]; while (fdA < -M_PI) fdA += 2*M_PI; while (fdA >= M_PI) fdA -= 2*M_PI; float fAngleBase; if (fdA >= 0.0f) { // going up from angles[0] fAngleBase = angles[0]; ASTRA_DEBUG("Second angle >= first angle, so assuming angles are incrementing"); } else { // going down from angles[0] fAngleBase = angles[dims.iProjAngles - 1]; ASTRA_DEBUG("Second angle < first angle, so assuming angles are decrementing"); } // We pick the lowest end of the range, and then // move all angles so they fall in [0,2pi) std::vector fRelAngles(dims.iProjAngles); for (unsigned int i = 0; i < dims.iProjAngles; ++i) { float f = angles[i] - fAngleBase; while (f >= 2*M_PI) f -= 2*M_PI; while (f < 0) f += 2*M_PI; fRelAngles[i] = f; } float fRange = fabs(fRelAngles[dims.iProjAngles-1] - fRelAngles[0]); // Adjust for discretisation fRange /= dims.iProjAngles - 1; fRange *= dims.iProjAngles; ASTRA_DEBUG("Assuming angles are linearly ordered and equally spaced for Parker weighting. Angular range %f radians", fRange); float fScale = fRange / M_PI; bool ok = true; ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_angle, &fRelAngles[0], dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice, stream), "FDK_PreWeight transfer"); ok &= checkCuda(cudaStreamSynchronize(stream), "FDK_PreWeight"); if (!ok) { cudaStreamDestroy(stream); return false; } float fCentralFanAngle = fabs(atanf(fDetUSize * (dims.iProjU*0.5f) / (fSrcOrigin + fDetOrigin))); if (fRange + 1e-3 < M_PI + 2*fCentralFanAngle) { ASTRA_WARN("Angular range (%f rad) smaller than Parker weighting range (%f rad)", fRange, M_PI + 2*fCentralFanAngle); } devFDK_ParkerWeight<<>>(D_projData.ptr, projPitch, 0, dims.iProjAngles, fSrcOrigin, fDetOrigin, fDetUSize, fCentralFanAngle, fScale, dims); } if (!checkCuda(cudaStreamSynchronize(stream), "FDK_PreWeight")) { cudaStreamDestroy(stream); return false; } cudaStreamDestroy(stream); return true; } bool FDK_Filter(cudaPitchedPtr D_projData, const astra::SFilterConfig &filterConfig, const SDimensions3D& dims) { cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "FDK_Filter stream")) return false; bool singleFilter; cufftComplex *D_filter; bool ok = astraCUDA::prepareCuFFTFilter(filterConfig, D_filter, singleFilter, dims.iProjAngles, dims.iProjU, stream); if (!ok) { if (D_filter) astraCUDA::freeComplexOnDevice(D_filter); cudaStreamDestroy(stream); return false; } if (!D_filter) { // For FILTER_NONE cudaStreamDestroy(stream); return true; } int iPaddedDetCount = calcNextPowerOfTwo(2 * dims.iProjU); int iHalfFFTSize = astra::calcFFTFourierSize(iPaddedDetCount); cufftHandle planF; cufftHandle planI; if (!checkCufft(cufftPlan1d(&planF, iPaddedDetCount, CUFFT_R2C, dims.iProjAngles), "FDK filter FFT plan")) { cudaStreamDestroy(stream); astraCUDA::freeComplexOnDevice(D_filter); return false; } if (!checkCufft(cufftSetStream(planF, stream), "FDK filter FFT plan stream")) { cudaStreamDestroy(stream); astraCUDA::freeComplexOnDevice(D_filter); return false; } if (!checkCufft(cufftPlan1d(&planI, iPaddedDetCount, CUFFT_C2R, dims.iProjAngles), "FDK filter IFFT plan")) { cudaStreamDestroy(stream); astraCUDA::freeComplexOnDevice(D_filter); cufftDestroy(planF); return false; } if (!checkCufft(cufftSetStream(planI, stream), "FDK filter IFFT plan stream")) { cudaStreamDestroy(stream); astraCUDA::freeComplexOnDevice(D_filter); cufftDestroy(planF); cufftDestroy(planI); return false; } int projPitch = D_projData.pitch/sizeof(float); // We process one sinogram at a time. float* D_sinoData = (float*)D_projData.ptr; cufftComplex * D_pcSinoFFT = NULL; if (!astraCUDA::allocateComplexOnDevice(dims.iProjAngles, iHalfFFTSize, &D_pcSinoFFT)) { cudaStreamDestroy(stream); astraCUDA::freeComplexOnDevice(D_filter); cufftDestroy(planF); cufftDestroy(planI); return false; } float * D_pfPadded = NULL; size_t bufferMemSize = sizeof(float) * dims.iProjAngles * iPaddedDetCount; if (!checkCuda(cudaMalloc((void **)&D_pfPadded, bufferMemSize), "FDK filter malloc")) { cudaStreamDestroy(stream); astraCUDA::freeComplexOnDevice(D_pcSinoFFT); astraCUDA::freeComplexOnDevice(D_filter); cufftDestroy(planF); cufftDestroy(planI); return false; } ok = true; for (int v = 0; v < dims.iProjV; ++v) { if (!checkCuda(cudaMemsetAsync(D_pfPadded, 0, bufferMemSize, stream), "FDK filter memset")) { ok = false; break; } // pitched memcpy 2D to handle both source pitch and target padding if (!checkCuda(cudaMemcpy2DAsync(D_pfPadded, iPaddedDetCount*sizeof(float), D_sinoData, projPitch*sizeof(float), dims.iProjU*sizeof(float), dims.iProjAngles, cudaMemcpyDeviceToDevice, stream), "FDK filter memcpy")) { ok = false; break; } if (!checkCufft(cufftExecR2C(planF, (cufftReal *)D_pfPadded, D_pcSinoFFT), "FDK filter forward exec")) { ok = false; break; } if (!astraCUDA::applyFilter(dims.iProjAngles, iHalfFFTSize, D_pcSinoFFT, D_filter, singleFilter, stream)) { ok = false; break; } // Getting rid of the const qualifier is due to cufft API issue? if (!checkCufft(cufftExecC2R(planI, (cufftComplex *)D_pcSinoFFT, (cufftReal *)D_pfPadded), "FDK filter inverse exec")) { ok = false; break; } if (!astraCUDA::rescaleInverseFourier(dims.iProjAngles, iPaddedDetCount, D_pfPadded, stream)) { ok = false; break; } if (!checkCuda(cudaMemsetAsync(D_sinoData, 0, sizeof(float) * dims.iProjAngles * projPitch, stream), "FDK filter memset")) { ok = false; break; } // pitched memcpy 2D to handle both source padding and target pitch if (!checkCuda(cudaMemcpy2DAsync(D_sinoData, projPitch*sizeof(float), D_pfPadded, iPaddedDetCount*sizeof(float), dims.iProjU*sizeof(float), dims.iProjAngles, cudaMemcpyDeviceToDevice, stream), "FDK filter memcpy")) { ok = false; break; } D_sinoData += (dims.iProjAngles * projPitch); } ok &= checkCuda(cudaStreamSynchronize(stream), "FDK filter sync"); cufftDestroy(planF); cufftDestroy(planI); cudaFree(D_pfPadded); astraCUDA::freeComplexOnDevice(D_pcSinoFFT); astraCUDA::freeComplexOnDevice(D_filter); cudaStreamDestroy(stream); return ok; } bool FDK(cudaPitchedPtr D_volumeData, cudaPitchedPtr D_projData, const SConeProjection* angles, const SDimensions3D& dims, SProjectorParams3D params, bool bShortScan, const astra::SFilterConfig &filterConfig) { bool ok; // NB: We don't support arbitrary cone_vec geometries here. // Only those that are vertical sub-geometries // (cf. CompositeGeometryManager) of regular cone geometries. assert(dims.iProjAngles > 0); const SConeProjection& p0 = angles[0]; // assuming U is in the XY plane, V is parallel to Z axis float fDetCX = p0.fDetSX + 0.5*dims.iProjU*p0.fDetUX; float fDetCY = p0.fDetSY + 0.5*dims.iProjU*p0.fDetUY; float fDetCZ = p0.fDetSZ + 0.5*dims.iProjV*p0.fDetVZ; float fSrcOrigin = sqrt(p0.fSrcX*p0.fSrcX + p0.fSrcY*p0.fSrcY); float fDetOrigin = sqrt(fDetCX*fDetCX + fDetCY*fDetCY); float fDetUSize = sqrt(p0.fDetUX*p0.fDetUX + p0.fDetUY*p0.fDetUY); float fDetVSize = abs(p0.fDetVZ); float fZShift = fDetCZ - p0.fSrcZ; float *pfAngles = new float[dims.iProjAngles]; for (unsigned int i = 0; i < dims.iProjAngles; ++i) { // FIXME: Sign/order pfAngles[i] = -atan2(angles[i].fSrcX, angles[i].fSrcY) + M_PI; } #if 1 ok = FDK_PreWeight(D_projData, fSrcOrigin, fDetOrigin, fZShift, fDetUSize, fDetVSize, bShortScan, dims, pfAngles); #else ok = true; #endif delete[] pfAngles; if (!ok) return false; #if 1 // Perform filtering ok = FDK_Filter(D_projData, filterConfig, dims); #endif if (!ok) return false; // Perform BP assert(params.projKernel == ker3d_fdk_weighting); //ok = FDK_BP(D_volumeData, D_projData, fSrcOrigin, fDetOrigin, 0.0f, 0.0f, fDetUSize, fDetVSize, dims, pfAngles); ok = ConeBP(D_volumeData, D_projData, dims, angles, params); if (!ok) return false; return true; } } astra-toolbox-2.3.0/cuda/3d/mem3d.cu000066400000000000000000000245451475635207100171160ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/util3d.h" #include "astra/cuda/3d/mem3d.h" #include "astra/cuda/3d/astra3d.h" #include "astra/cuda/3d/cone_fp.h" #include "astra/cuda/3d/cone_bp.h" #include "astra/cuda/3d/par3d_fp.h" #include "astra/cuda/3d/par3d_bp.h" #include "astra/cuda/3d/fdk.h" #include "astra/cuda/2d/astra.h" #include "astra/Logging.h" #include "astra/Filters.h" #include #include namespace astraCUDA3d { struct SMemHandle3D_internal { cudaPitchedPtr ptr; cudaArray *arr; unsigned int nx; unsigned int ny; unsigned int nz; }; int maxBlockDimension() { int dev; if (!checkCuda(cudaGetDevice(&dev), "maxBlockDimension getDevice")) { ASTRA_WARN("Error querying device"); return 0; } cudaDeviceProp props; if (!checkCuda(cudaGetDeviceProperties(&props, dev), "maxBlockDimension getDviceProps")) { ASTRA_WARN("Error querying device %d properties", dev); return 0; } return std::min(props.maxTexture3D[0], std::min(props.maxTexture3D[1], props.maxTexture3D[2])); } MemHandle3D allocateGPUMemory(unsigned int x, unsigned int y, unsigned int z, Mem3DZeroMode zero) { SMemHandle3D_internal hnd; hnd.nx = x; hnd.ny = y; hnd.nz = z; hnd.arr = 0; size_t free = astraCUDA::availableGPUMemory(); if (!checkCuda(cudaMalloc3D(&hnd.ptr, make_cudaExtent(sizeof(float)*x, y, z)), "allocateGPUMemory malloc3d")) { return MemHandle3D(); } size_t free2 = astraCUDA::availableGPUMemory(); ASTRA_DEBUG("Allocated %d x %d x %d on GPU. (Pre: %lu, post: %lu)", x, y, z, free, free2); if (zero == INIT_ZERO) { if (!checkCuda(cudaMemset3D(hnd.ptr, 0, make_cudaExtent(sizeof(float)*x, y, z)), "allocateGPUMemory memset3d")) { cudaFree(hnd.ptr.ptr); return MemHandle3D(); } } MemHandle3D ret; ret.d = std::make_shared(); *ret.d = hnd; return ret; } bool zeroGPUMemory(MemHandle3D &handle, unsigned int x, unsigned int y, unsigned int z) { SMemHandle3D_internal& hnd = *handle.d.get(); assert(!hnd.arr); return checkCuda(cudaMemset3D(hnd.ptr, 0, make_cudaExtent(sizeof(float)*x, y, z)), "zeroGPUMemory"); } bool freeGPUMemory(MemHandle3D &handle) { size_t free = astraCUDA::availableGPUMemory(); bool ok; if (handle.d->arr) ok = checkCuda(cudaFreeArray(handle.d->arr), "freeGPUMemory array"); else ok = checkCuda(cudaFree(handle.d->ptr.ptr), "freeGPUMemory"); size_t free2 = astraCUDA::availableGPUMemory(); ASTRA_DEBUG("Freeing memory. (Pre: %lu, post: %lu)", free, free2); return ok; } bool copyToGPUMemory(const float *src, MemHandle3D &dst, const SSubDimensions3D &pos) { ASTRA_DEBUG("Copying %d x %d x %d to GPU", pos.subnx, pos.subny, pos.subnz); ASTRA_DEBUG("Offset %d,%d,%d", pos.subx, pos.suby, pos.subz); assert(!dst.d->arr); cudaPitchedPtr s; s.ptr = (void*)src; // const cast away s.pitch = pos.pitch * sizeof(float); s.xsize = pos.nx * sizeof(float); s.ysize = pos.ny; ASTRA_DEBUG("Pitch %zu, xsize %zu, ysize %zu", s.pitch, s.xsize, s.ysize); cudaMemcpy3DParms p; p.srcArray = 0; p.srcPos = make_cudaPos(pos.subx * sizeof(float), pos.suby, pos.subz); p.srcPtr = s; p.dstArray = 0; p.dstPos = make_cudaPos(0, 0, 0); p.dstPtr = dst.d->ptr; p.extent = make_cudaExtent(pos.subnx * sizeof(float), pos.subny, pos.subnz); p.kind = cudaMemcpyHostToDevice; return checkCuda(cudaMemcpy3D(&p), "copyToGPUMemory"); } bool copyFromGPUMemory(float *dst, MemHandle3D &src, const SSubDimensions3D &pos) { ASTRA_DEBUG("Copying %d x %d x %d from GPU", pos.subnx, pos.subny, pos.subnz); ASTRA_DEBUG("Offset %d,%d,%d", pos.subx, pos.suby, pos.subz); assert(!src.d->arr); cudaPitchedPtr d; d.ptr = (void*)dst; d.pitch = pos.pitch * sizeof(float); d.xsize = pos.nx * sizeof(float); d.ysize = pos.ny; ASTRA_DEBUG("Pitch %zu, xsize %zu, ysize %zu", d.pitch, d.xsize, d.ysize); cudaMemcpy3DParms p; p.srcPos = make_cudaPos(0, 0, 0); p.dstArray = 0; p.dstPos = make_cudaPos(pos.subx * sizeof(float), pos.suby, pos.subz); p.dstPtr = d; if (src.d->ptr.ptr) { p.srcArray = 0; p.srcPtr = src.d->ptr; p.extent = make_cudaExtent(pos.subnx * sizeof(float), pos.subny, pos.subnz); } else { p.srcArray = src.d->arr; p.srcPtr.ptr = 0; p.extent = make_cudaExtent(pos.subnx, pos.subny, pos.subnz); } p.kind = cudaMemcpyDeviceToHost; return checkCuda(cudaMemcpy3D(&p), "copyFromGPUMemory"); } bool FP(const astra::CProjectionGeometry3D* pProjGeom, MemHandle3D &projData, const astra::CVolumeGeometry3D* pVolGeom, MemHandle3D &volData, int iDetectorSuperSampling, astra::Cuda3DProjectionKernel projKernel) { assert(!projData.d->arr); assert(!volData.d->arr); SDimensions3D dims; SProjectorParams3D params; params.projKernel = projKernel; bool ok = convertAstraGeometry_dims(pVolGeom, pProjGeom, dims); if (!ok) return false; params.iRaysPerDetDim = iDetectorSuperSampling; if (iDetectorSuperSampling == 0) return false; auto res = convertAstraGeometry(pVolGeom, pProjGeom, params); if (res.isParallel()) { const SPar3DProjection* pParProjs = res.getParallel(); switch (projKernel) { case ker3d_default: case ker3d_2d_weighting: ok &= Par3DFP(volData.d->ptr, projData.d->ptr, dims, pParProjs, params); break; case ker3d_sum_square_weights: ok &= Par3DFP_SumSqW(volData.d->ptr, projData.d->ptr, dims, pParProjs, params); break; default: ok = false; } } else if (res.isCone()) { const SConeProjection* pConeProjs = res.getCone(); switch (projKernel) { case ker3d_default: case ker3d_fdk_weighting: case ker3d_2d_weighting: ok &= ConeFP(volData.d->ptr, projData.d->ptr, dims, pConeProjs, params); break; default: ok = false; } } else { ok = false; } return ok; } bool BP(const astra::CProjectionGeometry3D* pProjGeom, MemHandle3D &projData, const astra::CVolumeGeometry3D* pVolGeom, MemHandle3D &volData, int iVoxelSuperSampling, astra::Cuda3DProjectionKernel projKernel) { assert(!volData.d->arr); SDimensions3D dims; SProjectorParams3D params; params.projKernel = projKernel; bool ok = convertAstraGeometry_dims(pVolGeom, pProjGeom, dims); if (!ok) return false; params.iRaysPerVoxelDim = iVoxelSuperSampling; auto res = convertAstraGeometry(pVolGeom, pProjGeom, params); if (res.isParallel()) { const SPar3DProjection* pParProjs = res.getParallel(); switch (projKernel) { case ker3d_default: case ker3d_2d_weighting: if (projData.d->arr) ok &= Par3DBP_Array(volData.d->ptr, projData.d->arr, dims, pParProjs, params); else ok &= Par3DBP(volData.d->ptr, projData.d->ptr, dims, pParProjs, params); break; default: ok = false; } } else if (res.isCone()) { const SConeProjection* pConeProjs = res.getCone(); switch (projKernel) { case ker3d_default: case ker3d_fdk_weighting: case ker3d_2d_weighting: if (projData.d->arr) ok &= ConeBP_Array(volData.d->ptr, projData.d->arr, dims, pConeProjs, params); else ok &= ConeBP(volData.d->ptr, projData.d->ptr, dims, pConeProjs, params); break; default: ok = false; } } else { ok = false; } return ok; } bool FDK(const astra::CProjectionGeometry3D* pProjGeom, MemHandle3D &projData, const astra::CVolumeGeometry3D* pVolGeom, MemHandle3D &volData, bool bShortScan, const astra::SFilterConfig &filterConfig, float fOutputScale) { assert(!projData.d->arr); assert(!volData.d->arr); SDimensions3D dims; SProjectorParams3D params; params.fOutputScale = fOutputScale; params.projKernel = ker3d_fdk_weighting; bool ok = convertAstraGeometry_dims(pVolGeom, pProjGeom, dims); if (!ok) return false; astra::Geometry3DParameters res = convertAstraGeometry(pVolGeom, pProjGeom, params); if (!res.isCone()) return false; const SConeProjection* pConeProjs = res.getCone(); ok &= FDK(volData.d->ptr, projData.d->ptr, pConeProjs, dims, params, bShortScan, filterConfig); return ok; } _AstraExport MemHandle3D wrapHandle(float *D_ptr, unsigned int x, unsigned int y, unsigned int z, unsigned int pitch) { cudaPitchedPtr ptr; ptr.ptr = D_ptr; ptr.xsize = sizeof(float) * x; ptr.pitch = sizeof(float) * pitch; ptr.ysize = y; SMemHandle3D_internal h; h.ptr = ptr; h.arr = 0; MemHandle3D hnd; hnd.d = std::make_shared(); *hnd.d = h; return hnd; } MemHandle3D createProjectionArrayHandle(const float *ptr, unsigned int x, unsigned int y, unsigned int z) { SDimensions3D dims; dims.iProjU = x; dims.iProjAngles = y; dims.iProjV = z; cudaArray* cuArray = allocateProjectionArray(dims); transferHostProjectionsToArray(ptr, cuArray, dims); SMemHandle3D_internal h; h.arr = cuArray; h.ptr.ptr = 0; MemHandle3D hnd; hnd.d = std::make_shared(); *hnd.d = h; return hnd; } bool copyIntoArray(MemHandle3D &handle, MemHandle3D &subdata, const SSubDimensions3D &pos) { assert(handle.d->arr); assert(!handle.d->ptr.ptr); assert(!subdata.d->arr); assert(subdata.d->ptr.ptr); ASTRA_DEBUG("Copying %d x %d x %d into GPU array", pos.subnx, pos.subny, pos.subnz); ASTRA_DEBUG("Offset %d,%d,%d", pos.subx, pos.suby, pos.subz); ASTRA_DEBUG("Pitch %zu, xsize %zu, ysize %zu", subdata.d->ptr.pitch, subdata.d->ptr.xsize, subdata.d->ptr.ysize); cudaMemcpy3DParms p; p.srcArray = 0; p.srcPos = make_cudaPos(0, 0, 0); p.srcPtr = subdata.d->ptr; p.dstArray = handle.d->arr; p.dstPos = make_cudaPos(pos.subx, pos.suby, pos.subz); p.dstPtr.ptr = 0; p.extent = make_cudaExtent(pos.subnx, pos.subny, pos.subnz); p.kind = cudaMemcpyHostToDevice; return checkCuda(cudaMemcpy3D(&p), "copyIntoArray"); } } astra-toolbox-2.3.0/cuda/3d/par3d_bp.cu000066400000000000000000000242251475635207100175760ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/util3d.h" #include "astra/cuda/3d/dims3d.h" #include #include #include namespace astraCUDA3d { static const unsigned int g_volBlockZ = 6; static const unsigned int g_anglesPerBlock = 32; static const unsigned int g_volBlockX = 16; static const unsigned int g_volBlockY = 32; static const unsigned g_MaxAngles = 1024; struct DevPar3DParams { float4 fNumU; float4 fNumV; }; __constant__ DevPar3DParams gC_C[g_MaxAngles]; __constant__ float gC_scale[g_MaxAngles]; template __global__ void dev_par3D_BP(void* D_volData, unsigned int volPitch, cudaTextureObject_t tex, int startAngle, int angleOffset, const SDimensions3D dims, float fOutputScale) { float* volData = (float*)D_volData; int endAngle = startAngle + g_anglesPerBlock; if (endAngle > dims.iProjAngles - angleOffset) endAngle = dims.iProjAngles - angleOffset; // threadIdx: x = rel x // y = rel y // blockIdx: x = x + y // y = z const int X = blockIdx.x % ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockX + threadIdx.x; const int Y = blockIdx.x / ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockY + threadIdx.y; if (X >= dims.iVolX) return; if (Y >= dims.iVolY) return; const int startZ = blockIdx.y * g_volBlockZ; float fX = X - 0.5f*dims.iVolX + 0.5f; float fY = Y - 0.5f*dims.iVolY + 0.5f; float fZ = startZ - 0.5f*dims.iVolZ + 0.5f; float Z[ZSIZE]; for(int i=0; i < ZSIZE; i++) Z[i] = 0.0f; { float fAngle = startAngle + angleOffset + 0.5f; for (int angle = startAngle; angle < endAngle; ++angle, fAngle += 1.0f) { float4 fCu = gC_C[angle].fNumU; float4 fCv = gC_C[angle].fNumV; float fS = gC_scale[angle]; float fU = fCu.w + fX * fCu.x + fY * fCu.y + fZ * fCu.z; float fV = fCv.w + fX * fCv.x + fY * fCv.y + fZ * fCv.z; for (int idx = 0; idx < ZSIZE; ++idx) { float fVal = tex3D(tex, fU, fAngle, fV); Z[idx] += fVal * fS; fU += fCu.z; fV += fCv.z; } } } int endZ = ZSIZE; if (endZ > dims.iVolZ - startZ) endZ = dims.iVolZ - startZ; for(int i=0; i < endZ; i++) volData[(size_t)((startZ+i)*dims.iVolY+Y)*volPitch+X] += Z[i] * fOutputScale; } // supersampling version __global__ void dev_par3D_BP_SS(void* D_volData, unsigned int volPitch, cudaTextureObject_t tex, int startAngle, int angleOffset, const SDimensions3D dims, int iRaysPerVoxelDim, float fOutputScale) { float* volData = (float*)D_volData; int endAngle = startAngle + g_anglesPerBlock; if (endAngle > dims.iProjAngles - angleOffset) endAngle = dims.iProjAngles - angleOffset; // threadIdx: x = rel x // y = rel y // blockIdx: x = x + y // y = z // TO TRY: precompute part of detector intersection formulas in shared mem? // TO TRY: inner loop over z, gather ray values in shared mem const int X = blockIdx.x % ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockX + threadIdx.x; const int Y = blockIdx.x / ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockY + threadIdx.y; if (X >= dims.iVolX) return; if (Y >= dims.iVolY) return; const int startZ = blockIdx.y * g_volBlockZ; int endZ = startZ + g_volBlockZ; if (endZ > dims.iVolZ) endZ = dims.iVolZ; float fX = X - 0.5f*dims.iVolX + 0.5f - 0.5f + 0.5f/iRaysPerVoxelDim; float fY = Y - 0.5f*dims.iVolY + 0.5f - 0.5f + 0.5f/iRaysPerVoxelDim; float fZ = startZ - 0.5f*dims.iVolZ + 0.5f - 0.5f + 0.5f/iRaysPerVoxelDim; const float fSubStep = 1.0f/iRaysPerVoxelDim; fOutputScale /= (iRaysPerVoxelDim*iRaysPerVoxelDim*iRaysPerVoxelDim); for (int Z = startZ; Z < endZ; ++Z, fZ += 1.0f) { float fVal = 0.0f; float fAngle = startAngle + angleOffset + 0.5f; for (int angle = startAngle; angle < endAngle; ++angle, fAngle += 1.0f) { float4 fCu = gC_C[angle].fNumU; float4 fCv = gC_C[angle].fNumV; float fS = gC_scale[angle]; float fXs = fX; for (int iSubX = 0; iSubX < iRaysPerVoxelDim; ++iSubX) { float fYs = fY; for (int iSubY = 0; iSubY < iRaysPerVoxelDim; ++iSubY) { float fZs = fZ; for (int iSubZ = 0; iSubZ < iRaysPerVoxelDim; ++iSubZ) { const float fU = fCu.w + fXs * fCu.x + fYs * fCu.y + fZs * fCu.z; const float fV = fCv.w + fXs * fCv.x + fYs * fCv.y + fZs * fCv.z; fVal += tex3D(tex, fU, fAngle, fV) * fS; fZs += fSubStep; } fYs += fSubStep; } fXs += fSubStep; } } volData[(size_t)(Z*dims.iVolY+Y)*volPitch+X] += fVal * fOutputScale; } } using TransferConstantsBuffer = TransferConstantsBuffer_t; bool transferConstants(const SPar3DProjection* angles, unsigned int iProjAngles, const SProjectorParams3D& params, TransferConstantsBuffer& buf, cudaStream_t stream) { DevPar3DParams *p = &(std::get<0>(buf.d))[0]; float *s = &(std::get<1>(buf.d))[0]; // We use an event to assure that the previous transferConstants has completed before // re-using the buffer. (Even if it is very unlikely that it hasn't.) bool ok = checkCuda(cudaStreamWaitEvent(stream, buf.event, 0), "transferConstants wait"); for (unsigned int i = 0; i < iProjAngles; ++i) { Vec3 u(angles[i].fDetUX, angles[i].fDetUY, angles[i].fDetUZ); Vec3 v(angles[i].fDetVX, angles[i].fDetVY, angles[i].fDetVZ); Vec3 r(angles[i].fRayX, angles[i].fRayY, angles[i].fRayZ); Vec3 d(angles[i].fDetSX, angles[i].fDetSY, angles[i].fDetSZ); double fDen = det3(r,u,v); p[i].fNumU.x = -det3x(r,v) / fDen; p[i].fNumU.y = -det3y(r,v) / fDen; p[i].fNumU.z = -det3z(r,v) / fDen; p[i].fNumU.w = -det3(r,d,v) / fDen; p[i].fNumV.x = det3x(r,u) / fDen; p[i].fNumV.y = det3y(r,u) / fDen; p[i].fNumV.z = det3z(r,u) / fDen; p[i].fNumV.w = det3(r,d,u) / fDen; if (params.projKernel == ker3d_2d_weighting) { // We set the scale here to approximate the adjoint // of a 2d parallel beam kernel. To be used when only // operating on a single slice. Vec3 ev(0, 0, 1); s[i] = 1.0 / scaled_cross3(u,ev,Vec3(params.fVolScaleX,params.fVolScaleY,params.fVolScaleZ)).norm(); } else { s[i] = 1.0 / scaled_cross3(u,v,Vec3(params.fVolScaleX,params.fVolScaleY,params.fVolScaleZ)).norm(); } } ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_C, p, iProjAngles*sizeof(DevPar3DParams), 0, cudaMemcpyHostToDevice, stream), "transferConstants transfer C"); ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_scale, s, iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice, stream), "transferConstants transfer scale"); ok &= checkCuda(cudaEventRecord(buf.event, stream), "transferConstants event"); return ok; } bool Par3DBP_Array(cudaPitchedPtr D_volumeData, cudaArray *D_projArray, const SDimensions3D& dims, const SPar3DProjection* angles, const SProjectorParams3D& params) { TransferConstantsBuffer tcbuf(g_MaxAngles); cudaTextureObject_t D_texObj; if (!createTextureObject3D(D_projArray, D_texObj)) return false; cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "Par3DBP_Array stream")) { cudaDestroyTextureObject(D_texObj); return false; } float fOutputScale = params.fOutputScale * params.fVolScaleX * params.fVolScaleY * params.fVolScaleZ; bool ok = true; for (unsigned int th = 0; th < dims.iProjAngles; th += g_MaxAngles) { unsigned int angleCount = g_MaxAngles; if (th + angleCount > dims.iProjAngles) angleCount = dims.iProjAngles - th; ok = transferConstants(angles, angleCount, params, tcbuf, stream); if (!ok) break; dim3 dimBlock(g_volBlockX, g_volBlockY); dim3 dimGrid(((dims.iVolX+g_volBlockX-1)/g_volBlockX)*((dims.iVolY+g_volBlockY-1)/g_volBlockY), (dims.iVolZ+g_volBlockZ-1)/g_volBlockZ); // timeval t; // tic(t); for (unsigned int i = 0; i < angleCount; i += g_anglesPerBlock) { // printf("Calling BP: %d, %dx%d, %dx%d to %p\n", i, dimBlock.x, dimBlock.y, dimGrid.x, dimGrid.y, (void*)D_volumeData.ptr); if (params.iRaysPerVoxelDim == 1) { if (dims.iVolZ == 1) { dev_par3D_BP<1><<>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), D_texObj, i, th, dims, fOutputScale); } else { dev_par3D_BP<<>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), D_texObj, i, th, dims, fOutputScale); } } else dev_par3D_BP_SS<<>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), D_texObj, i, th, dims, params.iRaysPerVoxelDim, fOutputScale); } // After kernels are done, signal we're ready to transfer new constants ok = checkCuda(cudaEventRecord(tcbuf.event, stream), "Par3DBP event"); if (!ok) break; angles = angles + angleCount; // printf("%f\n", toc(t)); } ok = checkCuda(cudaStreamSynchronize(stream), "Par3DBP sync"); cudaDestroyTextureObject(D_texObj); cudaStreamDestroy(stream); return ok; } bool Par3DBP(cudaPitchedPtr D_volumeData, cudaPitchedPtr D_projData, const SDimensions3D& dims, const SPar3DProjection* angles, const SProjectorParams3D& params) { // transfer projections to array cudaArray* cuArray = allocateProjectionArray(dims); if (!cuArray) return false; if (!transferProjectionsToArray(D_projData, cuArray, dims)) { cudaFreeArray(cuArray); return false; } bool ret = Par3DBP_Array(D_volumeData, cuArray, dims, angles, params); cudaFreeArray(cuArray); return ret; } } astra-toolbox-2.3.0/cuda/3d/par3d_fp.cu000066400000000000000000000631161475635207100176040ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/util3d.h" #include "astra/cuda/3d/dims3d.h" #include #include #include namespace astraCUDA3d { static const unsigned int g_anglesPerBlock = 4; // thickness of the slices we're splitting the volume up into static const unsigned int g_blockSlices = 32; static const unsigned int g_detBlockU = 32; static const unsigned int g_detBlockV = 32; static const unsigned g_MaxAngles = 1024; __constant__ float gC_RayX[g_MaxAngles]; __constant__ float gC_RayY[g_MaxAngles]; __constant__ float gC_RayZ[g_MaxAngles]; __constant__ float gC_DetSX[g_MaxAngles]; __constant__ float gC_DetSY[g_MaxAngles]; __constant__ float gC_DetSZ[g_MaxAngles]; __constant__ float gC_DetUX[g_MaxAngles]; __constant__ float gC_DetUY[g_MaxAngles]; __constant__ float gC_DetUZ[g_MaxAngles]; __constant__ float gC_DetVX[g_MaxAngles]; __constant__ float gC_DetVY[g_MaxAngles]; __constant__ float gC_DetVZ[g_MaxAngles]; // x=0, y=1, z=2 struct DIR_X { __device__ float nSlices(const SDimensions3D& dims) const { return dims.iVolX; } __device__ float nDim1(const SDimensions3D& dims) const { return dims.iVolY; } __device__ float nDim2(const SDimensions3D& dims) const { return dims.iVolZ; } __device__ float c0(float x, float y, float z) const { return x; } __device__ float c1(float x, float y, float z) const { return y; } __device__ float c2(float x, float y, float z) const { return z; } __device__ float tex(cudaTextureObject_t tex, float f0, float f1, float f2) const { return tex3D(tex, f0, f1, f2); } __device__ float x(float f0, float f1, float f2) const { return f0; } __device__ float y(float f0, float f1, float f2) const { return f1; } __device__ float z(float f0, float f1, float f2) const { return f2; } }; // y=0, x=1, z=2 struct DIR_Y { __device__ float nSlices(const SDimensions3D& dims) const { return dims.iVolY; } __device__ float nDim1(const SDimensions3D& dims) const { return dims.iVolX; } __device__ float nDim2(const SDimensions3D& dims) const { return dims.iVolZ; } __device__ float c0(float x, float y, float z) const { return y; } __device__ float c1(float x, float y, float z) const { return x; } __device__ float c2(float x, float y, float z) const { return z; } __device__ float tex(cudaTextureObject_t tex, float f0, float f1, float f2) const { return tex3D(tex, f1, f0, f2); } __device__ float x(float f0, float f1, float f2) const { return f1; } __device__ float y(float f0, float f1, float f2) const { return f0; } __device__ float z(float f0, float f1, float f2) const { return f2; } }; // z=0, x=1, y=2 struct DIR_Z { __device__ float nSlices(const SDimensions3D& dims) const { return dims.iVolZ; } __device__ float nDim1(const SDimensions3D& dims) const { return dims.iVolX; } __device__ float nDim2(const SDimensions3D& dims) const { return dims.iVolY; } __device__ float c0(float x, float y, float z) const { return z; } __device__ float c1(float x, float y, float z) const { return x; } __device__ float c2(float x, float y, float z) const { return y; } __device__ float tex(cudaTextureObject_t tex, float f0, float f1, float f2) const { return tex3D(tex, f1, f2, f0); } __device__ float x(float f0, float f1, float f2) const { return f1; } __device__ float y(float f0, float f1, float f2) const { return f2; } __device__ float z(float f0, float f1, float f2) const { return f0; } }; struct SCALE_CUBE { float fOutputScale; __device__ float scale(float a1, float a2) const { return sqrt(a1*a1+a2*a2+1.0f) * fOutputScale; } }; struct SCALE_NONCUBE { float fScale1; float fScale2; float fOutputScale; __device__ float scale(float a1, float a2) const { return sqrt(a1*a1*fScale1+a2*a2*fScale2+1.0f) * fOutputScale; } }; using TransferConstantsBuffer = TransferConstantsBuffer_t; bool transferConstants(const SPar3DProjection* angles, unsigned int iProjAngles, TransferConstantsBuffer& buf, cudaStream_t stream) { float* tmp = &(std::get<0>(buf.d))[0]; // We use an event to assure that the previous transferConstants has completed before // re-using the buffer. (Even if it is very unlikely that it hasn't.) bool ok = checkCuda(cudaStreamWaitEvent(stream, buf.event, 0), "transferConstants wait"); #define TRANSFER_TO_CONSTANT(name) do { for (unsigned int i = 0; i < iProjAngles; ++i) tmp[i] = angles[i].f##name ; ok &= checkCuda(cudaMemcpyToSymbolAsync(gC_##name, tmp, iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice, stream), "transferConstants transfer"); } while (0) TRANSFER_TO_CONSTANT(RayX); TRANSFER_TO_CONSTANT(RayY); TRANSFER_TO_CONSTANT(RayZ); TRANSFER_TO_CONSTANT(DetSX); TRANSFER_TO_CONSTANT(DetSY); TRANSFER_TO_CONSTANT(DetSZ); TRANSFER_TO_CONSTANT(DetUX); TRANSFER_TO_CONSTANT(DetUY); TRANSFER_TO_CONSTANT(DetUZ); TRANSFER_TO_CONSTANT(DetVX); TRANSFER_TO_CONSTANT(DetVY); TRANSFER_TO_CONSTANT(DetVZ); #undef TRANSFER_TO_CONSTANT return true; } // threadIdx: x = u detector // y = relative angle // blockIdx: x = u/v detector // y = angle block template __global__ void par3D_FP_t(float* D_projData, unsigned int projPitch, cudaTextureObject_t tex, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions3D dims, SCALE sc) { COORD c; int angle = startAngle + blockIdx.y * g_anglesPerBlock + threadIdx.y; if (angle >= endAngle) return; const float fRayX = gC_RayX[angle]; const float fRayY = gC_RayY[angle]; const float fRayZ = gC_RayZ[angle]; const float fDetUX = gC_DetUX[angle]; const float fDetUY = gC_DetUY[angle]; const float fDetUZ = gC_DetUZ[angle]; const float fDetVX = gC_DetVX[angle]; const float fDetVY = gC_DetVY[angle]; const float fDetVZ = gC_DetVZ[angle]; const float fDetSX = gC_DetSX[angle] + 0.5f * fDetUX + 0.5f * fDetVX; const float fDetSY = gC_DetSY[angle] + 0.5f * fDetUY + 0.5f * fDetVY; const float fDetSZ = gC_DetSZ[angle] + 0.5f * fDetUZ + 0.5f * fDetVZ; const float a1 = c.c1(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ); const float a2 = c.c2(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ); const float fDistCorr = sc.scale(a1, a2); const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x; if (detectorU >= dims.iProjU) return; const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV; int endDetectorV = startDetectorV + g_detBlockV; if (endDetectorV > dims.iProjV) endDetectorV = dims.iProjV; int endSlice = startSlice + g_blockSlices; if (endSlice > c.nSlices(dims)) endSlice = c.nSlices(dims); for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV) { /* Trace ray in direction Ray to (detectorU,detectorV) from */ /* X = startSlice to X = endSlice */ const float fDetX = fDetSX + detectorU*fDetUX + detectorV*fDetVX; const float fDetY = fDetSY + detectorU*fDetUY + detectorV*fDetVY; const float fDetZ = fDetSZ + detectorU*fDetUZ + detectorV*fDetVZ; /* (x) ( 1) ( 0) */ /* ray: (y) = (ay) * x + (by) */ /* (z) (az) (bz) */ const float b1 = c.c1(fDetX,fDetY,fDetZ) - a1 * c.c0(fDetX,fDetY,fDetZ); const float b2 = c.c2(fDetX,fDetY,fDetZ) - a2 * c.c0(fDetX,fDetY,fDetZ); float fVal = 0.0f; float f0 = startSlice + 0.5f; float f1 = a1 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b1 + 0.5f*c.nDim1(dims) - 0.5f + 0.5f; float f2 = a2 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b2 + 0.5f*c.nDim2(dims) - 0.5f + 0.5f; for (int s = startSlice; s < endSlice; ++s) { fVal += c.tex(tex, f0, f1, f2); f0 += 1.0f; f1 += a1; f2 += a2; } fVal *= fDistCorr; D_projData[(size_t)(detectorV*dims.iProjAngles+angle)*projPitch+detectorU] += fVal; } } // Supersampling version template __global__ void par3D_FP_SS_t(float* D_projData, unsigned int projPitch, cudaTextureObject_t tex, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions3D dims, int iRaysPerDetDim, SCALE_NONCUBE sc) { COORD c; int angle = startAngle + blockIdx.y * g_anglesPerBlock + threadIdx.y; if (angle >= endAngle) return; const float fRayX = gC_RayX[angle]; const float fRayY = gC_RayY[angle]; const float fRayZ = gC_RayZ[angle]; const float fDetUX = gC_DetUX[angle]; const float fDetUY = gC_DetUY[angle]; const float fDetUZ = gC_DetUZ[angle]; const float fDetVX = gC_DetVX[angle]; const float fDetVY = gC_DetVY[angle]; const float fDetVZ = gC_DetVZ[angle]; const float fDetSX = gC_DetSX[angle] + 0.5f * fDetUX + 0.5f * fDetVX; const float fDetSY = gC_DetSY[angle] + 0.5f * fDetUY + 0.5f * fDetVY; const float fDetSZ = gC_DetSZ[angle] + 0.5f * fDetUZ + 0.5f * fDetVZ; const float a1 = c.c1(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ); const float a2 = c.c2(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ); const float fDistCorr = sc.scale(a1, a2); const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x; if (detectorU >= dims.iProjU) return; const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV; int endDetectorV = startDetectorV + g_detBlockV; if (endDetectorV > dims.iProjV) endDetectorV = dims.iProjV; int endSlice = startSlice + g_blockSlices; if (endSlice > c.nSlices(dims)) endSlice = c.nSlices(dims); const float fSubStep = 1.0f/iRaysPerDetDim; for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV) { float fV = 0.0f; float fdU = detectorU - 0.5f + 0.5f*fSubStep; for (int iSubU = 0; iSubU < iRaysPerDetDim; ++iSubU, fdU+=fSubStep) { float fdV = detectorV - 0.5f + 0.5f*fSubStep; for (int iSubV = 0; iSubV < iRaysPerDetDim; ++iSubV, fdV+=fSubStep) { /* Trace ray in direction Ray to (detectorU,detectorV) from */ /* X = startSlice to X = endSlice */ const float fDetX = fDetSX + fdU*fDetUX + fdV*fDetVX; const float fDetY = fDetSY + fdU*fDetUY + fdV*fDetVY; const float fDetZ = fDetSZ + fdU*fDetUZ + fdV*fDetVZ; /* (x) ( 1) ( 0) */ /* ray: (y) = (ay) * x + (by) */ /* (z) (az) (bz) */ const float b1 = c.c1(fDetX,fDetY,fDetZ) - a1 * c.c0(fDetX,fDetY,fDetZ); const float b2 = c.c2(fDetX,fDetY,fDetZ) - a2 * c.c0(fDetX,fDetY,fDetZ); float fVal = 0.0f; float f0 = startSlice + 0.5f; float f1 = a1 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b1 + 0.5f*c.nDim1(dims) - 0.5f + 0.5f; float f2 = a2 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b2 + 0.5f*c.nDim2(dims) - 0.5f + 0.5f; for (int s = startSlice; s < endSlice; ++s) { fVal += c.tex(tex, f0, f1, f2); f0 += 1.0f; f1 += a1; f2 += a2; } fV += fVal; } } fV *= fDistCorr; D_projData[(size_t)(detectorV*dims.iProjAngles+angle)*projPitch+detectorU] += fV / (iRaysPerDetDim * iRaysPerDetDim); } } __device__ float dirWeights(float fX, float fN) { if (fX <= -0.5f) // outside image on left return 0.0f; if (fX <= 0.5f) // half outside image on left return (fX + 0.5f) * (fX + 0.5f); if (fX <= fN - 0.5f) { // inside image float t = fX + 0.5f - floorf(fX + 0.5f); return t*t + (1-t)*(1-t); } if (fX <= fN + 0.5f) // half outside image on right return (fN + 0.5f - fX) * (fN + 0.5f - fX); return 0.0f; // outside image on right } template __global__ void par3D_FP_SumSqW_t(float* D_projData, unsigned int projPitch, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions3D dims, SCALE_NONCUBE sc) { COORD c; int angle = startAngle + blockIdx.y * g_anglesPerBlock + threadIdx.y; if (angle >= endAngle) return; const float fRayX = gC_RayX[angle]; const float fRayY = gC_RayY[angle]; const float fRayZ = gC_RayZ[angle]; const float fDetUX = gC_DetUX[angle]; const float fDetUY = gC_DetUY[angle]; const float fDetUZ = gC_DetUZ[angle]; const float fDetVX = gC_DetVX[angle]; const float fDetVY = gC_DetVY[angle]; const float fDetVZ = gC_DetVZ[angle]; const float fDetSX = gC_DetSX[angle] + 0.5f * fDetUX + 0.5f * fDetVX; const float fDetSY = gC_DetSY[angle] + 0.5f * fDetUY + 0.5f * fDetVY; const float fDetSZ = gC_DetSZ[angle] + 0.5f * fDetUZ + 0.5f * fDetVZ; const float a1 = c.c1(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ); const float a2 = c.c2(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ); const float fDistCorr = sc.scale(a1, a2); const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x; if (detectorU >= dims.iProjU) return; const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV; int endDetectorV = startDetectorV + g_detBlockV; if (endDetectorV > dims.iProjV) endDetectorV = dims.iProjV; int endSlice = startSlice + g_blockSlices; if (endSlice > c.nSlices(dims)) endSlice = c.nSlices(dims); for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV) { /* Trace ray in direction Ray to (detectorU,detectorV) from */ /* X = startSlice to X = endSlice */ const float fDetX = fDetSX + detectorU*fDetUX + detectorV*fDetVX; const float fDetY = fDetSY + detectorU*fDetUY + detectorV*fDetVY; const float fDetZ = fDetSZ + detectorU*fDetUZ + detectorV*fDetVZ; /* (x) ( 1) ( 0) */ /* ray: (y) = (ay) * x + (by) */ /* (z) (az) (bz) */ const float b1 = c.c1(fDetX,fDetY,fDetZ) - a1 * c.c0(fDetX,fDetY,fDetZ); const float b2 = c.c2(fDetX,fDetY,fDetZ) - a2 * c.c0(fDetX,fDetY,fDetZ); float fVal = 0.0f; float f0 = startSlice + 0.5f; float f1 = a1 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b1 + 0.5f*c.nDim1(dims) - 0.5f + 0.5f; float f2 = a2 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b2 + 0.5f*c.nDim2(dims) - 0.5f + 0.5f; for (int s = startSlice; s < endSlice; ++s) { fVal += dirWeights(f1, c.nDim1(dims)) * dirWeights(f2, c.nDim2(dims)); f0 += 1.0f; f1 += a1; f2 += a2; } fVal *= fDistCorr * fDistCorr; D_projData[(size_t)(detectorV*dims.iProjAngles+angle)*projPitch+detectorU] += fVal; } } // Supersampling version // TODO bool Par3DFP_Array_internal(cudaPitchedPtr D_projData, cudaTextureObject_t D_texObj, const SDimensions3D& dims, unsigned int angleCount, const SPar3DProjection* angles, const SProjectorParams3D& params, cudaStream_t stream) { dim3 dimBlock(g_detBlockU, g_anglesPerBlock); // region size, angles // Run over all angles, grouping them into groups of the same // orientation (roughly horizontal vs. roughly vertical). // Start a stream of grids for each such group. unsigned int blockStart = 0; unsigned int blockEnd = 0; int blockDirection = 0; bool cube = true; if (abs(params.fVolScaleX / params.fVolScaleY - 1.0) > 0.00001) cube = false; if (abs(params.fVolScaleX / params.fVolScaleZ - 1.0) > 0.00001) cube = false; SCALE_CUBE scube; scube.fOutputScale = params.fOutputScale * params.fVolScaleX; SCALE_NONCUBE snoncubeX; float fS1 = params.fVolScaleY / params.fVolScaleX; snoncubeX.fScale1 = fS1 * fS1; float fS2 = params.fVolScaleZ / params.fVolScaleX; snoncubeX.fScale2 = fS2 * fS2; snoncubeX.fOutputScale = params.fOutputScale * params.fVolScaleX; SCALE_NONCUBE snoncubeY; fS1 = params.fVolScaleX / params.fVolScaleY; snoncubeY.fScale1 = fS1 * fS1; fS2 = params.fVolScaleY / params.fVolScaleY; snoncubeY.fScale2 = fS2 * fS2; snoncubeY.fOutputScale = params.fOutputScale * params.fVolScaleY; SCALE_NONCUBE snoncubeZ; fS1 = params.fVolScaleX / params.fVolScaleZ; snoncubeZ.fScale1 = fS1 * fS1; fS2 = params.fVolScaleY / params.fVolScaleZ; snoncubeZ.fScale2 = fS2 * fS2; snoncubeZ.fOutputScale = params.fOutputScale * params.fVolScaleZ; // timeval t; // tic(t); for (unsigned int a = 0; a <= angleCount; ++a) { int dir = -1; if (a != angleCount) { float dX = fabsf(angles[a].fRayX); float dY = fabsf(angles[a].fRayY); float dZ = fabsf(angles[a].fRayZ); if (dX >= dY && dX >= dZ) dir = 0; else if (dY >= dX && dY >= dZ) dir = 1; else dir = 2; } if (a == angleCount || dir != blockDirection) { // block done blockEnd = a; if (blockStart != blockEnd) { dim3 dimGrid( ((dims.iProjU+g_detBlockU-1)/g_detBlockU)*((dims.iProjV+g_detBlockV-1)/g_detBlockV), (blockEnd-blockStart+g_anglesPerBlock-1)/g_anglesPerBlock); // printf("angle block: %d to %d, %d (%dx%d, %dx%d)\n", blockStart, blockEnd, blockDirection, dimGrid.x, dimGrid.y, dimBlock.x, dimBlock.y); if (blockDirection == 0) { for (unsigned int i = 0; i < dims.iVolX; i += g_blockSlices) if (params.iRaysPerDetDim == 1) if (cube) par3D_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj, i, blockStart, blockEnd, dims, scube); else par3D_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj,i, blockStart, blockEnd, dims, snoncubeX); else par3D_FP_SS_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj,i, blockStart, blockEnd, dims, params.iRaysPerDetDim, snoncubeX); } else if (blockDirection == 1) { for (unsigned int i = 0; i < dims.iVolY; i += g_blockSlices) if (params.iRaysPerDetDim == 1) if (cube) par3D_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj,i, blockStart, blockEnd, dims, scube); else par3D_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj,i, blockStart, blockEnd, dims, snoncubeY); else par3D_FP_SS_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj,i, blockStart, blockEnd, dims, params.iRaysPerDetDim, snoncubeY); } else if (blockDirection == 2) { for (unsigned int i = 0; i < dims.iVolZ; i += g_blockSlices) if (params.iRaysPerDetDim == 1) if (cube) par3D_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj,i, blockStart, blockEnd, dims, scube); else par3D_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj,i, blockStart, blockEnd, dims, snoncubeZ); else par3D_FP_SS_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), D_texObj,i, blockStart, blockEnd, dims, params.iRaysPerDetDim, snoncubeZ); } } blockDirection = dir; blockStart = a; } } // printf("%f\n", toc(t)); return true; } bool Par3DFP(cudaPitchedPtr D_volumeData, cudaPitchedPtr D_projData, const SDimensions3D& dims, const SPar3DProjection* angles, const SProjectorParams3D& params) { TransferConstantsBuffer tcbuf(g_MaxAngles); cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "Par3DFP stream")) return false; // transfer volume to array cudaArray* cuArray = allocateVolumeArray(dims); if (!cuArray) { cudaStreamDestroy(stream); return false; } cudaTextureObject_t D_texObj; if (!createTextureObject3D(cuArray, D_texObj)) { cudaStreamDestroy(stream); cudaFreeArray(cuArray); return false; } if (!transferVolumeToArray(D_volumeData, cuArray, dims, stream)) { cudaDestroyTextureObject(D_texObj); cudaStreamDestroy(stream); cudaFreeArray(cuArray); return false; } bool ok = true; for (unsigned int iAngle = 0; iAngle < dims.iProjAngles; iAngle += g_MaxAngles) { unsigned int iEndAngle = iAngle + g_MaxAngles; if (iEndAngle >= dims.iProjAngles) iEndAngle = dims.iProjAngles; ok = transferConstants(angles + iAngle, iEndAngle - iAngle, tcbuf, stream); if (!ok) break; cudaPitchedPtr D_subprojData = D_projData; D_subprojData.ptr = (char*)D_projData.ptr + iAngle * D_projData.pitch; ok = Par3DFP_Array_internal(D_subprojData, D_texObj, dims, iEndAngle - iAngle, angles + iAngle, params, stream); if (!ok) break; } ok &= checkCuda(cudaStreamSynchronize(stream), "Par3DFP sync"); cudaDestroyTextureObject(D_texObj); cudaFreeArray(cuArray); cudaStreamDestroy(stream); return ok; } bool Par3DFP_SumSqW(cudaPitchedPtr D_volumeData, cudaPitchedPtr D_projData, const SDimensions3D& dims, const SPar3DProjection* angles, const SProjectorParams3D& params) { TransferConstantsBuffer tcbuf(dims.iProjAngles); cudaStream_t stream; if (!checkCuda(cudaStreamCreate(&stream), "Par3DFP_SumSqW stream")) return false; if (!transferConstants(angles, dims.iProjAngles, tcbuf, stream)) { cudaStreamDestroy(stream); return false; } dim3 dimBlock(g_detBlockU, g_anglesPerBlock); // region size, angles // Run over all angles, grouping them into groups of the same // orientation (roughly horizontal vs. roughly vertical). // Start a stream of grids for each such group. unsigned int blockStart = 0; unsigned int blockEnd = 0; int blockDirection = 0; SCALE_NONCUBE snoncubeX; float fS1 = params.fVolScaleY / params.fVolScaleX; snoncubeX.fScale1 = fS1 * fS1; float fS2 = params.fVolScaleZ / params.fVolScaleX; snoncubeX.fScale2 = fS2 * fS2; snoncubeX.fOutputScale = params.fOutputScale * params.fVolScaleX; SCALE_NONCUBE snoncubeY; fS1 = params.fVolScaleX / params.fVolScaleY; snoncubeY.fScale1 = fS1 * fS1; fS2 = params.fVolScaleY / params.fVolScaleY; snoncubeY.fScale2 = fS2 * fS2; snoncubeY.fOutputScale = params.fOutputScale * params.fVolScaleY; SCALE_NONCUBE snoncubeZ; fS1 = params.fVolScaleX / params.fVolScaleZ; snoncubeZ.fScale1 = fS1 * fS1; fS2 = params.fVolScaleY / params.fVolScaleZ; snoncubeZ.fScale2 = fS2 * fS2; snoncubeZ.fOutputScale = params.fOutputScale * params.fVolScaleZ; // timeval t; // tic(t); for (unsigned int a = 0; a <= dims.iProjAngles; ++a) { int dir; if (a != dims.iProjAngles) { float dX = fabsf(angles[a].fRayX); float dY = fabsf(angles[a].fRayY); float dZ = fabsf(angles[a].fRayZ); if (dX >= dY && dX >= dZ) dir = 0; else if (dY >= dX && dY >= dZ) dir = 1; else dir = 2; } if (a == dims.iProjAngles || dir != blockDirection) { // block done blockEnd = a; if (blockStart != blockEnd) { dim3 dimGrid( ((dims.iProjU+g_detBlockU-1)/g_detBlockU)*((dims.iProjV+g_detBlockV-1)/g_detBlockV), (blockEnd-blockStart+g_anglesPerBlock-1)/g_anglesPerBlock); // printf("angle block: %d to %d, %d (%dx%d, %dx%d)\n", blockStart, blockEnd, blockDirection, dimGrid.x, dimGrid.y, dimBlock.x, dimBlock.y); if (blockDirection == 0) { for (unsigned int i = 0; i < dims.iVolX; i += g_blockSlices) if (params.iRaysPerDetDim == 1) par3D_FP_SumSqW_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, snoncubeX); else #if 0 par3D_FP_SS_SumSqW_dirX<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale); #else assert(false); #endif } else if (blockDirection == 1) { for (unsigned int i = 0; i < dims.iVolY; i += g_blockSlices) if (params.iRaysPerDetDim == 1) par3D_FP_SumSqW_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, snoncubeY); else #if 0 par3D_FP_SS_SumSqW_dirY<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale); #else assert(false); #endif } else if (blockDirection == 2) { for (unsigned int i = 0; i < dims.iVolZ; i += g_blockSlices) if (params.iRaysPerDetDim == 1) par3D_FP_SumSqW_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, snoncubeZ); else #if 0 par3D_FP_SS_SumSqW_dirZ<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale); #else assert(false); #endif } } blockDirection = dir; blockStart = a; } } bool ok = checkCuda(cudaStreamSynchronize(stream), "Par3DFP_SumSqW"); cudaStreamDestroy(stream); // printf("%f\n", toc(t)); return ok; } } astra-toolbox-2.3.0/cuda/3d/sirt3d.cu000066400000000000000000000203101475635207100173030ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/sirt3d.h" #include "astra/cuda/3d/util3d.h" #include "astra/cuda/3d/arith3d.h" #include "astra/cuda/3d/cone_fp.h" #include #include namespace astraCUDA3d { SIRT::SIRT() : ReconAlgo3D() { D_maskData.ptr = 0; D_smaskData.ptr = 0; D_sinoData.ptr = 0; D_volumeData.ptr = 0; D_projData.ptr = 0; D_tmpData.ptr = 0; D_lineWeight.ptr = 0; D_pixelWeight.ptr = 0; useVolumeMask = false; useSinogramMask = false; useMinConstraint = false; useMaxConstraint = false; fRelaxation = 1.0f; } SIRT::~SIRT() { reset(); } void SIRT::reset() { cudaFree(D_projData.ptr); cudaFree(D_tmpData.ptr); cudaFree(D_lineWeight.ptr); cudaFree(D_pixelWeight.ptr); D_maskData.ptr = 0; D_smaskData.ptr = 0; D_sinoData.ptr = 0; D_volumeData.ptr = 0; D_projData.ptr = 0; D_tmpData.ptr = 0; D_lineWeight.ptr = 0; D_pixelWeight.ptr = 0; useVolumeMask = false; useSinogramMask = false; fRelaxation = 1.0f; ReconAlgo3D::reset(); } bool SIRT::enableVolumeMask() { useVolumeMask = true; return true; } bool SIRT::enableSinogramMask() { useSinogramMask = true; return true; } bool SIRT::init() { D_pixelWeight = allocateVolumeData(dims); zeroVolumeData(D_pixelWeight, dims); D_tmpData = allocateVolumeData(dims); zeroVolumeData(D_tmpData, dims); D_projData = allocateProjectionData(dims); zeroProjectionData(D_projData, dims); D_lineWeight = allocateProjectionData(dims); zeroProjectionData(D_lineWeight, dims); // We can't precompute lineWeights and pixelWeights when using a mask if (!useVolumeMask && !useSinogramMask) precomputeWeights(); // TODO: check if allocations succeeded return true; } bool SIRT::setMinConstraint(float fMin) { fMinConstraint = fMin; useMinConstraint = true; return true; } bool SIRT::setMaxConstraint(float fMax) { fMaxConstraint = fMax; useMaxConstraint = true; return true; } bool SIRT::precomputeWeights() { zeroProjectionData(D_lineWeight, dims); if (useVolumeMask) { callFP(D_maskData, D_lineWeight, 1.0f); } else { processVol3D(D_tmpData, 1.0f, dims); callFP(D_tmpData, D_lineWeight, 1.0f); } processSino3D(D_lineWeight, dims); if (useSinogramMask) { // scale line weights with sinogram mask to zero out masked sinogram pixels processSino3D(D_lineWeight, D_smaskData, dims); } zeroVolumeData(D_pixelWeight, dims); if (useSinogramMask) { callBP(D_pixelWeight, D_smaskData, 1.0f); } else { processSino3D(D_projData, 1.0f, dims); callBP(D_pixelWeight, D_projData, 1.0f); } #if 0 float* bufp = new float[512*512]; for (int i = 0; i < 180; ++i) { for (int j = 0; j < 512; ++j) { cudaMemcpy(bufp+512*j, ((float*)D_projData.ptr)+180*512*j+512*i, 512*sizeof(float), cudaMemcpyDeviceToHost); } char fname[20]; sprintf(fname, "ray%03d.png", i); saveImage(fname, 512, 512, bufp); } #endif #if 0 float* buf = new float[256*256]; for (int i = 0; i < 256; ++i) { cudaMemcpy(buf, ((float*)D_pixelWeight.ptr)+256*256*i, 256*256*sizeof(float), cudaMemcpyDeviceToHost); char fname[20]; sprintf(fname, "pix%03d.png", i); saveImage(fname, 256, 256, buf); } #endif processVol3D(D_pixelWeight, dims); if (useVolumeMask) { // scale pixel weights with mask to zero out masked pixels processVol3D(D_pixelWeight, D_maskData, dims); } processVol3D(D_pixelWeight, fRelaxation, dims); return true; } bool SIRT::setVolumeMask(cudaPitchedPtr& _D_maskData) { assert(useVolumeMask); D_maskData = _D_maskData; return true; } bool SIRT::setSinogramMask(cudaPitchedPtr& _D_smaskData) { assert(useSinogramMask); D_smaskData = _D_smaskData; return true; } bool SIRT::setBuffers(cudaPitchedPtr& _D_volumeData, cudaPitchedPtr& _D_projData) { D_volumeData = _D_volumeData; D_sinoData = _D_projData; return true; } bool SIRT::iterate(unsigned int iterations) { if (useVolumeMask || useSinogramMask) precomputeWeights(); #if 0 float* buf = new float[256*256]; for (int i = 0; i < 256; ++i) { cudaMemcpy(buf, ((float*)D_pixelWeight.ptr)+256*256*i, 256*256*sizeof(float), cudaMemcpyDeviceToHost); char fname[20]; sprintf(fname, "pix%03d.png", i); saveImage(fname, 256, 256, buf); } #endif #if 0 float* bufp = new float[512*512]; for (int i = 0; i < 100; ++i) { for (int j = 0; j < 512; ++j) { cudaMemcpy(bufp+512*j, ((float*)D_lineWeight.ptr)+100*512*j+512*i, 512*sizeof(float), cudaMemcpyDeviceToHost); } char fname[20]; sprintf(fname, "ray%03d.png", i); saveImage(fname, 512, 512, bufp); } #endif // iteration for (unsigned int iter = 0; iter < iterations && !astra::shouldAbort(); ++iter) { // copy sinogram to projection data duplicateProjectionData(D_projData, D_sinoData, dims); // do FP, subtracting projection from sinogram if (useVolumeMask) { duplicateVolumeData(D_tmpData, D_volumeData, dims); processVol3D(D_tmpData, D_maskData, dims); callFP(D_tmpData, D_projData, -1.0f); } else { callFP(D_volumeData, D_projData, -1.0f); } processSino3D(D_projData, D_lineWeight, dims); zeroVolumeData(D_tmpData, dims); #if 0 float* bufp = new float[512*512]; printf("Dumping projData: %p\n", (void*)D_projData.ptr); for (int i = 0; i < 180; ++i) { for (int j = 0; j < 512; ++j) { cudaMemcpy(bufp+512*j, ((float*)D_projData.ptr)+180*512*j+512*i, 512*sizeof(float), cudaMemcpyDeviceToHost); } char fname[20]; sprintf(fname, "diff%03d.png", i); saveImage(fname, 512, 512, bufp); } #endif callBP(D_tmpData, D_projData, 1.0f); #if 0 printf("Dumping tmpData: %p\n", (void*)D_tmpData.ptr); float* buf = new float[256*256]; for (int i = 0; i < 256; ++i) { cudaMemcpy(buf, ((float*)D_tmpData.ptr)+256*256*i, 256*256*sizeof(float), cudaMemcpyDeviceToHost); char fname[20]; sprintf(fname, "add%03d.png", i); saveImage(fname, 256, 256, buf); } #endif // pixel weights also contain the volume mask and relaxation factor processVol3D(D_volumeData, D_tmpData, D_pixelWeight, dims); if (useMinConstraint) processVol3D(D_volumeData, fMinConstraint, dims); if (useMaxConstraint) processVol3D(D_volumeData, fMaxConstraint, dims); } return true; } float SIRT::computeDiffNorm() { // copy sinogram to projection data duplicateProjectionData(D_projData, D_sinoData, dims); // do FP, subtracting projection from sinogram if (useVolumeMask) { duplicateVolumeData(D_tmpData, D_volumeData, dims); processVol3D(D_tmpData, D_maskData, dims); callFP(D_tmpData, D_projData, -1.0f); } else { callFP(D_volumeData, D_projData, -1.0f); } float s = dotProduct3D(D_projData, dims.iProjU, dims.iProjAngles, dims.iProjV); return sqrt(s); } bool doSIRT(cudaPitchedPtr& D_volumeData, cudaPitchedPtr& D_sinoData, cudaPitchedPtr& D_maskData, const SDimensions3D& dims, const SConeProjection* angles, unsigned int iterations) { SIRT sirt; bool ok = true; ok &= sirt.setConeGeometry(dims, angles, SProjectorParams3D()); if (D_maskData.ptr) ok &= sirt.enableVolumeMask(); if (!ok) return false; ok = sirt.init(); if (!ok) return false; if (D_maskData.ptr) ok &= sirt.setVolumeMask(D_maskData); ok &= sirt.setBuffers(D_volumeData, D_sinoData); if (!ok) return false; ok = sirt.iterate(iterations); return ok; } } astra-toolbox-2.3.0/cuda/3d/util3d.cu000066400000000000000000000255431475635207100173140ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/cuda/3d/util3d.h" #include "astra/cuda/2d/util.h" #include "astra/Logging.h" #include #include namespace astraCUDA3d { cudaPitchedPtr allocateVolumeData(const SDimensions3D& dims) { cudaExtent extentV; extentV.width = dims.iVolX*sizeof(float); extentV.height = dims.iVolY; extentV.depth = dims.iVolZ; cudaPitchedPtr volData; if (!checkCuda(cudaMalloc3D(&volData, extentV), "allocateVolumeData 3D")) { ASTRA_ERROR("Failed to allocate %dx%dx%d GPU buffer", dims.iVolX, dims.iVolY, dims.iVolZ); volData.ptr = 0; } return volData; } cudaPitchedPtr allocateProjectionData(const SDimensions3D& dims) { cudaExtent extentP; extentP.width = dims.iProjU*sizeof(float); extentP.height = dims.iProjAngles; extentP.depth = dims.iProjV; cudaPitchedPtr projData; if (!checkCuda(cudaMalloc3D(&projData, extentP), "allocateProjectionData 3D")) { ASTRA_ERROR("Failed to allocate %dx%dx%d GPU buffer", dims.iProjU, dims.iProjAngles, dims.iProjV); projData.ptr = 0; } return projData; } bool zeroVolumeData(cudaPitchedPtr& D_data, const SDimensions3D& dims) { char* t = (char*)D_data.ptr; for (unsigned int z = 0; z < dims.iVolZ; ++z) { if (!checkCuda(cudaMemset2D(t, D_data.pitch, 0, dims.iVolX*sizeof(float), dims.iVolY), "zeroVolumeData 3D")) { return false; } t += D_data.pitch * dims.iVolY; } return true; } bool zeroProjectionData(cudaPitchedPtr& D_data, const SDimensions3D& dims) { char* t = (char*)D_data.ptr; for (unsigned int z = 0; z < dims.iProjV; ++z) { if (!checkCuda(cudaMemset2D(t, D_data.pitch, 0, dims.iProjU*sizeof(float), dims.iProjAngles), "zeroProjectionData 3D")) { return false; } t += D_data.pitch * dims.iProjAngles; } return true; } bool copyVolumeToDevice(const float* data, cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch) { if (!pitch) pitch = dims.iVolX; cudaPitchedPtr ptr; ptr.ptr = (void*)data; // const cast away ptr.pitch = pitch*sizeof(float); ptr.xsize = dims.iVolX*sizeof(float); ptr.ysize = dims.iVolY; cudaExtent extentV; extentV.width = dims.iVolX*sizeof(float); extentV.height = dims.iVolY; extentV.depth = dims.iVolZ; cudaPos zp = { 0, 0, 0 }; cudaMemcpy3DParms p; p.srcArray = 0; p.srcPos = zp; p.srcPtr = ptr; p.dstArray = 0; p.dstPos = zp; p.dstPtr = D_data; p.extent = extentV; p.kind = cudaMemcpyHostToDevice; return checkCuda(cudaMemcpy3D(&p), "copyVolumeToDevice 3D"); } bool copyProjectionsToDevice(const float* data, cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch) { if (!pitch) pitch = dims.iProjU; cudaPitchedPtr ptr; ptr.ptr = (void*)data; // const cast away ptr.pitch = pitch*sizeof(float); ptr.xsize = dims.iProjU*sizeof(float); ptr.ysize = dims.iProjAngles; cudaExtent extentV; extentV.width = dims.iProjU*sizeof(float); extentV.height = dims.iProjAngles; extentV.depth = dims.iProjV; cudaPos zp = { 0, 0, 0 }; cudaMemcpy3DParms p; p.srcArray = 0; p.srcPos = zp; p.srcPtr = ptr; p.dstArray = 0; p.dstPos = zp; p.dstPtr = D_data; p.extent = extentV; p.kind = cudaMemcpyHostToDevice; return checkCuda(cudaMemcpy3D(&p), "copyProjectionsToDevice 3D"); } bool copyVolumeFromDevice(float* data, const cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch) { if (!pitch) pitch = dims.iVolX; cudaPitchedPtr ptr; ptr.ptr = data; ptr.pitch = pitch*sizeof(float); ptr.xsize = dims.iVolX*sizeof(float); ptr.ysize = dims.iVolY; cudaExtent extentV; extentV.width = dims.iVolX*sizeof(float); extentV.height = dims.iVolY; extentV.depth = dims.iVolZ; cudaPos zp = { 0, 0, 0 }; cudaMemcpy3DParms p; p.srcArray = 0; p.srcPos = zp; p.srcPtr = D_data; p.dstArray = 0; p.dstPos = zp; p.dstPtr = ptr; p.extent = extentV; p.kind = cudaMemcpyDeviceToHost; return checkCuda(cudaMemcpy3D(&p), "copyVolumeFromDevice 3D"); } bool copyProjectionsFromDevice(float* data, const cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch) { if (!pitch) pitch = dims.iProjU; cudaPitchedPtr ptr; ptr.ptr = data; ptr.pitch = pitch*sizeof(float); ptr.xsize = dims.iProjU*sizeof(float); ptr.ysize = dims.iProjAngles; cudaExtent extentV; extentV.width = dims.iProjU*sizeof(float); extentV.height = dims.iProjAngles; extentV.depth = dims.iProjV; cudaPos zp = { 0, 0, 0 }; cudaMemcpy3DParms p; p.srcArray = 0; p.srcPos = zp; p.srcPtr = D_data; p.dstArray = 0; p.dstPos = zp; p.dstPtr = ptr; p.extent = extentV; p.kind = cudaMemcpyDeviceToHost; return checkCuda(cudaMemcpy3D(&p), "copyProjectionsFromDevice 3D"); } bool duplicateVolumeData(cudaPitchedPtr& D_dst, const cudaPitchedPtr& D_src, const SDimensions3D& dims) { cudaExtent extentV; extentV.width = dims.iVolX*sizeof(float); extentV.height = dims.iVolY; extentV.depth = dims.iVolZ; cudaPos zp = { 0, 0, 0 }; cudaMemcpy3DParms p; p.srcArray = 0; p.srcPos = zp; p.srcPtr = D_src; p.dstArray = 0; p.dstPos = zp; p.dstPtr = D_dst; p.extent = extentV; p.kind = cudaMemcpyDeviceToDevice; return checkCuda(cudaMemcpy3D(&p), "duplicateVolumeData 3D"); } bool duplicateProjectionData(cudaPitchedPtr& D_dst, const cudaPitchedPtr& D_src, const SDimensions3D& dims) { cudaExtent extentV; extentV.width = dims.iProjU*sizeof(float); extentV.height = dims.iProjAngles; extentV.depth = dims.iProjV; cudaPos zp = { 0, 0, 0 }; cudaMemcpy3DParms p; p.srcArray = 0; p.srcPos = zp; p.srcPtr = D_src; p.dstArray = 0; p.dstPos = zp; p.dstPtr = D_dst; p.extent = extentV; p.kind = cudaMemcpyDeviceToDevice; return checkCuda(cudaMemcpy3D(&p), "duplicateProjectionData 3D"); } // TODO: Consider using a single array of size max(proj,volume) (per dim) // instead of allocating a new one each time cudaArray* allocateVolumeArray(const SDimensions3D& dims) { cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(); cudaArray* cuArray; cudaExtent extentA; extentA.width = dims.iVolX; extentA.height = dims.iVolY; extentA.depth = dims.iVolZ; if (!checkCuda(cudaMalloc3DArray(&cuArray, &channelDesc, extentA), "allocateVolumeArray 3D")) { ASTRA_ERROR("Failed to allocate %dx%dx%d GPU array", dims.iVolX, dims.iVolY, dims.iVolZ); return 0; } return cuArray; } cudaArray* allocateProjectionArray(const SDimensions3D& dims) { cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(); cudaArray* cuArray; cudaExtent extentA; extentA.width = dims.iProjU; extentA.height = dims.iProjAngles; extentA.depth = dims.iProjV; if (!checkCuda(cudaMalloc3DArray(&cuArray, &channelDesc, extentA), "allocateProjectionArray 3D")) { ASTRA_ERROR("Failed to allocate %dx%dx%d GPU array", dims.iProjU, dims.iProjAngles, dims.iProjV); return 0; } return cuArray; } bool transferVolumeToArray(cudaPitchedPtr D_volumeData, cudaArray* array, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; cudaExtent extentA; extentA.width = dims.iVolX; extentA.height = dims.iVolY; extentA.depth = dims.iVolZ; cudaMemcpy3DParms p; cudaPos zp = {0, 0, 0}; p.srcArray = 0; p.srcPos = zp; p.srcPtr = D_volumeData; p.dstArray = array; p.dstPtr.ptr = 0; p.dstPtr.pitch = 0; p.dstPtr.xsize = 0; p.dstPtr.ysize = 0; p.dstPos = zp; p.extent = extentA; p.kind = cudaMemcpyDeviceToDevice; bool ok = checkCuda(cudaMemcpy3DAsync(&p, stream()), "transferVolumeToArray 3D"); ok &= stream.syncIfSync("transferVolumeToArray 3D sync"); return ok; } bool transferProjectionsToArray(cudaPitchedPtr D_projData, cudaArray* array, const SDimensions3D& dims, std::optional _stream) { StreamHelper stream(_stream); if (!stream) return false; cudaExtent extentA; extentA.width = dims.iProjU; extentA.height = dims.iProjAngles; extentA.depth = dims.iProjV; cudaMemcpy3DParms p; cudaPos zp = {0, 0, 0}; p.srcArray = 0; p.srcPos = zp; p.srcPtr = D_projData; p.dstArray = array; p.dstPtr.ptr = 0; p.dstPtr.pitch = 0; p.dstPtr.xsize = 0; p.dstPtr.ysize = 0; p.dstPos = zp; p.extent = extentA; p.kind = cudaMemcpyDeviceToDevice; bool ok = checkCuda(cudaMemcpy3DAsync(&p, stream()), "transferProjectionsToArray 3D"); ok &= stream.syncIfSync("transferProjectionsToArray 3D sync"); return ok; } bool transferHostProjectionsToArray(const float *projData, cudaArray* array, const SDimensions3D& dims) { cudaExtent extentA; extentA.width = dims.iProjU; extentA.height = dims.iProjAngles; extentA.depth = dims.iProjV; cudaPitchedPtr ptr; ptr.ptr = (void*)projData; // const cast away ptr.pitch = dims.iProjU*sizeof(float); ptr.xsize = dims.iProjU*sizeof(float); ptr.ysize = dims.iProjAngles; cudaMemcpy3DParms p; cudaPos zp = {0, 0, 0}; p.srcArray = 0; p.srcPos = zp; p.srcPtr = ptr; p.dstArray = array; p.dstPtr.ptr = 0; p.dstPtr.pitch = 0; p.dstPtr.xsize = 0; p.dstPtr.ysize = 0; p.dstPos = zp; p.extent = extentA; p.kind = cudaMemcpyHostToDevice; return checkCuda(cudaMemcpy3D(&p), "transferHostProjectionsToArray 3D"); } bool createTextureObject3D(cudaArray* array, cudaTextureObject_t& texObj) { cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat); cudaResourceDesc resDesc; memset(&resDesc, 0, sizeof(resDesc)); resDesc.resType = cudaResourceTypeArray; resDesc.res.array.array = array; cudaTextureDesc texDesc; memset(&texDesc, 0, sizeof(texDesc)); texDesc.addressMode[0] = cudaAddressModeBorder; texDesc.addressMode[1] = cudaAddressModeBorder; texDesc.addressMode[2] = cudaAddressModeBorder; texDesc.filterMode = cudaFilterModeLinear; texDesc.readMode = cudaReadModeElementType; texDesc.normalizedCoords = 0; return checkCuda(cudaCreateTextureObject(&texObj, &resDesc, &texDesc, NULL), "createTextureObject3D"); } float dotProduct3D(cudaPitchedPtr data, unsigned int x, unsigned int y, unsigned int z) { return astraCUDA::dotProduct2D((float*)data.ptr, data.pitch/sizeof(float), x, y*z); } int calcNextPowerOfTwo(int _iValue) { int iOutput = 1; while(iOutput < _iValue) iOutput *= 2; return iOutput; } } astra-toolbox-2.3.0/include/000077500000000000000000000000001475635207100157475ustar00rootroot00000000000000astra-toolbox-2.3.0/include/astra/000077500000000000000000000000001475635207100170615ustar00rootroot00000000000000astra-toolbox-2.3.0/include/astra/Algorithm.h000066400000000000000000000052211475635207100211600ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_ALGORITHM #define _INC_ASTRA_ALGORITHM #include "Globals.h" #include "Config.h" namespace astra { /** * This class contains the interface for an algorithm implementation. */ class _AstraExport CAlgorithm { public: /** Default constructor, containing no code. */ CAlgorithm(); /** Destructor. */ virtual ~CAlgorithm(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg) = 0; /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0) = 0; /** Has this class been initialized? * * @return initialized */ bool isInitialized() const; /** get a description of the class * * @return description string */ virtual std::string description() const; /** Set the GPU Index to run on. * TODO: Move this from CAlgorithm to a Context-like class */ virtual void setGPUIndex(int /*_iGPUIndex*/) { }; protected: //< Has this class been initialized? bool m_bIsInitialized; private: /** * Private copy constructor to prevent CAlgorithms from being copied. */ CAlgorithm(const CAlgorithm&); /** * Private assignment operator to prevent CAlgorithms from being copied. */ CAlgorithm& operator=(const CAlgorithm&); //< For Config unused argument checking ConfigCheckData* configCheckData; friend class ConfigReader; }; // inline functions inline std::string CAlgorithm::description() const { return "Algorithm"; }; inline bool CAlgorithm::isInitialized() const { return m_bIsInitialized; } } // end namespace #endif astra-toolbox-2.3.0/include/astra/AlgorithmTypelist.h000066400000000000000000000053731475635207100227260ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_ALGORITHMTYPELIST #define _INC_ASTRA_ALGORITHMTYPELIST #include "Algorithm.h" #include "TypeList.h" #include "ArtAlgorithm.h" #include "SirtAlgorithm.h" #include "SartAlgorithm.h" #include "ForwardProjectionAlgorithm.h" #include "BackProjectionAlgorithm.h" #include "FilteredBackProjectionAlgorithm.h" #include "CudaBackProjectionAlgorithm.h" #include "CudaSartAlgorithm.h" #include "CudaSirtAlgorithm.h" #include "CudaCglsAlgorithm.h" #include "CudaEMAlgorithm.h" #include "CudaForwardProjectionAlgorithm.h" #include "CglsAlgorithm.h" #include "CudaCglsAlgorithm3D.h" #include "CudaSirtAlgorithm3D.h" #include "CudaForwardProjectionAlgorithm3D.h" #include "CudaBackProjectionAlgorithm3D.h" #include "CudaFDKAlgorithm3D.h" #include "CudaDartMaskAlgorithm.h" #include "CudaDartMaskAlgorithm3D.h" #include "CudaDartSmoothingAlgorithm.h" #include "CudaDartSmoothingAlgorithm3D.h" #include "CudaDataOperationAlgorithm.h" #include "CudaRoiSelectAlgorithm.h" #include "CudaFilteredBackProjectionAlgorithm.h" namespace astra { typedef TypeList< #ifdef ASTRA_CUDA CCudaSartAlgorithm, CCudaBackProjectionAlgorithm, CCudaDartMaskAlgorithm, CCudaDartMaskAlgorithm3D, CCudaDartSmoothingAlgorithm, CCudaDartSmoothingAlgorithm3D, CCudaDataOperationAlgorithm, CCudaRoiSelectAlgorithm, CCudaSirtAlgorithm, CCudaCglsAlgorithm, CCudaEMAlgorithm, CCudaForwardProjectionAlgorithm, CCudaCglsAlgorithm3D, CCudaFilteredBackProjectionAlgorithm, CCudaFDKAlgorithm3D, CCudaSirtAlgorithm3D, CCudaForwardProjectionAlgorithm3D, CCudaBackProjectionAlgorithm3D, #endif CArtAlgorithm, CSartAlgorithm, CSirtAlgorithm, CCglsAlgorithm, CBackProjectionAlgorithm, CForwardProjectionAlgorithm, CFilteredBackProjectionAlgorithm > AlgorithmTypeList; } #endif astra-toolbox-2.3.0/include/astra/ArtAlgorithm.h000066400000000000000000000143571475635207100216410ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_ARTALGORITHM #define _INC_ASTRA_ARTALGORITHM #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "ReconstructionAlgorithm2D.h" #include "Projector2D.h" #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" namespace astra { /** * This class contains the implementation of the ART (Algebraic Reconstruction Technique) algorithm. * * The update step of pixel \f$v_j\f$ for ray \f$i\f$ and iteration \f$k\f$ is given by: * \f[ * v_j^{(k+1)} = v_j^{(k)} + \lambda \frac{p_i - \sum_{r=1}^{N} w_{ir}v_r^{(k)}}{\sum_{k=1}^{N} w_{ik}^2} * \f] * * \par XML Configuration * \astra_xml_item{ProjectorId, integer, Identifier of a projector as it is stored in the ProjectorManager.} * \astra_xml_item{ProjectionDataId, integer, Identifier of a projection data object as it is stored in the DataManager.} * \astra_xml_item{ReconstructionDataId, integer, Identifier of a volume data object as it is stored in the DataManager.} * \astra_xml_item_option{ReconstructionMaskId, integer, not used, Identifier of a volume data object that acts as a reconstruction mask. 1 = reconstruct on this pixel. 0 = don't reconstruct on this pixel.} * \astra_xml_item_option{SinogramMaskId, integer, not used, Identifier of a projection data object that acts as a projection mask. 1 = reconstruct using this ray. 0 = don't use this ray while reconstructing.} * \astra_xml_item_option{UseMinConstraint, bool, false, Use minimum value constraint.} * \astra_xml_item_option{MinConstraintValue, float, 0, Minimum constraint value.} * \astra_xml_item_option{UseMaxConstraint, bool, false, Use maximum value constraint.} * \astra_xml_item_option{MaxConstraintValue, float, 255, Maximum constraint value.} * \astra_xml_item_option{Relaxation, float, 1, The relaxation factor.} * \astra_xml_item_option{RayOrder, string, "sequential", the order in which the rays are updated. 'sequential' or 'custom'} * \astra_xml_item_option{RayOrderList, n by 2 vector of float, not used, if RayOrder='custom': use this ray order. Each row consist of a projection id and detector id.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('ART');\n * cfg.ProjectorId = proj_id;\n * cfg.ProjectionDataId = sino_id;\n * cfg.ReconstructionDataId = recon_id;\n * cfg.option.MaskId = mask_id;\n * cfg.option.UseMinConstraint = 'yes';\n * cfg.option.UseMaxConstraint = 'yes';\n * cfg.option.MaxConstraintValue = 1024;\n * cfg.option.Relaxation = 0.7;\n * cfg.option.RayOrder = 'custom';\n * cfg.option.RayOrderList = [0\,0; 0\,2; 1\,0];\n * alg_id = astra_mex_algorithm('create'\, cfg);\n * astra_mex_algorithm('iterate'\, alg_id\, 1000);\n * astra_mex_algorithm('delete'\, alg_id);\n * } */ class _AstraExport CArtAlgorithm : public CReconstructionAlgorithm2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - the projector is compatible with both data objects * - the ray order list only contains valid values */ virtual bool _check(); public: // type of the algorithm, needed to register with CAlgorithmFactory inline static const char* const type = "ART"; /** Default constructor, containing no code. */ CArtAlgorithm(); /** Destructor. */ virtual ~CArtAlgorithm(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class, use sequential ray order. * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. */ bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Clear this class. */ virtual void clear(); /** Set the relaxation factor. * * @param _fLambda Relaxation factor */ void setLambda(float32 _fLambda); /** Set the order in which the rays will be selected * * @param _piProjectionOrder Order of the rays, the projections. (size should be _piRayCount) * @param _piDetectorOrder Order of the rays, the detectors. (size should be _piRayCount) * @param _piRayCount Number of rays in the two previous arrays. */ void setRayOrder(int* _piProjectionOrder, int* _piDetectorOrder, int _piRayCount); /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); /** Get a description of the class. * * @return description string */ virtual std::string description() const; protected: //< Relaxation Factor float32 m_fLambda; //< Order of the rays, the projections. std::vector m_piProjectionOrder; //< Order of the rays, the detectors. std::vector m_piDetectorOrder; //< Current index in the ray order arrays. int m_iCurrentRay; }; // inline functions inline std::string CArtAlgorithm::description() const { return CArtAlgorithm::type; }; } // end namespace #endif astra-toolbox-2.3.0/include/astra/AstraObjectFactory.h000066400000000000000000000100041475635207100227560ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_ASTRAOBJECTFACTORY #define _INC_ASTRA_ASTRAOBJECTFACTORY #include "Globals.h" #include "Config.h" #include "Singleton.h" #include "Utilities.h" #include "TypeList.h" #include "ProjectorTypelist.h" #include "AlgorithmTypelist.h" #include "PluginAlgorithmFactory.h" namespace astra { /** * This class contains functionality to create data objects based on their type or on a configuration object. */ template class CAstraObjectFactory : public Singleton > { public: /** A default constructor that contains not a single line of code. */ CAstraObjectFactory(); /** Destructor. */ ~CAstraObjectFactory(); /** Create, but don't initialize, a new object. * * @param _sType Type of the new object. * @return Pointer to a new, uninitialized object. */ T* create(std::string _sType); /** Create and initialize a new object. * * @param _cfg Configuration object to create and initialize a new object. * @return Pointer to a new, initialized projector. */ T* create(const Config& _cfg); /** Find a plugin. * * @param _sType Name of plugin to find. * @return Pointer to a new, uninitialized object, or NULL if not found. */ T* findPlugin(std::string _sType); }; //---------------------------------------------------------------------------------------- // Constructor template CAstraObjectFactory::CAstraObjectFactory() { } //---------------------------------------------------------------------------------------- // Destructor template CAstraObjectFactory::~CAstraObjectFactory() { } //---------------------------------------------------------------------------------------- // Hook for finding plugin in registered plugins. template <> CAlgorithm* CAstraObjectFactory::findPlugin(std::string _sType); template T* CAstraObjectFactory::findPlugin(std::string _sType) { return NULL; } //---------------------------------------------------------------------------------------- // Create template T* CAstraObjectFactory::create(std::string _sType) { T* res = createObject(_sType, TypeList{} ); if (!res) res = findPlugin(_sType); return res; } //---------------------------------------------------------------------------------------- // Create the necessary Object Managers /** * Class used to create algorithms from a string or a config object */ class _AstraExport CAlgorithmFactory : public CAstraObjectFactory {}; /** * Class used to create 2D projectors from a string or a config object */ class _AstraExport CProjector2DFactory : public CAstraObjectFactory {}; /** * Class used to create 3D projectors from a string or a config object */ class _AstraExport CProjector3DFactory : public CAstraObjectFactory {}; } // end namespace #endif astra-toolbox-2.3.0/include/astra/AstraObjectManager.h000066400000000000000000000244671475635207100227430ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_ASTRAOBJECTMANAGER #define _INC_ASTRA_ASTRAOBJECTMANAGER #include #include #include "Globals.h" #include "Singleton.h" #include "Projector2D.h" #include "Projector3D.h" #include "Float32Data2D.h" #include "Data3D.h" #include "SparseMatrix.h" #include "Algorithm.h" namespace astra { /** * This class contains functionality to store objects. A unique index handle * will be assigned to each data object by which it can be accessed in the * future. Indices are always >= 1. * * We store them in a special common base class to make indices unique * among all ObjectManagers. */ class CAstraObjectManagerBase { public: virtual std::string getInfo(int index) const =0; virtual void remove(int index) =0; virtual std::string getType() const =0; }; class _AstraExport CAstraIndexManager : public Singleton { public: CAstraIndexManager() : m_iLastIndex(0) { } int store(CAstraObjectManagerBase* m) { m_table[++m_iLastIndex] = m; return m_iLastIndex; } CAstraObjectManagerBase* get(int index) const { std::map::const_iterator i; i = m_table.find(index); if (i != m_table.end()) return i->second; else return 0; } void remove(int index) { std::map::iterator i; i = m_table.find(index); if (i != m_table.end()) m_table.erase(i); } private: /** The index last handed out */ int m_iLastIndex; std::map m_table; }; template class CAstraObjectManager : public CAstraObjectManagerBase { public: /** Default constructor. */ CAstraObjectManager(); /** Destructor. */ ~CAstraObjectManager(); /** Store the object in the manager and assign a unique index handle to it. * * @param _pObject A pointer to the object that should be stored. * @return The index of the stored data object. If the index in negative, an error occurred * and the object was NOT stored. */ int store(T* _pObject); /** Does the manager contain an object with the index _iIndex? * * @param _iIndex Index handle to the data object in question. * @return True if the manager contains an object with the index handle _iIndex. */ bool hasIndex(int _iIndex) const; /** Fetch the object to which _iIndex refers to. * * @param _iIndex Index handle to the data object in question. * @return Pointer to the stored data object. A null pointer is returned if no object with index _iIndex is found. */ T* get(int _iIndex) const; /** Delete an object that was previously stored. This actually DELETES the objecy. Therefore, after this * function call, the object in question will have passed on. It will be no more. It will have ceased * to be. It will be expired and will go to meet its maker. Bereft of life, it will rest in peace. * It will be an EX-OBJECT. * * @param _iIndex Index handle to the object in question. * @return Error code. 0 for success. */ void remove(int _iIndex); /** Get the index of the object, zero if it doesn't exist. * * @param _pObject The data object. * @return Index of the stored object, 0 if not found. */ int getIndex(const T* _pObject) const; /** Clear all data. This will also delete all the content of each object. */ void clear(); /** Get info of object. */ std::string getInfo(int index) const; /** Get list with info of all managed objects. */ std::string info(); protected: /** Map each data object to a unique index. */ std::map m_mIndexToObject; }; //---------------------------------------------------------------------------------------- // Constructor template CAstraObjectManager::CAstraObjectManager() { } //---------------------------------------------------------------------------------------- // Destructor template CAstraObjectManager::~CAstraObjectManager() { } //---------------------------------------------------------------------------------------- // store data template int CAstraObjectManager::store(T* _pDataObject) { int iIndex = CAstraIndexManager::getSingleton().store(this); m_mIndexToObject[iIndex] = _pDataObject; return iIndex; } //---------------------------------------------------------------------------------------- // has data? template bool CAstraObjectManager::hasIndex(int _iIndex) const { typename std::map::const_iterator it = m_mIndexToObject.find(_iIndex); return it != m_mIndexToObject.end(); } //---------------------------------------------------------------------------------------- // get data template T* CAstraObjectManager::get(int _iIndex) const { typename std::map::const_iterator it = m_mIndexToObject.find(_iIndex); if (it != m_mIndexToObject.end()) return it->second; else return 0; } //---------------------------------------------------------------------------------------- // delete data template void CAstraObjectManager::remove(int _iIndex) { // find data typename std::map::iterator it = m_mIndexToObject.find(_iIndex); if (it == m_mIndexToObject.end()) return; // delete data delete (*it).second; // delete from map m_mIndexToObject.erase(it); CAstraIndexManager::getSingleton().remove(_iIndex); } //---------------------------------------------------------------------------------------- // Get Index template int CAstraObjectManager::getIndex(const T* _pObject) const { for (typename std::map::const_iterator it = m_mIndexToObject.begin(); it != m_mIndexToObject.end(); it++) { if ((*it).second == _pObject) return (*it).first; } return 0; } //---------------------------------------------------------------------------------------- // clear template void CAstraObjectManager::clear() { for (typename std::map::iterator it = m_mIndexToObject.begin(); it != m_mIndexToObject.end(); it++) { // delete data delete (*it).second; (*it).second = 0; } m_mIndexToObject.clear(); } //---------------------------------------------------------------------------------------- // Print info to string template std::string CAstraObjectManager::getInfo(int index) const { typename std::map::const_iterator it = m_mIndexToObject.find(index); if (it == m_mIndexToObject.end()) return ""; const T* pObject = it->second; std::stringstream res; res << index << " \t"; if (pObject->isInitialized()) { res << "v "; } else { res << "x "; } res << pObject->description(); return res.str(); } template std::string CAstraObjectManager::info() { std::stringstream res; res << "id init description" << std::endl; res << "-----------------------------------------" << std::endl; for (typename std::map::const_iterator it = m_mIndexToObject.begin(); it != m_mIndexToObject.end(); it++) { res << getInfo(it->first) << std::endl; } res << "-----------------------------------------" << std::endl; return res.str(); } //---------------------------------------------------------------------------------------- // Create the necessary Object Managers /** * This class contains functionality to store 2D projector objects. A unique index handle will be * assigned to each data object by which it can be accessed in the future. * Indices are always >= 1. */ class _AstraExport CProjector2DManager : public Singleton, public CAstraObjectManager { virtual std::string getType() const { return "projector2d"; } }; /** * This class contains functionality to store 3D projector objects. A unique index handle will be * assigned to each data object by which it can be accessed in the future. * Indices are always >= 1. */ class _AstraExport CProjector3DManager : public Singleton, public CAstraObjectManager { virtual std::string getType() const { return "projector3d"; } }; /** * This class contains functionality to store 2D data objects. A unique index handle will be * assigned to each data object by which it can be accessed in the future. * Indices are always >= 1. */ class _AstraExport CData2DManager : public Singleton, public CAstraObjectManager { virtual std::string getType() const { return "data2d"; } }; /** * This class contains functionality to store 3D data objects. A unique index handle will be * assigned to each data object by which it can be accessed in the future. * Indices are always >= 1. */ class _AstraExport CData3DManager : public Singleton, public CAstraObjectManager { virtual std::string getType() const { return "data3d"; } }; /** * This class contains functionality to store algorithm objects. A unique index handle will be * assigned to each data object by which it can be accessed in the future. * Indices are always >= 1. */ class _AstraExport CAlgorithmManager : public Singleton, public CAstraObjectManager { virtual std::string getType() const { return "algorithm"; } }; /** * This class contains functionality to store matrix objects. A unique index handle will be * assigned to each data object by which it can be accessed in the future. * Indices are always >= 1. */ class _AstraExport CMatrixManager : public Singleton, public CAstraObjectManager { virtual std::string getType() const { return "matrix"; } }; } // end namespace #endif astra-toolbox-2.3.0/include/astra/BackProjectionAlgorithm.h000066400000000000000000000105341475635207100240010ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_BACKPROJECTIONALGORITHM #define _INC_ASTRA_BACKPROJECTIONALGORITHM #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "ReconstructionAlgorithm2D.h" #include "Projector2D.h" #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" #include "DataProjector.h" namespace astra { /** * \brief * This class performs an unfiltered backprojection. * * \par XML Configuration * \astra_xml_item{ProjectorId, integer, Identifier of a projector as it is stored in the ProjectorManager.} * \astra_xml_item{ProjectionDataId, integer, Identifier of a projection data object as it is stored in the DataManager.} * \astra_xml_item{ReconstructionDataId, integer, Identifier of a volume data object as it is stored in the DataManager.} * \astra_xml_item_option{ReconstructionMaskId, integer, not used, Identifier of a volume data object that acts as a reconstruction mask. 1 = reconstruct on this pixel. 0 = don't reconstruct on this pixel.} * \astra_xml_item_option{SinogramMaskId, integer, not used, Identifier of a projection data object that acts as a projection mask. 1 = reconstruct using this ray. 0 = don't use this ray while reconstructing.} * */ class _AstraExport CBackProjectionAlgorithm : public CReconstructionAlgorithm2D { protected: /** Init stuff */ virtual void _init(); /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - valid projector * - valid data objects */ virtual bool _check(); public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "BP"; /** Default constructor, containing no code. */ CBackProjectionAlgorithm(); /** Default constructor * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. */ CBackProjectionAlgorithm(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Destructor. */ virtual ~CBackProjectionAlgorithm(); /** Clear this class. */ virtual void clear(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return Initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. * @return Initialization successful? */ bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); /** Get a description of the class. * * @return description string */ virtual std::string description() const; }; // inline functions inline std::string CBackProjectionAlgorithm::description() const { return CBackProjectionAlgorithm::type; }; } // end namespace #endif astra-toolbox-2.3.0/include/astra/CglsAlgorithm.h000066400000000000000000000123621475635207100217750ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CGLSALGORITHM #define _INC_ASTRA_CGLSALGORITHM #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "ReconstructionAlgorithm2D.h" #include "Projector2D.h" #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" #include "DataProjector.h" namespace astra { /** * \brief * This class contains the implementation of the CGLS (Conguent Gradient Algorithm) algorithm. * * \par XML Configuration * \astra_xml_item{ProjectorId, integer, Identifier of a projector as it is stored in the ProjectorManager.} * \astra_xml_item{ProjectionDataId, integer, Identifier of a projection data object as it is stored in the DataManager.} * \astra_xml_item{ReconstructionDataId, integer, Identifier of a volume data object as it is stored in the DataManager.} * \astra_xml_item_option{ReconstructionMaskId, integer, not used, Identifier of a volume data object that acts as a reconstruction mask. 0 = reconstruct on this pixel. 1 = don't reconstruct on this pixel.} * \astra_xml_item_option{SinogramMaskId, integer, not used, Identifier of a projection data object that acts as a projection mask. 0 = reconstruct using this ray. 1 = don't use this ray while reconstructing.} * \astra_xml_item_option{UseMinConstraint, bool, false, Use minimum value constraint.} * \astra_xml_item_option{MinConstraintValue, float, 0, Minimum constraint value.} * \astra_xml_item_option{UseMaxConstraint, bool, false, Use maximum value constraint.} * \astra_xml_item_option{MaxConstraintValue, float, 255, Maximum constraint value.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('CGLS');\n * cfg.ProjectorId = proj_id;\n * cfg.ProjectionDataId = sino_id;\n * cfg.ReconstructionDataId = recon_id;\n * cfg.option.MaskId = mask_id;\n * cfg.option.UseMinConstraint = 'yes';\n * cfg.option.UseMaxConstraint = 'yes';\n * cfg.option.MaxConstraintValue = 1024;\n * alg_id = astra_mex_algorithm('create'\, cfg);\n * astra_mex_algorithm('iterate'\, alg_id\, 10);\n * astra_mex_algorithm('delete'\, alg_id);\n * } * */ class _AstraExport CCglsAlgorithm : public CReconstructionAlgorithm2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - valid projector * - valid data objects */ virtual bool _check(); CFloat32ProjectionData2D* r; CFloat32ProjectionData2D* w; CFloat32VolumeData2D* z; CFloat32VolumeData2D* p; float32 alpha; float32 beta; float32 gamma; int m_iIteration; public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "CGLS"; /** Default constructor, containing no code. */ CCglsAlgorithm(); /** Default constructor * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. */ CCglsAlgorithm(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Destructor. */ virtual ~CCglsAlgorithm(); /** Clear this class. */ virtual void clear(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return Initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. * @return Initialization successful? */ bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); /** Get a description of the class. * * @return description string */ virtual std::string description() const; }; // inline functions inline std::string CCglsAlgorithm::description() const { return CCglsAlgorithm::type; }; } // end namespace #endif astra-toolbox-2.3.0/include/astra/CompositeGeometryManager.h000066400000000000000000000134251475635207100242100ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_COMPOSITEGEOMETRYMANAGER #define _INC_ASTRA_COMPOSITEGEOMETRYMANAGER #include "Globals.h" #include "Filters.h" #ifdef ASTRA_CUDA #include #include #include #include namespace astra { class CCompositeVolume; class CCompositeProjections; class CData3D; class CFloat32ProjectionData3D; class CFloat32VolumeData3D; class CVolumeGeometry3D; class CProjectionGeometry3D; class CProjector3D; struct SGPUParams { std::vector GPUIndices; size_t memory; }; struct SFDKSettings { bool bShortScan; SFilterConfig filterConfig; }; class _AstraExport CCompositeGeometryManager { public: CCompositeGeometryManager(); class CPart; typedef std::list > TPartList; class CPart { public: CPart() : eType(PART_INVALID), pData(0), subX(0), subY(0), subZ(0) { } CPart(const CPart& other); virtual ~CPart() { } enum { PART_INVALID, PART_VOL, PART_PROJ } eType; CData3D* pData; unsigned int subX; unsigned int subY; unsigned int subZ; bool uploadToGPU(); bool downloadFromGPU(/*mode?*/); virtual void getDims(size_t &x, size_t &y, size_t &z) const = 0; size_t getSize() const; bool canSplitAndReduce() const; bool isFull() const; }; class CVolumePart : public CPart { public: CVolumePart() { eType = PART_VOL; } CVolumePart(const CVolumePart& other); CVolumePart(CFloat32VolumeData3D *pVolData); virtual ~CVolumePart(); CVolumeGeometry3D* pGeom; virtual void getDims(size_t &x, size_t &y, size_t &z) const; CVolumePart* clone() const; }; class CProjectionPart : public CPart { public: CProjectionPart() { eType = PART_PROJ; } CProjectionPart(const CProjectionPart& other); CProjectionPart(CFloat32ProjectionData3D *pProjData); virtual ~CProjectionPart(); CProjectionGeometry3D* pGeom; virtual void getDims(size_t &x, size_t &y, size_t &z) const; CProjectionPart* clone() const; }; enum EJobType { JOB_FP, JOB_BP, JOB_FDK, JOB_NOP }; enum EJobMode { MODE_ADD = 0, MODE_SET = 1 }; struct SJob { public: SJob(std::unique_ptr &&_pInput, std::unique_ptr &&_pOutput) : pInput(std::move(_pInput)), pOutput(std::move(_pOutput)), pProjector(0), FDKSettings{} { } std::unique_ptr pInput; std::unique_ptr pOutput; CProjector3D *pProjector; // For a `global' geometry. It will not match // the geometries of the input and output. SFDKSettings FDKSettings; EJobType eType; EJobMode eMode; }; // job structure that is lacking the output part pointer struct SJobInternal { public: SJobInternal(std::unique_ptr &&_pInput) : pInput(std::move(_pInput)), pProjector(0), FDKSettings{} { } std::unique_ptr pInput; CProjector3D *pProjector; SFDKSettings FDKSettings; EJobType eType; EJobMode eMode; }; typedef std::list TJobList; typedef std::list TJobListInternal; // pairs of (output part, list of jobs for that output) typedef std::list, TJobListInternal>> TJobSetInternal; // Perform a list of jobs. The outputs are assumed to be disjoint. bool doJobs(TJobList &jobs); // Perform a list of jobs. The outputs are assumed to be disjoint. bool doJobs(TJobSetInternal &jobs); SJob createJobFP(CProjector3D *pProjector, CFloat32VolumeData3D *pVolData, CFloat32ProjectionData3D *pProjData, EJobMode eMode); SJob createJobBP(CProjector3D *pProjector, CFloat32VolumeData3D *pVolData, CFloat32ProjectionData3D *pProjData, EJobMode eMode); // Convenience functions for creating and running a single FP or BP job bool doFP(CProjector3D *pProjector, CFloat32VolumeData3D *pVolData, CFloat32ProjectionData3D *pProjData, EJobMode eMode = MODE_SET); bool doBP(CProjector3D *pProjector, CFloat32VolumeData3D *pVolData, CFloat32ProjectionData3D *pProjData, EJobMode eMode = MODE_SET); bool doFDK(CProjector3D *pProjector, CFloat32VolumeData3D *pVolData, CFloat32ProjectionData3D *pProjData, bool bShortScan, const SFilterConfig &filterConfig, EJobMode eMode = MODE_SET); bool doFP(CProjector3D *pProjector, const std::vector& volData, const std::vector& projData, EJobMode eMode = MODE_SET); bool doBP(CProjector3D *pProjector, const std::vector& volData, const std::vector& projData, EJobMode eMode = MODE_SET); void setGPUIndices(const std::vector& GPUIndices); static void setGlobalGPUParams(const SGPUParams& params); protected: bool splitJobs(TJobSetInternal &jobs, size_t maxSize, int div, TJobSetInternal &split); std::vector m_GPUIndices; size_t m_iMaxSize; static SGPUParams* s_params; }; } #endif #endif astra-toolbox-2.3.0/include/astra/ConeProjectionGeometry3D.h000066400000000000000000000173611475635207100240660ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CONEPROJECTIONGEOMETRY3D #define _INC_ASTRA_CONEPROJECTIONGEOMETRY3D #include "ProjectionGeometry3D.h" namespace astra { /** * This class defines a 3D cone beam projection geometry. * * \par XML Configuration * \astra_xml_item{DetectorRowCount, int, Number of detectors for each projection.} * \astra_xml_item{DetectorColCount, int, Number of detectors for each projection.} * \astra_xml_item{DetectorSpacingX, float, Width of each detector.} * \astra_xml_item{DetectorSpacingY, float, Width of each detector.} * \astra_xml_item{ProjectionAngles, vector of float, projection angles in radians.} * \astra_xml_item{DistanceOriginDetector, float, Distance between the center of rotation and the detectorarray.} * \astra_xml_item{DistanceOriginSource, float, Distance between the center of rotation the the x-ray source.} * * \par MATLAB example * \astra_code{ * proj_geom = astra_struct('cone');\n * proj_geom.DetectorRowCount = 512;\n * proj_geom.DetectorColCount = 512;\n * proj_geom.DetectorSpacingX = 1.0;\n * proj_geom.DetectorSpacingY = 1.0;\n * proj_geom.ProjectionAngles = linspace(0,pi,100);\n * proj_geom.DistanceOriginDetector = 10000;\n * proj_geom.DistanceOriginSource = 10000;\n * } */ class _AstraExport CConeProjectionGeometry3D : public CProjectionGeometry3D { protected: /** * Distance from the origin of the coordinate system to the source. */ float32 m_fOriginSourceDistance; /** * Distance from the origin of the coordinate system to the detector (i.e., the distance between the origin and its orthogonal projection * onto the detector array). */ float32 m_fOriginDetectorDistance; public: /** Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the initialize() methods before the object can be used. Any use before calling initialize() * is not allowed, except calling the member function isInitialized(). */ CConeProjectionGeometry3D(); /** Constructor. Create an instance of the CParallelProjectionGeometry3D class. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorRowCount Number of rows of detectors. * @param _iDetectorColCount Number of columns detectors. * @param _fDetectorWidth Width of a detector cell, in unit lengths. All detector cells are assumed to have equal width. * @param _fDetectorHeight Height of a detector cell, in unit lengths. All detector cells are assumed to have equal width. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. All angles * are represented in radians and lie in the [0,2pi[ interval. */ CConeProjectionGeometry3D(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, float32 _fDetectorWidth, float32 _fDetectorHeight, std::vector &&_pfProjectionAngles, float32 _fOriginSourceDistance, float32 _fOriginDetectorDistance); /** Copy constructor. */ CConeProjectionGeometry3D(const CConeProjectionGeometry3D& _projGeom); /** Destructor. */ ~CConeProjectionGeometry3D(); /** Initialize the geometry with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize the geometry. If the object has been initialized before, the object is reinitialized * and memory is freed and reallocated if necessary. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorRowCount Number of rows of detectors. * @param _iDetectorColCount Number of columns detectors. * @param _fDetectorWidth Width of a detector cell, in unit lengths. All detector cells are assumed to have equal width. * @param _fDetectorHeight Height of a detector cell, in unit lengths. All detector cells are assumed to have equal height. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. All angles * are represented in radians and lie in the [0,2pi[ interval. */ bool initialize(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, float32 _fDetectorWidth, float32 _fDetectorHeight, std::vector &&_pfProjectionAngles, float32 _fOriginSourceDistance, float32 _fOriginDetectorDistance); /** Create a hard copy. */ virtual CProjectionGeometry3D* clone() const; /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CProjectionGeometry3D*) const; /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const; /** Returns true if the type of geometry defined in this class is the one specified in _sType. * * @param _sType geometry type to compare to. * @return true if _sType == "cone". */ virtual bool isOfType(const std::string& _sType) const; /** Returns the distance from the origin of the coordinate system to the source. * * @return Distance from the origin of the coordinate system to the source */ float32 getOriginSourceDistance() const; /** Returns the distance from the origin of the coordinate system to the detector * (i.e., the distance between the origin and its orthogonal projection onto the detector array). * * @return Distance from the origin of the coordinate system to the detector */ float32 getOriginDetectorDistance() const; /** Returns the distance from the source to the detector * (i.e., the distance between the source and its orthogonal projection onto the detector array). * * @return Distance from the source to the detector */ float32 getSourceDetectorDistance() const; virtual void projectPoint(double fX, double fY, double fZ, int iAngleIndex, double &fU, double &fV) const; }; // Returns the distance from the origin of the coordinate system to the source. inline float32 CConeProjectionGeometry3D::getOriginSourceDistance() const { return m_fOriginSourceDistance; } // Returns the distance from the origin of the coordinate system to the detector. inline float32 CConeProjectionGeometry3D::getOriginDetectorDistance() const { return m_fOriginDetectorDistance; } // Returns the distance from the source to the detector. inline float32 CConeProjectionGeometry3D::getSourceDetectorDistance() const { return (m_fOriginSourceDistance + m_fOriginDetectorDistance); } } // namespace astra #endif /* _INC_ASTRA_CONEPROJECTIONGEOMETRY3D */ astra-toolbox-2.3.0/include/astra/ConeVecProjectionGeometry3D.h000066400000000000000000000125051475635207100245170ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CONEVECPROJECTIONGEOMETRY3D #define _INC_ASTRA_CONEVECPROJECTIONGEOMETRY3D #include "ProjectionGeometry3D.h" #include "GeometryUtil3D.h" namespace astra { /** * This class defines a 3D cone beam projection geometry. * * \par XML Configuration * \astra_xml_item{DetectorRowCount, int, Number of detectors for each projection.} * \astra_xml_item{DetectorColCount, int, Number of detectors for each projection.} * \astra_xml_item{Vectors, matrix defining the 3D position of source and detector.} * * \par MATLAB example * \astra_code{ * proj_geom = astra_struct('cone_vec');\n * proj_geom.DetectorRowCount = 512;\n * proj_geom.DetectorColCount = 512;\n * proj_geom.Vectors = V;\n * } * * \par Vectors * Vectors is a matrix containing the actual geometry. Each row corresponds * to a single projection, and consists of: * ( srcX, srcY, srcZ, dX, dY, dZ, uX, uY, uZ, vX, vY, vZ ) * src: the ray source * d : the centre of the detector plane * u : the vector from detector pixel (0,0) to (0,1) * v : the vector from detector pixel (0,0) to (1,0) */ class _AstraExport CConeVecProjectionGeometry3D : public CProjectionGeometry3D { protected: std::vector m_ProjectionAngles; public: /** Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the initialize() methods before the object can be used. Any use before calling initialize() * is not allowed, except calling the member function isInitialized(). */ CConeVecProjectionGeometry3D(); /** Constructor. Create an instance of the CConeVecProjectionGeometry3D class. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorRowCount Number of rows of detectors. * @param _iDetectorColCount Number of columns detectors. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. */ CConeVecProjectionGeometry3D(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, std::vector &&_pProjectionAngles); /** Copy constructor. */ CConeVecProjectionGeometry3D(const CConeVecProjectionGeometry3D& _projGeom); /** Destructor. */ ~CConeVecProjectionGeometry3D(); /** Initialize the geometry with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize the geometry. If the object has been initialized before, the object is reinitialized * and memory is freed and reallocated if necessary. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorRowCount Number of rows of detectors. * @param _iDetectorColCount Number of columns detectors. * @param _pProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. */ bool initialize(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, std::vector &&_pProjectionAngles); virtual bool _check(); /** Create a hard copy. */ virtual CProjectionGeometry3D* clone() const; /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CProjectionGeometry3D*) const; /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const; /** Returns true if the type of geometry defined in this class is the one specified in _sType. * * @param _sType geometry type to compare to. * @return true if _sType == "cone_vec". */ virtual bool isOfType(const std::string& _sType) const; const SConeProjection* getProjectionVectors() const { return &m_ProjectionAngles[0]; } virtual void projectPoint(double fX, double fY, double fZ, int iAngleIndex, double &fU, double &fV) const; protected: virtual bool initializeAngles(const Config& _cfg); }; } // namespace astra #endif /* _INC_ASTRA_CONEVECPROJECTIONGEOMETRY3D */ astra-toolbox-2.3.0/include/astra/Config.h000066400000000000000000000132071475635207100204420ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CONFIG #define _INC_ASTRA_CONFIG #include "Globals.h" #include "XMLNode.h" #include "XMLDocument.h" #include namespace astra { struct ConfigCheckData { // For checking for unparsed nodes/options std::set parsedNodes; std::set parsedOptions; unsigned int parseDepth; }; /** * Configuration options for an ASTRA class. */ class _AstraExport Config { public: Config() { } virtual ~Config() { } void initialize(const std::string &rootname); template friend class ConfigReader; friend class ConfigWriter; protected: virtual bool has(const std::string &name) const = 0; virtual bool hasOption(const std::string &name) const = 0; virtual bool getSubConfig(const std::string &name, Config *&_cfg, std::string &type) const = 0; virtual bool getInt(const std::string &name, int &iValue) const = 0; virtual bool getFloat(const std::string &name, float &fValue) const = 0; virtual bool getDoubleArray(const std::string &name, std::vector &values) const = 0; virtual bool getIntArray(const std::string &name, std::vector &values) const = 0; virtual bool getString(const std::string &name, std::string &sValue) const = 0; virtual bool getOptionFloat(const std::string &name, float &fValue) const = 0; virtual bool getOptionInt(const std::string &name, int &iValue) const = 0; virtual bool getOptionUInt(const std::string &name, unsigned int &iValue) const = 0; virtual bool getOptionBool(const std::string &name, bool &bValue) const = 0; virtual bool getOptionString(const std::string &name, std::string &sValue) const = 0; virtual bool getOptionIntArray(const std::string &name, std::vector &values) const = 0; virtual std::list checkUnparsed(const ConfigCheckData &data) const = 0; }; template class ConfigReader { public: ConfigReader(const char *_name, T *_obj, const Config &_cfg); ~ConfigReader(); // Return true if config has a value bool has(const std::string &name); // Get and parse values, and return true if successful. // In case of missing values or parse errors, report the error and // return false. bool getRequiredInt(const std::string &name, int &iValue); bool getRequiredNumerical(const std::string &name, float &fValue); bool getRequiredID(const std::string &name, int &iValue); bool getRequiredNumericalArray(const std::string &name, std::vector &values); bool getRequiredIntArray(const std::string &name, std::vector &values); bool getRequiredString(const std::string &name, std::string &sValue); // Get a sub-configuration, and return true if succesful. // In case of missing values or parse errors, report the error and // return false. // For convenience, also directly get the "type" attribute of the subcfg. // If it has no type attribute, return empty string as type. (That is not // considered an error.) bool getRequiredSubConfig(const std::string &name, Config *&_cfg, std::string &type); // Get a value and parse it as an ID. Returns true if successful, // and false otherwise (returning -1 as iValue). Reports no errors. bool getID(const std::string &name, int &iValue); // Get a string value. Returns true if successful, and false otherwise // (return default value). Reports no errors. bool getString(const std::string &name, std::string &sValue, const std::string &sDefaultValue); // Return true if config has an option bool hasOption(const std::string &name); // Get and parse an option value. Returns true if the option is present // and successfully parsed. Returns true and the default value if the option // is not present. Returns false and the default value if the option // is present but malformed. Reports parsing errors. bool getOptionNumerical(const std::string &name, float &fValue, float fDefaultValue); bool getOptionInt(const std::string &name, int &iValue, int iDefaultValue); bool getOptionUInt(const std::string &name, unsigned int &iValue, unsigned int iDefaultValue); bool getOptionBool(const std::string &name, bool &bValue, bool bDefaultValue); bool getOptionString(const std::string &name, std::string &sValue, std::string sDefaultValue); bool getOptionIntArray(const std::string &name, std::vector &values); // Get and parse an option value as ID. Returns true if the option is // present and successfully parsed. Returns false and -1 if the option is // not present or malformed. Reports parsing errors. bool getOptionID(const std::string &name, int &iValue); private: T* object; const Config* cfg; const char* objName; bool stopParsing(); // returns true if no unused nodes/options void markNodeParsed(const std::string& name); void markOptionParsed(const std::string& name); }; } // end namespace #endif astra-toolbox-2.3.0/include/astra/CudaBackProjectionAlgorithm.h000066400000000000000000000061111475635207100245720ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDABACKPROJECTIONALGORITHM #define _INC_ASTRA_CUDABACKPROJECTIONALGORITHM #include "Globals.h" #include "Config.h" #include "CudaReconstructionAlgorithm2D.h" #ifdef ASTRA_CUDA namespace astra { /** * \brief * This class contains a GPU implementation of backprojection. * * \par XML Configuration * \astra_xml_item{ProjectionDataId, integer, Identifier of a projection data object as it is stored in the DataManager.} * \astra_xml_item{ReconstructionDataId, integer, Identifier of a volume data object as it is stored in the DataManager.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('BP_CUDA');\n * cfg.ProjectionDataId = sino_id;\n * cfg.ReconstructionDataId = recon_id;\n * alg_id = astra_mex_algorithm('create'\, cfg);\n * astra_mex_algorithm('iterate'\, alg_id\, 1);\n * astra_mex_algorithm('delete'\, alg_id);\n * } * */ class _AstraExport CCudaBackProjectionAlgorithm : public CCudaReconstructionAlgorithm2D { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "BP_CUDA"; /** Default constructor, containing no code. */ CCudaBackProjectionAlgorithm(); /** Destructor. */ virtual ~CCudaBackProjectionAlgorithm(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. (Ignored) * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. */ bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Get a description of the class. * * @return description string */ virtual std::string description() const; }; // inline functions inline std::string CCudaBackProjectionAlgorithm::description() const { return CCudaBackProjectionAlgorithm::type; }; } // end namespace #endif // ASTRA_CUDA #endif astra-toolbox-2.3.0/include/astra/CudaBackProjectionAlgorithm3D.h000066400000000000000000000101241475635207100247600ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDABACKPROJECTIONALGORITHM3D #define _INC_ASTRA_CUDABACKPROJECTIONALGORITHM3D #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "Data3D.h" #include "ReconstructionAlgorithm3D.h" #ifdef ASTRA_CUDA namespace astra { class _AstraExport CCudaBackProjectionAlgorithm3D : public CReconstructionAlgorithm3D { protected: /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - the projector is compatible with both data objects */ virtual bool _check(); public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "BP3D_CUDA"; /** Default constructor, does not initialize the object. */ CCudaBackProjectionAlgorithm3D(); /** Constructor with initialization. * * @param _pProjector Projector Object. * @param _pProjectionData ProjectionData3D object containing the projection data. * @param _pReconstruction VolumeData3D object for storing the reconstructed volume. */ CCudaBackProjectionAlgorithm3D(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pProjectionData, CFloat32VolumeData3D* _pReconstruction); /** Copy constructor. */ CCudaBackProjectionAlgorithm3D(const CCudaBackProjectionAlgorithm3D&); /** Destructor. */ virtual ~CCudaBackProjectionAlgorithm3D(); /** Clear this class. */ /* virtual void clear();*/ /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. * @param _pProjectionData ProjectionData3D object containing the projection data. * @param _pReconstruction VolumeData3D object for storing the reconstructed volume. * @return initialization successful? */ bool initialize(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pSinogram, CFloat32VolumeData3D* _pReconstruction); /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** * Sets the index of the used GPU index: first GPU has index 0 * * @param _iGPUIndex New GPU index. */ void setGPUIndex(int _iGPUIndex) { m_iGPUIndex = _iGPUIndex; } protected: int m_iGPUIndex; int m_iVoxelSuperSampling; /** Option to compute the column weights on the fly, divide by * them, and add the result to the current volume. This is both * more expensive and more GPU memory intensive than the regular * BP, but allows saving system RAM. */ bool m_bSIRTWeighting; void initializeFromProjector(); }; // inline functions inline std::string CCudaBackProjectionAlgorithm3D::description() const { return CCudaBackProjectionAlgorithm3D::type; }; } // end namespace #endif #endif astra-toolbox-2.3.0/include/astra/CudaCglsAlgorithm.h000066400000000000000000000070031475635207100225660ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDACGLSALGORITHM #define _INC_ASTRA_CUDACGLSALGORITHM #include "Globals.h" #include "Config.h" #include "CudaReconstructionAlgorithm2D.h" #ifdef ASTRA_CUDA namespace astra { /** * \brief * This class contains a GPU implementation of the CGLS algorithm. * * \par XML Configuration * \astra_xml_item{ProjectionGeometry, integer, Geometry of the projection data.} * \astra_xml_item{VolumeGeometry, integer, Geometry of the volume data.} * \astra_xml_item{ProjectionDataId, integer, Identifier of a projection data object as it is stored in the DataManager.} * \astra_xml_item{ReconstructionDataId, integer, Identifier of a volume data object as it is stored in the DataManager.} * \astra_xml_item_option{ReconstructionMaskId, integer, not used, Identifier of a volume data object that acts as a reconstruction mask. 0 = reconstruct on this pixel. 1 = don't reconstruct on this pixel.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('CGLS_CUDA');\n * cfg.ProjectionGeometry = proj_geom;\n * cfg.VolumeGeometry = vol_geom;\n * cfg.ProjectionDataId = sino_id;\n * cfg.ReconstructionDataId = recon_id;\n * cfg.option.ReconstructionMaskId = mask_id;\n * alg_id = astra_mex_algorithm('create'\, cfg);\n * astra_mex_algorithm('iterate'\, alg_id\, 10);\n * astra_mex_algorithm('delete'\, alg_id);\n * } * */ class AstraCGLS; class _AstraExport CCudaCglsAlgorithm : public CCudaReconstructionAlgorithm2D { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "CGLS_CUDA"; /** Default constructor, containing no code. */ CCudaCglsAlgorithm(); /** Destructor. */ virtual ~CCudaCglsAlgorithm(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class, use sequential order. * * @param _pProjector Projector Object. (Optional) * @param _pSinogram ProjectionData2D object containing the sinogram * @param _pReconstruction VolumeData2D for storing the reconstruction */ bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Get a description of the class. * * @return description string */ virtual std::string description() const; }; // inline functions inline std::string CCudaCglsAlgorithm::description() const { return CCudaCglsAlgorithm::type; }; } // end namespace #endif // ASTRA_CUDA #endif astra-toolbox-2.3.0/include/astra/CudaCglsAlgorithm3D.h000066400000000000000000000102331475635207100227540ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDACGLSALGORITHM3D #define _INC_ASTRA_CUDACGLSALGORITHM3D #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "Data3D.h" #include "ReconstructionAlgorithm3D.h" #ifdef ASTRA_CUDA namespace astra { class AstraCGLS3d; /** * \brief * This class contains the 3D implementation of the CGLS algorithm * */ class _AstraExport CCudaCglsAlgorithm3D : public CReconstructionAlgorithm3D { protected: /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - the projector is compatible with both data objects */ virtual bool _check(); public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "CGLS3D_CUDA"; /** Default constructor, does not initialize the object. */ CCudaCglsAlgorithm3D(); /** Constructor with initialization. * * @param _pProjector Projector Object. * @param _pProjectionData ProjectionData3D object containing the projection data. * @param _pReconstruction VolumeData3D object for storing the reconstructed volume. */ CCudaCglsAlgorithm3D(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pProjectionData, CFloat32VolumeData3D* _pReconstruction); /** Copy constructor. */ CCudaCglsAlgorithm3D(const CCudaCglsAlgorithm3D&); /** Destructor. */ virtual ~CCudaCglsAlgorithm3D(); /** Clear this class. */ /* virtual void clear();*/ /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. * @param _pProjectionData ProjectionData3D object containing the projection data. * @param _pReconstruction VolumeData3D object for storing the reconstructed volume. * @return initialization successful? */ bool initialize(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pSinogram, CFloat32VolumeData3D* _pReconstruction); /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** * Sets the index of the used GPU index: first GPU has index 0 * * @param _iGPUIndex New GPU index. */ void setGPUIndex(int _iGPUIndex) { m_iGPUIndex = _iGPUIndex; } /** Get the norm of the residual image. * Only a few algorithms support this method. * * @param _fNorm if supported, the norm is returned here * @return true if this operation is supported */ virtual bool getResidualNorm(float32& _fNorm); protected: AstraCGLS3d* m_pCgls; int m_iGPUIndex; bool m_bAstraCGLSInit; int m_iDetectorSuperSampling; int m_iVoxelSuperSampling; void initializeFromProjector(); }; // inline functions inline std::string CCudaCglsAlgorithm3D::description() const { return CCudaCglsAlgorithm3D::type; }; } // end namespace #endif #endif astra-toolbox-2.3.0/include/astra/CudaDartMaskAlgorithm.h000066400000000000000000000051551475635207100234120ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDADARTMASKALGORITHM #define _INC_ASTRA_CUDADARTMASKALGORITHM #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "Float32VolumeData2D.h" #ifdef ASTRA_CUDA namespace astraCUDA { class PDART; } namespace astra { class _AstraExport CCudaDartMaskAlgorithm : public CAlgorithm { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "DARTMASK_CUDA"; /** Default constructor, containing no code. */ CCudaDartMaskAlgorithm(); /** Destructor. */ virtual ~CCudaDartMaskAlgorithm(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class, use sequential order. * * @param _pSegmentation ... * @param iConn ... */ //bool initialize(CFloat32VolumeData2D* _pSegmentation, int _iConn); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); protected: /** Check this object. * * @return object initialized */ bool _check(); unsigned int m_iConn; unsigned int m_iThreshold; unsigned int m_iRadius; int m_iGPUIndex; CFloat32VolumeData2D* m_pSegmentation; CFloat32VolumeData2D* m_pMask; }; // inline functions inline std::string CCudaDartMaskAlgorithm::description() const { return CCudaDartMaskAlgorithm::type; }; } // end namespace #endif // ASTRA_CUDA #endif astra-toolbox-2.3.0/include/astra/CudaDartMaskAlgorithm3D.h000066400000000000000000000051121475635207100235720ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDADARTMASKALGORITHM3D #define _INC_ASTRA_CUDADARTMASKALGORITHM3D #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "Data3D.h" #ifdef ASTRA_CUDA namespace astra { class _AstraExport CCudaDartMaskAlgorithm3D : public CAlgorithm { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "DARTMASK3D_CUDA"; /** Default constructor, containing no code. */ CCudaDartMaskAlgorithm3D(); /** Destructor. */ virtual ~CCudaDartMaskAlgorithm3D(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class, use sequential order. * * @param _pSegmentation ... * @param iConn ... */ //bool initialize(CFloat32VolumeData2D* _pSegmentation, int _iConn); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); protected: /** Check this object. * * @return object initialized */ bool _check(); unsigned int m_iConn; unsigned int m_iThreshold; unsigned int m_iRadius; int m_iGPUIndex; CFloat32VolumeData3D* m_pSegmentation; CFloat32VolumeData3D* m_pMask; }; // inline functions inline std::string CCudaDartMaskAlgorithm3D::description() const { return CCudaDartMaskAlgorithm3D::type; }; } // end namespace #endif // ASTRA_CUDA #endif astra-toolbox-2.3.0/include/astra/CudaDartSmoothingAlgorithm.h000066400000000000000000000051441475635207100244640ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDADARTSMOOTHINGALGORITHM #define _INC_ASTRA_CUDADARTSMOOTHINGALGORITHM #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "Float32VolumeData2D.h" #ifdef ASTRA_CUDA namespace astraCUDA { class PDART; } namespace astra { class _AstraExport CCudaDartSmoothingAlgorithm : public CAlgorithm { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "DARTSMOOTHING_CUDA"; /** Default constructor, containing no code. */ CCudaDartSmoothingAlgorithm(); /** Destructor. */ virtual ~CCudaDartSmoothingAlgorithm(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class, use sequential order. * * @param _pSegmentation ... * @param iConn ... */ //bool initialize(CFloat32VolumeData2D* _pSegmentation, int _iConn); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); protected: /** Check this object. * * @return object initialized */ bool _check(); float m_fB; unsigned int m_iRadius; int m_iGPUIndex; CFloat32VolumeData2D* m_pIn; CFloat32VolumeData2D* m_pOut; }; // inline functions inline std::string CCudaDartSmoothingAlgorithm::description() const { return CCudaDartSmoothingAlgorithm::type; }; } // end namespace #endif // ASTRA_CUDA #endif astra-toolbox-2.3.0/include/astra/CudaDartSmoothingAlgorithm3D.h000066400000000000000000000051021475635207100246450ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDADARTSMOOTHINGALGORITHM3D #define _INC_ASTRA_CUDADARTSMOOTHINGALGORITHM3D #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "Data3D.h" #ifdef ASTRA_CUDA namespace astra { class _AstraExport CCudaDartSmoothingAlgorithm3D : public CAlgorithm { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "DARTSMOOTHING3D_CUDA"; /** Default constructor, containing no code. */ CCudaDartSmoothingAlgorithm3D(); /** Destructor. */ virtual ~CCudaDartSmoothingAlgorithm3D(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class, use sequential order. * * @param _pSegmentation ... * @param iConn ... */ //bool initialize(CFloat32VolumeData2D* _pSegmentation, int _iConn); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); protected: /** Check this object. * * @return object initialized */ bool _check(); float m_fB; unsigned int m_iRadius; int m_iGPUIndex; CFloat32VolumeData3D* m_pIn; CFloat32VolumeData3D* m_pOut; }; // inline functions inline std::string CCudaDartSmoothingAlgorithm3D::description() const { return CCudaDartSmoothingAlgorithm3D::type; }; } // end namespace #endif // ASTRA_CUDA #endif astra-toolbox-2.3.0/include/astra/CudaDataOperationAlgorithm.h000066400000000000000000000052001475635207100244250ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDADATAOPERATIONALGORITHM #define _INC_ASTRA_CUDADATAOPERATIONALGORITHM #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "Float32VolumeData2D.h" #ifdef ASTRA_CUDA namespace astraCUDA { class PDART; } namespace astra { class _AstraExport CCudaDataOperationAlgorithm : public CAlgorithm { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "DataOperation_CUDA"; /** Default constructor, containing no code. */ CCudaDataOperationAlgorithm(); /** Destructor. */ virtual ~CCudaDataOperationAlgorithm(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class, use sequential order. * * @param _pSegmentation ... * @param iConn ... */ //bool initialize(CFloat32VolumeData2D* _pSegmentation, int _iConn); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); protected: /** Check this object. * * @return object initialized */ bool _check(); int m_iGPUIndex; CFloat32Data2D* m_pMask; std::vector m_pData; std::vector m_fScalar; std::string m_sOperation; }; // inline functions inline std::string CCudaDataOperationAlgorithm::description() const { return CCudaDataOperationAlgorithm::type; }; } // end namespace #endif // ASTRA_CUDA #endif astra-toolbox-2.3.0/include/astra/CudaEMAlgorithm.h000066400000000000000000000045421475635207100222040ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDAEMALGORITHM #define _INC_ASTRA_CUDAEMALGORITHM #include "Globals.h" #include "Config.h" #include "CudaReconstructionAlgorithm2D.h" #ifdef ASTRA_CUDA namespace astra { class _AstraExport CCudaEMAlgorithm : public CCudaReconstructionAlgorithm2D { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "EM_CUDA"; /** Default constructor, containing no code. */ CCudaEMAlgorithm(); /** Destructor. */ virtual ~CCudaEMAlgorithm(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. (Optional) * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. */ bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Get a description of the class. * * @return description string */ virtual std::string description() const; }; // inline functions inline std::string CCudaEMAlgorithm::description() const { return CCudaEMAlgorithm::type; }; } // end namespace #endif // ASTRA_CUDA #endif astra-toolbox-2.3.0/include/astra/CudaFDKAlgorithm3D.h000066400000000000000000000077101475635207100224760ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDAFDKALGORITHM3D #define _INC_ASTRA_CUDAFDKALGORITHM3D #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "Data3D.h" #include "Filters.h" #include "ReconstructionAlgorithm3D.h" #ifdef ASTRA_CUDA namespace astra { /** * \brief * This class contains the 3D implementation of the FDK algorithm. * * \par XML Configuration * * \par MATLAB example * \astra_code{ * * } * */ class _AstraExport CCudaFDKAlgorithm3D : public CReconstructionAlgorithm3D { protected: /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - the projector is compatible with both data objects */ virtual bool _check(); public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "FDK_CUDA"; /** Default constructor, does not initialize the object. */ CCudaFDKAlgorithm3D(); /** Constructor with initialization. * * @param _pProjector Projector Object. * @param _pProjectionData ProjectionData3D object containing the projection data. * @param _pReconstruction VolumeData3D object for storing the reconstructed volume. */ CCudaFDKAlgorithm3D(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pProjectionData, CFloat32VolumeData3D* _pReconstruction); /** Copy constructor. */ CCudaFDKAlgorithm3D(const CCudaFDKAlgorithm3D&); /** Destructor. */ virtual ~CCudaFDKAlgorithm3D(); /** Clear this class. */ /* virtual void clear();*/ /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. * @param _pProjectionData ProjectionData3D object containing the projection data. * @param _pReconstruction VolumeData3D object for storing the reconstructed volume. * @return initialization successful? */ bool initialize(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pSinogram, CFloat32VolumeData3D* _pReconstruction); /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** * Sets the index of the used GPU index: first GPU has index 0 * * @param _iGPUIndex New GPU index. */ void setGPUIndex(int _iGPUIndex) { m_iGPUIndex = _iGPUIndex; } protected: int m_iGPUIndex; int m_iVoxelSuperSampling; bool m_bShortScan; SFilterConfig m_filterConfig; void initializeFromProjector(); }; // inline functions inline std::string CCudaFDKAlgorithm3D::description() const { return CCudaFDKAlgorithm3D::type; }; } // end namespace #endif #endif astra-toolbox-2.3.0/include/astra/CudaFilteredBackProjectionAlgorithm.h000066400000000000000000000044141475635207100262550ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef CUDAFILTEREDBACKPROJECTIONALGORITHM_H #define CUDAFILTEREDBACKPROJECTIONALGORITHM_H #ifdef ASTRA_CUDA #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" #include "CudaReconstructionAlgorithm2D.h" #include "Filters.h" #include "cuda/2d/astra.h" namespace astra { class _AstraExport CCudaFilteredBackProjectionAlgorithm : public CCudaReconstructionAlgorithm2D { public: static inline const char* const type = "FBP_CUDA"; private: SFilterConfig m_filterConfig; bool m_bShortScan; // short-scan mode for fan beam public: CCudaFilteredBackProjectionAlgorithm(); virtual ~CCudaFilteredBackProjectionAlgorithm(); virtual bool initialize(const Config& _cfg); bool initialize(CFloat32ProjectionData2D * _pSinogram, CFloat32VolumeData2D * _pReconstruction, E_FBPFILTER _eFilter, const float * _pfFilter = NULL, int _iFilterWidth = 0, int _iGPUIndex = -1, float _fFilterParameter = -1.0f); /** Get a description of the class. * * @return description string */ virtual std::string description() const; protected: bool check(); virtual void initCUDAAlgorithm(); }; // inline functions inline std::string CCudaFilteredBackProjectionAlgorithm::description() const { return CCudaFilteredBackProjectionAlgorithm::type; }; } #endif #endif /* CUDAFILTEREDBACKPROJECTIONALGORITHM2_H */ astra-toolbox-2.3.0/include/astra/CudaForwardProjectionAlgorithm.h000066400000000000000000000105571475635207100253470ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDAFORWARDPROJECTIONALGORITHM2 #define _INC_ASTRA_CUDAFORWARDPROJECTIONALGORITHM2 #include "Globals.h" #include "Algorithm.h" #ifdef ASTRA_CUDA namespace astra { class CProjector2D; class CProjectionGeometry2D; class CFloat32ProjectionData2D; class CFloat32VolumeData2D; /** * \brief * This class contains a GPU implementation of an algorithm that creates a forward projection * of a volume object and stores it into a sinogram. * * \par XML Configuration * \astra_xml_item{VolumeGeometry, integer, Geometry of the volume data.} * \astra_xml_item{ProjectionGeometry, integer, Geometry of the projection data.} * \astra_xml_item{VolumeDataId, integer, Identifier of the volume data object as it is stored in the DataManager.} * \astra_xml_item{ProjectionDataId, integer, Identifier of the resulting projection data object as it is stored in the DataManager.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('FP_CUDA2');\n * cfg.VolumeGeometry = vol_geom;\n * cfg.ProjectionGeometry = proj_geom;\n * cfg.VolumeDataId = vol_id;\n * cfg.ProjectionDataId = sino_id;\n * alg_id = astra_mex_algorithm('create'\, cfg);\n * astra_mex_algorithm('run'\, alg_id);\n * astra_mex_algorithm('delete'\, alg_id);\n * } * */ class _AstraExport CCudaForwardProjectionAlgorithm : public CAlgorithm { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "FP_CUDA"; /** Default constructor, containing no code. */ CCudaForwardProjectionAlgorithm(); /** Destructor. */ virtual ~CCudaForwardProjectionAlgorithm(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector2D object. (Optional) * @param _pVolume VolumeData2D object containing the phantom to compute sinogram from * @param _pSinogram ProjectionData2D object to store sinogram data in. * @return success */ bool initialize(CProjector2D* _pProjector, CFloat32VolumeData2D* _pVolume, CFloat32ProjectionData2D* _pSinogram); /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** Check this object. * * @return object initialized */ bool check(); CFloat32VolumeData2D* getVolume() { return m_pVolume; } CFloat32ProjectionData2D* getSinogram() { return m_pSinogram; } /** * Sets the index of the used GPU index: first GPU has index 0 * * @param _iGPUIndex New GPU index. */ void setGPUIndex(int _iGPUIndex); protected: //< Optional Projector2D object CProjector2D* m_pProjector; //< ProjectionData2D object containing the sinogram. CFloat32ProjectionData2D* m_pSinogram; //< VolumeData2D object containing the phantom. CFloat32VolumeData2D* m_pVolume; //< Index of GPU to use int m_iGPUIndex; //< Number of rays per detector element int m_iDetectorSuperSampling; void initializeFromProjector(); }; // inline functions inline std::string CCudaForwardProjectionAlgorithm::description() const { return CCudaForwardProjectionAlgorithm::type; }; } // end namespace #endif // ASTRA_CUDA #endif astra-toolbox-2.3.0/include/astra/CudaForwardProjectionAlgorithm3D.h000066400000000000000000000060511475635207100255300ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDAFORWARDPROJECTIONALGORITHM3D #define _INC_ASTRA_CUDAFORWARDPROJECTIONALGORITHM3D #include "Globals.h" #include "Algorithm.h" #include "Data3D.h" #ifdef ASTRA_CUDA namespace astra { class CProjector3D; class _AstraExport CCudaForwardProjectionAlgorithm3D : public CAlgorithm { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "FP3D_CUDA"; /** Default constructor, containing no code. */ CCudaForwardProjectionAlgorithm3D(); /** Destructor. */ virtual ~CCudaForwardProjectionAlgorithm3D(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. * @param _pProjectionData ProjectionData3D object for storing the projection data. * @param _pReconstruction VolumeData3D object containing the volume. * @return initialization successful? */ bool initialize(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pSinogram, CFloat32VolumeData3D* _pReconstruction, int _iGPUindex = -1, int _iDetectorSuperSampling = 1); /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** Check this object. * * @return object initialized */ bool check(); /** * Sets the index of the used GPU index: first GPU has index 0 * * @param _iGPUIndex New GPU index. */ void setGPUIndex(int _iGPUIndex); protected: CProjector3D* m_pProjector; CFloat32ProjectionData3D* m_pProjections; CFloat32VolumeData3D* m_pVolume; int m_iGPUIndex; int m_iDetectorSuperSampling; void initializeFromProjector(); }; // inline functions inline std::string CCudaForwardProjectionAlgorithm3D::description() const { return CCudaForwardProjectionAlgorithm3D::type; }; } #endif #endif astra-toolbox-2.3.0/include/astra/CudaProjector2D.h000066400000000000000000000102061475635207100221630ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDAPROJECTOR2D #define _INC_ASTRA_CUDAPROJECTOR2D #ifdef ASTRA_CUDA #include "ParallelProjectionGeometry2D.h" #include "Float32Data2D.h" #include "Projector2D.h" #include "cuda/2d/astra.h" namespace astra { /** This is a two-dimensional CUDA-projector. * It is essentially a fake projector, containing settings relevant for the * actual CUDA code. */ class _AstraExport CCudaProjector2D : public CProjector2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - blobvalues are ok */ virtual bool _check(); public: // type of the projector, needed to register with CProjectorFactory static inline const char* const type = "cuda"; /** Default constructor. */ CCudaProjector2D(); /** Constructor. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. */ CCudaProjector2D(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); ~CCudaProjector2D(); /** Initialize the projector with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Clear this class. */ virtual void clear(); virtual int getProjectionWeightsCount(int _iProjectionIndex) { return 0; } virtual void computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount) {} virtual std::vector projectPoint(int _iRow, int _iCol) { std::vector x; return x; } template void project(Policy& _policy) {} template void projectSingleProjection(int _iProjection, Policy& _policy) {} template void projectSingleRay(int _iProjection, int _iDetector, Policy& _policy) {} /** Return the type of this projector. * * @return identification type of this projector */ virtual std::string getType(); /** get a description of the class * * @return description string */ virtual std::string description() const; Cuda2DProjectionKernel getProjectionKernel() const { return m_projectionKernel; } int getVoxelSuperSampling() const { return m_iVoxelSuperSampling; } int getDetectorSuperSampling() const { return m_iDetectorSuperSampling; } int getGPUIndex() const { return m_iGPUIndex; } protected: Cuda2DProjectionKernel m_projectionKernel; int m_iVoxelSuperSampling; int m_iDetectorSuperSampling; int m_iGPUIndex; }; //---------------------------------------------------------------------------------------- inline std::string CCudaProjector2D::getType() { return type; } } // namespace astra #endif #endif astra-toolbox-2.3.0/include/astra/CudaProjector3D.h000066400000000000000000000071551475635207100221750ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef INC_ASTRA_CUDAPROJECTOR3D #define INC_ASTRA_CUDAPROJECTOR3D #ifdef ASTRA_CUDA #include #include #include "Globals.h" #include "Config.h" #include "Projector3D.h" #include "cuda/3d/astra3d.h" namespace astra { /** This is a three-dimensional CUDA-projector. * It is essentially a fake projector, containing settings relevant for the * actual CUDA code. */ class _AstraExport CCudaProjector3D : public CProjector3D { protected: /** Check variable values. */ bool _check(); /** Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. * Should only be used by constructors. Otherwise use the clear() function. */ void _clear(); public: // type of the projector, needed to register with CProjectorFactory static inline const char* const type = "cuda3d"; /** * Default Constructor. */ CCudaProjector3D(); /** Destructor, is virtual to show that we are aware subclass destructor is called. */ virtual ~CCudaProjector3D(); /** Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. */ void clear(); /** Initialize the projector with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); virtual void computeSingleRayWeights(int _iProjectionIndex, int _iSliceIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount) {} virtual int getProjectionWeightsCount(int _iProjectionIndex) { return 0; } template void project(Policy& _policy) {} template void projectSingleProjection(int _iProjection, Policy& _policy) {} template void projectSingleRay(int _iProjection, int _iSlice, int _iDetector, Policy& _policy) {} /** Return the type of this projector. * * @return identification type of this projector */ virtual std::string getType() { return type; } /** get a description of the class * * @return description string */ virtual std::string description() const; Cuda3DProjectionKernel getProjectionKernel() const { return m_projectionKernel; } int getVoxelSuperSampling() const { return m_iVoxelSuperSampling; } int getDetectorSuperSampling() const { return m_iDetectorSuperSampling; } int getGPUIndex() const { return m_iGPUIndex; } protected: Cuda3DProjectionKernel m_projectionKernel; int m_iVoxelSuperSampling; int m_iDetectorSuperSampling; int m_iGPUIndex; }; } // namespace astra #endif // ASTRA_CUDA #endif /* INC_ASTRA_CUDAPROJECTOR3D */ astra-toolbox-2.3.0/include/astra/CudaReconstructionAlgorithm2D.h000066400000000000000000000070541475635207100251130ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDARECONSTRUCTIONALGORITHM2D #define _INC_ASTRA_CUDARECONSTRUCTIONALGORITHM2D #include "Globals.h" #include "Config.h" #include "ReconstructionAlgorithm2D.h" #include "Projector2D.h" #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" namespace astraCUDA { class ReconAlgo; } namespace astra { /** * This is a base class for the different CUDA implementations of 2D reconstruction algorithms. * They don't use a Projector, and share GPUIndex and DetectorSuperSampling options. * */ class _AstraExport CCudaReconstructionAlgorithm2D : public CReconstructionAlgorithm2D { public: /** Destructor. */ virtual ~CCudaReconstructionAlgorithm2D(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Clear this class. */ virtual void clear(); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** Get the norm of the residual image. * Only a few algorithms support this method. * * @param _fNorm if supported, the norm is returned here * @return true if this operation is supported */ virtual bool getResidualNorm(float32& _fNorm); /** * Sets the index of the used GPU index: first GPU has index 0 * * @param _iGPUIndex New GPU index. */ void setGPUIndex(int _iGPUIndex); /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); protected: CCudaReconstructionAlgorithm2D(); /** Check this object. * * @return object initialized */ bool _check(); /** Initial clearing. Only to be used by constructors. */ void _clear(); /** Initialize class. For internal use only. */ bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Set up geometry. For internal use only. */ bool setupGeometry(); /** Initialize CUDA algorithm. For internal use only. */ virtual void initCUDAAlgorithm(); /** The internally used CUDA algorithm object */ astraCUDA::ReconAlgo *m_pAlgo; int m_iDetectorSuperSampling; int m_iPixelSuperSampling; int m_iGPUIndex; bool m_bAlgoInit; void initializeFromProjector(); virtual bool requiresProjector() const { return false; } }; // inline functions inline std::string CCudaReconstructionAlgorithm2D::description() const { return "2D CUDA Reconstruction Algorithm"; }; } // end namespace #endif astra-toolbox-2.3.0/include/astra/CudaRoiSelectAlgorithm.h000066400000000000000000000050251475635207100235710ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDAROISELECTALGORITHM #define _INC_ASTRA_CUDAROISELECTALGORITHM #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "Float32VolumeData2D.h" #ifdef ASTRA_CUDA namespace astraCUDA { class PDART; } namespace astra { class _AstraExport CCudaRoiSelectAlgorithm : public CAlgorithm { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "RoiSelect_CUDA"; /** Default constructor, containing no code. */ CCudaRoiSelectAlgorithm(); /** Destructor. */ virtual ~CCudaRoiSelectAlgorithm(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class, use sequential order. * * @param _pSegmentation ... * @param iConn ... */ //bool initialize(CFloat32VolumeData2D* _pSegmentation, int _iConn); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); protected: /** Check this object. * * @return object initialized */ bool _check(); float32 m_fRadius; int m_iGPUIndex; CFloat32VolumeData2D* m_pData; }; // inline functions inline std::string CCudaRoiSelectAlgorithm::description() const { return CCudaRoiSelectAlgorithm::type; }; } // end namespace #endif // ASTRA_CUDA #endif astra-toolbox-2.3.0/include/astra/CudaSartAlgorithm.h000066400000000000000000000067421475635207100226200ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDASARTALGORITHM #define _INC_ASTRA_CUDASARTALGORITHM #include "Globals.h" #include "Config.h" #include "CudaReconstructionAlgorithm2D.h" #ifdef ASTRA_CUDA namespace astra { /** * \brief * This class contains a GPU implementation of the SART algorithm. * * \par XML Configuration * \astra_xml_item{ProjectionDataId, integer, Identifier of a projection data object as it is stored in the DataManager.} * \astra_xml_item{ReconstructionDataId, integer, Identifier of a volume data object as it is stored in the DataManager.} * \astra_xml_item_option{ReconstructionMaskId, integer, not used, Identifier of a volume data object that acts as a reconstruction mask. 0 = reconstruct on this pixel. 1 = don't reconstruct on this pixel.} * \astra_xml_item_option{Relaxation, float, 1, The relaxation factor.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('SART_CUDA');\n * cfg.ProjectionDataId = sino_id;\n * cfg.ReconstructionDataId = recon_id;\n * cfg.option.ReconstructionMaskId = mask_id;\n * cfg.option.Relaxation = 1.0;\n * alg_id = astra_mex_algorithm('create'\, cfg);\n * astra_mex_algorithm('iterate'\, alg_id\, 10);\n * astra_mex_algorithm('delete'\, alg_id);\n * } * */ class _AstraExport CCudaSartAlgorithm : public CCudaReconstructionAlgorithm2D { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "SART_CUDA"; /** Default constructor, containing no code. */ CCudaSartAlgorithm(); /** Destructor. */ virtual ~CCudaSartAlgorithm(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. (Optional) * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. */ bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Get a description of the class. * * @return description string */ virtual std::string description() const; protected: /** Relaxation factor */ float m_fLambda; virtual void initCUDAAlgorithm(); }; // inline functions inline std::string CCudaSartAlgorithm::description() const { return CCudaSartAlgorithm::type; }; } // end namespace #endif // ASTRA_CUDA #endif astra-toolbox-2.3.0/include/astra/CudaSirtAlgorithm.h000066400000000000000000000103741475635207100226240ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDASIRTALGORITHM2 #define _INC_ASTRA_CUDASIRTALGORITHM2 #include "Globals.h" #include "Config.h" #include "CudaReconstructionAlgorithm2D.h" #ifdef ASTRA_CUDA namespace astra { /** * \brief * This class contains a GPU implementation of the SIRT (Simultaneous Iterative Reconstruction Technique) algorithm. * * The update step of pixel \f$v_j\f$ for iteration \f$k\f$ is given by: * \f[ * v_j^{(k+1)} = v_j^{(k)} + \alpha \sum_{i=1}^{M} \left( \frac{w_{ij}\left( p_i - \sum_{r=1}^{N} w_{ir}v_r^{(k)}\right)}{\sum_{k=1}^{N} w_{ik}} \right) \frac{1}{\sum_{l=1}^{M}w_{lj}} * \f] * * \par XML Configuration * \astra_xml_item{ProjectionGeometry, integer, Geometry of the projection data.} * \astra_xml_item{VolumeGeometry, integer, Geometry of the volume data.} * \astra_xml_item{ProjectionDataId, integer, Identifier of a projection data object as it is stored in the DataManager.} * \astra_xml_item{ReconstructionDataId, integer, Identifier of a volume data object as it is stored in the DataManager.} * \astra_xml_item_option{ReconstructionMaskId, integer, not used, Identifier of a volume data object that acts as a reconstruction mask. 0 = reconstruct on this pixel. 1 = don't reconstruct on this pixel.} * \astra_xml_item_option{Relaxation, float, 1, Relaxation parameter.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('SIRT_CUDA2');\n * cfg.ProjectionGeometry = proj_geom;\n * cfg.VolumeGeometry = vol_geom;\n * cfg.ProjectionDataId = sino_id;\n * cfg.ReconstructionDataId = recon_id;\n * cfg.option.ReconstructionMaskId = mask_id;\n * cfg.option.Relaxation = 1.0;\n * alg_id = astra_mex_algorithm('create'\, cfg);\n * astra_mex_algorithm('iterate'\, alg_id\, 10);\n * astra_mex_algorithm('delete'\, alg_id);\n * } * * \par References * [1] "Computational Analysis and Improvement of SIRT", J. Gregor, T. Benson, IEEE Transactions on Medical Imaging, Vol. 22, No. 7, July 2008. */ class _AstraExport CCudaSirtAlgorithm : public CCudaReconstructionAlgorithm2D { public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "SIRT_CUDA"; /** Default constructor, containing no code. */ CCudaSirtAlgorithm(); /** Destructor. */ virtual ~CCudaSirtAlgorithm(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. (Optional) * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. */ bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Get a description of the class. * * @return description string */ virtual std::string description() const; protected: CFloat32VolumeData2D* m_pMinMask; CFloat32VolumeData2D* m_pMaxMask; /** Relaxation factor */ float m_fLambda; virtual void initCUDAAlgorithm(); }; // inline functions inline std::string CCudaSirtAlgorithm::description() const { return CCudaSirtAlgorithm::type; }; } // end namespace #endif // ASTRA_CUDA #endif astra-toolbox-2.3.0/include/astra/CudaSirtAlgorithm3D.h000066400000000000000000000113451475635207100230120ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_CUDASIRTALGORITHM3D #define _INC_ASTRA_CUDASIRTALGORITHM3D #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "Data3D.h" #include "ReconstructionAlgorithm3D.h" #ifdef ASTRA_CUDA namespace astra { class AstraSIRT3d; /** * \brief * This class contains the 3D implementation of the SIRT (Simultaneous Iterative Reconstruction Technique) algorithm. * * The update step of pixel \f$v_j\f$ for iteration \f$k\f$ is given by: * \f[ * v_j^{(k+1)} = v_j^{(k)} + \lambda \sum_{i=1}^{M} \left( \frac{w_{ij}\left( p_i - \sum_{r=1}^{N} w_{ir}v_r^{(k)}\right)}{\sum_{k=1}^{N} w_{ik}} \right) \frac{1}{\sum_{l=1}^{M}w_{lj}} * \f] * * \par XML Configuration * * \par MATLAB example * \astra_code{ * * } * * \par References * [1] "Computational Analysis and Improvement of SIRT", J. Gregor, T. Benson, IEEE Transactions on Medical Imaging, Vol. 22, No. 7, July 2008. */ class _AstraExport CCudaSirtAlgorithm3D : public CReconstructionAlgorithm3D { protected: /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - the projector is compatible with both data objects */ virtual bool _check(); public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "SIRT3D_CUDA"; /** Default constructor, does not initialize the object. */ CCudaSirtAlgorithm3D(); /** Constructor with initialization. * * @param _pProjector Projector Object. * @param _pProjectionData ProjectionData3D object containing the projection data. * @param _pReconstruction VolumeData3D object for storing the reconstructed volume. */ CCudaSirtAlgorithm3D(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pProjectionData, CFloat32VolumeData3D* _pReconstruction); /** Copy constructor. */ CCudaSirtAlgorithm3D(const CCudaSirtAlgorithm3D&); /** Destructor. */ virtual ~CCudaSirtAlgorithm3D(); /** Clear this class. */ /* virtual void clear();*/ /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. * @param _pProjectionData ProjectionData3D object containing the projection data. * @param _pReconstruction VolumeData3D object for storing the reconstructed volume. * @return initialization successful? */ bool initialize(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pSinogram, CFloat32VolumeData3D* _pReconstruction); /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** * Sets the index of the used GPU index: first GPU has index 0 * * @param _iGPUIndex New GPU index. */ void setGPUIndex(int _iGPUIndex) { m_iGPUIndex = _iGPUIndex; } /** Get the norm of the residual image. * Only a few algorithms support this method. * * @param _fNorm if supported, the norm is returned here * @return true if this operation is supported */ virtual bool getResidualNorm(float32& _fNorm); protected: AstraSIRT3d* m_pSirt; int m_iGPUIndex; bool m_bAstraSIRTInit; int m_iDetectorSuperSampling; int m_iVoxelSuperSampling; float m_fLambda; void initializeFromProjector(); }; // inline functions inline std::string CCudaSirtAlgorithm3D::description() const { return CCudaSirtAlgorithm3D::type; }; } // end namespace #endif #endif astra-toolbox-2.3.0/include/astra/Data3D.h000066400000000000000000000147031475635207100202770ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_DATA3D #define _INC_ASTRA_DATA3D #include "Globals.h" #include "VolumeGeometry3D.h" #include "ProjectionGeometry3D.h" #include "cuda/3d/mem3d.h" #include namespace astra { class _AstraExport CDataStorage { public: CDataStorage() { } virtual ~CDataStorage() { } virtual bool isMemory() const =0; virtual bool isGPU() const =0; virtual bool isFloat32() const =0; }; template class _AstraExport CDataMemory : public CDataStorage { public: CDataMemory(size_t size) : m_pfData(nullptr) { _allocateData(size); } virtual ~CDataMemory() { _freeData(); } T* getData() { return m_pfData; } const T* getData() const { return m_pfData; } virtual bool isMemory() const { return true; } virtual bool isGPU() const { return false; } virtual bool isFloat32() const { return std::is_same_v; } protected: T* m_pfData; CDataMemory() : m_pfData(nullptr) { } private: void _allocateData(size_t size); void _freeData(); }; class _AstraExport CData3D { public: typedef enum {BASE, PROJECTION, VOLUME} EDataType; virtual ~CData3D() { delete m_storage; } int getDimensionCount() const { return 3; } std::array getShape() const { return m_iDims; } int getWidth() const { return m_iDims[0]; } int getHeight() const { return m_iDims[1]; } int getDepth() const { return m_iDims[2]; } size_t getSize() const { return m_iSize; } virtual EDataType getType() const =0; std::string description() const; CDataStorage *getStorage() const { return m_storage; } // Convenience functions as this is the common case bool isFloat32Memory() const { return m_storage->isMemory() && m_storage->isFloat32(); } float32 *getFloat32Memory() const { return isFloat32Memory() ? dynamic_cast*>(m_storage)->getData() : nullptr; } bool isInitialized() const { return true; } protected: CData3D(int x, int y, int z, CDataStorage *storage) : m_iDims{x, y, z}, m_iSize((size_t)x*y*z), m_storage(storage) { } std::array m_iDims; ///< dimensions of the data (width, height, depth) size_t m_iSize; ///< size of the data (width*height*depth) CDataStorage *m_storage; }; template class _AstraExport CData3DObject : public CData3D { protected: std::unique_ptr m_pGeometry; CData3DObject(int x, int y, int z, std::unique_ptr&& geom, CDataStorage *storage) : CData3D(x, y, z, storage), m_pGeometry(std::move(geom)) { } CData3DObject(int x, int y, int z, const G &geom, CDataStorage *storage) : CData3D(x, y, z, storage), m_pGeometry(geom.clone()) { } virtual ~CData3DObject() { } public: const G& getGeometry() const { return *m_pGeometry; } void changeGeometry(std::unique_ptr &&geom) { m_pGeometry = std::move(geom); } void changeGeometry(const G &geom) { m_pGeometry.reset(geom.clone()); } }; class _AstraExport CFloat32ProjectionData3D : public CData3DObject { public: CFloat32ProjectionData3D(std::unique_ptr&& geom, CDataStorage *storage) : CData3DObject(geom->getDetectorColCount(), geom->getProjectionCount(), geom->getDetectorRowCount(), std::move(geom), storage) { } CFloat32ProjectionData3D(const CProjectionGeometry3D &geom, CDataStorage *storage) : CData3DObject(geom.getDetectorColCount(), geom.getProjectionCount(), geom.getDetectorRowCount(), geom, storage) { } int getDetectorRowCount() const { return m_iDims[2]; } int getDetectorColCount() const { return m_iDims[0]; } int getDetectorTotCount() const { return m_iDims[0] * m_iDims[2]; } int getAngleCount() const { return m_iDims[1]; } virtual EDataType getType() const { return PROJECTION; } }; class _AstraExport CFloat32VolumeData3D : public CData3DObject { public: CFloat32VolumeData3D(std::unique_ptr&& geom, CDataStorage *storage) : CData3DObject(geom->getGridColCount(), geom->getGridRowCount(), geom->getGridSliceCount(), std::move(geom), storage) { } CFloat32VolumeData3D(const CVolumeGeometry3D &geom, CDataStorage *storage) : CData3DObject(geom.getGridColCount(), geom.getGridRowCount(), geom.getGridSliceCount(), geom, storage) { } int getColCount() const { return m_iDims[0]; } int getRowCount() const { return m_iDims[1]; } int getSliceCount() const { return m_iDims[2]; } virtual EDataType getType() const { return VOLUME; } }; #ifdef ASTRA_CUDA class _AstraExport CDataGPU : public CDataStorage { protected: /** Handle for the memory block */ astraCUDA3d::MemHandle3D m_hnd; CDataGPU() { } public: CDataGPU(astraCUDA3d::MemHandle3D hnd) : m_hnd(hnd) { } virtual bool isMemory() const { return false; } virtual bool isGPU() const { return true; } virtual bool isFloat32() const { return true; } // TODO astraCUDA3d::MemHandle3D& getHandle() { return m_hnd; } }; #endif template class CDataMemory; template class CData3DObject; template class CData3DObject; // Utility functions that create CDataMemory and Data3D objects together _AstraExport CFloat32ProjectionData3D *createCFloat32ProjectionData3DMemory(const CProjectionGeometry3D &geom); _AstraExport CFloat32ProjectionData3D *createCFloat32ProjectionData3DMemory(std::unique_ptr &&geom); _AstraExport CFloat32VolumeData3D *createCFloat32VolumeData3DMemory(const CVolumeGeometry3D &geom); _AstraExport CFloat32VolumeData3D *createCFloat32VolumeData3DMemory(std::unique_ptr &&geom); } // end namespace astra #endif // _INC_ASTRA_FLOAT32DATA2D astra-toolbox-2.3.0/include/astra/DataProjector.h000066400000000000000000000252341475635207100220010ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_DATAPROJECTOR #define _INC_ASTRA_DATAPROJECTOR #include "Projector2D.h" #include "TypeList.h" #include "ProjectorTypelist.h" #include "DataProjectorPolicies.h" namespace astra { /** * Interface class for the Data Projector. The sole purpose of this class is to force child classes to implement a series of methods */ class CDataProjectorInterface { public: CDataProjectorInterface() { } virtual ~CDataProjectorInterface() { } virtual void project() = 0; virtual void projectSingleProjection(int _iProjection) = 0; virtual void projectSingleRay(int _iProjection, int _iDetector) = 0; // virtual void projectSingleVoxel(int _iRow, int _iCol) = 0; // virtual void projectAllVoxels() = 0; }; /** * Templated Data Projector Class. In this class a specific projector and policies are combined. */ template class CDataProjector: public CDataProjectorInterface { private: Projector* m_pProjector; Policy m_pPolicy; public: CDataProjector() {}; CDataProjector(Projector* _p, Policy _a); ~CDataProjector(); virtual void project(); virtual void projectSingleProjection(int _iProjection); virtual void projectSingleRay(int _iProjection, int _iDetector); // virtual void projectSingleVoxel(int _iRow, int _iCol); // virtual void projectAllVoxels(); }; //---------------------------------------------------------------------------------------- /** * Constructor */ template CDataProjector::CDataProjector(Projector* _p, Policy _a) { m_pProjector = _p; m_pPolicy = _a; } //---------------------------------------------------------------------------------------- /** * Destructor */ template CDataProjector::~CDataProjector() { // does nothing } //---------------------------------------------------------------------------------------- /** * Compute projection using the algorithm specific to the projector type */ template void CDataProjector::project() { m_pProjector->project(m_pPolicy); } //---------------------------------------------------------------------------------------- /** * Compute just one projection using the algorithm specific to the projector type */ template void CDataProjector::projectSingleProjection(int _iProjection) { m_pProjector->projectSingleProjection(_iProjection, m_pPolicy); } //---------------------------------------------------------------------------------------- /** * Compute projection of one ray using the algorithm specific to the projector type */ template void CDataProjector::projectSingleRay(int _iProjection, int _iDetector) { m_pProjector->projectSingleRay(_iProjection, _iDetector, m_pPolicy); } //---------------------------------------------------------------------------------------- //template //void CDataProjector::projectSingleVoxel(int _iRow, int _iCol) //{ // m_pProjector->projectSingleVoxel(_iRow, _iCol, m_pPolicy); //} //---------------------------------------------------------------------------------------- //template //void CDataProjector::projectAllVoxels() //{ // m_pProjector->projectAllVoxels(m_pPolicy); //} //---------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------- // Create a new datainterface from the projector TypeList template CDataProjectorInterface *createDataProjector_internal(const std::string &name, CProjector2D* _pProjector, const Policy& _policy) { if (name == T::type) return new CDataProjector(static_cast(_pProjector), _policy); if constexpr (sizeof...(Ts) > 0) return createDataProjector_internal(name, _pProjector, _policy); else return nullptr; } template CDataProjectorInterface *createDataProjector(const std::string &name, CProjector2D* _pProjector, const Policy& _policy, TypeList) { if constexpr (sizeof...(Ts) > 0) return createDataProjector_internal(name, _pProjector, _policy); else return nullptr; } //----------------------------------------------------------------------------------------- /** * Data Projector Dispatcher - 1 Policy */ template static CDataProjectorInterface* dispatchDataProjector(CProjector2D* _pProjector, const Policy& _policy) { return createDataProjector(_pProjector->getType(), _pProjector, _policy, Projector2DTypeList{}); } /** * Data Projector Dispatcher - 2 Policies */ template static CDataProjectorInterface* dispatchDataProjector(CProjector2D* _pProjector, const Policy1& _policy, const Policy2& _policy2, bool _bUsePolicy1 = true, bool _bUsePolicy2 = true) { if (!_bUsePolicy1 && !_bUsePolicy2) { return dispatchDataProjector(_pProjector, EmptyPolicy()); } else if (!_bUsePolicy1) { return dispatchDataProjector(_pProjector, _policy2); } else if (!_bUsePolicy2) { return dispatchDataProjector(_pProjector, _policy); } else { return dispatchDataProjector(_pProjector, CombinePolicy(_policy, _policy2)); } } /** * Data Projector Dispatcher - 3 Policies */ template static CDataProjectorInterface* dispatchDataProjector(CProjector2D* _pProjector, const Policy1& _policy1, const Policy2& _policy2, const Policy3& _policy3, bool _bUsePolicy1 = true, bool _bUsePolicy2 = true, bool _bUsePolicy3 = true) { if (!_bUsePolicy1) { return dispatchDataProjector(_pProjector, _policy2, _policy3, _bUsePolicy2, _bUsePolicy3); } else if (!_bUsePolicy2) { return dispatchDataProjector(_pProjector, _policy1, _policy3, _bUsePolicy1, _bUsePolicy3); } else if (!_bUsePolicy3) { return dispatchDataProjector(_pProjector, _policy1, _policy2, _bUsePolicy1, _bUsePolicy2); } else { return dispatchDataProjector(_pProjector, Combine3Policy(_policy1, _policy2, _policy3)); } } /** * Data Projector Dispatcher - 4 Policies */ template static CDataProjectorInterface* dispatchDataProjector(CProjector2D* _pProjector, const Policy1& _policy1, const Policy2& _policy2, const Policy3& _policy3, const Policy4& _policy4, bool _bUsePolicy1 = true, bool _bUsePolicy2 = true, bool _bUsePolicy3 = true, bool _bUsePolicy4 = true) { if (!_bUsePolicy1) { return dispatchDataProjector(_pProjector, _policy2, _policy3, _policy4, _bUsePolicy2, _bUsePolicy3, _bUsePolicy4); } else if (!_bUsePolicy2) { return dispatchDataProjector(_pProjector, _policy1, _policy3, _policy4, _bUsePolicy1, _bUsePolicy3, _bUsePolicy4); } else if (!_bUsePolicy3) { return dispatchDataProjector(_pProjector, _policy1, _policy2, _policy4, _bUsePolicy1, _bUsePolicy2, _bUsePolicy4); } else if (!_bUsePolicy4) { return dispatchDataProjector(_pProjector, _policy1, _policy2, _policy3, _bUsePolicy1, _bUsePolicy2, _bUsePolicy3); } else { return dispatchDataProjector(_pProjector, Combine4Policy(_policy1, _policy2, _policy3, _policy4)); } } /** * Data Projector Dispatcher - 5 Policies */ template static CDataProjectorInterface* dispatchDataProjector(CProjector2D* _pProjector, const Policy1& _policy1, const Policy2& _policy2, const Policy3& _policy3, const Policy4& _policy4, const Policy5& _policy5, bool _bUsePolicy1 = true, bool _bUsePolicy2 = true, bool _bUsePolicy3 = true, bool _bUsePolicy4 = true, bool _bUsePolicy5 = true) { if (!_bUsePolicy1) { return dispatchDataProjector(_pProjector, _policy2, _policy3, _policy4, _policy5, _bUsePolicy2, _bUsePolicy3, _bUsePolicy4, _bUsePolicy5); } else if (!_bUsePolicy2) { return dispatchDataProjector(_pProjector, _policy1, _policy3, _policy4, _policy5, _bUsePolicy1, _bUsePolicy3, _bUsePolicy4, _bUsePolicy5); } else if (!_bUsePolicy3) { return dispatchDataProjector(_pProjector, _policy1, _policy2, _policy4, _policy5, _bUsePolicy1, _bUsePolicy2, _bUsePolicy4, _bUsePolicy5); } else if (!_bUsePolicy4) { return dispatchDataProjector(_pProjector, _policy1, _policy2, _policy3, _policy5, _bUsePolicy1, _bUsePolicy2, _bUsePolicy3, _bUsePolicy5); } else if (!_bUsePolicy5) { return dispatchDataProjector(_pProjector, _policy1, _policy2, _policy3, _policy4, _bUsePolicy1, _bUsePolicy2, _bUsePolicy3, _bUsePolicy4); } else { return dispatchDataProjector(_pProjector, CombinePolicy< Combine4Policy, Policy5>( Combine4Policy(_policy1, _policy2, _policy3, _policy4), _policy5) ); } } //----------------------------------------------------------------------------------------- /** * Data Projector Project */ template static void projectData(CProjector2D* _pProjector, const Policy& _policy) { CDataProjectorInterface* dp = dispatchDataProjector(_pProjector, _policy); dp->project(); delete dp; } } // namespace astra #endif astra-toolbox-2.3.0/include/astra/DataProjectorPolicies.h000066400000000000000000000275561475635207100235020ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_DATAPROJECTORPOLICIES #define _INC_ASTRA_DATAPROJECTORPOLICIES #include "Globals.h" #include "Config.h" #include #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" namespace astra { //enum {PixelDrivenPolicy, RayDrivenPolicy, AllPolicy} PolicyType; //---------------------------------------------------------------------------------------- /** Policy for Default Forward Projection (Ray Driven) */ class DefaultFPPolicy { //< Projection Data CFloat32ProjectionData2D* m_pProjectionData; //< Volume Data CFloat32VolumeData2D* m_pVolumeData; public: FORCEINLINE DefaultFPPolicy(); FORCEINLINE DefaultFPPolicy(CFloat32VolumeData2D* _pVolumeData, CFloat32ProjectionData2D* _pProjectionData); FORCEINLINE ~DefaultFPPolicy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Policy for Default Back Projection. (Ray+Pixel Driven) * This does VolumeData += transpose(ProjectionMap) * ProjectionData. */ class DefaultBPPolicy { //< Projection Data CFloat32ProjectionData2D* m_pProjectionData; //< Volume Data CFloat32VolumeData2D* m_pVolumeData; public: FORCEINLINE DefaultBPPolicy(); FORCEINLINE DefaultBPPolicy(CFloat32VolumeData2D* _pVolumeData, CFloat32ProjectionData2D* _pProjectionData); FORCEINLINE ~DefaultBPPolicy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Policy For Calculating the Projection Difference between Volume Data and Projection Data (Ray Driven) */ class DiffFPPolicy { CFloat32ProjectionData2D* m_pDiffProjectionData; CFloat32ProjectionData2D* m_pBaseProjectionData; CFloat32VolumeData2D* m_pVolumeData; public: FORCEINLINE DiffFPPolicy(); FORCEINLINE DiffFPPolicy(CFloat32VolumeData2D* _vol_data, CFloat32ProjectionData2D* _proj_data, CFloat32ProjectionData2D* _proj_data_base); FORCEINLINE ~DiffFPPolicy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Store Pixel Weights (Ray+Pixel Driven) */ class StorePixelWeightsPolicy { SPixelWeight* m_pPixelWeights; int m_iMaxPixelCount; int m_iStoredPixelCount; public: FORCEINLINE StorePixelWeightsPolicy(); FORCEINLINE StorePixelWeightsPolicy(SPixelWeight* _pPixelWeights, int _iMaxPixelCount); FORCEINLINE ~StorePixelWeightsPolicy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); FORCEINLINE int getStoredPixelCount(); }; //---------------------------------------------------------------------------------------- /** Policy For Calculating the Total Pixel Weight Multiplied by Sinogram */ class TotalPixelWeightBySinogramPolicy { CFloat32VolumeData2D* m_pPixelWeight; CFloat32ProjectionData2D* m_pSinogram; public: FORCEINLINE TotalPixelWeightBySinogramPolicy(); FORCEINLINE TotalPixelWeightBySinogramPolicy(CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pPixelWeight); FORCEINLINE ~TotalPixelWeightBySinogramPolicy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Policy For Calculating the Total Pixel Weight */ class TotalPixelWeightPolicy { CFloat32VolumeData2D* m_pPixelWeight; public: FORCEINLINE TotalPixelWeightPolicy(); FORCEINLINE TotalPixelWeightPolicy(CFloat32VolumeData2D* _pPixelWeight); FORCEINLINE ~TotalPixelWeightPolicy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Policy For Calculating the the Total Ray Length */ class TotalRayLengthPolicy { CFloat32ProjectionData2D* m_pRayLength; public: FORCEINLINE TotalRayLengthPolicy(); FORCEINLINE TotalRayLengthPolicy(CFloat32ProjectionData2D* _pRayLength); FORCEINLINE ~TotalRayLengthPolicy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Policy For Combining Two Policies */ template class CombinePolicy { P1 policy1; P2 policy2; public: FORCEINLINE CombinePolicy(); FORCEINLINE CombinePolicy(P1 _policy1, P2 _policy2); FORCEINLINE ~CombinePolicy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Policy For Combining Three Policies */ template class Combine3Policy { P1 policy1; P2 policy2; P3 policy3; public: FORCEINLINE Combine3Policy(); FORCEINLINE Combine3Policy(P1 _policy1, P2 _policy2, P3 _policy3); FORCEINLINE ~Combine3Policy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Policy For Combining Four Policies */ template class Combine4Policy { P1 policy1; P2 policy2; P3 policy3; P4 policy4; public: FORCEINLINE Combine4Policy(); FORCEINLINE Combine4Policy(P1 _policy1, P2 _policy2, P3 _policy3, P4 _policy4); FORCEINLINE ~Combine4Policy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Policy For Combining a List of the same Policies */ template class CombineListPolicy { std::vector

policyList; unsigned int size; public: FORCEINLINE CombineListPolicy(); FORCEINLINE CombineListPolicy(std::vector

_policyList); FORCEINLINE ~CombineListPolicy(); FORCEINLINE void addPolicy(P _policy); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Empty Policy */ class EmptyPolicy { public: FORCEINLINE EmptyPolicy(); FORCEINLINE ~EmptyPolicy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Policy For SIRT Backprojection */ class SIRTBPPolicy { CFloat32ProjectionData2D* m_pSinogram; CFloat32VolumeData2D* m_pReconstruction; CFloat32ProjectionData2D* m_pTotalRayLength; CFloat32VolumeData2D* m_pTotalPixelWeight; float m_fRelaxation; public: FORCEINLINE SIRTBPPolicy(); FORCEINLINE SIRTBPPolicy(CFloat32VolumeData2D* _pReconstruction, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pTotalPixelWeight, CFloat32ProjectionData2D* _pTotalRayLength, float _fRelaxation); FORCEINLINE ~SIRTBPPolicy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Policy For Sinogram Mask */ class SinogramMaskPolicy { CFloat32ProjectionData2D* m_pSinogramMask; public: FORCEINLINE SinogramMaskPolicy(); FORCEINLINE SinogramMaskPolicy(CFloat32ProjectionData2D* _pSinogramMask); FORCEINLINE ~SinogramMaskPolicy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- /** Policy For Reconstruction Mask */ class ReconstructionMaskPolicy { CFloat32VolumeData2D* m_pReconstructionMask; public: FORCEINLINE ReconstructionMaskPolicy(); FORCEINLINE ReconstructionMaskPolicy(CFloat32VolumeData2D* _pReconstructionMask); FORCEINLINE ~ReconstructionMaskPolicy(); FORCEINLINE bool rayPrior(int _iRayIndex); FORCEINLINE bool pixelPrior(int _iVolumeIndex); FORCEINLINE void addWeight(int _iRayIndex, int _iVolumeIndex, float32 weight); FORCEINLINE void rayPosterior(int _iRayIndex); FORCEINLINE void pixelPosterior(int _iVolumeIndex); }; //---------------------------------------------------------------------------------------- #include "DataProjectorPolicies.inl" } // end namespace #endif astra-toolbox-2.3.0/include/astra/DataProjectorPolicies.inl000066400000000000000000000741711475635207100240300ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_DATAPROJECTORPOLICIES_INLINE #define _INC_ASTRA_DATAPROJECTORPOLICIES_INLINE //---------------------------------------------------------------------------------------- // DEFAULT FORWARD PROJECTION (Ray Driven) //---------------------------------------------------------------------------------------- DefaultFPPolicy::DefaultFPPolicy() { } //---------------------------------------------------------------------------------------- DefaultFPPolicy::DefaultFPPolicy(CFloat32VolumeData2D* _pVolumeData, CFloat32ProjectionData2D* _pProjectionData) { m_pProjectionData = _pProjectionData; m_pVolumeData = _pVolumeData; } //---------------------------------------------------------------------------------------- DefaultFPPolicy::~DefaultFPPolicy() { } //---------------------------------------------------------------------------------------- bool DefaultFPPolicy::rayPrior(int _iRayIndex) { m_pProjectionData->getData()[_iRayIndex] = 0.0f; return true; } //---------------------------------------------------------------------------------------- bool DefaultFPPolicy::pixelPrior(int _iVolumeIndex) { // do nothing return true; } //---------------------------------------------------------------------------------------- void DefaultFPPolicy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { m_pProjectionData->getData()[_iRayIndex] += m_pVolumeData->getData()[_iVolumeIndex] * _fWeight; } //---------------------------------------------------------------------------------------- void DefaultFPPolicy::rayPosterior(int _iRayIndex) { // nothing } //---------------------------------------------------------------------------------------- void DefaultFPPolicy::pixelPosterior(int _iVolumeIndex) { // nothing } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // DEFAULT BACK PROJECTION (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- DefaultBPPolicy::DefaultBPPolicy() { } //---------------------------------------------------------------------------------------- DefaultBPPolicy::DefaultBPPolicy(CFloat32VolumeData2D* _pVolumeData, CFloat32ProjectionData2D* _pProjectionData) { m_pProjectionData = _pProjectionData; m_pVolumeData = _pVolumeData; } //---------------------------------------------------------------------------------------- DefaultBPPolicy::~DefaultBPPolicy() { } //---------------------------------------------------------------------------------------- bool DefaultBPPolicy::rayPrior(int _iRayIndex) { // do nothing return true; } //---------------------------------------------------------------------------------------- bool DefaultBPPolicy::pixelPrior(int _iVolumeIndex) { // do nothing return true; } //---------------------------------------------------------------------------------------- void DefaultBPPolicy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { m_pVolumeData->getData()[_iVolumeIndex] += m_pProjectionData->getData()[_iRayIndex] * _fWeight; } //---------------------------------------------------------------------------------------- void DefaultBPPolicy::rayPosterior(int _iRayIndex) { // nothing } //---------------------------------------------------------------------------------------- void DefaultBPPolicy::pixelPosterior(int _iVolumeIndex) { // nothing } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // FORWARD PROJECTION DIFFERENCE CALCULATION (Ray Driven) //---------------------------------------------------------------------------------------- DiffFPPolicy::DiffFPPolicy() { } //---------------------------------------------------------------------------------------- DiffFPPolicy::DiffFPPolicy(CFloat32VolumeData2D* _pVolumeData, CFloat32ProjectionData2D* _pDiffProjectionData, CFloat32ProjectionData2D* _pBaseProjectionData) { m_pDiffProjectionData = _pDiffProjectionData; m_pBaseProjectionData = _pBaseProjectionData; m_pVolumeData = _pVolumeData; } //---------------------------------------------------------------------------------------- DiffFPPolicy::~DiffFPPolicy() { } //---------------------------------------------------------------------------------------- bool DiffFPPolicy::rayPrior(int _iRayIndex) { m_pDiffProjectionData->getData()[_iRayIndex] = m_pBaseProjectionData->getData()[_iRayIndex]; return true; } //---------------------------------------------------------------------------------------- bool DiffFPPolicy::pixelPrior(int _iVolumeIndex) { return true; } //---------------------------------------------------------------------------------------- void DiffFPPolicy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { m_pDiffProjectionData->getData()[_iRayIndex] -= m_pVolumeData->getData()[_iVolumeIndex] * _fWeight; } //---------------------------------------------------------------------------------------- void DiffFPPolicy::rayPosterior(int _iRayIndex) { // nothing } //---------------------------------------------------------------------------------------- void DiffFPPolicy::pixelPosterior(int _iVolumeIndex) { // nothing } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // STORE PIXEL WEIGHT (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- StorePixelWeightsPolicy::StorePixelWeightsPolicy() { } //---------------------------------------------------------------------------------------- StorePixelWeightsPolicy::StorePixelWeightsPolicy(SPixelWeight* _pPixelWeights, int _iMaxPixelCount) { m_iStoredPixelCount = 0; m_pPixelWeights = _pPixelWeights; m_iMaxPixelCount = _iMaxPixelCount; } //---------------------------------------------------------------------------------------- StorePixelWeightsPolicy::~StorePixelWeightsPolicy() { } //---------------------------------------------------------------------------------------- bool StorePixelWeightsPolicy::rayPrior(int _iRayIndex) { return (m_iStoredPixelCount < m_iMaxPixelCount); } //---------------------------------------------------------------------------------------- bool StorePixelWeightsPolicy::pixelPrior(int _iVolumeIndex) { return (m_iStoredPixelCount < m_iMaxPixelCount); } //---------------------------------------------------------------------------------------- void StorePixelWeightsPolicy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { m_pPixelWeights[m_iStoredPixelCount].m_fWeight = _fWeight; m_pPixelWeights[m_iStoredPixelCount].m_iIndex = _iVolumeIndex; ++m_iStoredPixelCount; } //---------------------------------------------------------------------------------------- void StorePixelWeightsPolicy::rayPosterior(int _iRayIndex) { // nothing } //---------------------------------------------------------------------------------------- void StorePixelWeightsPolicy::pixelPosterior(int _iVolumeIndex) { // nothing } //---------------------------------------------------------------------------------------- int StorePixelWeightsPolicy::getStoredPixelCount() { return m_iStoredPixelCount; } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // TOTAL PIXEL WEIGHT MULTIPLIED BY SINOGRAM (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- TotalPixelWeightBySinogramPolicy::TotalPixelWeightBySinogramPolicy() { } //---------------------------------------------------------------------------------------- TotalPixelWeightBySinogramPolicy::TotalPixelWeightBySinogramPolicy(CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pPixelWeight) { m_pPixelWeight = _pPixelWeight; m_pSinogram = _pSinogram; } //---------------------------------------------------------------------------------------- TotalPixelWeightBySinogramPolicy::~TotalPixelWeightBySinogramPolicy() { } //---------------------------------------------------------------------------------------- bool TotalPixelWeightBySinogramPolicy::rayPrior(int _iRayIndex) { return true; } //---------------------------------------------------------------------------------------- bool TotalPixelWeightBySinogramPolicy::pixelPrior(int _iVolumeIndex) { return true; } //---------------------------------------------------------------------------------------- void TotalPixelWeightBySinogramPolicy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { m_pPixelWeight->getData()[_iVolumeIndex] += _fWeight * m_pSinogram->getData()[_iRayIndex]; } //---------------------------------------------------------------------------------------- void TotalPixelWeightBySinogramPolicy::rayPosterior(int _iRayIndex) { // nothing } //---------------------------------------------------------------------------------------- void TotalPixelWeightBySinogramPolicy::pixelPosterior(int _iVolumeIndex) { // nothing } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // TOTAL PIXEL WEIGHT (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- TotalPixelWeightPolicy::TotalPixelWeightPolicy() { } //---------------------------------------------------------------------------------------- TotalPixelWeightPolicy::TotalPixelWeightPolicy(CFloat32VolumeData2D* _pPixelWeight) { m_pPixelWeight = _pPixelWeight; } //---------------------------------------------------------------------------------------- TotalPixelWeightPolicy::~TotalPixelWeightPolicy() { } //---------------------------------------------------------------------------------------- bool TotalPixelWeightPolicy::rayPrior(int _iRayIndex) { return true; } //---------------------------------------------------------------------------------------- bool TotalPixelWeightPolicy::pixelPrior(int _iVolumeIndex) { return true; } //---------------------------------------------------------------------------------------- void TotalPixelWeightPolicy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { m_pPixelWeight->getData()[_iVolumeIndex] += _fWeight; } //---------------------------------------------------------------------------------------- void TotalPixelWeightPolicy::rayPosterior(int _iRayIndex) { // nothing } //---------------------------------------------------------------------------------------- void TotalPixelWeightPolicy::pixelPosterior(int _iVolumeIndex) { // nothing } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // TOTAL RAY LENGTH (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- TotalRayLengthPolicy::TotalRayLengthPolicy() { } //---------------------------------------------------------------------------------------- TotalRayLengthPolicy::TotalRayLengthPolicy(CFloat32ProjectionData2D* _pRayLength) { m_pRayLength = _pRayLength; } //---------------------------------------------------------------------------------------- TotalRayLengthPolicy::~TotalRayLengthPolicy() { } //---------------------------------------------------------------------------------------- bool TotalRayLengthPolicy::rayPrior(int _iRayIndex) { return true; } //---------------------------------------------------------------------------------------- bool TotalRayLengthPolicy::pixelPrior(int _iVolumeIndex) { return true; } //---------------------------------------------------------------------------------------- void TotalRayLengthPolicy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { m_pRayLength->getData()[_iRayIndex] += _fWeight; } //---------------------------------------------------------------------------------------- void TotalRayLengthPolicy::rayPosterior(int _iRayIndex) { // nothing } //---------------------------------------------------------------------------------------- void TotalRayLengthPolicy::pixelPosterior(int _iVolumeIndex) { // nothing } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // COMBINE TWO POLICIES (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- template CombinePolicy::CombinePolicy() { } //---------------------------------------------------------------------------------------- template CombinePolicy::CombinePolicy(P1 _policy1, P2 _policy2) { policy1 = _policy1; policy2 = _policy2; } //---------------------------------------------------------------------------------------- template CombinePolicy::~CombinePolicy() { } //---------------------------------------------------------------------------------------- template bool CombinePolicy::rayPrior(int _iRayIndex) { if (!policy1.rayPrior(_iRayIndex)) return false; return policy2.rayPrior(_iRayIndex); } //---------------------------------------------------------------------------------------- template bool CombinePolicy::pixelPrior(int _iVolumeIndex) { if (!policy1.pixelPrior(_iVolumeIndex)) return false; return policy2.pixelPrior(_iVolumeIndex); } //---------------------------------------------------------------------------------------- template void CombinePolicy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { policy1.addWeight(_iRayIndex, _iVolumeIndex, _fWeight); policy2.addWeight(_iRayIndex, _iVolumeIndex, _fWeight); } //---------------------------------------------------------------------------------------- template void CombinePolicy::rayPosterior(int _iRayIndex) { policy1.rayPosterior(_iRayIndex); policy2.rayPosterior(_iRayIndex); } //---------------------------------------------------------------------------------------- template void CombinePolicy::pixelPosterior(int _iVolumeIndex) { policy1.pixelPosterior(_iVolumeIndex); policy2.pixelPosterior(_iVolumeIndex); } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // COMBINE THREE POLICIES (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- template Combine3Policy::Combine3Policy() { } //---------------------------------------------------------------------------------------- template Combine3Policy::Combine3Policy(P1 _policy1, P2 _policy2, P3 _policy3) { policy1 = _policy1; policy2 = _policy2; policy3 = _policy3; } //---------------------------------------------------------------------------------------- template Combine3Policy::~Combine3Policy() { } //---------------------------------------------------------------------------------------- template bool Combine3Policy::rayPrior(int _iRayIndex) { if (!policy1.rayPrior(_iRayIndex)) return false; if (!policy2.rayPrior(_iRayIndex)) return false; return policy3.rayPrior(_iRayIndex); } //---------------------------------------------------------------------------------------- template bool Combine3Policy::pixelPrior(int _iVolumeIndex) { if (!policy1.pixelPrior(_iVolumeIndex)) return false; if (!policy2.pixelPrior(_iVolumeIndex)) return false; return policy3.pixelPrior(_iVolumeIndex); } //---------------------------------------------------------------------------------------- template void Combine3Policy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { policy1.addWeight(_iRayIndex, _iVolumeIndex, _fWeight); policy2.addWeight(_iRayIndex, _iVolumeIndex, _fWeight); policy3.addWeight(_iRayIndex, _iVolumeIndex, _fWeight); } //---------------------------------------------------------------------------------------- template void Combine3Policy::rayPosterior(int _iRayIndex) { policy1.rayPosterior(_iRayIndex); policy2.rayPosterior(_iRayIndex); policy3.rayPosterior(_iRayIndex); } //---------------------------------------------------------------------------------------- template void Combine3Policy::pixelPosterior(int _iVolumeIndex) { policy1.pixelPosterior(_iVolumeIndex); policy2.pixelPosterior(_iVolumeIndex); policy3.pixelPosterior(_iVolumeIndex); } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // COMBINE FOUR POLICIES (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- template Combine4Policy::Combine4Policy() { } //---------------------------------------------------------------------------------------- template Combine4Policy::Combine4Policy(P1 _policy1, P2 _policy2, P3 _policy3, P4 _policy4) { policy1 = _policy1; policy2 = _policy2; policy3 = _policy3; policy4 = _policy4; } //---------------------------------------------------------------------------------------- template Combine4Policy::~Combine4Policy() { } //---------------------------------------------------------------------------------------- template bool Combine4Policy::rayPrior(int _iRayIndex) { if (!policy1.rayPrior(_iRayIndex)) return false; if (!policy2.rayPrior(_iRayIndex)) return false; if (!policy3.rayPrior(_iRayIndex)) return false; return policy4.rayPrior(_iRayIndex); } //---------------------------------------------------------------------------------------- template bool Combine4Policy::pixelPrior(int _iVolumeIndex) { if (!policy1.pixelPrior(_iVolumeIndex)) return false; if (!policy2.pixelPrior(_iVolumeIndex)) return false; if (!policy3.pixelPrior(_iVolumeIndex)) return false; return policy4.pixelPrior(_iVolumeIndex); } //---------------------------------------------------------------------------------------- template void Combine4Policy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { policy1.addWeight(_iRayIndex, _iVolumeIndex, _fWeight); policy2.addWeight(_iRayIndex, _iVolumeIndex, _fWeight); policy3.addWeight(_iRayIndex, _iVolumeIndex, _fWeight); policy4.addWeight(_iRayIndex, _iVolumeIndex, _fWeight); } //---------------------------------------------------------------------------------------- template void Combine4Policy::rayPosterior(int _iRayIndex) { policy1.rayPosterior(_iRayIndex); policy2.rayPosterior(_iRayIndex); policy3.rayPosterior(_iRayIndex); policy4.rayPosterior(_iRayIndex); } //---------------------------------------------------------------------------------------- template void Combine4Policy::pixelPosterior(int _iVolumeIndex) { policy1.pixelPosterior(_iVolumeIndex); policy2.pixelPosterior(_iVolumeIndex); policy3.pixelPosterior(_iVolumeIndex); policy4.pixelPosterior(_iVolumeIndex); } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // COMBINE LIST OF EQUAL POLICIES (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- template CombineListPolicy

::CombineListPolicy() { size = 0; } //---------------------------------------------------------------------------------------- template CombineListPolicy

::CombineListPolicy(std::vector

_policyList) { policyList = _policyList; size = policyList.size(); } //---------------------------------------------------------------------------------------- template CombineListPolicy

::~CombineListPolicy() { } //---------------------------------------------------------------------------------------- template void CombineListPolicy

::addPolicy(P _policy) { policyList.push_back(_policy); size = policyList.size(); } //---------------------------------------------------------------------------------------- template bool CombineListPolicy

::rayPrior(int _iRayIndex) { for(unsigned int i = 0; i < size; ++i) { if (!policyList[i].rayPrior(_iRayIndex)) return false; } return true; } //---------------------------------------------------------------------------------------- template bool CombineListPolicy

::pixelPrior(int _iVolumeIndex) { for(unsigned int i = 0; i < size; ++i) { if (!policyList[i].pixelPrior(_iVolumeIndex)) return false; } return true; } //---------------------------------------------------------------------------------------- template void CombineListPolicy

::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { for(unsigned int i = 0; i < size; ++i) { policyList[i].addWeight(_iRayIndex, _iVolumeIndex, _fWeight); } } //---------------------------------------------------------------------------------------- template void CombineListPolicy

::rayPosterior(int _iRayIndex) { for(unsigned int i = 0; i < size; ++i) { policyList[i].rayPosterior(_iRayIndex); } } //---------------------------------------------------------------------------------------- template void CombineListPolicy

::pixelPosterior(int _iVolumeIndex) { for(unsigned int i = 0; i < size; ++i) { policyList[i].pixelPosterior(_iVolumeIndex); } } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // EMPTY POLICY (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- EmptyPolicy::EmptyPolicy() { } //---------------------------------------------------------------------------------------- EmptyPolicy::~EmptyPolicy() { } //---------------------------------------------------------------------------------------- bool EmptyPolicy::rayPrior(int _iRayIndex) { return true; } //---------------------------------------------------------------------------------------- bool EmptyPolicy::pixelPrior(int _iVolumeIndex) { return true; } //---------------------------------------------------------------------------------------- void EmptyPolicy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { // nothing } //---------------------------------------------------------------------------------------- void EmptyPolicy::rayPosterior(int _iRayIndex) { // nothing } //---------------------------------------------------------------------------------------- void EmptyPolicy::pixelPosterior(int _iVolumeIndex) { // nothing } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // SIRT BACKPROJECTION (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- SIRTBPPolicy::SIRTBPPolicy() { } //---------------------------------------------------------------------------------------- SIRTBPPolicy::SIRTBPPolicy(CFloat32VolumeData2D* _pReconstruction, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pTotalPixelWeight, CFloat32ProjectionData2D* _pTotalRayLength, float _fRelaxation) { m_pReconstruction = _pReconstruction; m_pSinogram = _pSinogram; m_pTotalPixelWeight = _pTotalPixelWeight; m_pTotalRayLength = _pTotalRayLength; m_fRelaxation = _fRelaxation; } //---------------------------------------------------------------------------------------- SIRTBPPolicy::~SIRTBPPolicy() { } //---------------------------------------------------------------------------------------- bool SIRTBPPolicy::rayPrior(int _iRayIndex) { return true; } //---------------------------------------------------------------------------------------- bool SIRTBPPolicy::pixelPrior(int _iVolumeIndex) { return true; } //---------------------------------------------------------------------------------------- void SIRTBPPolicy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { float32 fGammaBeta = m_pTotalPixelWeight->getData()[_iVolumeIndex] * m_pTotalRayLength->getData()[_iRayIndex]; if ((fGammaBeta > 0.001f) || (fGammaBeta < -0.001f)) { m_pReconstruction->getData()[_iVolumeIndex] += _fWeight * m_fRelaxation * m_pSinogram->getData()[_iRayIndex] / fGammaBeta; } } //---------------------------------------------------------------------------------------- void SIRTBPPolicy::rayPosterior(int _iRayIndex) { // nothing } //---------------------------------------------------------------------------------------- void SIRTBPPolicy::pixelPosterior(int _iVolumeIndex) { // nothing } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // SINOGRAM MASK (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- SinogramMaskPolicy::SinogramMaskPolicy() { } //---------------------------------------------------------------------------------------- SinogramMaskPolicy::SinogramMaskPolicy(CFloat32ProjectionData2D* _pSinogramMask) { m_pSinogramMask = _pSinogramMask; } //---------------------------------------------------------------------------------------- SinogramMaskPolicy::~SinogramMaskPolicy() { } //---------------------------------------------------------------------------------------- bool SinogramMaskPolicy::rayPrior(int _iRayIndex) { return (m_pSinogramMask->getData()[_iRayIndex] != 0); } //---------------------------------------------------------------------------------------- bool SinogramMaskPolicy::pixelPrior(int _iVolumeIndex) { return true; } //---------------------------------------------------------------------------------------- void SinogramMaskPolicy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { // nothing } //---------------------------------------------------------------------------------------- void SinogramMaskPolicy::rayPosterior(int _iRayIndex) { // nothing } //---------------------------------------------------------------------------------------- void SinogramMaskPolicy::pixelPosterior(int _iVolumeIndex) { // nothing } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // RECONSTRUCTION MASK (Ray+Pixel Driven) //---------------------------------------------------------------------------------------- ReconstructionMaskPolicy::ReconstructionMaskPolicy() { } //---------------------------------------------------------------------------------------- ReconstructionMaskPolicy::ReconstructionMaskPolicy(CFloat32VolumeData2D* _pReconstructionMask) { m_pReconstructionMask = _pReconstructionMask; } //---------------------------------------------------------------------------------------- ReconstructionMaskPolicy::~ReconstructionMaskPolicy() { } //---------------------------------------------------------------------------------------- bool ReconstructionMaskPolicy::rayPrior(int _iRayIndex) { return true; } //---------------------------------------------------------------------------------------- bool ReconstructionMaskPolicy::pixelPrior(int _iVolumeIndex) { return (m_pReconstructionMask->getData()[_iVolumeIndex] != 0); } //---------------------------------------------------------------------------------------- void ReconstructionMaskPolicy::addWeight(int _iRayIndex, int _iVolumeIndex, float32 _fWeight) { // nothing } //---------------------------------------------------------------------------------------- void ReconstructionMaskPolicy::rayPosterior(int _iRayIndex) { // nothing } //---------------------------------------------------------------------------------------- void ReconstructionMaskPolicy::pixelPosterior(int _iVolumeIndex) { // nothing } //---------------------------------------------------------------------------------------- #endif astra-toolbox-2.3.0/include/astra/FanFlatBeamLineKernelProjector2D.h000066400000000000000000000156621475635207100253730ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FANFLATBEAMLINEKERNELPROJECTOR #define _INC_ASTRA_FANFLATBEAMLINEKERNELPROJECTOR #include "FanFlatProjectionGeometry2D.h" #include "FanFlatVecProjectionGeometry2D.h" #include "Float32Data2D.h" #include "Projector2D.h" namespace astra { /** This class implements a two-dimensional projector based on a line based kernel * with a fan flat projection geometry. * * \par XML Configuration * \astra_xml_item{ProjectionGeometry, xml node, The geometry of the projection.} * \astra_xml_item{VolumeGeometry, xml node, The geometry of the volume.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('fanflat_line');\n * cfg.ProjectionGeometry = proj_geom;\n * cfg.VolumeGeometry = vol_geom;\n * proj_id = astra_mex_projector('create'\, cfg);\n * } */ class _AstraExport CFanFlatBeamLineKernelProjector2D : public CProjector2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - blobvalues are ok */ virtual bool _check(); public: // type of the projector, needed to register with CProjectorFactory static inline const char* const type = "line_fanflat"; /** Default constructor. */ CFanFlatBeamLineKernelProjector2D(); /** Constructor. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. */ CFanFlatBeamLineKernelProjector2D(const CFanFlatProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); /** Destructor, is virtual to show that we are aware subclass destructor are called. */ ~CFanFlatBeamLineKernelProjector2D(); /** Initialize the projector with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize the projector. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. * @return initialization successful? */ bool initialize(const CFanFlatProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); /** Clear this class. */ virtual void clear(); /** Returns the number of weights required for storage of all weights of one projection. * * @param _iProjectionIndex Index of the projection (zero-based). * @return Size of buffer (given in SPixelWeight elements) needed to store weighted pixels. */ virtual int getProjectionWeightsCount(int _iProjectionIndex); /** Compute the pixel weights for a single ray, from the source to a detector pixel. * * @param _iProjectionIndex Index of the projection * @param _iDetectorIndex Index of the detector pixel * @param _pWeightedPixels Pointer to a pre-allocated array, consisting of _iMaxPixelCount elements * of type SPixelWeight. On return, this array contains a list of the index * and weight for all pixels on the ray. * @param _iMaxPixelCount Maximum number of pixels (and corresponding weights) that can be stored in _pWeightedPixels. * This number MUST be greater than the total number of pixels on the ray. * @param _iStoredPixelCount On return, this variable contains the total number of pixels on the * ray (that have been stored in the list _pWeightedPixels). */ virtual void computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount); /** Policy-based projection of all rays. This function will calculate each non-zero projection * weight and use this value for a task provided by the policy object. * * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void project(Policy& _policy); /** Policy-based projection of all rays of a single projection. This function will calculate * each non-zero projection weight and use this value for a task provided by the policy object. * * @param _iProjection Wwhich projection should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleProjection(int _iProjection, Policy& _policy); /** Policy-based projection of a single ray. This function will calculate each non-zero * projection weight and use this value for a task provided by the policy object. * * @param _iProjection Which projection should be projected? * @param _iDetector Which detector should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleRay(int _iProjection, int _iDetector, Policy& _policy); /** Return the type of this projector. * * @return identification type of this projector */ virtual std::string getType(); float32 angleBetweenVectors(float32 _fAX, float32 _fAY, float32 _fBX, float32 _fBY); protected: /** Internal policy-based projection of a range of angles and range. * (_i*From is inclusive, _i*To exclusive) */ template void projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& _policy); }; //---------------------------------------------------------------------------------------- inline std::string CFanFlatBeamLineKernelProjector2D::getType() { return type; } } // namespace astra #endif astra-toolbox-2.3.0/include/astra/FanFlatBeamLineKernelProjector2D.inl000066400000000000000000000163331475635207100257220ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define policy_weight(p,rayindex,volindex,weight) do { if (p.pixelPrior(volindex)) { p.addWeight(rayindex, volindex, weight); p.pixelPosterior(volindex); } } while (false) template void CFanFlatBeamLineKernelProjector2D::project(Policy& p) { projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(), 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CFanFlatBeamLineKernelProjector2D::projectSingleProjection(int _iProjection, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CFanFlatBeamLineKernelProjector2D::projectSingleRay(int _iProjection, int _iDetector, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, _iDetector, _iDetector + 1, p); } //---------------------------------------------------------------------------------------- // PROJECT BLOCK - vector projection geometry template void CFanFlatBeamLineKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p) { // get vector geometry const CFanFlatVecProjectionGeometry2D* pVecProjectionGeometry; if (dynamic_cast(m_pProjectionGeometry)) { pVecProjectionGeometry = dynamic_cast(m_pProjectionGeometry)->toVectorGeometry(); } else { pVecProjectionGeometry = dynamic_cast(m_pProjectionGeometry); } // precomputations const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX(); const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY(); const float32 inv_pixelLengthX = 1.0f / pixelLengthX; const float32 inv_pixelLengthY = 1.0f / pixelLengthY; const int colCount = m_pVolumeGeometry->getGridColCount(); const int rowCount = m_pVolumeGeometry->getGridRowCount(); const int detCount = pVecProjectionGeometry->getDetectorCount(); const float32 Ex = m_pVolumeGeometry->getWindowMinX() + pixelLengthX*0.5f; const float32 Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f; // loop angles for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { // variables float32 Dx, Dy, Rx, Ry, S, T, weight, c, r, deltac, deltar, offset, RxOverRy, RyOverRx; float32 lengthPerRow, lengthPerCol, invTminSTimesLengthPerRow, invTminSTimesLengthPerCol; int iVolumeIndex, iRayIndex, row, col, iDetector; const SFanProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle]; // loop detectors for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { iRayIndex = iAngle * detCount + iDetector; // POLICY: RAY PRIOR if (!p.rayPrior(iRayIndex)) continue; Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX; Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY; Rx = proj->fSrcX - Dx; Ry = proj->fSrcY - Dy; bool vertical = fabs(Rx) < fabs(Ry); bool isin = false; // vertically if (vertical) { RxOverRy = Rx/Ry; lengthPerRow = pixelLengthX * sqrt(Rx*Rx + Ry*Ry) / abs(Ry); deltac = -pixelLengthY * RxOverRy * inv_pixelLengthX; S = 0.5f - 0.5f*fabs(RxOverRy); T = 0.5f + 0.5f*fabs(RxOverRy); invTminSTimesLengthPerRow = lengthPerRow / (T - S); // calculate c for row 0 c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX; // for each row for (row = 0; row < rowCount; ++row, c += deltac) { col = int(floor(c+0.5f)); if (col < -1 || col > colCount) { if (!isin) continue; else break; } offset = c - float32(col); // left if (offset < -S) { weight = (offset + T) * invTminSTimesLengthPerRow; iVolumeIndex = row * colCount + col - 1; if (col > 0) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow-weight); } iVolumeIndex++; if (col >= 0 && col < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); } } // right else if (S < offset) { weight = (offset - S) * invTminSTimesLengthPerRow; iVolumeIndex = row * colCount + col; if (col >= 0 && col < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow-weight); } iVolumeIndex++; if (col + 1 < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); } } // centre else if (col >= 0 && col < colCount) { iVolumeIndex = row * colCount + col; policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow); } isin = true; } } // horizontally else { RyOverRx = Ry/Rx; lengthPerCol = pixelLengthY * sqrt(Rx*Rx + Ry*Ry) / abs(Rx); deltar = -pixelLengthX * RyOverRx * inv_pixelLengthY; S = 0.5f - 0.5f*fabs(RyOverRx); T = 0.5f + 0.5f*fabs(RyOverRx); invTminSTimesLengthPerCol = lengthPerCol / (T - S); // calculate r for col 0 r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY; // for each col for (col = 0; col < colCount; ++col, r += deltar) { row = int(floor(r+0.5f)); if (row < -1 || row > rowCount) { if (!isin) continue; else break; } offset = r - float32(row); // up if (offset < -S) { weight = (offset + T) * invTminSTimesLengthPerCol; iVolumeIndex = (row-1) * colCount + col; if (row > 0) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol-weight); } iVolumeIndex += colCount; if (row >= 0 && row < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); } } // down else if (S < offset) { weight = (offset - S) * invTminSTimesLengthPerCol; iVolumeIndex = row * colCount + col; if (row >= 0 && row < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol-weight); } iVolumeIndex += colCount; if (row + 1 < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); } } // centre else if (row >= 0 && row < rowCount) { iVolumeIndex = row * colCount + col; policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol); } isin = true; } } // POLICY: RAY POSTERIOR p.rayPosterior(iRayIndex); } // end loop detector } // end loop angles // Delete created vec geometry if required if (dynamic_cast(m_pProjectionGeometry)) delete pVecProjectionGeometry; } astra-toolbox-2.3.0/include/astra/FanFlatBeamStripKernelProjector2D.h000066400000000000000000000154421475635207100256010ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FANFLATBEAMSTRIPKERNELPROJECTOR #define _INC_ASTRA_FANFLATBEAMSTRIPKERNELPROJECTOR #include "FanFlatProjectionGeometry2D.h" #include "Float32Data2D.h" #include "Projector2D.h" namespace astra { /** This class implements a two-dimensional fan-flat-beam projector based on a strip based kernel. * * \par XML Configuration * \astra_xml_item{ProjectionGeometry, xml node, The geometry of the projection.} * \astra_xml_item{VolumeGeometry, xml node, The geometry of the volume.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('strip_fanflat');\n * cfg.ProjectionGeometry = proj_geom;\n * cfg.VolumeGeometry = vol_geom;\n * proj_id = astra_mex_projector('create'\, cfg);\n * } */ class _AstraExport CFanFlatBeamStripKernelProjector2D : public CProjector2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - blobvalues are ok */ virtual bool _check(); public: // type of the projector, needed to register with CProjectorFactory static inline const char* const type = "strip_fanflat"; /** Default constructor. */ CFanFlatBeamStripKernelProjector2D(); /** Constructor. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. */ CFanFlatBeamStripKernelProjector2D(const CFanFlatProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); /** Destructor, is virtual to show that we are aware subclass destructor are called. */ ~CFanFlatBeamStripKernelProjector2D(); /** Initialize the projector with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize the projector. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. * @return initialization successful? */ bool initialize(const CFanFlatProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); /** Clear this class. */ virtual void clear(); /** Returns the number of weights required for storage of all weights of one projection. * * @param _iProjectionIndex Index of the projection (zero-based). * @return Size of buffer (given in SPixelWeight elements) needed to store weighted pixels. */ virtual int getProjectionWeightsCount(int _iProjectionIndex); /** Compute the pixel weights for a single ray, from the source to a detector pixel. * * @param _iProjectionIndex Index of the projection * @param _iDetectorIndex Index of the detector pixel * @param _pWeightedPixels Pointer to a pre-allocated array, consisting of _iMaxPixelCount elements * of type SPixelWeight. On return, this array contains a list of the index * and weight for all pixels on the ray. * @param _iMaxPixelCount Maximum number of pixels (and corresponding weights) that can be stored in _pWeightedPixels. * This number MUST be greater than the total number of pixels on the ray. * @param _iStoredPixelCount On return, this variable contains the total number of pixels on the * ray (that have been stored in the list _pWeightedPixels). */ virtual void computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount); /** Policy-based projection of all rays. This function will calculate each non-zero projection * weight and use this value for a task provided by the policy object. * * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void project(Policy& _policy); /** Policy-based projection of all rays of a single projection. This function will calculate * each non-zero projection weight and use this value for a task provided by the policy object. * * @param _iProjection Wwhich projection should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleProjection(int _iProjection, Policy& _policy); /** Policy-based projection of a single ray. This function will calculate each non-zero * projection weight and use this value for a task provided by the policy object. * * @param _iProjection Which projection should be projected? * @param _iDetector Which detector should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleRay(int _iProjection, int _iDetector, Policy& _policy); /** Return the type of this projector. * * @return identification type of this projector */ virtual std::string getType(); protected: /** Internal policy-based projection of a range of angles and range. * (_i*From is inclusive, _i*To exclusive) */ template void projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& _policy); }; //---------------------------------------------------------------------------------------- inline std::string CFanFlatBeamStripKernelProjector2D::getType() { return type; } } // namespace astra #endif astra-toolbox-2.3.0/include/astra/FanFlatBeamStripKernelProjector2D.inl000066400000000000000000000367201475635207100261360ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ template void CFanFlatBeamStripKernelProjector2D::project(Policy& p) { projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(), 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CFanFlatBeamStripKernelProjector2D::projectSingleProjection(int _iProjection, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CFanFlatBeamStripKernelProjector2D::projectSingleRay(int _iProjection, int _iDetector, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, _iDetector, _iDetector + 1, p); } //---------------------------------------------------------------------------------------- // PROJECT BLOCK template void CFanFlatBeamStripKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p) { ASTRA_ASSERT(m_bIsInitialized); // Some variables float32 theta; int row, col; int iAngle, iDetector; float32 res; int x1L, x1R; float32 x2L, x2R; int iVolumeIndex, iRayIndex; CFanFlatProjectionGeometry2D* projgeom = static_cast(m_pProjectionGeometry); // Other precalculations float32 PW = m_pVolumeGeometry->getPixelLengthX(); float32 PH = m_pVolumeGeometry->getPixelLengthY(); float32 DW = m_pProjectionGeometry->getDetectorWidth(); float32 inv_PW = 1.0f / PW; float32 inv_PH = 1.0f / PH; // calculate alpha's float32 alpha; float32* cos_alpha = new float32[m_pProjectionGeometry->getDetectorCount() + 1]; float32* sin_alpha = new float32[m_pProjectionGeometry->getDetectorCount() + 1]; for (int i = 0; i < m_pProjectionGeometry->getDetectorCount() + 1; ++i) { alpha = -atan((i - m_pProjectionGeometry->getDetectorCount()*0.5f) * DW / projgeom->getSourceDetectorDistance()); cos_alpha[i] = cos(alpha); sin_alpha[i] = sin(alpha); } // loop angles for (iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { // get values theta = m_pProjectionGeometry->getProjectionAngle(iAngle); bool switch_t = true; if (theta >= 7*PIdiv4) theta -= 2*PI; if (theta >= 3*PIdiv4) { theta -= PI; switch_t = false; } // Precalculate sin, cos, 1/cos float32 sin_theta = sin(theta); float32 cos_theta = cos(theta); // [-45?,45?] and [135?,225?] if (theta < PIdiv4) { // loop detectors for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector; // POLICY: RAY PRIOR if (!p.rayPrior(iRayIndex)) continue; float32 dist_srcDetPixSquared = projgeom->getSourceDetectorDistance() * projgeom->getSourceDetectorDistance() + (iDetector + 0.5f - m_pProjectionGeometry->getDetectorCount()*0.5f) * (iDetector + 0.5f - m_pProjectionGeometry->getDetectorCount()*0.5f) * DW * DW; dist_srcDetPixSquared = dist_srcDetPixSquared * dist_srcDetPixSquared / (projgeom->getSourceDetectorDistance() * projgeom->getSourceDetectorDistance() * DW * DW); //float32 InvRayWidthSquared = (projgeom->getSourceDetectorDistance() * projgeom->getSourceDetectorDistance()) / dist_srcDetPixSquared; float32 sin_theta_left, cos_theta_left; float32 sin_theta_right, cos_theta_right; // get theta_l = alpha_left + theta and theta_r = alpha_right + theta float32 t_l, t_r; if (!switch_t) { sin_theta_left = sin_theta * cos_alpha[iDetector+1] + cos_theta * sin_alpha[iDetector+1]; sin_theta_right = sin_theta * cos_alpha[iDetector] + cos_theta * sin_alpha[iDetector]; cos_theta_left = cos_theta * cos_alpha[iDetector+1] - sin_theta * sin_alpha[iDetector+1]; cos_theta_right = cos_theta * cos_alpha[iDetector] - sin_theta * sin_alpha[iDetector]; t_l = sin_alpha[iDetector+1] * projgeom->getOriginSourceDistance(); t_r = sin_alpha[iDetector] * projgeom->getOriginSourceDistance(); } else { sin_theta_left = sin_theta * cos_alpha[iDetector] + cos_theta * sin_alpha[iDetector]; sin_theta_right = sin_theta * cos_alpha[iDetector+1] + cos_theta * sin_alpha[iDetector+1]; cos_theta_left = cos_theta * cos_alpha[iDetector] - sin_theta * sin_alpha[iDetector]; cos_theta_right = cos_theta * cos_alpha[iDetector+1] - sin_theta * sin_alpha[iDetector+1]; t_l = -sin_alpha[iDetector] * projgeom->getOriginSourceDistance(); t_r = -sin_alpha[iDetector+1] * projgeom->getOriginSourceDistance(); } float32 inv_cos_theta_left = 1.0f / cos_theta_left; float32 inv_cos_theta_right = 1.0f / cos_theta_right; float32 updateX_left = sin_theta_left * inv_cos_theta_left; // tan(theta_left) float32 updateX_right = sin_theta_right * inv_cos_theta_right; // tan(theta_right) // Precalculate kernel limits // BUG: If updateX_left > 1 (which can happen if theta_left >= pi/4 > theta), then T_l > U_l, and the expressions for res are no longer correct float32 S_l = -0.5f * updateX_left; if (S_l > 0) {S_l = -S_l;} float32 T_l = -S_l; float32 U_l = 1.0f + S_l; float32 V_l = 1.0f - S_l; float32 inv_4T_l = 0.25f / T_l; float32 S_r = -0.5f * updateX_right; if (S_r > 0) {S_r = -S_r;} float32 T_r = -S_r; float32 U_r = 1.0f + S_r; float32 V_r = 1.0f - S_r; float32 inv_4T_r = 0.25f / T_r; // calculate strip extremes (volume coordinates) float32 PL = (t_l - sin_theta_left * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta_left; float32 PR = (t_r - sin_theta_right * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta_right; float32 PLimitL = PL + S_l * PH; float32 PLimitR = PR - S_r * PH; // calculate strip extremes (pixel coordinates) float32 XLimitL = (PLimitL - m_pVolumeGeometry->getWindowMinX()) * inv_PW; float32 XLimitR = (PLimitR - m_pVolumeGeometry->getWindowMinX()) * inv_PW; float32 xL = (PL - m_pVolumeGeometry->getWindowMinX()) * inv_PW; float32 xR = (PR - m_pVolumeGeometry->getWindowMinX()) * inv_PW; // for each row for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) { // get strip extremes in column indices x1L = int((XLimitL > 0.0f) ? XLimitL : XLimitL-1.0f); x1R = int((XLimitR > 0.0f) ? XLimitR : XLimitR-1.0f); // get coords w.r.t leftmost column hit by strip x2L = xL - x1L; x2R = xR - x1L; // update strip extremes for the next row XLimitL += updateX_left; XLimitR += updateX_right; xL += updateX_left; xR += updateX_right; float32 diffSrcYSquared; if (switch_t) diffSrcYSquared = m_pVolumeGeometry->pixelRowToCenterY(row) + cos_theta * projgeom->getOriginSourceDistance(); else diffSrcYSquared = m_pVolumeGeometry->pixelRowToCenterY(row) - cos_theta * projgeom->getOriginSourceDistance(); diffSrcYSquared = diffSrcYSquared * diffSrcYSquared; // for each affected col for (col = x1L; col <= x1R; ++col) { if (col < 0 || col >= m_pVolumeGeometry->getGridColCount()) { x2L -= 1.0f; x2R -= 1.0f; continue; } iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col); // POLICY: PIXEL PRIOR if (!p.pixelPrior(iVolumeIndex)) { x2L -= 1.0f; x2R -= 1.0f; continue; } // right if (x2R >= V_r) res = 1.0f; else if (x2R > U_r) res = x2R - (x2R-U_r)*(x2R-U_r)*inv_4T_r; else if (x2R >= T_r) res = x2R; else if (x2R > S_r) res = (x2R-S_r)*(x2R-S_r) * inv_4T_r; else { x2L -= 1.0f; x2R -= 1.0f; p.pixelPosterior(iVolumeIndex); continue; } // left if (x2L <= S_l) {} else if (x2L < T_l) res -= (x2L-S_l)*(x2L-S_l) * inv_4T_l; else if (x2L <= U_l) res -= x2L; else if (x2L < V_l) res -= x2L - (x2L-U_l)*(x2L-U_l)*inv_4T_l; else { x2L -= 1.0f; x2R -= 1.0f; p.pixelPosterior(iVolumeIndex); continue; } float32 diffSrcX; if (switch_t) diffSrcX = m_pVolumeGeometry->pixelColToCenterX(col) - sin_theta * projgeom->getOriginSourceDistance(); else diffSrcX = m_pVolumeGeometry->pixelColToCenterX(col) + sin_theta * projgeom->getOriginSourceDistance(); float32 scale = sqrt(dist_srcDetPixSquared / (diffSrcYSquared + diffSrcX * diffSrcX)); // POLICY: ADD p.addWeight(iRayIndex, iVolumeIndex, PW*PH * res * scale); // POLICY: PIXEL POSTERIOR p.pixelPosterior(iVolumeIndex); x2L -= 1.0f; x2R -= 1.0f; } // end col loop } // end row loop // POLICY: RAY POSTERIOR p.rayPosterior(iRayIndex); } // end detector loop // [45?,135?] and [225?,315?] // horizontaly } else { // loop detectors for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector; // POLICY: RAY PRIOR if (!p.rayPrior(iRayIndex)) continue; float32 dist_srcDetPixSquared = projgeom->getSourceDetectorDistance() * projgeom->getSourceDetectorDistance() + (iDetector + 0.5f - m_pProjectionGeometry->getDetectorCount()*0.5f) * (iDetector + 0.5f - m_pProjectionGeometry->getDetectorCount()*0.5f) * DW * DW; dist_srcDetPixSquared = dist_srcDetPixSquared * dist_srcDetPixSquared / (projgeom->getSourceDetectorDistance() * projgeom->getSourceDetectorDistance() * DW * DW); // get theta_l = alpha_left + theta and theta_r = alpha_right + theta float32 sin_theta_left, cos_theta_left; float32 sin_theta_right, cos_theta_right; float32 t_l, t_r; if (!switch_t) { sin_theta_left = sin_theta * cos_alpha[iDetector] + cos_theta * sin_alpha[iDetector]; sin_theta_right = sin_theta * cos_alpha[iDetector+1] + cos_theta * sin_alpha[iDetector+1]; cos_theta_left = cos_theta * cos_alpha[iDetector] - sin_theta * sin_alpha[iDetector]; cos_theta_right = cos_theta * cos_alpha[iDetector+1] - sin_theta * sin_alpha[iDetector+1]; t_l = sin_alpha[iDetector] * projgeom->getOriginSourceDistance(); t_r = sin_alpha[iDetector+1] * projgeom->getOriginSourceDistance(); } else { sin_theta_left = sin_theta * cos_alpha[iDetector+1] + cos_theta * sin_alpha[iDetector+1]; sin_theta_right = sin_theta * cos_alpha[iDetector] + cos_theta * sin_alpha[iDetector]; cos_theta_left = cos_theta * cos_alpha[iDetector+1] - sin_theta * sin_alpha[iDetector+1]; cos_theta_right = cos_theta * cos_alpha[iDetector] - sin_theta * sin_alpha[iDetector]; t_l = -sin_alpha[iDetector+1] * projgeom->getOriginSourceDistance(); t_r = -sin_alpha[iDetector] * projgeom->getOriginSourceDistance(); } float32 inv_sin_theta_left = 1.0f / sin_theta_left; float32 inv_sin_theta_right = 1.0f / sin_theta_right; float32 updateX_left = cos_theta_left * inv_sin_theta_left; float32 updateX_right = cos_theta_right * inv_sin_theta_right; // Precalculate kernel limits // BUG: If updateX_left > 1 (which can happen if theta_left < pi/4 <= theta), then T_l > U_l, and the expressions for res are no longer correct float32 S_l = -0.5f * updateX_left; if (S_l > 0) { S_l = -S_l; } float32 T_l = -S_l; float32 U_l = 1.0f + S_l; float32 V_l = 1.0f - S_l; float32 inv_4T_l = 0.25f / T_l; float32 S_r = -0.5f * updateX_right; if (S_r > 0) { S_r = -S_r; } float32 T_r = -S_r; float32 U_r = 1.0f + S_r; float32 V_r = 1.0f - S_r; float32 inv_4T_r = 0.25f / T_r; // calculate strip extremes (volume coordinates) float32 PL = (t_l - cos_theta_left * m_pVolumeGeometry->pixelColToCenterX(0)) * inv_sin_theta_left; float32 PR = (t_r - cos_theta_right * m_pVolumeGeometry->pixelColToCenterX(0)) * inv_sin_theta_right; float32 PLimitL = PL - S_l * PW; float32 PLimitR = PR + S_r * PW; // calculate strip extremes (pixel coordinates) float32 XLimitL = (m_pVolumeGeometry->getWindowMaxY() - PLimitL) * inv_PH; float32 XLimitR = (m_pVolumeGeometry->getWindowMaxY() - PLimitR) * inv_PH; float32 xL = (m_pVolumeGeometry->getWindowMaxY() - PL) * inv_PH; float32 xR = (m_pVolumeGeometry->getWindowMaxY() - PR) * inv_PH; // for each col for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) { // get strip extremes in column indices x1L = int((XLimitL > 0.0f) ? XLimitL : XLimitL-1.0f); x1R = int((XLimitR > 0.0f) ? XLimitR : XLimitR-1.0f); // get coords w.r.t leftmost column hit by strip x2L = xL - x1L; x2R = xR - x1L; // update strip extremes for the next row XLimitL += updateX_left; XLimitR += updateX_right; xL += updateX_left; xR += updateX_right; float32 diffSrcXSquared; if (switch_t) diffSrcXSquared = m_pVolumeGeometry->pixelColToCenterX(col) - sin_theta * projgeom->getOriginSourceDistance(); else diffSrcXSquared = m_pVolumeGeometry->pixelColToCenterX(col) + sin_theta * projgeom->getOriginSourceDistance(); diffSrcXSquared = diffSrcXSquared * diffSrcXSquared; // for each affected row for (row = x1L; row <= x1R; ++row) { if (row < 0 || row >= m_pVolumeGeometry->getGridRowCount()) { x2L -= 1.0f; x2R -= 1.0f; continue; } iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col); // POLICY: PIXEL PRIOR if (!p.pixelPrior(iVolumeIndex)) { x2L -= 1.0f; x2R -= 1.0f; continue; } // right if (x2R >= V_r) res = 1.0f; else if (x2R > U_r) res = x2R - (x2R-U_r)*(x2R-U_r)*inv_4T_r; else if (x2R >= T_r) res = x2R; else if (x2R > S_r) res = (x2R-S_r)*(x2R-S_r) * inv_4T_r; else { x2L -= 1.0f; x2R -= 1.0f; p.pixelPosterior(iVolumeIndex); continue; } // left if (x2L <= S_l) {} else if (x2L < T_l) res -= (x2L-S_l)*(x2L-S_l) * inv_4T_l; else if (x2L <= U_l) res -= x2L; else if (x2L < V_l) res -= x2L - (x2L-U_l)*(x2L-U_l)*inv_4T_l; else { x2L -= 1.0f; x2R -= 1.0f; p.pixelPosterior(iVolumeIndex); continue; } float32 diffSrcY; if (switch_t) diffSrcY = m_pVolumeGeometry->pixelRowToCenterY(row) + cos_theta * projgeom->getOriginSourceDistance(); else diffSrcY = m_pVolumeGeometry->pixelRowToCenterY(row) - cos_theta * projgeom->getOriginSourceDistance(); float32 scale = sqrt(dist_srcDetPixSquared / (diffSrcXSquared + diffSrcY * diffSrcY)); // POLICY: ADD p.addWeight(iRayIndex, iVolumeIndex, PW*PH * res * scale); // POLICY: PIXEL POSTERIOR p.pixelPosterior(iVolumeIndex); x2L -= 1.0f; x2R -= 1.0f; } // end col loop } // end row loop // POLICY: RAY POSTERIOR p.rayPosterior(iRayIndex); } // end detector loop } // end theta switch } // end angle loop delete[] cos_alpha; delete[] sin_alpha; } astra-toolbox-2.3.0/include/astra/FanFlatProjectionGeometry2D.h000066400000000000000000000164271475635207100245160ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FANFLATPROJECTIONGEOMETRY2D #define _INC_ASTRA_FANFLATPROJECTIONGEOMETRY2D #include "ProjectionGeometry2D.h" #include "FanFlatVecProjectionGeometry2D.h" #include namespace astra { /** * This class defines a 2D fan beam geometry with a flat detector that has equally spaced detector cells. * * \par XML Configuration * \astra_xml_item{DetectorCount, int, Number of detectors for each projection.} * \astra_xml_item{DetectorWidth, float, Width of each detector.} * \astra_xml_item{ProjectionAngles, vector of float, projection angles in radians.} * \astra_xml_item{DistanceOriginDetector, float, Distance between the center of rotation and the detectorarray.} * \astra_xml_item{DistanceOriginSource, float, Distance between the center of rotation the the x-ray source.} * * \par MATLAB example * \astra_code{ * proj_geom = astra_struct('fanflat');\n * proj_geom.DetectorCount = 512;\n * proj_geom.DetectorWidth = 1.0;\n * proj_geom.ProjectionAngles = linspace(0,pi,100);\n * proj_geom.DistanceOriginDetector = 300;\n * proj_geom.DistanceOriginSource = 300;\n * } */ class _AstraExport CFanFlatProjectionGeometry2D : public CProjectionGeometry2D { /** * Distance from the origin of the coordinate system to the source. */ float32 m_fOriginSourceDistance; /** * Distance from the origin of the coordinate system to the detector (i.e., the distance between the origin and its orthogonal projection * onto the detector array). */ float32 m_fOriginDetectorDistance; public: /** Default constructor. Sets all variables to zero. Note that this constructor leaves the object in an unusable state and must * be followed by a call to init(). */ CFanFlatProjectionGeometry2D(); /** Constructor. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle. * @param _fDetectorWidth Width of a detector cell, in unit lengths. All detector cells are assumed to have equal width. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. * All angles are represented in radians. * @param _fOriginSourceDistance Distance from the origin of the coordinate system to the source * @param _fOriginDetectorDistance Distance from the origin of the coordinate system to the detector */ CFanFlatProjectionGeometry2D(int _iProjectionAngleCount, int _iDetectorCount, float32 _fDetectorWidth, const float32* _pfProjectionAngles, float32 _fOriginSourceDistance, float32 _fOriginDetectorDistance); /** Copy constructor. */ CFanFlatProjectionGeometry2D(const CFanFlatProjectionGeometry2D& _projGeom); /** Assignment operator. */ CFanFlatProjectionGeometry2D& operator=(const CFanFlatProjectionGeometry2D& _other); /** Destructor. */ virtual ~CFanFlatProjectionGeometry2D(); /** Initialize the geometry with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialization. This function MUST be called after using the default constructor and MAY be called to * reset a previously initialized object. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle. * @param _fDetectorWidth Width of a detector cell, in unit lengths. All detector cells are assumed to have equal width. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. * @param _fOriginSourceDistance Distance from the origin of the coordinate system to the source * @param _fOriginDetectorDistance Distance from the origin of the coordinate system to the detector */ bool initialize(int _iProjectionAngleCount, int _iDetectorCount, float32 _fDetectorWidth, const float32* _pfProjectionAngles, float32 _fOriginSourceDistance, float32 _fOriginDetectorDistance); /** Create a hard copy. */ virtual CProjectionGeometry2D* clone() const override; /** Returns true if the type of geometry defined in this class is the one specified in _sType. * * @param _sType geometry type to compare to. * @return true if _sType == "fanflat". */ virtual bool isOfType(const std::string& _sType); /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const; /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CProjectionGeometry2D &) const override; /** Returns the distance from the origin of the coordinate system to the source. * * @return Distance from the origin of the coordinate system to the source */ float32 getOriginSourceDistance() const; /** Returns the distance from the origin of the coordinate system to the detector * (i.e., the distance between the origin and its orthogonal projection onto the detector array). * * @return Distance from the origin of the coordinate system to the detector */ float32 getOriginDetectorDistance() const; /** Returns the distance from the source to the detector * (i.e., the distance between the source and its orthogonal projection onto the detector array). * * @return Distance from the source to the detector */ float32 getSourceDetectorDistance() const; /** Create a vector geom */ CFanFlatVecProjectionGeometry2D* toVectorGeometry(); }; // Returns the distance from the origin of the coordinate system to the source. inline float32 CFanFlatProjectionGeometry2D::getOriginSourceDistance() const { return m_fOriginSourceDistance; } // Returns the distance from the origin of the coordinate system to the detector. inline float32 CFanFlatProjectionGeometry2D::getOriginDetectorDistance() const { return m_fOriginDetectorDistance; } // Returns the distance from the source to the detector. inline float32 CFanFlatProjectionGeometry2D::getSourceDetectorDistance() const { return (m_fOriginSourceDistance + m_fOriginDetectorDistance); } } // namespace astra #endif /* _INC_ASTRA_FANFLATPROJECTIONGEOMETRY2D */ astra-toolbox-2.3.0/include/astra/FanFlatVecProjectionGeometry2D.h000066400000000000000000000113331475635207100251430ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FANFLATVECPROJECTIONGEOMETRY2D #define _INC_ASTRA_FANFLATVECPROJECTIONGEOMETRY2D #include "ProjectionGeometry2D.h" #include "GeometryUtil2D.h" #include namespace astra { /** * This class defines a 2D fan beam geometry. * * \par XML Configuration * \astra_xml_item{DetectorCount, int, Number of detectors for each projection.} * \astra_xml_item{Vectors, matrix defining the 2D position of source and detector.} * * \par MATLAB example * \astra_code{ * proj_geom = astra_struct('fanflat_vec');\n * proj_geom.DetectorCount = 512;\n * proj_geom.Vectors = V;\n * } * * \par Vectors * Vectors is a matrix containing the actual geometry. Each row corresponds * to a single projection, and consists of: * ( srcX, srcY, srcZ, dX, dY uX, uY) * src: the ray source * d : the centre of the detector array * u : the vector from detector 0 to detector 1 */ class _AstraExport CFanFlatVecProjectionGeometry2D : public CProjectionGeometry2D { protected: SFanProjection *m_pProjectionAngles; public: /** Default constructor. Sets all variables to zero. Note that this constructor leaves the object in an unusable state and must * be followed by a call to init(). */ CFanFlatVecProjectionGeometry2D(); /** Constructor. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. */ CFanFlatVecProjectionGeometry2D(int _iProjectionAngleCount, int _iDetectorCount, const SFanProjection* _pfProjectionAngles); /** Copy constructor. */ CFanFlatVecProjectionGeometry2D(const CFanFlatVecProjectionGeometry2D& _projGeom); /** Assignment operator. */ CFanFlatVecProjectionGeometry2D& operator=(const CFanFlatVecProjectionGeometry2D& _other); /** Destructor. */ virtual ~CFanFlatVecProjectionGeometry2D(); /** Initialize the geometry with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialization. This function MUST be called after using the default constructor and MAY be called to * reset a previously initialized object. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. */ bool initialize(int _iProjectionAngleCount, int _iDetectorCount, const SFanProjection* _pfProjectionAngles); virtual bool _check(); /** Create a hard copy. */ virtual CProjectionGeometry2D* clone() const override; /** Returns true if the type of geometry defined in this class is the one specified in _sType. * * @param _sType geometry type to compare to. * @return true if _sType == "fanflat_vec". */ virtual bool isOfType(const std::string& _sType); /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CProjectionGeometry2D &) const override; /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const; const SFanProjection* getProjectionVectors() const { return m_pProjectionAngles; } protected: virtual bool initializeAngles(const Config& _cfg); }; } // namespace astra #endif /* _INC_ASTRA_FANFLATVECPROJECTIONGEOMETRY2D */ astra-toolbox-2.3.0/include/astra/Features.h000066400000000000000000000040011475635207100210030ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FEATURES #define _INC_ASTRA_FEATURES #include "astra/Globals.h" namespace astra { _AstraExport bool hasFeature(const std::string &feature); } /* FEATURES: cuda is cuda support compiled in? NB: To check if there is also actually a usable GPU, use cudaAvailable() mex_link is there support for the matlab command astra_mex_data3d('link')? projectors_scaled_as_line_integrals This is set since all 2D and 3D, CPU and GPU projectors scale their outputs to approximate line integrals. (Previously, some 2D projectors were scaled as area integrals.) fan_cone_BP_density_weighting_by_default This is set since fan beam and cone beam BP operations perform ray density weighting by default to more closely approximate the true mathematical adjoint. The DensityWeighting cuda3d projector option is removed. unpadded_GPULink This is set since FP3D no longer silently fails with GPULink memory that is not padded to a multiple of 32 pixels For future backward-incompatible changes, extra features will be added here */ #endif astra-toolbox-2.3.0/include/astra/FilteredBackProjectionAlgorithm.h000066400000000000000000000103331475635207100254550ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FILTEREDBACKPROJECTION #define _INC_ASTRA_FILTEREDBACKPROJECTION #include "ReconstructionAlgorithm2D.h" #include "Globals.h" #include "Projector2D.h" #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" #include "Filters.h" namespace astra { /** * \brief * This class contains the implementation of the filtered back projection (FBP) * reconstruction algorithm. * * \par XML Configuration * \astra_xml_item{ProjectorId, integer, Identifier of a projector as it is stored in the ProjectorManager.} * \astra_xml_item{VolumeDataId, integer, Identifier of the volume data object as it is stored in the DataManager.} * \astra_xml_item{ReconstructionDataId, integer, Identifier of the resulting projection data object as it is stored in the DataManager.} * \astra_xml_item_option{ProjectionIndex, integer, 0, Only reconstruct this specific projection angle. } * \par MATLAB example * \astra_code{ * cfg = astra_struct('FP');\n * cfg.ProjectorId = proj_id;\n * cfg.ReconstructionDataId = vol_id;\n * cfg.ProjectionDataId = sino_id;\n * alg_id = astra_mex_algorithm('create'\, cfg);\n * astra_mex_algorithm('run'\, alg_id);\n * astra_mex_algorithm('delete'\, alg_id);\n * } * */ class _AstraExport CFilteredBackProjectionAlgorithm : public CReconstructionAlgorithm2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - valid projector * - valid data objects * - projection order all within range */ virtual bool _check(); public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "FBP"; /** Default constructor, containing no code. */ CFilteredBackProjectionAlgorithm(); /** Destructor. */ virtual ~CFilteredBackProjectionAlgorithm(); /** Initialize class. * * @param _pProjector Projector to use. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. * @return success */ bool initialize(CProjector2D* _pProjector, CFloat32VolumeData2D* _pReconstruction, CFloat32ProjectionData2D* _pSinogram); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Clear this class. */ virtual void clear(); /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); /** Performs the filtering of the projection data. * * @param _pFilteredSinogram will contain filtered sinogram afterwards */ void performFiltering(CFloat32ProjectionData2D * _pFilteredSinogram); /** Get a description of the class. * * @return description string */ virtual std::string description() const; protected: SFilterConfig m_filterConfig; }; // inline functions inline std::string CFilteredBackProjectionAlgorithm::description() const { return CFilteredBackProjectionAlgorithm::type; }; } // end namespace #endif astra-toolbox-2.3.0/include/astra/Filters.h000066400000000000000000000062561475635207100206530ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FILTERS_H #define _INC_ASTRA_FILTERS_H #include "Globals.h" #include #include namespace astra { class Config; class CAlgorithm; class CProjectionGeometry2D; enum E_FBPFILTER { FILTER_ERROR, //< not a valid filter FILTER_NONE, //< no filter (regular BP) FILTER_RAMLAK, //< default FBP filter FILTER_SHEPPLOGAN, //< Shepp-Logan FILTER_COSINE, //< Cosine FILTER_HAMMING, //< Hamming filter FILTER_HANN, //< Hann filter FILTER_TUKEY, //< Tukey filter FILTER_LANCZOS, //< Lanczos filter FILTER_TRIANGULAR, //< Triangular filter FILTER_GAUSSIAN, //< Gaussian filter FILTER_BARTLETTHANN, //< Bartlett-Hann filter FILTER_BLACKMAN, //< Blackman filter FILTER_NUTTALL, //< Nuttall filter, continuous first derivative FILTER_BLACKMANHARRIS, //< Blackman-Harris filter FILTER_BLACKMANNUTTALL, //< Blackman-Nuttall filter FILTER_FLATTOP, //< Flat top filter FILTER_KAISER, //< Kaiser filter FILTER_PARZEN, //< Parzen filter FILTER_PROJECTION, //< all projection directions share one filter FILTER_SINOGRAM, //< every projection direction has its own filter FILTER_RPROJECTION, //< projection filter in real space (as opposed to fourier space) FILTER_RSINOGRAM, //< sinogram filter in real space }; struct SFilterConfig { E_FBPFILTER m_eType; float m_fD; float m_fParameter; std::vector m_pfCustomFilter; int m_iCustomFilterWidth; int m_iCustomFilterHeight; SFilterConfig() : m_eType(FILTER_ERROR), m_fD(1.0f), m_fParameter(-1.0f), m_iCustomFilterWidth(0), m_iCustomFilterHeight(0) { } }; // Generate filter of given size and parameters. Returns newly allocated array. float *genFilter(const SFilterConfig &_cfg, int _iFFTRealDetectorCount, int _iFFTFourierDetectorCount); // Convert string to filter type. Returns FILTER_ERROR if unrecognized. E_FBPFILTER convertStringToFilter(const std::string &_filterType); SFilterConfig getFilterConfigForAlgorithm(const Config& _cfg, CAlgorithm *_alg); bool checkCustomFilterSize(const SFilterConfig &_cfg, const CProjectionGeometry2D &_geom); int calcNextPowerOfTwo(int _iValue); int calcFFTFourierSize(int _iFFTRealSize); } #endif astra-toolbox-2.3.0/include/astra/Float32Data.h000066400000000000000000000043111475635207100212350ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FLOAT32DATA #define _INC_ASTRA_FLOAT32DATA #include "Globals.h" namespace astra { /** * This is a virtual base class for floating point data classes. */ class _AstraExport CFloat32Data { protected: // Protected Member Variables bool m_bInitialized; ///< has the object been initialized? int m_iDimensions; ///< the number of dimensions public: /** * Default constructor. */ CFloat32Data(); /** * Destructor. Free allocated memory */ virtual ~CFloat32Data(); /** * Get the initialization state of the object. * * @return true iff the object has been initialized */ bool isInitialized() const; /** * Get the number of dimensions of this object. * * @return number of dimensions */ virtual int getDimensionCount() const = 0; }; //---------------------------------------------------------------------------------------- // Inline member functions //---------------------------------------------------------------------------------------- // Get the initialization state of the object. inline bool CFloat32Data::isInitialized() const { return m_bInitialized; } //---------------------------------------------------------------------------------------- } // end namespace #endif astra-toolbox-2.3.0/include/astra/Float32Data2D.h000066400000000000000000000464041475635207100214340ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FLOAT32DATA2D #define _INC_ASTRA_FLOAT32DATA2D #include "Globals.h" #include "Float32Data.h" namespace astra { template class _AstraExport CCustomMemory { public: virtual ~CCustomMemory()=0; T* m_fPtr; }; template class CCustomMemory; typedef CCustomMemory CFloat32CustomMemory; /** * This class represents a 2-dimensional block of 32-bit floating point data. * It contains member functions for accessing this data and for performing * elementary computations on the data. * The data block is "owned" by the class, meaning that the class is * responsible for deallocation of the memory involved. */ class _AstraExport CFloat32Data2D : public CFloat32Data { protected: int m_iWidth; ///< width of the data (x) int m_iHeight; ///< height of the data (y) size_t m_iSize; ///< total size of the data /** Pointer to the data block, represented as a 1-dimensional array. * Note that the data memory is "owned" by this class, meaning that the * class is responsible for deallocation of the memory involved. * To access element (ix, iy) internally, use * m_pData[iy * m_iWidth + ix] */ float32* m_pfData; /** Array of float32 pointers, each pointing to a single horizontal * line in the m_pfData memory block. * To access element (ix, iy) internally, use m_ppfData2D[iy][ix] */ float32** m_ppfData2D; float32 m_fGlobalMin; ///< minimum value of the data float32 m_fGlobalMax; ///< maximum value of the data float32 m_fGlobalMean; ///< mean value of the data /** Allocate memory for m_pfData and m_ppfData2D arrays. * * The allocated block consists of m_iSize float32s. The block is * not cleared after allocation and its contents is undefined. * This function may NOT be called if memory has already been allocated. */ void _allocateData(); /** Free memory for m_pfData and m_ppfData2D arrays. * * This function may ONLY be called if the memory for both blocks has been * allocated before. */ void _freeData(); /** Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. */ void _clear(); /** Un-initialize the object, bringing it back in the uninitialized state. */ void _unInit(); /** Find the minimum and maximum data value and store them in * m_fGlobalMin and m_fGlobalMax */ void _computeGlobalMinMax(); /** Initialization. Initializes an instance of the CFloat32Data2D class, without filling the data block. * Can only be called by derived classes. * * Initializes an instance of the CFloat32Data2D class. Memory is allocated for the * data block. The allocated memory is not cleared and its contents after * construction is undefined. Initialization may be followed by a call to * copyData() to fill the memory block. If the object has been initialized before, the * object is reinitialized and memory is freed and reallocated if necessary. * This function does not set m_bInitialized to true if everything is ok. * * @param _iWidth width of the 2D data (x-axis), must be > 0 * @param _iHeight height of the 2D data (y-axis), must be > 0 * @return initialization of the base class successfull */ bool _initialize(int _iWidth, int _iHeight); /** Initialization. Initializes an instance of the CFloat32Data2D class with initialization of the data block. * Can only be called by derived classes. * * Initializes an instance of the CFloat32Data2D class. Memory * is allocated for the data block and the contents of the memory pointed to by * _pfData is copied into the allocated memory. If the object has been initialized before, the * object is reinitialized and memory is freed and reallocated if necessary. * This function does not set m_bInitialized to true if everything is ok. * * @param _iWidth width of the 2D data (x-axis), must be > 0 * @param _iHeight height of the 2D data (y-axis), must be > 0 * @param _pfData pointer to a one-dimensional float32 data block */ bool _initialize(int _iWidth, int _iHeight, const float32* _pfData); /** Initialization. Initializes an instance of the CFloat32Data2D class with initialization of the data * block with a scalar value. Can only be called by derived classes. * * Initializes an instance of the CFloat32Data2D class. Memory * is allocated for the data block and the contents of the memory pointed to by * _pfData is copied into the allocated memory. If the object has been initialized before, the * object is reinitialized and memory is freed and reallocated if necessary. * This function does not set m_bInitialized to true if everything is ok. * * @param _iWidth width of the 2D data (x-axis), must be > 0 * @param _iHeight height of the 2D data (y-axis), must be > 0 * @param _fScalar scalar value to put at each index */ bool _initialize(int _iWidth, int _iHeight, float32 _fScalar); /** Initialization. Initializes an instance of the CFloat32Data2D class with pre-allocated memory. * Can only be called by derived classes. * * Initializes an instance of the CFloat32Data2D class. Memory * is pre-allocated and passed via the abstract CFloat32CustomMemory handle * class. The handle will be deleted when the memory can be freed. * You should override the destructor to provide custom behaviour on free. * If the object has been initialized before, the * object is reinitialized and memory is freed and reallocated if necessary. * This function does not set m_bInitialized to true if everything is ok. * * @param _iWidth width of the 2D data (x-axis), must be > 0 * @param _iHeight height of the 2D data (y-axis), must be > 0 * @param _pfData pointer to a one-dimensional float32 data block */ bool _initialize(int _iWidth, int _iHeight, CFloat32CustomMemory* _pCustomMemory); /** Constructor. Create an instance of the CFloat32Data2D class without initializing the data block. * Can only be called by derived classes. * * Creates an instance of the CFloat32Data2D class. Memory is allocated for the * data block. The allocated memory is not cleared and its contents after * construction is undefined. Construction may be followed by a call to * copyData() to fill the memory block. * * @param _iWidth width of the 2D data (x-axis), must be > 0 * @param _iHeight height of the 2D data (y-axis), must be > 0 */ CFloat32Data2D(int _iWidth, int _iHeight); /** Constructor. Create an instance of the CFloat32Data2D class with initialization of the data block. * Can only be called by derived classes. * * Creates an instance of the CFloat32Data2D class. Memory * is allocated for the data block and the contents of the memory pointed to by * _pfData is copied into the allocated memory. * * @param _iWidth width of the 2D data (x-axis), must be > 0 * @param _iHeight height of the 2D data (y-axis), must be > 0 * @param _pfData pointer to a one-dimensional float32 data block */ CFloat32Data2D(int _iWidth, int _iHeight, const float32* _pfData); /** Constructor. Create an instance of the CFloat32Data2D class with initialization of the data block * with a scalar value. Can only be called by derived classes. * * Creates an instance of the CFloat32Data2D class. Memory * is allocated for the data block and the contents of the memory pointed to by * _pfData is copied into the allocated memory. * * @param _iWidth width of the 2D data (x-axis), must be > 0 * @param _iHeight height of the 2D data (y-axis), must be > 0 * @param _fScalar scalar value to put at each index */ CFloat32Data2D(int _iWidth, int _iHeight, float32 _fScalar); /** Constructor. Create an instance of the CFloat32Data2D class with pre-allocated memory. * Can only be called by derived classes. * * Creates an instance of the CFloat32Data2D class. Memory * is pre-allocated and passed via the abstract CFloat32CustomMemory handle * class. The handle will be deleted when the memory can be freed. * You should override the destructor to provide custom behaviour on free. * * @param _iWidth width of the 2D data (x-axis), must be > 0 * @param _iHeight height of the 2D data (y-axis), must be > 0 * @param _pfData pointer to a one-dimensional float32 data block */ CFloat32Data2D(int _iWidth, int _iHeight, CFloat32CustomMemory* _pCustomMemory); /** Copy constructor. */ CFloat32Data2D(const CFloat32Data2D&); public: /** Typedef with available datatypes: BASE, PROJECTION, VOLUME. */ typedef enum {BASE, PROJECTION, VOLUME} EDataType; /** Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the initialize() methods before the object can be used. Any use before calling init() is not allowed, * except calling the member function isInitialized(). * */ CFloat32Data2D(); /** Destructor. Free allocated memory */ virtual ~CFloat32Data2D(); /** Copy the data block pointed to by _pfData to the data block pointed to by m_pfData. * The pointer _pfData must point to a block of m_iSize float32s. * * @param _pfData source data block */ void copyData(const float32* _pfData); /** scale the grey value of the data from 0-255. * */ void scale(); /** Set each element of the data to a specified scalar value. * * @param _fScalar scalar value */ void setData(float32 _fScalar); /** Set all data to zero */ void clearData(); /** Get a pointer to the data block, represented as a 1-dimensional * array of float32 values. The data memory is still "owned" by the * CFloat32Data2D instance; this memory may NEVER be freed by the * caller of this function. If changes are made to this data, the * function updateStatistics() should be called after completion of * all changes. * * @return pointer to the 1-dimensional 32-bit floating point data block */ float32* getData(); /** Get a const pointer to the data block, represented as a 1-dimensional * array of float32 values. The data memory is still "owned" by the * CFloat32Data2D instance; this memory may NEVER be freed by the * caller of this function. If changes are made to this data, the * function updateStatistics() should be called after completion of * all changes. * * @return pointer to the 1-dimensional 32-bit floating point data block */ const float32* getDataConst() const; /** Get a float32** to the data block, represented as a 2-dimensional array of float32 values. * * After the call p = getData2D(), use p[iy][ix] to access element (ix, iy). * The data memory and pointer array are still "owned" by the CFloat32Data2D * instance; this memory may NEVER be freed by the caller of this function. * If changes are made to this data, the function updateStatistics() * should be called after completion of all changes. * * @return pointer to the 2-dimensional 32-bit floating point data block */ float32** getData2D(); /** Get a const float32** to the data block, represented as a 2-dimensional array of float32 values. * * After the call p = getData2D(), use p[iy][ix] to access element (ix, iy). * The data memory and pointer array are still "owned" by the CFloat32Data2D * instance; this memory may NEVER be freed by the caller of this function. * If changes are made to this data, the function updateStatistics() * should be called after completion of all changes. * * @return pointer to the 2-dimensional 32-bit floating point data block */ const float32** getData2DConst() const; /** Update data statistics, such as minimum and maximum value, after the data has been modified. */ virtual void updateStatistics(); /** Get the minimum value in the data block. * If the data has been changed after construction, the function * updateStatistics() must be called at least once before * a query can be made on this value. * * @return minimum value in the data block */ virtual float32 getGlobalMin() const; /** Get the maximum value in the data block * If the data has been changed after construction, the function * updateStatistics() must be called at least once before * a query can be made on this value. * * @return maximum value in the data block */ virtual float32 getGlobalMax() const; /** Get the mean value in the data block * If the data has been changed after construction, the function * updateStatistics() must be called at least once before * a query can be made on this value. * * @return maximum value in the data block */ virtual float32 getGlobalMean() const; /** Get the width of the data block. * * @return width of the data block */ int getWidth() const; /** Get the height of the data block. * * @return height of the data block */ int getHeight() const; /** Get the total size (width*height) of the data block. * * @return size of the data block */ size_t getSize() const; /** which type is this class? * * @return DataType: ASTRA_DATATYPE_FLOAT32_PROJECTION or * ASTRA_DATATYPE_FLOAT32_VOLUME */ virtual EDataType getType() const; /** get a description of the class * * @return description string */ virtual std::string description() const; /** Get the number of dimensions of this object. * * @return number of dimensions */ int getDimensionCount() const; /** * Clamp data to minimum value * * @param _fMin minimum value * @return l-value */ CFloat32Data2D& clampMin(float32& _fMin); /** * Clamp data to maximum value * * @param _fMax maximum value * @return l-value */ CFloat32Data2D& clampMax(float32& _fMax); /** * Overloaded Operator: data += data (pointwise) * * @param _data r-value * @return l-value */ CFloat32Data2D& operator+=(const CFloat32Data2D& _data); /** * Overloaded Operator: data -= data (pointwise) * * @param _data r-value * @return l-value */ CFloat32Data2D& operator-=(const CFloat32Data2D& _data); /** * Overloaded Operator: data *= data (pointwise) * * @param _data r-value * @return l-value */ CFloat32Data2D& operator*=(const CFloat32Data2D& _data); /** * Overloaded Operator: data *= scalar (pointwise) * * @param _fScalar r-value * @return l-value */ CFloat32Data2D& operator*=(const float32& _fScalar); /** * Overloaded Operator: data /= scalar (pointwise) * * @param _fScalar r-value * @return l-value */ CFloat32Data2D& operator/=(const float32& _fScalar); /** * Overloaded Operator: data += scalar (pointwise) * * @param _fScalar r-value * @return l-value */ CFloat32Data2D& operator+=(const float32& _fScalar); /** * Overloaded Operator: data -= scalar (pointwise) * * @param _fScalar r-value * @return l-value */ CFloat32Data2D& operator-=(const float32& _fScalar); CFloat32Data2D& operator=(const CFloat32Data2D& _dataIn); float32& getData(int _index); private: CFloat32CustomMemory* m_pCustomMemory; }; //---------------------------------------------------------------------------------------- // Inline member functions //---------------------------------------------------------------------------------------- // Get the number of dimensions of this object. inline int CFloat32Data2D::getDimensionCount() const { return 2; } //---------------------------------------------------------------------------------------- // Get the type of this object. inline CFloat32Data2D::EDataType CFloat32Data2D::getType() const { return BASE; } //---------------------------------------------------------------------------------------- // Get the width of the data block. inline int CFloat32Data2D::getWidth() const { ASTRA_ASSERT(m_bInitialized); return m_iWidth; } //---------------------------------------------------------------------------------------- // Get the height of the data block. inline int CFloat32Data2D::getHeight() const { ASTRA_ASSERT(m_bInitialized); return m_iHeight; } //---------------------------------------------------------------------------------------- // Get the total size (width*height*depth) of the data block. inline size_t CFloat32Data2D::getSize() const { ASTRA_ASSERT(m_bInitialized); return m_iSize; } //---------------------------------------------------------------------------------------- // Get a pointer to the data block, represented as a 1-dimensional array of float32 values. inline float32* CFloat32Data2D::getData() { //ASTRA_ASSERT(m_bInitialized); return m_pfData; } //---------------------------------------------------------------------------------------- // Get a pointer to the data block, represented as a 1-dimensional array of float32 values. inline float32& CFloat32Data2D::getData(int _index) { //ASTRA_ASSERT(m_bInitialized); return m_pfData[_index]; } //---------------------------------------------------------------------------------------- // Get a const pointer to the data block, represented as a 1-dimensional array of float32 values. inline const float32* CFloat32Data2D::getDataConst() const { ASTRA_ASSERT(m_bInitialized); return (const float32*)m_pfData; } //---------------------------------------------------------------------------------------- // Get a float32** to the data block, represented as a 2-dimensional array of float32 values. inline float32** CFloat32Data2D::getData2D() { ASTRA_ASSERT(m_bInitialized); return m_ppfData2D; } //---------------------------------------------------------------------------------------- // Get a const float32** to the data block, represented as a 2-dimensional array of float32 values. inline const float32** CFloat32Data2D::getData2DConst() const { ASTRA_ASSERT(m_bInitialized); return (const float32**)m_ppfData2D; } //---------------------------------------------------------------------------------------- // Get the minimum value in the data block. inline float32 CFloat32Data2D::getGlobalMin() const { ASTRA_ASSERT(m_bInitialized); return m_fGlobalMin; } //---------------------------------------------------------------------------------------- // Get the maximum value in the data block inline float32 CFloat32Data2D::getGlobalMax() const { ASTRA_ASSERT(m_bInitialized); return m_fGlobalMax; } //---------------------------------------------------------------------------------------- // Get the mean value in the data block inline float32 CFloat32Data2D::getGlobalMean() const { ASTRA_ASSERT(m_bInitialized); return m_fGlobalMean; } } // end namespace astra #endif // _INC_ASTRA_FLOAT32DATA2D astra-toolbox-2.3.0/include/astra/Float32ProjectionData2D.h000066400000000000000000000243171475635207100234700ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FLOAT32PROJECTIONDATA2D #define _INC_ASTRA_FLOAT32PROJECTIONDATA2D #include "Float32Data2D.h" #include "ProjectionGeometry2D.h" namespace astra { /** * This class represents two-dimensional Projection Data. * * It contains member functions for accessing this data and for performing * elementary computations on the data. * The data block is "owned" by the class, meaning that the class is * responsible for deallocation of the memory involved. * * The projection data is stored as a series of consecutive rows, where * each row contains the data for a single projection. */ class _AstraExport CFloat32ProjectionData2D : public CFloat32Data2D { public: /** * Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the init() methods before the object can be used. Any use before calling init() is not allowed, * except calling the member function isInitialized(). * */ CFloat32ProjectionData2D(); /** * Constructor. Create an instance of the CFloat32ProjectionData2D class without initializing the data. * * Memory is allocated for the data block. The allocated memory is not cleared and * its contents after construction is undefined. Construction may be followed by a * call to copyData() to fill the memory block. * The size of the data is determined by the specified projection geometry object. * * @param _pGeometry Projection Geometry object. This object will be HARDCOPIED into this class. */ CFloat32ProjectionData2D(const CProjectionGeometry2D &_pGeometry); /** * Constructor. Create an instance of the CFloat32ProjectionData2D class with initialization of the data. * * Creates an instance of the CFloat32ProjectionData2D class. Memory * is allocated for the data block and the contents of the memory pointed to by * _pfData is copied into the allocated memory. * The size of the data is determined by the specified projection geometry object. * * @param _pGeometry Projection Geometry object. This object will be HARDCOPIED into this class. * @param _pfData pointer to a one-dimensional float32 data block */ CFloat32ProjectionData2D(const CProjectionGeometry2D &_pGeometry, float32* _pfData); /** * Constructor. Create an instance of the CFloat32ProjectionData2D class with initialization of the data. * * Creates an instance of the CFloat32ProjectionData2D class. Memory * is allocated for the data block and the contents of the memory pointed to by * _pfData is copied into the allocated memory. * The size of the data is determined by the specified projection geometry object. * * @param _pGeometry Projection Geometry object. This object will be HARDCOPIED into this class. * @param _fScalar scalar value to be put at each index. */ CFloat32ProjectionData2D(const CProjectionGeometry2D &_pGeometry, float32 _fScalar); /** * Copy constructor */ CFloat32ProjectionData2D(const CFloat32ProjectionData2D& _other); /** Constructor. Create an instance of the CFloat32ProjectionData2D class with pre-allocated memory. * * Creates an instance of the CFloat32ProjectionData2D class. Memory * is pre-allocated and passed via the abstract CFloat32CustomMemory handle * class. The handle will be deleted when the memory can be freed. * You should override the destructor to provide custom behaviour on free. * * @param _pGeometry Projection Geometry object. This object will be HARDCOPIED into this class. * @param _pCustomMemory custom memory handle * */ CFloat32ProjectionData2D(const CProjectionGeometry2D &_pGeometry, CFloat32CustomMemory* _pCustomMemory); /** * Assignment operator */ CFloat32ProjectionData2D& operator=(const CFloat32ProjectionData2D& _other); /** * Destructor. */ virtual ~CFloat32ProjectionData2D(); /** Initialization. Initializes an instance of the CFloat32ProjectionData2D class, without filling the data block. * * Initializes an instance of the CFloat32Data2D class. Memory is allocated for the * data block. The allocated memory is not cleared and its contents after * construction is undefined. Initialization may be followed by a call to * copyData() to fill the memory block. If the object has been initialized before, the * object is reinitialized and memory is freed and reallocated if necessary. * * @param _pGeometry Projection Geometry of the data. This object will be HARDCOPIED into this class. * @return Initialization of the base class successfull. */ bool initialize(const CProjectionGeometry2D &_pGeometry); /** Initialization. Initializes an instance of the CFloat32Data2D class with initialization of the data block. * * Initializes an instance of the CFloat32Data2D class. Memory * is allocated for the data block and the contents of the memory pointed to by * _pfData is copied into the allocated memory. If the object has been initialized before, the * object is reinitialized and memory is freed and reallocated if necessary. * * @param _pGeometry Projection Geometry of the data. This object will be HARDCOPIED into this class. * @param _pfData pointer to a one-dimensional float32 data block */ bool initialize(const CProjectionGeometry2D &_pGeometry, const float32* _pfData); /** Initialization. Initializes an instance of the CFloat32Data2D class with initialization of the data block. * * Initializes an instance of the CFloat32Data2D class. Memory * is allocated for the data block and the contents of the memory pointed to by * _pfData is copied into the allocated memory. If the object has been initialized before, the * object is reinitialized and memory is freed and reallocated if necessary. * * @param _pGeometry Projection Geometry of the data. This object will be HARDCOPIED into this class. * @param _fScalar scalar value to be put at each index. */ bool initialize(const CProjectionGeometry2D &_pGeometry, float32 _fScalar); /** Initialization. Initializes an instance of the CFloat32ProjectionData2D class with pre-allocated memory. * * Memory is pre-allocated and passed via the abstract CFloat32CustomMemory handle * class. The handle will be deleted when the memory can be freed. * You should override the destructor to provide custom behaviour on free. * * @param _pGeometry Projection Geometry object. This object will be HARDCOPIED into this class. * @param _pCustomMemory custom memory handle * */ bool initialize(const CProjectionGeometry2D &_pGeometry, CFloat32CustomMemory* _pCustomMemory); /** Get the number of detectors. * * @return number of detectors */ int getDetectorCount() const; /** Get the number of projection angles. * * @return number of projection angles */ int getAngleCount() const; /** Get a pointer to the data of a single projection angle. * * The data memory is still "owned" by the * CFloat32ProjectionData2D instance; this memory may NEVER be freed by the * caller of this function. If changes are made to this data, the * function updateStatistics() should be called after completion of * all changes. * * @return pointer to the data */ float32* getSingleProjectionData(int _iAngleIndex); /** Get a const pointer to the data of a single projection angle. * * The data memory is still "owned" by the * CFloat32ProjectionData2D instance; this memory may NEVER be freed by the * caller of this function. * * @return pointer to the data */ const float32* getSingleProjectionDataConst(int _iAngleIndex) const; /** Which type is this class? * * @return DataType: PROJECTION */ virtual EDataType getType() const; /** Get the projection geometry. * * @return pointer to projection geometry. */ virtual const CProjectionGeometry2D &getGeometry() const; /** Change the projection geometry. * Note that this can't change the dimensions of the data. */ virtual void changeGeometry(const CProjectionGeometry2D &pGeometry); protected: /** The projection geometry for this data. */ CProjectionGeometry2D* m_pGeometry; }; //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // Get the number of detectors. inline int CFloat32ProjectionData2D::getDetectorCount() const { ASTRA_ASSERT(m_bInitialized); return m_iWidth; } //---------------------------------------------------------------------------------------- // Get the number of projection angles. inline int CFloat32ProjectionData2D::getAngleCount() const { ASTRA_ASSERT(m_bInitialized); return m_iHeight; } //---------------------------------------------------------------------------------------- // Get the projection geometry. inline const CProjectionGeometry2D& CFloat32ProjectionData2D::getGeometry() const { ASTRA_ASSERT(m_bInitialized); return *m_pGeometry; } //---------------------------------------------------------------------------------------- // Get type. inline CFloat32Data2D::EDataType CFloat32ProjectionData2D::getType() const { return PROJECTION; } //---------------------------------------------------------------------------------------- } // end namespace astra #endif // _INC_ASTRA_FLOAT32PROJECTIONDATA2D astra-toolbox-2.3.0/include/astra/Float32VolumeData2D.h000066400000000000000000000174531475635207100226260ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FLOAT32VOLUMEDATA2D #define _INC_ASTRA_FLOAT32VOLUMEDATA2D #include "Float32Data2D.h" #include "VolumeGeometry2D.h" namespace astra { /** * This class represents two-dimensional Volume Data. * * It contains member functions for accessing this data and for performing * elementary computations on the data. * The data block is "owned" by the class, meaning that the class is * responsible for deallocation of the memory involved. */ class _AstraExport CFloat32VolumeData2D : public CFloat32Data2D { public: /** Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the init() methods before the object can be used. Any use before calling init() is not allowed, * except calling the member function isInitialized(). * */ CFloat32VolumeData2D(); /** Constructor. Create an instance of the CFloat32VolumeData2D class without initializing the data. * * Memory is allocated for the data block. The allocated memory is not cleared and * its contents after construction is undefined. Construction may be followed by a * call to copyData() to fill the memory block. * The size of the data is determined by the specified volume geometry object. * * @param _pGeometry Volume Geometry object. This object will be HARDCOPIED into this class. */ CFloat32VolumeData2D(const CVolumeGeometry2D& _pGeometry); /** Constructor. Create an instance of the CFloat32VolumeData2D class with initialization of the data. * * Memory is allocated for the data block and the contents of the memory pointed to by * _pfData is copied into the allocated memory. * The size of the data is determined by the specified volume geometry object. * * @param _pGeometry Volume Geometry object. This object will be HARDCOPIED into this class. * @param _pfData pointer to a one-dimensional float32 data block */ CFloat32VolumeData2D(const CVolumeGeometry2D& _pGeometry, float32* _pfData); /** Constructor. Create an instance of the CFloat32VolumeData2D class with a scalar initialization of the data. * * Memory is allocated for the data block and the contents of the memory pointed to by * _pfData is copied into the allocated memory. * The size of the data is determined by the specified volume geometry object. * * @param _pGeometry Volume Geometry object. This object will be HARDCOPIED into this class. * @param _fScalar scalar value to be put at each index. */ CFloat32VolumeData2D(const CVolumeGeometry2D& _pGeometry, float32 _fScalar); /** * Copy constructor */ CFloat32VolumeData2D(const CFloat32VolumeData2D& _other); /** Constructor. Create an instance of the CFloat32VolumeData2D class with pre-allocated memory. * * Creates an instance of the CFloat32VolumeData2D class. Memory * is pre-allocated and passed via the abstract CFloat32CustomMemory handle * class. The handle will be deleted when the memory can be freed. * You should override the destructor to provide custom behaviour on free. * * @param _pGeometry Volume Geometry object. This object will be HARDCOPIED into this class. * @param _pCustomMemory custom memory handle * */ CFloat32VolumeData2D(const CVolumeGeometry2D& _pGeometry, CFloat32CustomMemory* _pCustomMemory); /** * Assignment operator */ CFloat32VolumeData2D& operator=(const CFloat32VolumeData2D& _other); /** Initialization. Initializes of the CFloat32VolumeData2D class without initializing the data. * * Memory is allocated for the data block. The allocated memory is not cleared and * its contents after construction is undefined. Construction may be followed by a * call to copyData() to fill the memory block. * The size of the data is determined by the specified volume geometry object. * * @param _pGeometry Volume Geometry of the data. This object will be HARDCOPIED into this class. * @return Initialization of the base class successfull. */ bool initialize(const CVolumeGeometry2D& _pGeometry); /** Initialization. Initializes an instance of the CFloat32VolumeData2D class with initialization of the data. * * Memory is allocated for the data block and the contents of the memory pointed to by * _pfData is copied into the allocated memory. * The size of the data is determined by the specified volume geometry object. * * @param _pGeometry Volume Geometry of the data. This object will be HARDCOPIED into this class. * @param _pfData pointer to a one-dimensional float32 data block */ bool initialize(const CVolumeGeometry2D& _pGeometry, const float32* _pfData); /** Initialization. Initializes an instance of the CFloat32VolumeData2D class with scalar initialization of the data. * * Memory is allocated for the data block and the contents of the memory pointed to by * _pfData is copied into the allocated memory. * The size of the data is determined by the specified volume geometry object. * * @param _pGeometry Volume Geometry of the data. This object will be HARDCOPIED into this class. * @param _fScalar scalar value to be put at each index. */ bool initialize(const CVolumeGeometry2D& _pGeometry, float32 _fScalar); /** Initialization. Initializes an instance of the CFloat32VolumeData2D class with pre-allocated memory. * * Memory is pre-allocated and passed via the abstract CFloat32CustomMemory handle * class. The handle will be deleted when the memory can be freed. * You should override the destructor to provide custom behaviour on free. * * @param _pGeometry Volume Geometry object. This object will be HARDCOPIED into this class. * @param _pCustomMemory custom memory handle * */ bool initialize(const CVolumeGeometry2D& _pGeometry, CFloat32CustomMemory* _pCustomMemory); /** Destructor. */ virtual ~CFloat32VolumeData2D(); /** Which type is this class? * * @return DataType: VOLUME */ virtual EDataType getType() const; /** Get the volume geometry. * * @return pointer to volume geometry. */ virtual const CVolumeGeometry2D& getGeometry() const; /** Change the projection geometry. * Note that this can't change the dimensions of the data. */ virtual void changeGeometry(const CVolumeGeometry2D &pGeometry); protected: /** The projection geometry for this data. */ CVolumeGeometry2D* m_pGeometry; }; //---------------------------------------------------------------------------------------- // Get the projection geometry. inline const CVolumeGeometry2D& CFloat32VolumeData2D::getGeometry() const { ASTRA_ASSERT(m_bInitialized); return *m_pGeometry; } //---------------------------------------------------------------------------------------- // Get type inline CFloat32Data2D::EDataType CFloat32VolumeData2D::getType() const { return VOLUME; } } // end namespace astra #endif // _INC_ASTRA_FLOAT32VOLUMEDATA2D astra-toolbox-2.3.0/include/astra/ForwardProjectionAlgorithm.h000066400000000000000000000151041475635207100245430ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FORWARDPROJECTIONALGORITHM #define _INC_ASTRA_FORWARDPROJECTIONALGORITHM #include "Algorithm.h" #include "Globals.h" #include "Projector2D.h" #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" #include "DataProjector.h" namespace astra { /** * \brief * This class contains the implementation of an algorithm that creates a forward projection * of a volume object and stores it into a sinogram. * * \par XML Configuration * \astra_xml_item{ProjectorId, integer, Identifier of a projector as it is stored in the ProjectorManager.} * \astra_xml_item{VolumeDataId, integer, Identifier of the volume data object as it is stored in the DataManager.} * \astra_xml_item{ProjectionDataId, integer, Identifier of the resulting projection data object as it is stored in the DataManager.} * \astra_xml_item_option{VolumeMaskId, integer, not used, Identifier of a volume data object that acts as a volume mask. 0 = don't use this pixel. 1 = use this pixel. } * \astra_xml_item_option{SinogramMaskId, integer, not used, Identifier of a projection data object that acts as a projection mask. 0 = don't use this ray. 1 = use this ray.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('FP');\n * cfg.ProjectorId = proj_id;\n * cfg.VolumeDataId = vol_id;\n * cfg.ProjectionDataId = sino_id;\n * alg_id = astra_mex_algorithm('create'\, cfg);\n * astra_mex_algorithm('run'\, alg_id);\n * astra_mex_algorithm('delete'\, alg_id);\n * } * */ class _AstraExport CForwardProjectionAlgorithm : public CAlgorithm { protected: /** Init stuff */ virtual void _init(); /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - valid projector * - valid data objects */ virtual bool _check(); //< Projector object. CProjector2D* m_pProjector; //< ProjectionData2D object containing the sinogram. CFloat32ProjectionData2D* m_pSinogram; //< VolumeData2D object containing the phantom. CFloat32VolumeData2D* m_pVolume; // data projector astra::CDataProjectorInterface* m_pForwardProjector; // ray or voxel-driven projector code? bool m_bUseVoxelProjector; //< Dataobject containing fixed volume mask (0 = don't project) CFloat32VolumeData2D* m_pVolumeMask; //< Use the fixed reconstruction mask? bool m_bUseVolumeMask; //< Dataobject containing fixed reconstruction mask (0 = don't project) CFloat32ProjectionData2D* m_pSinogramMask; //< Use the fixed reconstruction mask? bool m_bUseSinogramMask; public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "FP"; /** Default constructor, containing no code. */ CForwardProjectionAlgorithm(); /** Initializing constructor. * * @param _pProjector Projector to use. * @param _pVolume VolumeData2D object containing the phantom to compute sinogram from * @param _pSinogram ProjectionData2D object to store sinogram data in. */ CForwardProjectionAlgorithm(CProjector2D* _pProjector, CFloat32VolumeData2D* _pVolume, CFloat32ProjectionData2D* _pSinogram); /** Destructor. */ virtual ~CForwardProjectionAlgorithm(); /** Clear this class. */ virtual void clear(); /** Initialize class. * * @param _pProjector Projector to use. * @param _pVolume VolumeData2D object containing the phantom to compute sinogram from * @param _pSinogram ProjectionData2D object to store sinogram data in. * @return success */ bool initialize(CProjector2D* _pProjector, CFloat32VolumeData2D* _pVolume, CFloat32ProjectionData2D* _pSinogram); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Set a fixed reconstruction mask. A pixel will only be used in the reconstruction if the * corresponding value in the mask is 1. * * @param _pMask Volume Data object containing fixed reconstruction mask * @param _bEnable enable the use of this mask */ void setVolumeMask(CFloat32VolumeData2D* _pMask, bool _bEnable = true); /** Set a fixed sinogram mask. A detector value will only be used in the reconstruction if the * corresponding value in the mask is 1. * * @param _pMask Projection Data object containing fixed sinogram mask * @param _bEnable enable the use of this mask */ void setSinogramMask(CFloat32ProjectionData2D* _pMask, bool _bEnable = true); /** Get projector object * * @return projector */ CProjector2D* getProjector() const; /** Get sinogram data object * * @return sinogram data object */ CFloat32ProjectionData2D* getSinogram() const; /** Get volume data object * * @return volume data object */ CFloat32VolumeData2D* getVolume() const; /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); /** Get a description of the class. * * @return description string */ virtual std::string description() const; }; // inline functions inline std::string CForwardProjectionAlgorithm::description() const { return CForwardProjectionAlgorithm::type; }; inline CProjector2D* CForwardProjectionAlgorithm::getProjector() const { return m_pProjector; } inline CFloat32ProjectionData2D* CForwardProjectionAlgorithm::getSinogram() const { return m_pSinogram; } inline CFloat32VolumeData2D* CForwardProjectionAlgorithm::getVolume() const { return m_pVolume; } } // end namespace #endif astra-toolbox-2.3.0/include/astra/Fourier.h000066400000000000000000000053161475635207100206520ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_FOURIER #define _INC_ASTRA_FOURIER #include "Globals.h" namespace astra { /* -------- Complex DFT (Discrete Fourier Transform) -------- [definition] X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k ip[0] = 0; // first time only cdft(2*n, 1, a, ip, w); ip[0] = 0; // first time only cdft(2*n, -1, a, ip, w); [parameters] 2*n :data length (int) n >= 1, n = power of 2 a[0...2*n-1] :input/output data (float32 *) input data a[2*j] = Re(x[j]), a[2*j+1] = Im(x[j]), 0<=j= 2+sqrt(n) strictly, length of ip >= 2+(1<<(int)(log(n+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n/2-1] :cos/sin table (float32 *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of cdft(2*n, -1, a, ip, w); is cdft(2*n, 1, a, ip, w); for (j = 0; j <= 2 * n - 1; j++) { a[j] *= 1.0 / n; } . */ _AstraExport void cdft(int n, int isgn, float32 *a, int *ip, float32 *w); } #endif astra-toolbox-2.3.0/include/astra/GeometryUtil2D.h000066400000000000000000000054561475635207100220630ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_GEOMETRYUTIL2D #define _INC_ASTRA_GEOMETRYUTIL2D namespace astra { struct SParProjection { // the ray direction float fRayX, fRayY; // the start of the (linear) detector float fDetSX, fDetSY; // the length of a single detector pixel float fDetUX, fDetUY; void translate(double dx, double dy) { fDetSX += dx; fDetSY += dy; } void scale(double factor) { fRayX *= factor; fRayY *= factor; fDetSX *= factor; fDetSY *= factor; fDetUX *= factor; fDetUY *= factor; } }; struct SFanProjection { // the source float fSrcX, fSrcY; // the start of the (linear) detector float fDetSX, fDetSY; // the length of a single detector pixel float fDetUX, fDetUY; void translate(double dx, double dy) { fSrcX += dx; fSrcY += dy; fDetSX += dx; fDetSY += dy; } void scale(double factor) { fSrcX *= factor; fSrcY *= factor; fDetSX *= factor; fDetSY *= factor; fDetUX *= factor; fDetUY *= factor; } }; SParProjection* genParProjections(unsigned int iProjAngles, unsigned int iProjDets, double fDetSize, const float *pfAngles, const float *pfExtraOffsets); SFanProjection* genFanProjections(unsigned int iProjAngles, unsigned int iProjDets, double fOriginSource, double fOriginDetector, double fDetSize, const float *pfAngles); bool getParParameters(const SParProjection &proj, unsigned int iProjDets, float &fAngle, float &fDetSize, float &fOffset); bool getFanParameters(const SFanProjection &proj, unsigned int iProjDets, float &fAngle, float &fOriginSource, float &fOriginDetector, float &fDetSize, float &fOffset); } #endif astra-toolbox-2.3.0/include/astra/GeometryUtil3D.h000066400000000000000000000101221475635207100220460ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_GEOMETRYUTIL3D #define _INC_ASTRA_GEOMETRYUTIL3D #include namespace astra { class CProjectionGeometry3D; struct SConeProjection { // the source double fSrcX, fSrcY, fSrcZ; // the origin ("bottom left") of the (flat-panel) detector double fDetSX, fDetSY, fDetSZ; // the U-edge of a detector pixel double fDetUX, fDetUY, fDetUZ; // the V-edge of a detector pixel double fDetVX, fDetVY, fDetVZ; void translate(double dx, double dy, double dz) { fSrcX += dx; fSrcY += dy; fSrcZ += dz; fDetSX += dx; fDetSY += dy; fDetSZ += dz; } void scale(double fx, double fy, double fz) { fSrcX *= fx; fSrcY *= fy; fSrcZ *= fz; fDetSX *= fx; fDetSY *= fy; fDetSZ *= fz; fDetUX *= fx; fDetUY *= fy; fDetUZ *= fz; fDetVX *= fx; fDetVY *= fy; fDetVZ *= fz; } }; struct SPar3DProjection { // the ray direction double fRayX, fRayY, fRayZ; // the origin ("bottom left") of the (flat-panel) detector double fDetSX, fDetSY, fDetSZ; // the U-edge of a detector pixel double fDetUX, fDetUY, fDetUZ; // the V-edge of a detector pixel double fDetVX, fDetVY, fDetVZ; void translate(double dx, double dy, double dz) { fDetSX += dx; fDetSY += dy; fDetSZ += dz; } void scale(double fx, double fy, double fz) { fRayX *= fx; fRayY *= fy; fRayZ *= fz; fDetSX *= fx; fDetSY *= fy; fDetSZ *= fz; fDetUX *= fx; fDetUY *= fy; fDetUZ *= fz; fDetVX *= fx; fDetVY *= fy; fDetVZ *= fz; } }; void computeBP_UV_Coeffs(const SPar3DProjection& proj, double &fUX, double &fUY, double &fUZ, double &fUC, double &fVX, double &fVY, double &fVZ, double &fVC); void computeBP_UV_Coeffs(const SConeProjection& proj, double &fUX, double &fUY, double &fUZ, double &fUC, double &fVX, double &fVY, double &fVZ, double &fVC, double &fDX, double &fDY, double &fDZ, double &fDC); std::vector genConeProjections(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, double fOriginSourceDistance, double fOriginDetectorDistance, double fDetUSize, double fDetVSize, const float *pfAngles); std::vector genPar3DProjections(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, double fDetUSize, double fDetVSize, const float *pfAngles); CProjectionGeometry3D* getSubProjectionGeometry_U(const CProjectionGeometry3D* pProjGeom, int u, int size); CProjectionGeometry3D* getSubProjectionGeometry_V(const CProjectionGeometry3D* pProjGeom, int v, int size); CProjectionGeometry3D* getSubProjectionGeometry_Angle(const CProjectionGeometry3D* pProjGeom, int th, int size); } #endif astra-toolbox-2.3.0/include/astra/Globals.h000066400000000000000000000124041475635207100206160ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_GLOBALS #define _INC_ASTRA_GLOBALS /*! \mainpage The ASTRA-toolbox * * */ //---------------------------------------------------------------------------------------- #ifdef _MSC_VER // disable warning: 'fopen' was declared deprecated #pragma warning (disable : 4996) // disable warning: C++ exception handler used, but unwind semantics are not enables #pragma warning (disable : 4530) // disable warning: no suitable definition provided for explicit template instantiation request #pragma warning (disable : 4661) #endif //---------------------------------------------------------------------------------------- // standard includes #include #include #include #include #include #include //---------------------------------------------------------------------------------------- // macro's #define ASTRA_TOOLBOXVERSION_MAJOR 2 #define ASTRA_TOOLBOXVERSION_MINOR 3 #define ASTRA_TOOLBOXVERSION ((ASTRA_TOOLBOXVERSION_MAJOR)*100 + (ASTRA_TOOLBOXVERSION_MINOR)) #define ASTRA_TOOLBOXVERSION_STRING "2.3.0" #define ASTRA_ASSERT(a) assert(a) #define ASTRA_DELETE(a) if (a) { delete a; a = NULL; } #define ASTRA_DELETE_ARRAY(a) if (a) { delete[] a; a = NULL; } #ifdef _MSC_VER #ifdef DLL_EXPORTS #define _AstraExport __declspec(dllexport) #define EXPIMP_TEMPLATE #else #define _AstraExport __declspec(dllimport) #define EXPIMP_TEMPLATE extern #endif #else #define _AstraExport #endif //---------------------------------------------------------------------------------------- // typedefs namespace astra { typedef float float32; typedef double float64; typedef unsigned short int uint16; typedef signed short int sint16; typedef unsigned char uchar8; typedef signed char schar8; typedef int int32; typedef short int int16; } //---------------------------------------------------------------------------------------- // variables namespace astra { const float32 PI = 3.14159265358979323846264338328f; const float32 PI32 = 3.14159265358979323846264338328f; const float32 PIdiv2 = PI / 2; const float32 PIdiv4 = PI / 4; const float32 eps = 1e-6f; extern _AstraExport bool running_in_matlab; } //---------------------------------------------------------------------------------------- // structs namespace astra { /** * Struct for storing pixel weigths **/ struct SPixelWeight { int m_iIndex; float32 m_fWeight; }; /** * Struct combining some properties of a detector in 1D detector row **/ struct SDetector2D { int m_iIndex; int m_iAngleIndex; int m_iDetectorIndex; }; /** * Struct combining some properties of a detector in 2D detector array **/ struct SDetector3D { int m_iIndex; int m_iAngleIndex; int m_iDetectorIndex; int m_iSliceIndex; }; } //---------------------------------------------------------------------------------------- // functions for internal use namespace astra { /** Specify a function) to be called by shouldAbort() to check if an external * abort signal has arrived, or NULL to disable. Intended to be used by the * matlab/python interfaces to check if Ctrl-C has been pressed. */ _AstraExport void setShouldAbortHook(bool (*pShouldAbortHook)(void)); /** Check if we should abort execution (due to an external signal). */ _AstraExport bool shouldAbort(); } //---------------------------------------------------------------------------------------- // functions for external use namespace astra { _AstraExport inline int getVersion() { return ASTRA_TOOLBOXVERSION; } _AstraExport inline const char* getVersionString() { return ASTRA_TOOLBOXVERSION_STRING; } _AstraExport bool cudaAvailable(); #ifdef ASTRA_CUDA _AstraExport inline bool cudaEnabled() { return true; } #else _AstraExport inline bool cudaEnabled() { return false; } #endif } //---------------------------------------------------------------------------------------- // portability between MSVC and Linux/gcc #ifndef _MSC_VER #define EXPIMP_TEMPLATE #if !defined(FORCEINLINE) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) #define FORCEINLINE inline __attribute__((__always_inline__)) #else #define FORCEINLINE inline #endif #define ATTRIBUTE_FORMAT(A,B,C) __attribute__ ((format (A, B, C))) #else #define FORCEINLINE __forceinline #define ATTRIBUTE_FORMAT(A,B,C) #endif #endif astra-toolbox-2.3.0/include/astra/Logging.h000066400000000000000000000116321475635207100206230ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_LOGGING #define _INC_ASTRA_LOGGING #include "astra/Globals.h" #define ASTRA_DEBUG(...) astra::CLogger::debug(__FILE__,__LINE__, __VA_ARGS__) #define ASTRA_INFO(...) astra::CLogger::info(__FILE__,__LINE__, __VA_ARGS__) #define ASTRA_WARN(...) astra::CLogger::warn(__FILE__,__LINE__, __VA_ARGS__) #define ASTRA_ERROR(...) astra::CLogger::error(__FILE__,__LINE__, __VA_ARGS__) #define ASTRA_CONFIG_CHECK(value, type, msg, ...) do { if (!(value)) { astra::CLogger::error(__FILE__,__LINE__,"Configuration error in " type ". " msg, ##__VA_ARGS__); return false; } } while(false) namespace astra { enum log_level { LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR }; class _AstraExport CLogger { CLogger(); ~CLogger(); static bool m_bEnabledFile; static bool m_bEnabledScreen; static bool m_bFileProvided; static bool m_bInitialized; static std::string m_sLastErrMsg; static void _assureIsInitialized(); static void _setLastErrMsg(const char *sfile, int sline, const char *fmt, va_list ap); static void _setLevel(int id, log_level m_eLevel); public: /** * Writes a line to the log file (newline is added). Ignored if logging is turned off. * * @param sfile * The name of the source file making this log call (e.g. __FILE__). * * @param sline * The line number of the call in the source code (e.g. __LINE__). * * @param id * The id of the logger to write to. * * @param fmt * The format string for the message (printf formatting). * * @param ... * Any additional format arguments. */ static void debug(const char *sfile, int sline, const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 3, 4); static void info(const char *sfile, int sline, const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 3, 4); static void warn(const char *sfile, int sline, const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 3, 4); static void error(const char *sfile, int sline, const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 3, 4); /** * Sets the file to log to, with logging level. * * @param filename * File to log to. * * @param m_eLevel * Logging level (LOG_DEBUG, LOG_WARN, LOG_INFO, LOG_ERROR). * */ static void setOutputFile(const char *filename, log_level m_eLevel); /** * Sets the screen to log to, with logging level. * * @param screen_fd * Screen file descriptor (1 for stdout, 2 for stderr) * * @param m_eLevel * Logging level (LOG_DEBUG, LOG_WARN, LOG_INFO, LOG_ERROR). * */ static void setOutputScreen(int fd, log_level m_eLevel); /** * Set the format string for log messages. Here are the substitutions you may * use: * * %f: Source file name generating the log call. * %n: Source line number where the log call was made. * %m: The message text sent to the logger (after printf formatting). * %d: The current date, formatted using the logger's date format. * %t: The current time, formatted using the logger's time format. * %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR"). * %%: A literal percent sign. * * The default format string is "%d %t %f(%n): %l: %m\n". * * @param fmt * The new format string, which must be less than 256 bytes. * You probably will want to end this with a newline (\n). * */ static void setFormatFile(const char *fmt); static void setFormatScreen(const char *fmt); /** * Enable logging. * */ static void enable(); static void enableScreen(); static void enableFile(); /** * Disable logging. * */ static void disable(); static void disableScreen(); static void disableFile(); /** * Set callback function for logging to screen. * @return whether callback was set succesfully. * */ static bool setCallbackScreen(void (*cb)(const char *msg, size_t len)); /** * Get last error message received by the logger. * */ static std::string getLastErrMsg(); }; } #endif /* _INC_ASTRA_LOGGING */ astra-toolbox-2.3.0/include/astra/ParallelBeamBlobKernelProjector2D.h000066400000000000000000000212711475635207100255740ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PARALLELBEAMBLOBPROJECTOR #define _INC_ASTRA_PARALLELBEAMBLOBPROJECTOR #include "ParallelProjectionGeometry2D.h" #include "Float32Data2D.h" #include "Projector2D.h" namespace astra { /** This class implements a two-dimensional projector based on a blob-kernel. * A more detailed description (in dutch) is available at * http://www.astra.ua.ac.be/wiki/images/6/6e/Uitleg_blob_projector.pdf * * \par XML Configuration * type = "blob" * \astra_xml_item{ProjectionGeometry, xml node, The geometry of the projection.} * \astra_xml_item{VolumeGeometry, xml node, The geometry of the volume.} * \astra_xml_item{Kernel, xml node, Kernel details. See below.} * * \par XML Configuration of the Kernel * \astra_xml_item{KernelSize, float, Radius of the kernel.} * \astra_xml_item{SampleRate, float, Sample rate of the kernel.} * \astra_xml_item{SampleCount, integer, Number of samples.} * \astra_xml_item{KernelValues, vector of float, Samples of the kernels starting at distance 0.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('blob');\n * cfg.ProjectionGeometry = proj_geom;\n * cfg.VolumeGeometry = vol_geom;\n * cfg.Kernel.KernelSize = 2;\n * cfg.Kernel.SampleRate = 0.01;\n * cfg.Kernel.SampleCount = length(0:0.01:2);\n * cfg.Kernel.KernelValues = kaiserBessel(2\, 10.4\, 2\, 0:0.01:2);\n * proj_id = astra_mex_projector('create'\, cfg);\n * } */ class _AstraExport CParallelBeamBlobKernelProjector2D : public CProjector2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - blobvalues are ok */ virtual bool _check(); public: // type of the projector, needed to register with CProjectorFactory static inline const char* const type = "blob"; /** Default constructor. */ CParallelBeamBlobKernelProjector2D(); /** Constructor. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. * @param _fBlobSize Width of the blob. In units of PixelSize. * @param _fBlobSampleRate Spacing between two blob samples. (= _fBlobSize/_iBlobSampleCount) * @param _iBlobSampleCount Number of samples. * @param _pfBlobValues Array of _iBlobSampleCount blob evaluations. */ CParallelBeamBlobKernelProjector2D(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry, float32 _fBlobSize, float32 _fBlobSampleRate, int _iBlobSampleCount, float32* _pfBlobValues); /** Destructor, is virtual to show that we are aware subclass destructor are called. */ ~CParallelBeamBlobKernelProjector2D(); /** Initialize the projector with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize the projector. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. * @param _fBlobSize Width of the blob. In units of PixelSize. * @param _fBlobSampleRate Spacing between two blob samples. (= _fBlobSize/_iBlobSampleCount) * @param _iBlobSampleCount Number of samples. * @param _pfBlobValues Array of _iBlobSampleCount blob evaluations. Will be HARDCOPIED. */ bool initialize(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry, float32 _fBlobSize, float32 _fBlobSampleRate, int _iBlobSampleCount, float32* _pfBlobValues); /** Clear this class. */ virtual void clear(); /** Returns the number of weights required for storage of all weights of one projection. * * @param _iProjectionIndex Index of the projection (zero-based). * @return Size of buffer (given in SPixelWeight elements) needed to store weighted pixels. */ virtual int getProjectionWeightsCount(int _iProjectionIndex); /** Compute the pixel weights for a single ray, from the source to a detector pixel. * * @param _iProjectionIndex Index of the projection * @param _iDetectorIndex Index of the detector pixel * @param _pWeightedPixels Pointer to a pre-allocated array, consisting of _iMaxPixelCount elements * of type SPixelWeight. On return, this array contains a list of the index * and weight for all pixels on the ray. * @param _iMaxPixelCount Maximum number of pixels (and corresponding weights) that can be stored in _pWeightedPixels. * This number MUST be greater than the total number of pixels on the ray. * @param _iStoredPixelCount On return, this variable contains the total number of pixels on the * ray (that have been stored in the list _pWeightedPixels). */ virtual void computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount); /** Policy-based projection of all rays. This function will calculate each non-zero projection * weight and use this value for a task provided by the policy object. * * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void project(Policy& _policy); /** Policy-based projection of all rays of a single projection. This function will calculate * each non-zero projection weight and use this value for a task provided by the policy object. * * @param _iProjection Wwhich projection should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleProjection(int _iProjection, Policy& _policy); /** Policy-based projection of a single ray. This function will calculate each non-zero * projection weight and use this value for a task provided by the policy object. * * @param _iProjection Which projection should be projected? * @param _iDetector Which detector should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleRay(int _iProjection, int _iDetector, Policy& _policy); /** Return the type of this projector. * * @return identification type of this projector */ virtual std::string getType(); protected: /** Evaluate the blob kernel for a given distance from its center. * * @param _fDiff distance between hit point and blob center * @return blob value */ float32 _getBlobValue(float32 _fDiff); float32 m_fBlobSize; //< Width of the blob float32 m_fBlobSampleRate; //< At which interval are the inserted blob values evaluated? int m_iBlobSampleCount; //< Number of evaluated blob samples std::vector m_pfBlobValues; //< Evaluated blob values /** Internal policy-based projection of a range of angles and range. * (_i*From is inclusive, _i*To exclusive) */ template void projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& _policy); }; //---------------------------------------------------------------------------------------- inline std::string CParallelBeamBlobKernelProjector2D::getType() { return type; } } // namespace astra #endif astra-toolbox-2.3.0/include/astra/ParallelBeamBlobKernelProjector2D.inl000066400000000000000000000202121475635207100261210ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ template void CParallelBeamBlobKernelProjector2D::project(Policy& p) { projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(), 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CParallelBeamBlobKernelProjector2D::projectSingleProjection(int _iProjection, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CParallelBeamBlobKernelProjector2D::projectSingleRay(int _iProjection, int _iDetector, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, _iDetector, _iDetector + 1, p); } //---------------------------------------------------------------------------------------- // PROJECT BLOCK - vector projection geometry // // Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY) // // For each angle/detector pair: // // Let D=(Dx,Dy) denote the centre of the detector (point) in volume coordinates, and // let R=(Rx,Ry) denote the direction of the ray (vector). // // For mainly vertical rays (|Rx|<=|Ry|), // let E=(Ex,Ey) denote the centre of the most upper left pixel: // E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2), // and let F=(Fx,Fy) denote a vector to the next pixel // F = (PixelLengthX, 0) // // The intersection of the ray (D+aR) with the centre line of the upper row of pixels (E+bF) is // { Dx + a*Rx = Ex + b*Fx // { Dy + a*Ry = Ey + b*Fy // Solving for (a,b) results in: // a = (Ey + b*Fy - Dy)/Ry // = (Ey - Dy)/Ry // b = (Dx + a*Rx - Ex)/Fx // = (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx // // Define c as the x-value of the intersection of the ray with the upper row in pixel coordinates. // c = b // // The intersection of the ray (D+aR) with the centre line of the second row of pixels (E'+bF) with // E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2) // expressed in x-value pixel coordinates is // c' = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx. // And thus: // deltac = c' - c = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx - (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx // = [(Ey' - Dy)*Rx/Ry - (Ey - Dy)*Rx/Ry]/Fx // = [Ey' - Ey]*(Rx/Ry)/Fx // = [Ey' - Ey]*(Rx/Ry)/Fx // = -PixelLengthY*(Rx/Ry)/Fx. // // Given c on a certain row, its pixel directly on its left (col), and the distance (offset) to it, can be found: // col = floor(c) // offset = c - col // // The index of this pixel is // volumeIndex = row * colCount + col // // // Mainly horizontal rays (|Rx|<=|Ry|) are handled in a similar fashion: // // E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2), // F = (0, -PixelLengthX) // // a = (Ex + b*Fx - Dx)/Rx = (Ex - Dx)/Rx // b = (Dy + a*Ry - Ey)/Fy = (Dy + (Ex - Dx)*Ry/Rx - Ey)/Fy // r = b // deltar = PixelLengthX*(Ry/Rx)/Fy. // row = floor(r+1/2) // offset = r - row // template void CParallelBeamBlobKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p) { // get vector geometry const CParallelVecProjectionGeometry2D* pVecProjectionGeometry; if (dynamic_cast(m_pProjectionGeometry)) { pVecProjectionGeometry = dynamic_cast(m_pProjectionGeometry)->toVectorGeometry(); } else { pVecProjectionGeometry = dynamic_cast(m_pProjectionGeometry); } // precomputations const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX(); const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY(); const float32 inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX(); const float32 inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY(); const int colCount = m_pVolumeGeometry->getGridColCount(); const int rowCount = m_pVolumeGeometry->getGridRowCount(); // loop angles for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { // variables float32 Dx, Dy, Ex, Ey, c, r, deltac, deltar, offset, invBlobExtent, RxOverRy, RyOverRx; int iVolumeIndex, iRayIndex, row, col, iDetector; int col_left, col_right, row_top, row_bottom, index; const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle]; bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY); if (vertical) { RxOverRy = proj->fRayX/proj->fRayY; deltac = -m_pVolumeGeometry->getPixelLengthY() * (proj->fRayX/proj->fRayY) * inv_pixelLengthX; invBlobExtent = m_pVolumeGeometry->getPixelLengthY() / abs(m_fBlobSize * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / proj->fRayY); } else { RyOverRx = proj->fRayY/proj->fRayX; deltar = -m_pVolumeGeometry->getPixelLengthX() * (proj->fRayY/proj->fRayX) * inv_pixelLengthY; invBlobExtent = m_pVolumeGeometry->getPixelLengthX() / abs(m_fBlobSize * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / proj->fRayX); } Ex = m_pVolumeGeometry->getWindowMinX() + pixelLengthX*0.5f; Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f; // loop detectors for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector; // POLICY: RAY PRIOR if (!p.rayPrior(iRayIndex)) continue; Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX; Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY; // vertically if (vertical) { // calculate c for row 0 c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX; // loop rows for (row = 0; row < rowCount; ++row, c += deltac) { col_left = int(c - 0.5f - m_fBlobSize); col_right = int(c + 0.5f + m_fBlobSize); if (col_left < 0) col_left = 0; if (col_right > colCount-1) col_right = colCount-1; // loop columns for (col = col_left; col <= col_right; ++col) { iVolumeIndex = row * colCount + col; // POLICY: PIXEL PRIOR + ADD + POSTERIOR if (p.pixelPrior(iVolumeIndex)) { offset = abs(c - float32(col)) * invBlobExtent; index = (int)(offset*m_iBlobSampleCount+0.5f); p.addWeight(iRayIndex, iVolumeIndex, m_pfBlobValues[min(index,m_iBlobSampleCount-1)]); p.pixelPosterior(iVolumeIndex); } } } } // horizontally else { // calculate r for col 0 r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY; // loop columns for (col = 0; col < colCount; ++col, r += deltar) { row_top = int(r - 0.5f - m_fBlobSize); row_bottom = int(r + 0.5f + m_fBlobSize); if (row_top < 0) row_top = 0; if (row_bottom > rowCount-1) row_bottom = rowCount-1; // loop rows for (row = row_top; row <= row_bottom; ++row) { iVolumeIndex = row * colCount + col; // POLICY: PIXEL PRIOR + ADD + POSTERIOR if (p.pixelPrior(iVolumeIndex)) { offset = abs(r - float32(row)) * invBlobExtent; index = (int)(offset*m_iBlobSampleCount+0.5f); p.addWeight(iRayIndex, iVolumeIndex, m_pfBlobValues[min(index,m_iBlobSampleCount-1)]); p.pixelPosterior(iVolumeIndex); } } } } // POLICY: RAY POSTERIOR p.rayPosterior(iRayIndex); } // end loop detector } // end loop angles } astra-toolbox-2.3.0/include/astra/ParallelBeamDistanceDrivenProjector2D.h000066400000000000000000000161051475635207100264570ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PARALLELDISTANCEDRIVENPROJECTOR #define _INC_ASTRA_PARALLELDISTANCEDRIVENPROJECTOR #include "ParallelProjectionGeometry2D.h" #include "ParallelVecProjectionGeometry2D.h" #include "Float32Data2D.h" #include "Projector2D.h" #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" namespace astra { /** This class implements a "distance driven" two-dimensional projector. * * Reference: * De Man, Bruno, and Samit Basu. “Distance-Driven Projection and Backprojection in Three Dimensions.†Physics in Medicine and Biology 49, no. 11 (2004): 2463. * * \par XML Configuration * \astra_xml_item{ProjectionGeometry, xml node, The geometry of the projection.} * \astra_xml_item{VolumeGeometry, xml node, The geometry of the volume.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('distance_driven');\n * cfg.ProjectionGeometry = proj_geom;\n * cfg.VolumeGeometry = vol_geom;\n * proj_id = astra_mex_projector('create'\, cfg);\n * } */ class _AstraExport CParallelBeamDistanceDrivenProjector2D : public CProjector2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - blobvalues are ok */ virtual bool _check(); public: // type of the projector, needed to register with CProjectorFactory static inline const char* const type = "distance_driven"; /** Default constructor. */ CParallelBeamDistanceDrivenProjector2D(); /** Constructor. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. */ CParallelBeamDistanceDrivenProjector2D(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); /** Destructor, is virtual to show that we are aware subclass destructor are called. */ ~CParallelBeamDistanceDrivenProjector2D(); /** Initialize the projector with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize the projector. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. * @return initialization successful? */ bool initialize(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pVolumeGeometry); /** Clear this class. */ virtual void clear(); /** Returns the number of weights required for storage of all weights of one projection. * * @param _iProjectionIndex Index of the projection (zero-based). * @return Size of buffer (given in SPixelWeight elements) needed to store weighted pixels. */ virtual int getProjectionWeightsCount(int _iProjectionIndex); /** Compute the pixel weights for a single ray, from the source to a detector pixel. * * @param _iProjectionIndex Index of the projection * @param _iDetectorIndex Index of the detector pixel * @param _pWeightedPixels Pointer to a pre-allocated array, consisting of _iMaxPixelCount elements * of type SPixelWeight. On return, this array contains a list of the index * and weight for all pixels on the ray. * @param _iMaxPixelCount Maximum number of pixels (and corresponding weights) that can be stored in _pWeightedPixels. * This number MUST be greater than the total number of pixels on the ray. * @param _iStoredPixelCount On return, this variable contains the total number of pixels on the * ray (that have been stored in the list _pWeightedPixels). */ virtual void computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount); /** Policy-based projection of all rays. This function will calculate each non-zero projection * weight and use this value for a task provided by the policy object. * * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void project(Policy& _policy); /** Policy-based projection of all rays of a single projection. This function will calculate * each non-zero projection weight and use this value for a task provided by the policy object. * * @param _iProjection Which projection should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleProjection(int _iProjection, Policy& _policy); /** Policy-based projection of a single ray. This function will calculate each non-zero * projection weight and use this value for a task provided by the policy object. * * @param _iProjection Which projection should be projected? * @param _iDetector Which detector should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleRay(int _iProjection, int _iDetector, Policy& _policy); /** Return the type of this projector. * * @return identification type of this projector */ virtual std::string getType(); protected: /** Internal policy-based projection of a range of angles and range. * (_i*From is inclusive, _i*To exclusive) */ template void projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& _policy); }; //---------------------------------------------------------------------------------------- inline std::string CParallelBeamDistanceDrivenProjector2D::getType() { return type; } } // namespace astra #endif astra-toolbox-2.3.0/include/astra/ParallelBeamDistanceDrivenProjector2D.inl000066400000000000000000000176701475635207100270220ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define policy_weight(p,rayindex,volindex,weight) do { if (p.pixelPrior(volindex)) { p.addWeight(rayindex, volindex, weight); p.pixelPosterior(volindex); } } while (false) template void CParallelBeamDistanceDrivenProjector2D::project(Policy& p) { projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(), 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CParallelBeamDistanceDrivenProjector2D::projectSingleProjection(int _iProjection, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CParallelBeamDistanceDrivenProjector2D::projectSingleRay(int _iProjection, int _iDetector, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, _iDetector, _iDetector + 1, p); } template void CParallelBeamDistanceDrivenProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p) { // get vector geometry const CParallelVecProjectionGeometry2D* pVecProjectionGeometry; if (dynamic_cast(m_pProjectionGeometry)) { pVecProjectionGeometry = dynamic_cast(m_pProjectionGeometry)->toVectorGeometry(); } else { pVecProjectionGeometry = dynamic_cast(m_pProjectionGeometry); } // precomputations const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX(); const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY(); const float32 inv_pixelLengthX = 1.0f / pixelLengthX; const float32 inv_pixelLengthY = 1.0f / pixelLengthY; const int colCount = m_pVolumeGeometry->getGridColCount(); const int rowCount = m_pVolumeGeometry->getGridRowCount(); // Performance note: // This is not a very well optimized version of the distance driven // projector. The CPU projector model in ASTRA requires ray-driven iteration, // which limits re-use of intermediate computations. // loop angles for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle]; const bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY); const float32 Ex = m_pVolumeGeometry->getWindowMinX() + pixelLengthX*0.5f; const float32 Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f; const float32 rayWidth = fabs(proj->fDetUX * proj->fRayY - proj->fDetUY * proj->fRayX) / sqrt(proj->fRayX * proj->fRayX + proj->fRayY * proj->fRayY); // loop detectors for (int iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { const int iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector; // POLICY: RAY PRIOR if (!p.rayPrior(iRayIndex)) continue; const float32 Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX; const float32 Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY; if (vertical) { const float32 RxOverRy = proj->fRayX/proj->fRayY; const float32 lengthPerRow = m_pVolumeGeometry->getPixelLengthX() * m_pVolumeGeometry->getPixelLengthY() / rayWidth; const float32 deltac = -pixelLengthY * RxOverRy * inv_pixelLengthX; const float32 deltad = 0.5f * fabs((proj->fDetUX - proj->fDetUY * RxOverRy) * inv_pixelLengthX); // calculate c for row 0 float32 c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX + 0.5f; // loop rows for (int row = 0; row < rowCount; ++row, c+= deltac) { // horizontal extent of ray in center of this row: // [ c - deltad , c + deltad ] // |-gapBegin-*---|------|----*-gapEnd-| // * = ray extent intercepts; c - deltad and c + deltad // | = pixel column edges const int colBegin = (int)floor(c - deltad); const int colEnd = (int)ceil(c + deltad); if (colBegin >= colCount || colEnd <= 0) continue; int iVolumeIndex = row * colCount + colBegin; if (colBegin + 1 == colEnd) { if (colBegin >= 0 && colBegin < colCount) policy_weight(p, iRayIndex, iVolumeIndex, 2.0f * deltad * lengthPerRow); } else { if (colBegin >= 0) { const float gapBegin = (c - deltad) - (float32)colBegin; policy_weight(p, iRayIndex, iVolumeIndex, (1.0f - gapBegin) * lengthPerRow); } const int clippedMColBegin = std::max(colBegin + 1, 0); const int clippedMColEnd = std::min(colEnd - 1, colCount); iVolumeIndex = row * colCount + clippedMColBegin; for (int col = clippedMColBegin; col < clippedMColEnd; ++col, ++iVolumeIndex) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow); } iVolumeIndex = row * colCount + colEnd - 1; if (colEnd <= colCount) { const float gapEnd = (float32)colEnd - (c + deltad); policy_weight(p, iRayIndex, iVolumeIndex, (1.0f - gapEnd) * lengthPerRow); } } } } else { const float32 RyOverRx = proj->fRayY/proj->fRayX; const float32 lengthPerCol = m_pVolumeGeometry->getPixelLengthX() * m_pVolumeGeometry->getPixelLengthY() / rayWidth; const float32 deltar = -pixelLengthX * RyOverRx * inv_pixelLengthY; const float32 deltad = 0.5f * fabs((proj->fDetUY - proj->fDetUX * RyOverRx) * inv_pixelLengthY); // calculate r for col 0 float32 r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY + 0.5f; // loop columns for (int col = 0; col < colCount; ++col, r+= deltar) { // vertical extent of ray in center of this column: // [ r - deltad , r + deltad ] const int rowBegin = (int)floor(r - deltad); const int rowEnd = (int)ceil(r + deltad); if (rowBegin >= rowCount || rowEnd <= 0) continue; int iVolumeIndex = rowBegin * colCount + col; if (rowBegin + 1 == rowEnd) { if (rowBegin >= 0 && rowBegin < rowCount) policy_weight(p, iRayIndex, iVolumeIndex, 2.0f * deltad * lengthPerCol); } else { if (rowBegin >= 0) { const float gapBegin = (r - deltad) - (float32)rowBegin; policy_weight(p, iRayIndex, iVolumeIndex, (1.0f - gapBegin) * lengthPerCol); } const int clippedMRowBegin = std::max(rowBegin + 1, 0); const int clippedMRowEnd = std::min(rowEnd - 1, rowCount); iVolumeIndex = clippedMRowBegin * colCount + col; for (int row = clippedMRowBegin; row < clippedMRowEnd; ++row, iVolumeIndex += colCount) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol); } iVolumeIndex = (rowEnd - 1) * colCount + col; if (rowEnd <= rowCount) { const float gapEnd = (float32)rowEnd - (r + deltad); policy_weight(p, iRayIndex, iVolumeIndex, (1.0f - gapEnd) * lengthPerCol); } } } } // POLICY: RAY POSTERIOR p.rayPosterior(iRayIndex); } } if (dynamic_cast(m_pProjectionGeometry)) delete pVecProjectionGeometry; } astra-toolbox-2.3.0/include/astra/ParallelBeamLineKernelProjector2D.h000066400000000000000000000153251475635207100256100ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PARALLELBEAMLINEKERNELPROJECTOR #define _INC_ASTRA_PARALLELBEAMLINEKERNELPROJECTOR #include "ParallelProjectionGeometry2D.h" #include "ParallelVecProjectionGeometry2D.h" #include "Float32Data2D.h" #include "Projector2D.h" namespace astra { /** This class implements a two-dimensional projector based on a line based kernel. * * \par XML Configuration * \astra_xml_item{ProjectionGeometry, xml node, The geometry of the projection.} * \astra_xml_item{VolumeGeometry, xml node, The geometry of the volume.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('line');\n * cfg.ProjectionGeometry = proj_geom;\n * cfg.VolumeGeometry = vol_geom;\n * proj_id = astra_mex_projector('create'\, cfg);\n * } */ class _AstraExport CParallelBeamLineKernelProjector2D : public CProjector2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - blobvalues are ok */ virtual bool _check(); public: // type of the projector, needed to register with CProjectorFactory static inline const char* const type = "line"; /** Default constructor. */ CParallelBeamLineKernelProjector2D(); /** Constructor. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. */ CParallelBeamLineKernelProjector2D(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); /** Destructor, is virtual to show that we are aware subclass destructor are called. */ ~CParallelBeamLineKernelProjector2D(); /** Initialize the projector with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize the projector. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. * @return initialization successful? */ bool initialize(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); /** Clear this class. */ virtual void clear(); /** Returns the number of weights required for storage of all weights of one projection. * * @param _iProjectionIndex Index of the projection (zero-based). * @return Size of buffer (given in SPixelWeight elements) needed to store weighted pixels. */ virtual int getProjectionWeightsCount(int _iProjectionIndex); /** Compute the pixel weights for a single ray, from the source to a detector pixel. * * @param _iProjectionIndex Index of the projection * @param _iDetectorIndex Index of the detector pixel * @param _pWeightedPixels Pointer to a pre-allocated array, consisting of _iMaxPixelCount elements * of type SPixelWeight. On return, this array contains a list of the index * and weight for all pixels on the ray. * @param _iMaxPixelCount Maximum number of pixels (and corresponding weights) that can be stored in _pWeightedPixels. * This number MUST be greater than the total number of pixels on the ray. * @param _iStoredPixelCount On return, this variable contains the total number of pixels on the * ray (that have been stored in the list _pWeightedPixels). */ virtual void computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount); /** Policy-based projection of all rays. This function will calculate each non-zero projection * weight and use this value for a task provided by the policy object. * * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void project(Policy& _policy); /** Policy-based projection of all rays of a single projection. This function will calculate * each non-zero projection weight and use this value for a task provided by the policy object. * * @param _iProjection Wwhich projection should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleProjection(int _iProjection, Policy& _policy); /** Policy-based projection of a single ray. This function will calculate each non-zero * projection weight and use this value for a task provided by the policy object. * * @param _iProjection Which projection should be projected? * @param _iDetector Which detector should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleRay(int _iProjection, int _iDetector, Policy& _policy); /** Return the type of this projector. * * @return identification type of this projector */ virtual std::string getType(); protected: /** Internal policy-based projection of a range of angles and range. * (_i*From is inclusive, _i*To exclusive) */ template void projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& _policy); }; inline std::string CParallelBeamLineKernelProjector2D::getType() { return type; } } // namespace astra #endif astra-toolbox-2.3.0/include/astra/ParallelBeamLineKernelProjector2D.inl000066400000000000000000000253711475635207100261450ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define policy_weight(p,rayindex,volindex,weight) do { if (p.pixelPrior(volindex)) { p.addWeight(rayindex, volindex, weight); p.pixelPosterior(volindex); } } while (false) template void CParallelBeamLineKernelProjector2D::project(Policy& p) { projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(), 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CParallelBeamLineKernelProjector2D::projectSingleProjection(int _iProjection, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CParallelBeamLineKernelProjector2D::projectSingleRay(int _iProjection, int _iDetector, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, _iDetector, _iDetector + 1, p); } //---------------------------------------------------------------------------------------- /* PROJECT BLOCK - vector projection geometry Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY) For each angle/detector pair: Let D=(Dx,Dy) denote the centre of the detector (point) in volume coordinates, and let R=(Rx,Ry) denote the direction of the ray (vector). For mainly vertical rays (|Rx|<=|Ry|), let E=(Ex,Ey) denote the centre of the most upper left pixel: E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2), and let F=(Fx,Fy) denote a vector to the next pixel F = (PixelLengthX, 0) The intersection of the ray (D+aR) with the centre line of the upper row of pixels (E+bF) is { Dx + a*Rx = Ex + b*Fx { Dy + a*Ry = Ey + b*Fy Solving for (a,b) results in: a = (Ey + b*Fy - Dy)/Ry = (Ey - Dy)/Ry b = (Dx + a*Rx - Ex)/Fx = (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx Define c as the x-value of the intersection of the ray with the upper row in pixel coordinates. c = b The intersection of the ray (D+aR) with the centre line of the second row of pixels (E'+bF) with E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2) expressed in x-value pixel coordinates is c' = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx. And thus: deltac = c' - c = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx - (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx = [(Ey' - Dy)*Rx/Ry - (Ey - Dy)*Rx/Ry]/Fx = [Ey' - Ey]*(Rx/Ry)/Fx = [Ey' - Ey]*(Rx/Ry)/Fx = -PixelLengthY*(Rx/Ry)/Fx. Given c on a certain row, its closest pixel (col), and the distance (offset) to it, can be found: col = floor(c+1/2) offset = c - col The index of this pixel is volumeIndex = row * colCount + col The projection kernel is defined by _____ LengthPerRow /| | |\ / | | | \ __/ | | | \__ 0 -T -S 0 S T with S = 1/2 - 1/2*|Rx/Ry|, T = 1/2 + 1/2*|Rx/Ry|, and LengthPerRow = pixelLengthX * sqrt(Rx^2+Ry^2) / |Ry| And thus { (offset+T)/(T-S) * LengthPerRow if -T <= offset < S W_(rayIndex,volIndex) = { LengthPerRow if -S <= offset <= S { (offset-S)/(T-S) * LengthPerRow if S < offset <= T If -T <= offset < S, the weight for the pixel directly to the left is W_(rayIndex,volIndex-1) = LengthPerRow - (offset+T)/(T-S) * LengthPerRow, and if S < offset <= T, the weight for the pixel directly to the right is W_(rayIndex,volIndex+1) = LengthPerRow - (offset-S)/(T-S) * LengthPerRow. Mainly horizontal rays (|Rx|<=|Ry|) are handled in a similar fashion: E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2), F = (0, -PixelLengthX) a = (Ex + b*Fx - Dx)/Rx = (Ex - Dx)/Rx b = (Dy + a*Ry - Ey)/Fy = (Dy + (Ex - Dx)*Ry/Rx - Ey)/Fy r = b deltar = PixelLengthX*(Ry/Rx)/Fy. row = floor(r+1/2) offset = r - row S = 1/2 - 1/2*|Ry/Rx| T = 1/2 + 1/2*|Ry/Rx| LengthPerCol = pixelLengthY * sqrt(Rx^2+Ry^2) / |Rx| { (offset+T)/(T-S) * LengthPerCol if -T <= offset < S W_(rayIndex,volIndex) = { LengthPerCol if -S <= offset <= S { (offset-S)/(T-S) * LengthPerCol if S < offset <= T W_(rayIndex,volIndex-colcount) = LengthPerCol - (offset+T)/(T-S) * LengthPerCol W_(rayIndex,volIndex+colcount) = LengthPerCol - (offset-S)/(T-S) * LengthPerCol */ template void CParallelBeamLineKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p) { // get vector geometry const CParallelVecProjectionGeometry2D* pVecProjectionGeometry; if (dynamic_cast(m_pProjectionGeometry)) { pVecProjectionGeometry = dynamic_cast(m_pProjectionGeometry)->toVectorGeometry(); } else { pVecProjectionGeometry = dynamic_cast(m_pProjectionGeometry); } // precomputations const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX(); const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY(); const float32 inv_pixelLengthX = 1.0f / pixelLengthX; const float32 inv_pixelLengthY = 1.0f / pixelLengthY; const int colCount = m_pVolumeGeometry->getGridColCount(); const int rowCount = m_pVolumeGeometry->getGridRowCount(); // loop angles for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { // variables float32 Dx, Dy, Ex, Ey, S, T, weight, c, r, deltac, deltar, offset; float32 RxOverRy, RyOverRx, lengthPerRow, lengthPerCol, invTminSTimesLengthPerRow, invTminSTimesLengthPerCol; int iVolumeIndex, iRayIndex, row, col; const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle]; bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY); Ex = m_pVolumeGeometry->getWindowMinX() + pixelLengthX*0.5f; Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f; // loop detectors for (int iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector; // POLICY: RAY PRIOR if (!p.rayPrior(iRayIndex)) continue; Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX; Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY; bool isin = false; // vertically if (vertical) { RxOverRy = proj->fRayX/proj->fRayY; lengthPerRow = pixelLengthX * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayY); deltac = -pixelLengthY * RxOverRy * inv_pixelLengthX; S = 0.5f - 0.5f*fabs(RxOverRy); T = 0.5f + 0.5f*fabs(RxOverRy); invTminSTimesLengthPerRow = lengthPerRow / (T - S); // calculate c for row 0 c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX; // loop rows for (row = 0; row < rowCount; ++row, c += deltac) { col = int(floor(c+0.5f)); if (col < -1 || col > colCount) { if (!isin) continue; else break; } offset = c - float32(col); // left if (offset < -S) { weight = (offset + T) * invTminSTimesLengthPerRow; iVolumeIndex = row * colCount + col - 1; if (col > 0) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow-weight); } iVolumeIndex++; if (col >= 0 && col < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); } } // right else if (S < offset) { weight = (offset - S) * invTminSTimesLengthPerRow; iVolumeIndex = row * colCount + col; if (col >= 0 && col < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow-weight); } iVolumeIndex++; if (col + 1 < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); } } // centre else if (col >= 0 && col < colCount) { iVolumeIndex = row * colCount + col; policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow); } isin = true; } } // horizontally else { RyOverRx = proj->fRayY/proj->fRayX; lengthPerCol = pixelLengthY * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayX); deltar = -pixelLengthX * RyOverRx * inv_pixelLengthY; S = 0.5f - 0.5f*fabs(RyOverRx); T = 0.5f + 0.5f*fabs(RyOverRx); invTminSTimesLengthPerCol = lengthPerCol / (T - S); // calculate r for col 0 r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY; // loop columns for (col = 0; col < colCount; ++col, r += deltar) { row = int(floor(r+0.5f)); if (row < -1 || row > rowCount) { if (!isin) continue; else break; } offset = r - float32(row); // up if (offset < -S) { weight = (offset + T) * invTminSTimesLengthPerCol; iVolumeIndex = (row-1) * colCount + col; if (row > 0) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol-weight); } iVolumeIndex += colCount; if (row >= 0 && row < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); } } // down else if (S < offset) { weight = (offset - S) * invTminSTimesLengthPerCol; iVolumeIndex = row * colCount + col; if (row >= 0 && row < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol-weight); } iVolumeIndex += colCount; if (row + 1 < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); } } // centre else if (row >= 0 && row < rowCount) { iVolumeIndex = row * colCount + col; policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol); } isin = true; } } // POLICY: RAY POSTERIOR p.rayPosterior(iRayIndex); } // end loop detector } // end loop angles // Delete created vec geometry if required if (dynamic_cast(m_pProjectionGeometry)) delete pVecProjectionGeometry; } astra-toolbox-2.3.0/include/astra/ParallelBeamLinearKernelProjector2D.h000066400000000000000000000156051475635207100261340ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PARALLELLINEARKERNELPROJECTOR #define _INC_ASTRA_PARALLELLINEARKERNELPROJECTOR #include "ParallelProjectionGeometry2D.h" #include "ParallelVecProjectionGeometry2D.h" #include "Float32Data2D.h" #include "Projector2D.h" #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" namespace astra { /** This class implements a two-dimensional projector based on a lineary interpolated kernel. * * \par XML Configuration * \astra_xml_item{ProjectionGeometry, xml node, The geometry of the projection.} * \astra_xml_item{VolumeGeometry, xml node, The geometry of the volume.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('linear');\n * cfg.ProjectionGeometry = proj_geom;\n * cfg.VolumeGeometry = vol_geom;\n * proj_id = astra_mex_projector('create'\, cfg);\n * } */ class _AstraExport CParallelBeamLinearKernelProjector2D : public CProjector2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - blobvalues are ok */ virtual bool _check(); public: // type of the projector, needed to register with CProjectorFactory static inline const char* const type = "linear"; /** Default constructor. */ CParallelBeamLinearKernelProjector2D(); /** Constructor. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. */ CParallelBeamLinearKernelProjector2D(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); /** Destructor, is virtual to show that we are aware subclass destructor are called. */ ~CParallelBeamLinearKernelProjector2D(); /** Initialize the projector with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize the projector. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. * @return initialization successful? */ bool initialize(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pVolumeGeometry); /** Clear this class. */ virtual void clear(); /** Returns the number of weights required for storage of all weights of one projection. * * @param _iProjectionIndex Index of the projection (zero-based). * @return Size of buffer (given in SPixelWeight elements) needed to store weighted pixels. */ virtual int getProjectionWeightsCount(int _iProjectionIndex); /** Compute the pixel weights for a single ray, from the source to a detector pixel. * * @param _iProjectionIndex Index of the projection * @param _iDetectorIndex Index of the detector pixel * @param _pWeightedPixels Pointer to a pre-allocated array, consisting of _iMaxPixelCount elements * of type SPixelWeight. On return, this array contains a list of the index * and weight for all pixels on the ray. * @param _iMaxPixelCount Maximum number of pixels (and corresponding weights) that can be stored in _pWeightedPixels. * This number MUST be greater than the total number of pixels on the ray. * @param _iStoredPixelCount On return, this variable contains the total number of pixels on the * ray (that have been stored in the list _pWeightedPixels). */ virtual void computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount); /** Policy-based projection of all rays. This function will calculate each non-zero projection * weight and use this value for a task provided by the policy object. * * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void project(Policy& _policy); /** Policy-based projection of all rays of a single projection. This function will calculate * each non-zero projection weight and use this value for a task provided by the policy object. * * @param _iProjection Which projection should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleProjection(int _iProjection, Policy& _policy); /** Policy-based projection of a single ray. This function will calculate each non-zero * projection weight and use this value for a task provided by the policy object. * * @param _iProjection Which projection should be projected? * @param _iDetector Which detector should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleRay(int _iProjection, int _iDetector, Policy& _policy); /** Return the type of this projector. * * @return identification type of this projector */ virtual std::string getType(); protected: /** Internal policy-based projection of a range of angles and range. * (_i*From is inclusive, _i*To exclusive) */ template void projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& _policy); }; //---------------------------------------------------------------------------------------- inline std::string CParallelBeamLinearKernelProjector2D::getType() { return type; } } // namespace astra #endif astra-toolbox-2.3.0/include/astra/ParallelBeamLinearKernelProjector2D.inl000066400000000000000000000204231475635207100264610ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define policy_weight(p,rayindex,volindex,weight) do { if (p.pixelPrior(volindex)) { p.addWeight(rayindex, volindex, weight); p.pixelPosterior(volindex); } } while (false) template void CParallelBeamLinearKernelProjector2D::project(Policy& p) { projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(), 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CParallelBeamLinearKernelProjector2D::projectSingleProjection(int _iProjection, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CParallelBeamLinearKernelProjector2D::projectSingleRay(int _iProjection, int _iDetector, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, _iDetector, _iDetector + 1, p); } //---------------------------------------------------------------------------------------- /* PROJECT BLOCK - vector projection geometry Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY) For each angle/detector pair: Let D=(Dx,Dy) denote the centre of the detector (point) in volume coordinates, and let R=(Rx,Ry) denote the direction of the ray (vector). For mainly vertical rays (|Rx|<=|Ry|), let E=(Ex,Ey) denote the centre of the most upper left pixel: E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2), and let F=(Fx,Fy) denote a vector to the next pixel F = (PixelLengthX, 0) The intersection of the ray (D+aR) with the centre line of the upper row of pixels (E+bF) is { Dx + a*Rx = Ex + b*Fx { Dy + a*Ry = Ey + b*Fy Solving for (a,b) results in: a = (Ey + b*Fy - Dy)/Ry = (Ey - Dy)/Ry b = (Dx + a*Rx - Ex)/Fx = (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx Define c as the x-value of the intersection of the ray with the upper row in pixel coordinates. c = b The intersection of the ray (D+aR) with the centre line of the second row of pixels (E'+bF) with E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2) expressed in x-value pixel coordinates is c' = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx. And thus: deltac = c' - c = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx - (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx = [(Ey' - Dy)*Rx/Ry - (Ey - Dy)*Rx/Ry]/Fx = [Ey' - Ey]*(Rx/Ry)/Fx = [Ey' - Ey]*(Rx/Ry)/Fx = -PixelLengthY*(Rx/Ry)/Fx. Given c on a certain row, its pixel directly on its left (col), and the distance (offset) to it, can be found: col = floor(c) offset = c - col The index of this pixel is volumeIndex = row * colCount + col The projection kernel is defined by LengthPerRow /|\ / | \ __/ | \__ 0 p0 p1 p2 And thus W_(rayIndex,volIndex) = (1 - offset) * lengthPerRow W_(rayIndex,volIndex+1) = offset * lengthPerRow Mainly horizontal rays (|Rx|<=|Ry|) are handled in a similar fashion: E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2), F = (0, -PixelLengthX) a = (Ex + b*Fx - Dx)/Rx = (Ex - Dx)/Rx b = (Dy + a*Ry - Ey)/Fy = (Dy + (Ex - Dx)*Ry/Rx - Ey)/Fy r = b deltar = PixelLengthX*(Ry/Rx)/Fy. row = floor(r+1/2) offset = r - row LengthPerCol = pixelLengthY * sqrt(Rx^2+Ry^2) / |Rx| W_(rayIndex,volIndex) = (1 - offset) * lengthPerCol W_(rayIndex,volIndex+colcount) = offset * lengthPerCol */ template void CParallelBeamLinearKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p) { // get vector geometry const CParallelVecProjectionGeometry2D* pVecProjectionGeometry; if (dynamic_cast(m_pProjectionGeometry)) { pVecProjectionGeometry = dynamic_cast(m_pProjectionGeometry)->toVectorGeometry(); } else { pVecProjectionGeometry = dynamic_cast(m_pProjectionGeometry); } // precomputations const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX(); const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY(); const float32 inv_pixelLengthX = 1.0f / pixelLengthX; const float32 inv_pixelLengthY = 1.0f / pixelLengthY; const int colCount = m_pVolumeGeometry->getGridColCount(); const int rowCount = m_pVolumeGeometry->getGridRowCount(); // loop angles for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { // variables float32 Dx, Dy, Ex, Ey, c, r, deltac, deltar, offset; float32 RxOverRy, RyOverRx, lengthPerRow, lengthPerCol; int iVolumeIndex, iRayIndex, row, col, iDetector; const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle]; const bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY); Ex = m_pVolumeGeometry->getWindowMinX() + pixelLengthX*0.5f; Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f; // loop detectors for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector; // POLICY: RAY PRIOR if (!p.rayPrior(iRayIndex)) continue; Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX; Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY; bool isin = false; // vertically if (vertical) { RxOverRy = proj->fRayX/proj->fRayY; lengthPerRow = m_pVolumeGeometry->getPixelLengthX() * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayY); deltac = -pixelLengthY * RxOverRy * inv_pixelLengthX; // calculate c for row 0 c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX; // loop rows for (row = 0; row < rowCount; ++row, c += deltac) { col = int(floor(c)); if (col < -1 || col >= colCount) { if (!isin) continue; else break; } offset = c - float32(col); iVolumeIndex = row * colCount + col; if (col >= 0) { policy_weight(p, iRayIndex, iVolumeIndex, (1.0f - offset) * lengthPerRow); } iVolumeIndex++; if (col + 1 < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, offset * lengthPerRow); } isin = true; } } // horizontally else { RyOverRx = proj->fRayY/proj->fRayX; lengthPerCol = m_pVolumeGeometry->getPixelLengthY() * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayX); deltar = -pixelLengthX * RyOverRx * inv_pixelLengthY; // calculate r for col 0 r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY; // loop columns for (col = 0; col < colCount; ++col, r += deltar) { row = int(floor(r)); if (row < -1 || row >= rowCount) { if (!isin) continue; else break; } offset = r - float32(row); iVolumeIndex = row * colCount + col; if (row >= 0) { policy_weight(p, iRayIndex, iVolumeIndex, (1.0f - offset) * lengthPerCol); } iVolumeIndex += colCount; if (row + 1 < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, offset * lengthPerCol); } isin = true; } } // POLICY: RAY POSTERIOR p.rayPosterior(iRayIndex); } // end loop detector } // end loop angles if (dynamic_cast(m_pProjectionGeometry)) delete pVecProjectionGeometry; } astra-toolbox-2.3.0/include/astra/ParallelBeamStripKernelProjector2D.h000066400000000000000000000155011475635207100260160ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PARALLELBEAMSTROKEKERNELPROJECTOR #define _INC_ASTRA_PARALLELBEAMSTROKEKERNELPROJECTOR #include "ParallelProjectionGeometry2D.h" #include "ParallelVecProjectionGeometry2D.h" #include "Float32Data2D.h" #include "Projector2D.h" namespace astra { /** This class implements a two-dimensional projector based on a strip based kernel. * * \par XML Configuration * \astra_xml_item{ProjectionGeometry, xml node, The geometry of the projection.} * \astra_xml_item{VolumeGeometry, xml node, The geometry of the volume.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('strip');\n * cfg.ProjectionGeometry = proj_geom;\n * cfg.VolumeGeometry = vol_geom;\n * proj_id = astra_mex_projector('create'\, cfg);\n * } */ class _AstraExport CParallelBeamStripKernelProjector2D : public CProjector2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - blobvalues are ok */ virtual bool _check(); public: // type of the projector, needed to register with CProjectorFactory static inline const char* const type = "strip"; /** Default constructor. */ CParallelBeamStripKernelProjector2D(); /** Constructor. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. */ CParallelBeamStripKernelProjector2D(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); /** Destructor, is virtual to show that we are aware subclass destructor are called. */ ~CParallelBeamStripKernelProjector2D(); /** Initialize the projector with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize the projector. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. * @return initialization successful? */ bool initialize(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); /** Clear this class. */ virtual void clear(); /** Returns the number of weights required for storage of all weights of one projection. * * @param _iProjectionIndex Index of the projection (zero-based). * @return Size of buffer (given in SPixelWeight elements) needed to store weighted pixels. */ virtual int getProjectionWeightsCount(int _iProjectionIndex); /** Compute the pixel weights for a single ray, from the source to a detector pixel. * * @param _iProjectionIndex Index of the projection * @param _iDetectorIndex Index of the detector pixel * @param _pWeightedPixels Pointer to a pre-allocated array, consisting of _iMaxPixelCount elements * of type SPixelWeight. On return, this array contains a list of the index * and weight for all pixels on the ray. * @param _iMaxPixelCount Maximum number of pixels (and corresponding weights) that can be stored in _pWeightedPixels. * This number MUST be greater than the total number of pixels on the ray. * @param _iStoredPixelCount On return, this variable contains the total number of pixels on the * ray (that have been stored in the list _pWeightedPixels). */ virtual void computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount); /** Policy-based projection of all rays. This function will calculate each non-zero projection * weight and use this value for a task provided by the policy object. * * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void project(Policy& _policy); /** Policy-based projection of all rays of a single projection. This function will calculate * each non-zero projection weight and use this value for a task provided by the policy object. * * @param _iProjection Wwhich projection should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleProjection(int _iProjection, Policy& _policy); /** Policy-based projection of a single ray. This function will calculate each non-zero * projection weight and use this value for a task provided by the policy object. * * @param _iProjection Which projection should be projected? * @param _iDetector Which detector should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleRay(int _iProjection, int _iDetector, Policy& _policy); protected: /** Return the type of this projector. * * @return identification type of this projector */ virtual std::string getType(); /** Internal policy-based projection of a range of angles and range. * (_i*From is inclusive, _i*To exclusive) */ template void projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& _policy); }; //---------------------------------------------------------------------------------------- inline std::string CParallelBeamStripKernelProjector2D::getType() { return type; } } // namespace astra #endif astra-toolbox-2.3.0/include/astra/ParallelBeamStripKernelProjector2D.inl000066400000000000000000000247731475635207100263640ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ template void CParallelBeamStripKernelProjector2D::project(Policy& p) { projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(), 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CParallelBeamStripKernelProjector2D::projectSingleProjection(int _iProjection, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, 0, m_pProjectionGeometry->getDetectorCount(), p); } template void CParallelBeamStripKernelProjector2D::projectSingleRay(int _iProjection, int _iDetector, Policy& p) { projectBlock_internal(_iProjection, _iProjection + 1, _iDetector, _iDetector + 1, p); } //---------------------------------------------------------------------------------------- /* PROJECT BLOCK Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY) For each angle/detector pair: Let DL=(DLx,DLy) denote the left of the detector (point) in volume coordinates, and Let DR=(DRx,DRy) denote the right of the detector (point) in volume coordinates, and let R=(Rx,Ry) denote the direction of the ray (vector). For mainly vertical rays (|Rx|<=|Ry|), let E=(Ex,Ey) denote the centre of the most upper left pixel: E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2), and let F=(Fx,Fy) denote a vector to the next pixel F = (PixelLengthX, 0) The intersection of the left edge of the strip (DL+aR) with the centre line of the upper row of pixels (E+bF) is { DLx + a*Rx = Ex + b*Fx { DLy + a*Ry = Ey + b*Fy Solving for (a,b) results in: a = (Ey + b*Fy - DLy)/Ry = (Ey - DLy)/Ry b = (DLx + a*Rx - Ex)/Fx = (DLx + (Ey - DLy)*Rx/Ry - Ex)/Fx Define cL as the x-value of the intersection of the left edge of the strip with the upper row in pixel coordinates. cL = b cR, the x-value of the intersection of the right edge of the strip with the upper row in pixel coordinates can be found similarly. The intersection of the ray (DL+aR) with the left line of the second row of pixels (E'+bF) with E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2) expressed in x-value pixel coordinates is cL' = (DLx + (Ey' - DLy)*Rx/Ry - Ex)/Fx. And thus: deltac = cL' - cL = (DLx + (Ey' - DLy)*Rx/Ry - Ex)/Fx - (DLx + (Ey - DLy)*Rx/Ry - Ex)/Fx = [(Ey' - DLy)*Rx/Ry - (Ey - DLy)*Rx/Ry]/Fx = [Ey' - Ey]*(Rx/Ry)/Fx = [Ey' - Ey]*(Rx/Ry)/Fx = -PixelLengthY*(Rx/Ry)/Fx. The projection weight for a certain pixel is defined by the area between two points of _____ LengthPerRow /| | |\ / | | | \ __/ | | | \__ 0 -T -S 0 S T with S = 1/2 - 1/2*|Rx/Ry|, T = 1/2 + 1/2*|Rx/Ry|, and LengthPerRow = pixelLengthX * sqrt(Rx^2+Ry^2) / |Ry| For a certain row, all columns that are 'hit' by this kernel lie in the interval (col_left, col_right) = (floor(cL-1/2+S), floor(cR+3/2-S)) The offsets for both is (offsetL, offsetR) = (cL - floor(col_left), cR - floor(col_left)) The projection weight is found by the difference between the integrated values of the kernel offset <= -T Kernel = 0 -T < offset <= -S Kernel = PixelArea/2*(T+offset)^2/(T-S) -S < offset <= S Kernel = PixelArea/2 + offset S < offset <= T Kernel = PixelArea - PixelArea/2*(T-offset)^2/(T-S) T <= offset: Kernel = PixelArea */ template void CParallelBeamStripKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p) { // get vector geometry const CParallelVecProjectionGeometry2D* pVecProjectionGeometry; if (dynamic_cast(m_pProjectionGeometry)) { pVecProjectionGeometry = dynamic_cast(m_pProjectionGeometry)->toVectorGeometry(); } else { pVecProjectionGeometry = dynamic_cast(m_pProjectionGeometry); } // precomputations const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX(); const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY(); const float32 pixelArea = pixelLengthX * pixelLengthY; const float32 inv_pixelLengthX = 1.0f / pixelLengthX; const float32 inv_pixelLengthY = 1.0f / pixelLengthY; const int colCount = m_pVolumeGeometry->getGridColCount(); const int rowCount = m_pVolumeGeometry->getGridRowCount(); const int detCount = pVecProjectionGeometry->getDetectorCount(); // loop angles for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { // variables float32 DLx, DLy, DRx, DRy, Ex, Ey, S, T, deltac, deltar, offsetL, offsetR, invTminS; float32 res, RxOverRy, RyOverRx, cL, cR, rL, rR; int iVolumeIndex, iRayIndex, iDetector; int row, row_top, row_bottom, col, col_left, col_right; const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle]; const float32 rayWidth = fabs(proj->fDetUX * proj->fRayY - proj->fDetUY * proj->fRayX) / sqrt(proj->fRayX * proj->fRayX + proj->fRayY * proj->fRayY); const float32 relPixelArea = pixelArea / rayWidth; bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY); Ex = m_pVolumeGeometry->getWindowMinX() + pixelLengthX*0.5f; Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f; // loop detectors for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { iRayIndex = iAngle * detCount + iDetector; // POLICY: RAY PRIOR if (!p.rayPrior(iRayIndex)) continue; DLx = proj->fDetSX + iDetector * proj->fDetUX; DLy = proj->fDetSY + iDetector * proj->fDetUY; DRx = DLx + proj->fDetUX; DRy = DLy + proj->fDetUY; // vertically if (vertical) { RxOverRy = proj->fRayX/proj->fRayY; deltac = -m_pVolumeGeometry->getPixelLengthY() * RxOverRy * inv_pixelLengthX; S = 0.5f - 0.5f*fabs(RxOverRy); T = 0.5f + 0.5f*fabs(RxOverRy); invTminS = 1.0f / (T-S); // calculate cL and cR for row 0 cL = (DLx + (Ey - DLy)*RxOverRy - Ex) * inv_pixelLengthX; cR = (DRx + (Ey - DRy)*RxOverRy - Ex) * inv_pixelLengthX; if (cR < cL) { float32 tmp = cL; cL = cR; cR = tmp; } // loop rows for (row = 0; row < rowCount; ++row, cL += deltac, cR += deltac) { col_left = int(cL-0.5f+S); col_right = int(cR+1.5-S); if (col_left < 0) col_left = 0; if (col_right > colCount-1) col_right = colCount-1; float32 tmp = float32(col_left); offsetL = cL - tmp; offsetR = cR - tmp; // loop columns for (col = col_left; col <= col_right; ++col, offsetL -= 1.0f, offsetR -= 1.0f) { iVolumeIndex = row * colCount + col; // POLICY: PIXEL PRIOR + ADD + POSTERIOR if (p.pixelPrior(iVolumeIndex)) { // right ray edge if (T <= offsetR) res = 1.0f; else if (S < offsetR) res = 1.0f - 0.5f*(T-offsetR)*(T-offsetR)*invTminS; else if (-S < offsetR) res = 0.5f + offsetR; else if (-T < offsetR) res = 0.5f*(offsetR+T)*(offsetR+T)*invTminS; else res = 0.0f; // left ray edge if (T <= offsetL) res -= 1.0f; else if (S < offsetL) res -= 1.0f - 0.5f*(T-offsetL)*(T-offsetL)*invTminS; else if (-S < offsetL) res -= 0.5f + offsetL; else if (-T < offsetL) res -= 0.5f*(offsetL+T)*(offsetL+T)*invTminS; p.addWeight(iRayIndex, iVolumeIndex, relPixelArea*res); p.pixelPosterior(iVolumeIndex); } } } } // horizontally else { RyOverRx = proj->fRayY/proj->fRayX; deltar = -m_pVolumeGeometry->getPixelLengthX() * RyOverRx * inv_pixelLengthY; S = 0.5f - 0.5f*fabs(RyOverRx); T = 0.5f + 0.5f*fabs(RyOverRx); invTminS = 1.0f / (T-S); // calculate rL and rR for row 0 rL = -(DLy + (Ex - DLx)*RyOverRx - Ey) * inv_pixelLengthY; rR = -(DRy + (Ex - DRx)*RyOverRx - Ey) * inv_pixelLengthY; if (rR < rL) { float32 tmp = rL; rL = rR; rR = tmp; } // loop columns for (col = 0; col < colCount; ++col, rL += deltar, rR += deltar) { row_top = int(rL-0.5f+S); row_bottom = int(rR+1.5-S); if (row_top < 0) row_top = 0; if (row_bottom > rowCount-1) row_bottom = rowCount-1; float32 tmp = float32(row_top); offsetL = rL - tmp; offsetR = rR - tmp; // loop rows for (row = row_top; row <= row_bottom; ++row, offsetL -= 1.0f, offsetR -= 1.0f) { iVolumeIndex = row * colCount + col; // POLICY: PIXEL PRIOR + ADD + POSTERIOR if (p.pixelPrior(iVolumeIndex)) { // right ray edge if (T <= offsetR) res = 1.0f; else if (S < offsetR) res = 1.0f - 0.5f*(T-offsetR)*(T-offsetR)*invTminS; else if (-S < offsetR) res = 0.5f + offsetR; else if (-T < offsetR) res = 0.5f*(offsetR+T)*(offsetR+T)*invTminS; else res = 0.0f; // left ray edge if (T <= offsetL) res -= 1.0f; else if (S < offsetL) res -= 1.0f - 0.5f*(T-offsetL)*(T-offsetL)*invTminS; else if (-S < offsetL) res -= 0.5f + offsetL; else if (-T < offsetL) res -= 0.5f*(offsetL+T)*(offsetL+T)*invTminS; p.addWeight(iRayIndex, iVolumeIndex, relPixelArea*res); p.pixelPosterior(iVolumeIndex); } } } } // POLICY: RAY POSTERIOR p.rayPosterior(iRayIndex); } // end loop detector } // end loop angles if (dynamic_cast(m_pProjectionGeometry)) delete pVecProjectionGeometry; } astra-toolbox-2.3.0/include/astra/ParallelProjectionGeometry2D.h000066400000000000000000000132241475635207100247270ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PARALLELPROJECTIONGEOMETRY2D #define _INC_ASTRA_PARALLELPROJECTIONGEOMETRY2D #include "ProjectionGeometry2D.h" #include "ParallelVecProjectionGeometry2D.h" namespace astra { /** * This class defines a 2D parallel beam projection geometry. * * * This geometry is defined by a number of parameters: * - The number of detectors (DetCount). * The distance between the first detector and the projection of the origin \f$O\f$ is equal to * the distance between the last detector and the projection of \f$O\f$. * - The width of each detector (detector width). All detectors are equidistant. * - A list of projection angles (\f$\theta\f$), measured w.r.t. the y-axis of the volume. In Radians. Should lie in the interval \f$[-\frac{\pi}{4},\frac{7\pi}{4}]\f$. * * This class provides functionality to convert between detector index and detector offset \f$t\f$. * * \par XML Configuration * \astra_xml_item{DetectorCount, int, Number of detectors for each projection.} * \astra_xml_item{DetectorWidth, float, Width of each detector.} * \astra_xml_item{ProjectionAngles, vector of float, projection angles w.r.t. the y-axis of the volume in radians.} * * \par MATLAB example * \astra_code{ * proj_geom = astra_struct('parallel');\n * proj_geom.DetectorCount = 512;\n * proj_geom.DetectorWidth = 1.0;\n * proj_geom.ProjectionAngles = linspace(0\,pi\,100);\n * } */ class _AstraExport CParallelProjectionGeometry2D : public CProjectionGeometry2D { public: /** Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the init() methods before the object can be used. Any use before calling init() is not allowed, * except calling the member function isInitialized(). * */ CParallelProjectionGeometry2D(); /** Constructor. Create an instance of the CParallelProjectionGeometry2D class. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle. * @param _fDetectorWidth Width of a detector cell, in unit lengths. All detector cells are assumed to have equal width. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. */ CParallelProjectionGeometry2D(int _iProjectionAngleCount, int _iDetectorCount, float32 _fDetectorWidth, const float32* _pfProjectionAngles); /** Copy constructor. */ CParallelProjectionGeometry2D(const CParallelProjectionGeometry2D& _projGeom); /** Destructor. */ ~CParallelProjectionGeometry2D(); /** Assignment operator. */ CParallelProjectionGeometry2D& operator=(const CParallelProjectionGeometry2D& _other); /** Initialize the geometry with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialization. Initializes an instance of the CProjectionGeometry2D class. If the object has been * initialized before, the object is reinitialized and memory is freed and reallocated if necessary. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle. * @param _fDetectorWidth Width of a detector cell, in unit lengths. All detector cells are assumed to have equal width. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. */ bool initialize(int _iProjectionAngleCount, int _iDetectorCount, float32 _fDetectorWidth, const float32* _pfProjectionAngles); /** Create a hard copy. */ virtual CProjectionGeometry2D* clone() const override; /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CProjectionGeometry2D &) const override; /** Returns true if the type of geometry defined in this class is the one specified in _sType. * * @param _sType geometry type to compare to. * @return true if _sType == "parallel". */ virtual bool isOfType(const std::string& _sType); /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const; /** Create a vector geom */ CParallelVecProjectionGeometry2D* toVectorGeometry(); }; } // namespace astra #endif /* _INC_ASTRA_PARALLELPROJECTIONGEOMETRY2D */ astra-toolbox-2.3.0/include/astra/ParallelProjectionGeometry3D.h000066400000000000000000000137221475635207100247330ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PARALLELPROJECTIONGEOMETRY3D #define _INC_ASTRA_PARALLELPROJECTIONGEOMETRY3D #include "ProjectionGeometry3D.h" #include "ParallelProjectionGeometry2D.h" namespace astra { /** * This class defines a 3D parallel beam projection geometry. * * \par XML Configuration * \astra_xml_item{DetectorRowCount, int, Number of detectors for each projection.} * \astra_xml_item{DetectorColCount, int, Number of detectors for each projection.} * \astra_xml_item{DetectorSpacingX, float, Width of each detector.} * \astra_xml_item{DetectorSpacingY, float, Width of each detector.} * \astra_xml_item{ProjectionAngles, vector of float, projection angles in radians.} * * \par MATLAB example * \astra_code{ * proj_geom = astra_struct('parallel');\n * proj_geom.DetectorRowCount = 512;\n * proj_geom.DetectorColCount = 512;\n * proj_geom.DetectorSpacingX = 1.0;\n * proj_geom.DetectorSpacingY = 1.0;\n * proj_geom.ProjectionAngles = linspace(0,pi,100);\n * } */ class _AstraExport CParallelProjectionGeometry3D : public CProjectionGeometry3D { protected: public: /** Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the initialize() methods before the object can be used. Any use before calling initialize() * is not allowed, except calling the member function isInitialized(). */ CParallelProjectionGeometry3D(); /** Constructor. Create an instance of the CParallelProjectionGeometry3D class. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorRowCount Number of rows of detectors. * @param _iDetectorColCount Number of columns detectors. * @param _fDetectorWidth Width of a detector cell, in unit lengths. All detector cells are assumed to have equal width. * @param _fDetectorHeight Height of a detector cell, in unit lengths. All detector cells are assumed to have equal width. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. All angles * are represented in radians and lie in the [0,2pi[ interval. */ CParallelProjectionGeometry3D(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, float32 _fDetectorWidth, float32 _fDetectorHeight, std::vector &&_pfProjectionAngles); /** Copy constructor. */ CParallelProjectionGeometry3D(const CParallelProjectionGeometry3D& _projGeom); /** Destructor. */ ~CParallelProjectionGeometry3D(); /** Initialize the geometry with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize the geometry. If the object has been initialized before, the object is reinitialized * and memory is freed and reallocated if necessary. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorRowCount Number of rows of detectors. * @param _iDetectorColCount Number of columns detectors. * @param _fDetectorWidth Width of a detector cell, in unit lengths. All detector cells are assumed to have equal width. * @param _fDetectorHeight Height of a detector cell, in unit lengths. All detector cells are assumed to have equal height. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. All angles * are represented in radians and lie in the [0,2pi[ interval. */ bool initialize(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, float32 _fDetectorWidth, float32 _fDetectorHeight, std::vector &&_pfProjectionAngles); /** Create a hard copy. */ virtual CProjectionGeometry3D* clone() const; /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CProjectionGeometry3D*) const; /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const; /** Returns true if the type of geometry defined in this class is the one specified in _sType. * * @param _sType geometry type to compare to. * @return true if _sType == "parallel". */ virtual bool isOfType(const std::string& _sType) const; virtual void projectPoint(double fX, double fY, double fZ, int iAngleIndex, double &fU, double &fV) const; /** * Creates (= allocates) a 2D projection geometry used when projecting one slice using a 2D projector * * @return the 2D geometry, this pointer needs to be delete-ed after use. */ CParallelProjectionGeometry2D * createProjectionGeometry2D() const; }; } // namespace astra #endif /* _INC_ASTRA_PARALLELPROJECTIONGEOMETRY3D */ astra-toolbox-2.3.0/include/astra/ParallelVecProjectionGeometry2D.h000066400000000000000000000112161475635207100253640ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PARALLELVECPROJECTIONGEOMETRY2D #define _INC_ASTRA_PARALLELVECPROJECTIONGEOMETRY2D #include "ProjectionGeometry2D.h" #include "GeometryUtil2D.h" namespace astra { /** * This class defines a 2D parallel beam projection geometry. * * \par XML Configuration * \astra_xml_item{DetectorCount, int, Number of detectors for each projection.} * * \par MATLAB example * \astra_code{ * proj_geom = astra_struct('parallel_vec');\n * proj_geom.DetectorCount = 512;\n * proj_geom.Vectors = V;\n * } * * \par Vectors * Vectors is a matrix containing the actual geometry. Each row corresponds * to a single projection, and consists of: * ( rayX, rayY, dX, dY, uX, uY) * ray: the ray direction * d : the centre of the detector line * u : the vector from detector pixel (0) to (1) */ class _AstraExport CParallelVecProjectionGeometry2D : public CProjectionGeometry2D { protected: SParProjection *m_pProjectionAngles; public: /** Default constructor. Sets all variables to zero. Note that this constructor leaves the object in an unusable state and must * be followed by a call to init(). */ CParallelVecProjectionGeometry2D(); /** Constructor. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. */ CParallelVecProjectionGeometry2D(int _iProjectionAngleCount, int _iDetectorCount, const SParProjection* _pfProjectionAngles); /** Copy constructor. */ CParallelVecProjectionGeometry2D(const CParallelVecProjectionGeometry2D& _projGeom); /** Assignment operator. */ CParallelVecProjectionGeometry2D& operator=(const CParallelVecProjectionGeometry2D& _other); /** Destructor. */ virtual ~CParallelVecProjectionGeometry2D(); /** Initialize the geometry with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialization. This function MUST be called after using the default constructor and MAY be called to * reset a previously initialized object. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. */ bool initialize(int _iProjectionAngleCount, int _iDetectorCount, const SParProjection* _pfProjectionAngles); virtual bool _check(); /** Create a hard copy. */ virtual CProjectionGeometry2D* clone() const override; /** Returns true if the type of geometry defined in this class is the one specified in _sType. * * @param _sType geometry type to compare to. * @return true if _sType == "fanflat_vec". */ virtual bool isOfType(const std::string& _sType); /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CProjectionGeometry2D &) const override; /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const; const SParProjection* getProjectionVectors() const { return m_pProjectionAngles; } protected: virtual bool initializeAngles(const Config& _cfg); }; } // namespace astra #endif /* _INC_ASTRA_PARALLELVECPROJECTIONGEOMETRY2D */ astra-toolbox-2.3.0/include/astra/ParallelVecProjectionGeometry3D.h000066400000000000000000000124701475635207100253700ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PARALLELVECPROJECTIONGEOMETRY3D #define _INC_ASTRA_PARALLELVECPROJECTIONGEOMETRY3D #include "ProjectionGeometry3D.h" #include "GeometryUtil3D.h" namespace astra { /** * This class defines a 3D parallel beam projection geometry. * * \par XML Configuration * \astra_xml_item{DetectorRowCount, int, Number of detectors for each projection.} * \astra_xml_item{DetectorColCount, int, Number of detectors for each projection.} * * \par MATLAB example * \astra_code{ * proj_geom = astra_struct('parallel3d_vec');\n * proj_geom.DetectorRowCount = 512;\n * proj_geom.DetectorColCount = 512;\n * proj_geom.Vectors = V;\n * } * * \par Vectors * Vectors is a matrix containing the actual geometry. Each row corresponds * to a single projection, and consists of: * ( rayX, rayY, rayZ, dX, dY, dZ, uX, uY, uZ, vX, vY, vZ ) * ray: the ray direction * d : the centre of the detector plane * u : the vector from detector pixel (0,0) to (0,1) * v : the vector from detector pixel (0,0) to (1,0) */ class _AstraExport CParallelVecProjectionGeometry3D : public CProjectionGeometry3D { protected: std::vector m_ProjectionAngles; public: /** Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the initialize() methods before the object can be used. Any use before calling initialize() * is not allowed, except calling the member function isInitialized(). */ CParallelVecProjectionGeometry3D(); /** Constructor. Create an instance of the CParallelProjectionGeometry3D class. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorRowCount Number of rows of detectors. * @param _iDetectorColCount Number of columns detectors. * @param _pProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. */ CParallelVecProjectionGeometry3D(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, std::vector &&_pProjectionAngles); /** Copy constructor. */ CParallelVecProjectionGeometry3D(const CParallelVecProjectionGeometry3D& _projGeom); /** Destructor. */ ~CParallelVecProjectionGeometry3D(); /** Initialize the geometry with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize the geometry. If the object has been initialized before, the object is reinitialized * and memory is freed and reallocated if necessary. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorRowCount Number of rows of detectors. * @param _iDetectorColCount Number of columns detectors. * @param _pProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. */ bool initialize(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, std::vector &&_pProjectionAngles); virtual bool _check(); /** Create a hard copy. */ virtual CProjectionGeometry3D* clone() const; /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CProjectionGeometry3D*) const; /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const; /** Returns true if the type of geometry defined in this class is the one specified in _sType. * * @param _sType geometry type to compare to. * @return true if _sType == "parallel3d_vec". */ virtual bool isOfType(const std::string& _sType) const; const SPar3DProjection* getProjectionVectors() const { return &m_ProjectionAngles[0]; } virtual void projectPoint(double fX, double fY, double fZ, int iAngleIndex, double &fU, double &fV) const; protected: virtual bool initializeAngles(const Config& _cfg); }; } // namespace astra #endif /* _INC_ASTRA_PARALLELVECPROJECTIONGEOMETRY3D */ astra-toolbox-2.3.0/include/astra/PlatformDepSystemCode.h000066400000000000000000000042641475635207100234550ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef PLATFORMDEPSYSTEMCODE_H #define PLATFORMDEPSYSTEMCODE_H #include #ifndef _WIN32 #include #endif namespace astra { #ifdef _WIN32 typedef __int64 int64; #else typedef int64_t int64; #endif class CPlatformDepSystemCode { public: /** * Clock with resolution of 1 ms. Windows implementation will return number of ms since system start, * but this is not a requirement for the implementation. Just as long as the subtraction of two acquired * values will result in a time interval in ms. * * @return a value that increases with 1 every ms */ static unsigned long getMSCount(); /** * fseek variant that works with 64 bit ints. * * @param _pStream file handler of file in which needs to be seek-ed * @param _iOffset 64 bit int telling the new offset in the file * @param _iOrigin typical fseek directive telling how _iOffset needs to be interpreted (SEEK_SET, ...) * * @return 0 if successful */ static int fseek64(FILE * _pStream, astra::int64 _iOffset, int _iOrigin); /** * 64-bit ftell variant * * @param _pStream file handle * * @return the position in the file */ static astra::int64 ftell64(FILE * _pStream); }; } #endif /* PLATFORMDEPSYSTEMCODE_H */ astra-toolbox-2.3.0/include/astra/PluginAlgorithmFactory.h000066400000000000000000000035141475635207100236720ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PLUGINALGORITHM #define _INC_ASTRA_PLUGINALGORITHM #include "astra/Globals.h" #include #include namespace astra { class CAlgorithm; class _AstraExport CPluginAlgorithmFactory { public: CPluginAlgorithmFactory() { } virtual ~CPluginAlgorithmFactory() { } virtual CAlgorithm * getPlugin(const std::string &name) = 0; virtual bool registerPlugin(std::string name, std::string className) = 0; virtual bool registerPlugin(std::string className) = 0; virtual std::map getRegisteredMap() = 0; virtual std::string getHelp(const std::string &name) = 0; static void registerFactory(CPluginAlgorithmFactory *factory) { m_factory = factory; } static CPluginAlgorithmFactory* getFactory() { return m_factory; } private: static CPluginAlgorithmFactory *m_factory; }; } #endif astra-toolbox-2.3.0/include/astra/ProjectionGeometry2D.h000066400000000000000000000251771475635207100232640ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PROJECTIONGEOMETRY2D #define _INC_ASTRA_PROJECTIONGEOMETRY2D #include "Globals.h" #include "Config.h" #include "Vector3D.h" #include #include #include namespace astra { /** * This abstract base class defines the projection geometry. * It has a number of data fields, such as width of detector * pixels, projection angles, number of detector pixels and object offsets * for every projection angle. */ class _AstraExport CProjectionGeometry2D { protected: bool m_bInitialized; ///< Has the object been intialized? /** Number of projection angles */ int m_iProjectionAngleCount; /** Number of detectors, i.e., the number of detector measurements for each projection angle. */ int m_iDetectorCount; /** Width of a detector pixel, i.e., the distance between projected rays (or width of projected strips). */ float32 m_fDetectorWidth; /** Dynamically allocated array of projection angles. All angles are represented in radians and lie in * the [0,2pi[ interval. */ float32* m_pfProjectionAngles; /** Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the init() methods before the object can be used. Any use before calling init() is not * allowed, except calling the member function isInitialized(). * */ CProjectionGeometry2D(); /** Constructor. Create an instance of the CProjectionGeometry2D class. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle. * @param _fDetectorWidth Width of a detector cell, in unit lengths. All detector cells are assumed to have equal width. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. * All angles are represented in radians. */ CProjectionGeometry2D(int _iProjectionAngleCount, int _iDetectorCount, float32 _fDetectorWidth, const float32* _pfProjectionAngles); /** Copy constructor. */ CProjectionGeometry2D(const CProjectionGeometry2D& _projGeom); /** Check variable values. */ bool _check(); /** Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. * Should only be used by constructors. Otherwise use the clear() function. */ void _clear(); /** Initialization. Initializes an instance of the CProjectionGeometry2D class. If the object has been * initialized before, the object is reinitialized and memory is freed and reallocated if necessary. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle. * @param _fDetectorWidth Width of a detector cell, in unit lengths. All detector cells are assumed to have equal width. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. */ bool _initialize(int _iProjectionAngleCount, int _iDetectorCount, float32 _fDetectorWidth, const float32* _pfProjectionAngles); public: /** Destructor */ virtual ~CProjectionGeometry2D(); /** Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. */ virtual void clear(); /** Create a hard copy. */ virtual CProjectionGeometry2D* clone() const = 0; /** Initialize the geometry with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Get the initialization state of the object. * * @return true iff the object has been initialized */ bool isInitialized() const; /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CProjectionGeometry2D&) const = 0; /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const = 0; /** Get the number of projection angles. * * @return Number of projection angles */ int getProjectionAngleCount() const; /** Get the number of detectors. * * @return Number of detectors, i.e., the number of detector measurements for each projection angle. */ int getDetectorCount() const; /** Get the width of a detector. * * @return Width of a detector, in unit lengths */ float32 getDetectorWidth() const; /** Get a projection angle, given by its index. The angle is represented in Radians. * * @return Projection angle with index _iProjectionIndex */ float32 getProjectionAngle(int _iProjectionIndex) const; /** Returns a buffer containing all projection angles. The element count of the buffer is equal * to the number given by getProjectionAngleCount. * * The angles are in radians. * * @return Pointer to buffer containing the angles. */ const float32* getProjectionAngles() const; /** Get a projection angle, given by its index. The angle is represented in degrees. * * @return Projection angle with index _iProjectionIndex */ float32 getProjectionAngleDegrees(int _iProjectionIndex) const; /** Get the index coordinate of a point on a detector array. * * @param _fOffset distance between the center of the detector array and a certain point * @return the location of the point in index coordinates (still float, not rounded) */ virtual float32 detectorOffsetToIndexFloat(float32 _fOffset) const; /** Get the index coordinate of a point on a detector array. * * @param _fOffset distance between the center of the detector array and a certain point * @return the index of the detector that is hit, -1 if detector array isn't hit. */ virtual int detectorOffsetToIndex(float32 _fOffset) const; /** Get the offset of a detector based on its index coordinate. * * @param _iIndex the index of the detector. * @return the offset from the center of the detector array. */ virtual float32 indexToDetectorOffset(int _iIndex) const; /** Get the angle and detector index of a sinogram pixel * * @param _iIndex the index of the detector pixel in the sinogram. * @param _iAngleIndex output: index of angle * @param _iDetectorIndex output: index of detector */ virtual void indexToAngleDetectorIndex(int _iIndex, int& _iAngleIndex, int& _iDetectorIndex) const; /** Returns true if the type of geometry defined in this class is the one specified in _sType. * * @param _sType geometry type to compare to. * @return true if the type of geometry defined in this class is the one specified in _sType. */ virtual bool isOfType(const std::string& _sType) = 0; private: //< For Config unused argument checking ConfigCheckData* configCheckData; friend class ConfigReader; protected: virtual bool initializeAngles(const Config& _cfg); }; //---------------------------------------------------------------------------------------- // Inline member functions //---------------------------------------------------------------------------------------- // Get the initialization state. inline bool CProjectionGeometry2D::isInitialized() const { return m_bInitialized; } // Get the number of detectors. inline int CProjectionGeometry2D::getDetectorCount() const { ASTRA_ASSERT(m_bInitialized); return m_iDetectorCount; } // Get the width of a single detector (in unit lengths). inline float32 CProjectionGeometry2D::getDetectorWidth() const { ASTRA_ASSERT(m_bInitialized); return m_fDetectorWidth; } // Get the number of projection angles. inline int CProjectionGeometry2D::getProjectionAngleCount() const { ASTRA_ASSERT(m_bInitialized); return m_iProjectionAngleCount; } // Get pointer to buffer used to store projection angles. inline const float32* CProjectionGeometry2D::getProjectionAngles() const { ASTRA_ASSERT(m_bInitialized); return m_pfProjectionAngles; } // Get a projection angle, represented in Radians. inline float32 CProjectionGeometry2D::getProjectionAngle(int _iProjectionIndex) const { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iProjectionIndex >= 0); ASTRA_ASSERT(_iProjectionIndex < m_iProjectionAngleCount); return m_pfProjectionAngles[_iProjectionIndex]; } // Get a projection angle, represented in degrees. inline float32 CProjectionGeometry2D::getProjectionAngleDegrees(int _iProjectionIndex) const { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iProjectionIndex >= 0); ASTRA_ASSERT(_iProjectionIndex < m_iProjectionAngleCount); return (m_pfProjectionAngles[_iProjectionIndex] * 180.0f / PI32); } // detector offset -> detector index inline int CProjectionGeometry2D::detectorOffsetToIndex(float32 _fOffset) const { int res = (int)(detectorOffsetToIndexFloat(_fOffset) + 0.5f); return (res > 0 && res <= m_iDetectorCount) ? res : -1; } // detector offset -> detector index (float) inline float32 CProjectionGeometry2D::detectorOffsetToIndexFloat(float32 _fOffset) const { return (_fOffset / m_fDetectorWidth) + ((m_iDetectorCount-1.0f) * 0.5f); } // detector index -> detector offset inline float32 CProjectionGeometry2D::indexToDetectorOffset(int _iIndex) const { return (_iIndex - (m_iDetectorCount-1.0f) * 0.5f) * m_fDetectorWidth; } // sinogram index -> angle and detecor index inline void CProjectionGeometry2D::indexToAngleDetectorIndex(int _iIndex, int& _iAngleIndex, int& _iDetectorIndex) const { _iAngleIndex = _iIndex / m_iDetectorCount; _iDetectorIndex = _iIndex % m_iDetectorCount; } } // end namespace astra #endif /* _INC_ASTRA_PROJECTIONGEOMETRY2D */ astra-toolbox-2.3.0/include/astra/ProjectionGeometry2DFactory.h000066400000000000000000000024171475635207100246040ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PROJECTIONGEOMETRY2DFACTORY #define _INC_ASTRA_PROJECTIONGEOMETRY2DFACTORY #include "Globals.h" #include namespace astra { class CProjectionGeometry2D; _AstraExport std::unique_ptr constructProjectionGeometry2D(const std::string &type); } #endif astra-toolbox-2.3.0/include/astra/ProjectionGeometry3D.h000066400000000000000000000463641475635207100232660ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PROJECTIONGEOMETRY3D #define _INC_ASTRA_PROJECTIONGEOMETRY3D #include "Globals.h" #include "Config.h" #include "Vector3D.h" #include #include #include namespace astra { class XMLNode; /** * This class defines the interface for each 3D projection geometry. * It has a number of data fields, such as width and height of detector * pixels, projection angles and number of rows and columns of detector pixels. * * \par XML Configuration * \astra_xml_item{DetectorRowCount, int, Number of detectors for each projection.} * \astra_xml_item{DetectorColCount, int, Number of detectors for each projection.} * \astra_xml_item{DetectorWidth, float, Width of each detector.} * \astra_xml_item{DetectorHeight, float, Width of each detector.} * \astra_xml_item{ProjectionAngles, vector of float, projection angles in radians.} */ class _AstraExport CProjectionGeometry3D { protected: /** Has the object been intialized with acceptable values? */ bool m_bInitialized; /** Number of projection angles. */ int m_iProjectionAngleCount; /** Number of rows of detectors. */ int m_iDetectorRowCount; /** Number of columns of detectors. */ int m_iDetectorColCount; /** Total number of detectors. */ int m_iDetectorTotCount; /** The x-distance between projected rays on the detector plate (or width of projected strips). */ float32 m_fDetectorSpacingX; /** The y-distance between projected rays on the detector plate (or height of projected strips). */ float32 m_fDetectorSpacingY; /** Dynamically allocated array of projection angles. All angles are represented in radians and lie in * the [0,2pi[ interval. */ std::vector m_pfProjectionAngles; /** Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the initialize() methods before the object can be used. Any use before calling initialize() * is not allowed, except calling the member function isInitialized(). * */ CProjectionGeometry3D(); /** Constructor. Create an instance of the CProjectionGeometry3D class. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorRowCount Number of rows of detectors. * @param _iDetectorColCount Number of columns detectors. * @param _fDetectorSpacingX Spacing between the detector points on the X-axis, in unit lengths. Assumed to be constant throughout the entire detector plate. * @param _fDetectorSpacingY Spacing between the detector points on the Y-axis, in unit lengths. Assumed to be constant throughout the entire detector plate. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. All angles * are represented in radians and lie in the [0,2pi[ interval. */ CProjectionGeometry3D(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, float32 _fDetectorSpacingX, float32 _fDetectorSpacingY, std::vector &&_pfProjectionAngles); /** Copy constructor. */ CProjectionGeometry3D(const CProjectionGeometry3D& _projGeom); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - number of rows and columns is larger than zero * - detector spacing is larger than zero * - number of angles is larger than zero * - (autofix) each angle lies in [0,2pi[ */ bool _check(); /** Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. * Should only be used by constructors. Otherwise use the clear() function. */ void _clear(); /** Initialize the geometry. If the object has been initialized before, the object is reinitialized * and memory is freed and reallocated if necessary. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorRowCount Number of rows of detectors. * @param _iDetectorColCount Number of columns detectors. * @param _fDetectorSpacingX Spacing between the detector points on the X-axis, in unit lengths. Assumed to be constant throughout the entire detector plate. * @param _fDetectorSpacingY Spacing between the detector points on the Y-axis, in unit lengths. Assumed to be constant throughout the entire detector plate. * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array. All angles * are represented in radians and lie in the [0,2pi[ interval. */ bool _initialize(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, float32 _fDetectorSpacingX, float32 _fDetectorSpacingY, std::vector &&_pfProjectionAngles); public: /** Destructor */ virtual ~CProjectionGeometry3D(); /** Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. */ virtual void clear(); /** Create a hard copy. */ virtual CProjectionGeometry3D* clone() const = 0; /** Initialize the geometry with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Get the initialization state of the object. * * @return true iff the object has been initialized */ bool isInitialized() const; /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CProjectionGeometry3D *) const = 0; /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const = 0; /** Get the number of projections. * * @return Number of projections */ int getProjectionCount() const; /** Get the number of rows of detectors. * * @return Number of rows of detectors. */ int getDetectorRowCount() const; /** Get the number of columns of detectors. * * @return Number of columns of detectors. */ int getDetectorColCount() const; /** Get the total number of detectors. * * @return Total number of detectors. */ int getDetectorTotCount() const; /** Get the width of a detector. * * @return Width of a detector, in unit lengths */ float32 getDetectorSpacingX() const; /** Get the height of a detector. * * @return Height of a detector, in unit lengths */ float32 getDetectorSpacingY() const; /** Get a projection angle, given by its index. The angle is represented in Radians. * * @return Projection angle with index _iProjectionIndex */ float32 getProjectionAngle(int _iProjectionIndex) const; /** Get a projection angle, given by its index. The angle is represented in degrees. * * @return Projection angle with index _iProjectionIndex */ // float32 getProjectionAngleDegrees(int _iProjectionIndex) const; /** Returns a buffer containing all projection angles. The element count of the buffer is equal * to the number given by getProjectionAngleCount. * * The angles are in radians. * * @return Pointer to buffer containing the angles. */ const float32* getProjectionAngles() const; /** Get the column index coordinate of a point on a detector array. * * @param _fOffsetX Distance between the center of the detector array and a certain point (both on the X-axis). * @return The location of the point in index X-coordinates (still float, not rounded) */ virtual float32 detectorOffsetXToColIndexFloat(float32 _fOffsetX) const; /** Get the row index coordinate of a point on a detector array. * * @param _fOffsetY Distance between the center of the detector array and a certain point (both on the Y-axis). * @return The location of the point in index Y-coordinates (still float, not rounded) */ virtual float32 detectorOffsetYToRowIndexFloat(float32 _fOffsetY) const; /** Get the offset of a detector on the X-axis based on its index coordinate. * * @param _iIndex the index of the detector. * @return the offset from the center of the detector array on the X-axis. */ virtual float32 indexToDetectorOffsetX(int _iIndex) const; /** Get the offset of a detector on the Y-axis based on its index coordinate. * * @param _iIndex the index of the detector. * @return the offset from the center of the detector array on the Y-axis. */ virtual float32 indexToDetectorOffsetY(int _iIndex) const; /** Get the offset of a detector on the X-axis based on its column index coordinate. * * @param _iIndex the index of the detector. * @return the offset from the center of the detector array on the X-axis. */ virtual float32 colIndexToDetectorOffsetX(int _iIndex) const; /** Get the offset of a detector on the Y-axis based on its row index coordinate. * * @param _iIndex the index of the detector. * @return the offset from the center of the detector array on the Y-axis. */ virtual float32 rowIndexToDetectorOffsetY(int _iIndex) const; /** Get the row and column index of a detector based on its index. * * @param _iDetectorIndex in: the index of the detector. * @param _iDetectorRow out: the row index of the detector. * @param _iDetectorCol out: the column index of the detector. */ virtual void detectorIndexToRowCol(int _iDetectorIndex, int& _iDetectorRow, int& _iDetectorCol) const; /** Get the angle and detector index of a detector * * @param _iIndex the index of the detector. * @param _iAngleIndex output: index of angle * @param _iDetectorIndex output: index of detector */ virtual void indexToAngleDetectorIndex(int _iIndex, int& _iAngleIndex, int& _iDetectorIndex) const; /** Find a bounding box of the projections of a box in the volume. * It may not be the tighest possible bounding box. * This may fall (partially or fully) outside of the actual detector. */ virtual void getProjectedBBox(double fXMin, double fXMax, double fYMin, double fYMax, double fZMin, double fZMax, double &fUMin, double &fUMax, double &fVMin, double &fVMax) const; /** Project a point onto the detector. The 3D point coordinates * are in units. The output fU,fV are the (unrounded) indices of the * detector column and row. * This may fall outside of the actual detector. * * @param fX,fY,fZ coordinates of the point to project * @param iAngleIndex the index of the angle to use * @param fU,fV the projected point. */ virtual void projectPoint(double fX, double fY, double fZ, int iAngleIndex, double &fU, double &fV) const = 0; /** Returns true if the type of geometry defined in this class is the one specified in _sType. * * @param _sType geometry type to compare to. * @return true if the type of geometry defined in this class is the one specified in _sType. */ virtual bool isOfType(const std::string& _sType) const = 0; //< For Config unused argument checking ConfigCheckData* configCheckData; friend class ConfigReader; protected: virtual bool initializeAngles(const Config& _cfg); }; //---------------------------------------------------------------------------------------- // Inline member functions //---------------------------------------------------------------------------------------- // Get the initialization state. inline bool CProjectionGeometry3D::isInitialized() const { return m_bInitialized; } //---------------------------------------------------------------------------------------- // Get the number of detectors. inline int CProjectionGeometry3D::getDetectorRowCount() const { ASTRA_ASSERT(m_bInitialized); return m_iDetectorRowCount; } //---------------------------------------------------------------------------------------- // Get the number of detectors. inline int CProjectionGeometry3D::getDetectorColCount() const { ASTRA_ASSERT(m_bInitialized); return m_iDetectorColCount; } //---------------------------------------------------------------------------------------- // Get the number of detectors. inline int CProjectionGeometry3D::getDetectorTotCount() const { ASTRA_ASSERT(m_bInitialized); return m_iDetectorTotCount; } //---------------------------------------------------------------------------------------- // Get the width of a single detector (in unit lengths). inline float32 CProjectionGeometry3D::getDetectorSpacingX() const { ASTRA_ASSERT(m_bInitialized); return m_fDetectorSpacingX; } //---------------------------------------------------------------------------------------- // Get the width of a single detector (in unit lengths). inline float32 CProjectionGeometry3D::getDetectorSpacingY() const { ASTRA_ASSERT(m_bInitialized); return m_fDetectorSpacingY; } //---------------------------------------------------------------------------------------- // Get the number of projection angles. inline int CProjectionGeometry3D::getProjectionCount() const { ASTRA_ASSERT(m_bInitialized); return m_iProjectionAngleCount; } //---------------------------------------------------------------------------------------- // Get a projection angle, represented in Radians. inline float32 CProjectionGeometry3D::getProjectionAngle(int _iProjectionIndex) const { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iProjectionIndex >= 0); ASTRA_ASSERT(_iProjectionIndex < m_iProjectionAngleCount); return m_pfProjectionAngles[_iProjectionIndex]; } /* //---------------------------------------------------------------------------------------- // Get a projection angle, represented in degrees. inline float32 CProjectionGeometry3D::getProjectionAngleDegrees(int _iProjectionIndex) const { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iProjectionIndex >= 0); ASTRA_ASSERT(_iProjectionIndex < m_iProjectionAngleCount); return (m_pfProjectionAngles[_iProjectionIndex] * 180.0f / PI32); } */ //---------------------------------------------------------------------------------------- // Get pointer to buffer used to store projection angles. inline const float32* CProjectionGeometry3D::getProjectionAngles() const { // basic checks ASTRA_ASSERT(m_bInitialized); return &m_pfProjectionAngles[0]; } //---------------------------------------------------------------------------------------- // detector offset X -> detector column index (float) inline float32 CProjectionGeometry3D::detectorOffsetXToColIndexFloat(float32 _fOffsetX) const { // basic checks ASTRA_ASSERT(m_bInitialized); return (_fOffsetX / m_fDetectorSpacingX) + ((m_iDetectorColCount-1.0f) / 2.0f); } //---------------------------------------------------------------------------------------- // detector offset Y -> detector row index (float) inline float32 CProjectionGeometry3D::detectorOffsetYToRowIndexFloat(float32 _fOffsetY) const { // basic checks ASTRA_ASSERT(m_bInitialized); return (_fOffsetY / m_fDetectorSpacingY) + ((m_iDetectorRowCount-1.0f) / 2.0f); } //---------------------------------------------------------------------------------------- // detector index -> detector offset X inline float32 CProjectionGeometry3D::indexToDetectorOffsetX(int _iIndex) const { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iIndex >= 0); ASTRA_ASSERT(_iIndex < m_iDetectorTotCount); _iIndex = _iIndex % m_iDetectorColCount; return (_iIndex - (m_iDetectorColCount-1.0f) / 2.0f) * m_fDetectorSpacingX; } //---------------------------------------------------------------------------------------- // detector index -> detector offset Y inline float32 CProjectionGeometry3D::indexToDetectorOffsetY(int _iIndex) const { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iIndex >= 0); ASTRA_ASSERT(_iIndex < m_iDetectorTotCount); _iIndex = _iIndex / m_iDetectorColCount; return -(_iIndex - (m_iDetectorRowCount-1.0f) / 2.0f) * m_fDetectorSpacingY; } //---------------------------------------------------------------------------------------- // detector index -> detector offset X inline float32 CProjectionGeometry3D::colIndexToDetectorOffsetX(int _iIndex) const { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iIndex >= 0); ASTRA_ASSERT(_iIndex < m_iDetectorColCount); return (_iIndex - (m_iDetectorColCount-1.0f) / 2.0f) * m_fDetectorSpacingX; } //---------------------------------------------------------------------------------------- // detector index -> detector offset Y inline float32 CProjectionGeometry3D::rowIndexToDetectorOffsetY(int _iIndex) const { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iIndex >= 0); ASTRA_ASSERT(_iIndex < m_iDetectorRowCount); return (_iIndex - (m_iDetectorRowCount-1.0f) / 2.0f) * m_fDetectorSpacingY; } //---------------------------------------------------------------------------------------- // detector index -> row index & column index inline void CProjectionGeometry3D::detectorIndexToRowCol(int _iDetectorIndex, int& _iDetectorRow, int& _iDetectorCol) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iDetectorIndex >= 0); ASTRA_ASSERT(_iDetectorIndex < m_iDetectorTotCount); _iDetectorRow = _iDetectorIndex / m_iDetectorColCount; _iDetectorCol = _iDetectorIndex % m_iDetectorColCount; } //---------------------------------------------------------------------------------------- inline void CProjectionGeometry3D::indexToAngleDetectorIndex(int _iIndex, int& _iAngleIndex, int& _iDetectorIndex) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iIndex >= 0); ASTRA_ASSERT(_iIndex < m_iDetectorTotCount * m_iProjectionAngleCount); // int det_row = _iIndex / (m_iDetectorColCount*m_iProjectionAngleCount); // int det_col = _iIndex % m_iDetectorColCount; // // _iAngleIndex = _iIndex % (m_iDetectorColCount*m_iProjectionAngleCount) / m_iDetectorColCount; // _iDetectorIndex = det_row * m_iDetectorColCount + det_col; _iAngleIndex = (_iIndex % (m_iDetectorColCount*m_iProjectionAngleCount)) / m_iDetectorColCount; _iDetectorIndex = _iIndex / m_iProjectionAngleCount + (_iIndex % m_iDetectorColCount); } //---------------------------------------------------------------------------------------- } // end namespace astra #endif /* _INC_ASTRA_PROJECTIONGEOMETRY2D */ astra-toolbox-2.3.0/include/astra/ProjectionGeometry3DFactory.h000066400000000000000000000024171475635207100246050ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PROJECTIONGEOMETRY3DFACTORY #define _INC_ASTRA_PROJECTIONGEOMETRY3DFACTORY #include "Globals.h" #include namespace astra { class CProjectionGeometry3D; _AstraExport std::unique_ptr constructProjectionGeometry3D(const std::string &type); } #endif astra-toolbox-2.3.0/include/astra/Projector2D.h000066400000000000000000000157111475635207100213740ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef INC_ASTRA_PROJECTOR2D #define INC_ASTRA_PROJECTOR2D #include #include #include "Globals.h" #include "Config.h" #include "Float32Data2D.h" #include "ParallelProjectionGeometry2D.h" #include "ProjectionGeometry2D.h" #include "VolumeGeometry2D.h" namespace astra { class CSparseMatrix; /** This is a base interface class for a two-dimensional projector. Each subclass should at least * implement the core projection functions computeProjectionRayWeights and projectPoint. For * extra efficiency one might also like to overwrite other functions such as computeProjectionRayWeights, * computeRayForwardProj_ART, ... * * \par XML Configuration * \astra_xml_item{ProjectionGeometry, xml node, The geometry of the projection.} * \astra_xml_item{VolumeGeometry, xml node, The geometry of the volume.} */ class _AstraExport CProjector2D { protected: CProjectionGeometry2D* m_pProjectionGeometry; ///< Used projection geometry CVolumeGeometry2D* m_pVolumeGeometry; ///< Used volume geometry bool m_bIsInitialized; ///< Has this class been initialized? /** Default Constructor. */ CProjector2D(); /** Constructor. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pVolumeGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. */ CProjector2D(const CProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pVolumeGeometry); /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly */ virtual bool _check(); public: /** Destructor. */ virtual ~CProjector2D(); /** Clear this class. */ virtual void clear(); /** Initialize the projector with a config object. * This function does not set m_bInitialized to true. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Fetch the Projection Geometry of this projector. * * @return Projection Geometry class. */ const CProjectionGeometry2D& getProjectionGeometry(); /** Fetch the Volume Geometry of this projector. * * @return Volume Geometry class. */ const CVolumeGeometry2D& getVolumeGeometry(); /** Compute the pixel weights for a single ray, from the source to a detector pixel. * * @param _iProjectionIndex Index of the projection * @param _iDetectorIndex Index of the detector pixel * @param _pWeightedPixels Pointer to a pre-allocated array, consisting of _iMaxPixelCount elements * of type SPixelWeight. On return, this array contains a list of the index * and weight for all pixels on the ray. * @param _iMaxPixelCount Maximum number of pixels (and corresponding weights) that can be stored in _pWeightedPixels. * This number MUST be greater than the total number of pixels on the ray. * @param _iStoredPixelCount On return, this variable contains the total number of pixels on the * ray (that have been stored in the list _pWeightedPixels). */ virtual void computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight *_pWeightedPixels, int _iMaxPixelCount, int &_iStoredPixelCount) = 0; /** Compute the pixel weights for all rays in a single projection, from the source to a each of the * detector pixels. All pixels and their weights are stored consecutively in the array _pWeightedPixels. * The array starts with all pixels on the first ray, followed by all pixels on the second ray, the third * ray, etc. Note that a pixel may occur in the list more than once, as it can be on several rays. * * @param _iProjectionIndex Index of the projection (zero-based). * @param _pfWeightedPixels Pointer to a pre-allocated array, consisting of getProjectionWeightsCount() * elements of type SPixelWeight. On return, this array contains a list of * the index and weight for all pixels on each of the rays. The elements for * every ray start at equal offsets (ray_index * _pWeightedPixels / ray_count). * @param _piRayStoredPixelCount Pointer to a pre-allocated array, containing a single integer for each * ray in the projection. On return, this array contains the number of * pixels on the ray, for each ray in the given projection. */ virtual void computeProjectionRayWeights(int _iProjectionIndex, SPixelWeight* _pfWeightedPixels, int* _piRayStoredPixelCount); /** Returns the number of weights required for storage of all weights of one projection ray. * * @param _iProjectionIndex Index of the projection (zero-based). * @return Size of buffer (given in SPixelWeight elements) needed to store weighted pixels. */ virtual int getProjectionWeightsCount(int _iProjectionIndex) = 0; /** Returns the projection as an explicit sparse matrix. * @return a newly allocated CSparseMatrix. Delete afterwards. */ CSparseMatrix* getMatrix(); /** Has the projector been initialized? * * @return initialized successfully */ bool isInitialized() const; /** get a description of the class * * @return description string */ virtual std::string description() const {return " ";}; virtual std::string getType() { return " "; } private: //< For Config unused argument checking ConfigCheckData* configCheckData; friend class ConfigReader; }; // inline functions inline bool CProjector2D::isInitialized() const { return m_bIsInitialized; } inline const CProjectionGeometry2D& CProjector2D::getProjectionGeometry() { return *m_pProjectionGeometry; } inline const CVolumeGeometry2D& CProjector2D::getVolumeGeometry() { return *m_pVolumeGeometry; } } // namespace astra #endif /* INC_ASTRA_PROJECTOR2D */ astra-toolbox-2.3.0/include/astra/Projector2DImpl.inl000066400000000000000000000025451475635207100225520ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "ParallelBeamDistanceDrivenProjector2D.inl" #include "ParallelBeamLinearKernelProjector2D.inl" #include "ParallelBeamLineKernelProjector2D.inl" #include "ParallelBeamStripKernelProjector2D.inl" #include "ParallelBeamBlobKernelProjector2D.inl" #include "FanFlatBeamStripKernelProjector2D.inl" #include "FanFlatBeamLineKernelProjector2D.inl" #include "SparseMatrixProjector2D.inl" astra-toolbox-2.3.0/include/astra/Projector3D.h000066400000000000000000000151141475635207100213720ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef INC_ASTRA_PROJECTOR3D #define INC_ASTRA_PROJECTOR3D #include #include "Globals.h" #include "Config.h" namespace astra { class CSparseMatrix; class CProjectionGeometry3D; class CVolumeGeometry3D; /** This is a base interface class for a three-dimensional projector. Each subclass should at least * implement the core projection functions computeProjectionRayWeights and projectPoint. * * \par XML Configuration * \astra_xml_item{ProjectionGeometry, xml node, The geometry of the projection.} * \astra_xml_item{VolumeGeometry, xml node, The geometry of the volume.} */ class _AstraExport CProjector3D { protected: std::unique_ptr m_pProjectionGeometry; ///< Used projection geometry std::unique_ptr m_pVolumeGeometry; ///< Used volume geometry bool m_bIsInitialized; ///< Has this class been initialized? /** Check variable values. */ bool _check(); /** Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. * Should only be used by constructors. Otherwise use the clear() function. */ void _clear(); public: /** * Default Constructor. */ CProjector3D(); /** Destructor, is virtual to show that we are aware subclass destructor is called. */ virtual ~CProjector3D(); /** Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. */ void clear(); /** Initialize the projector with a config object. * This function does not set m_bInitialized to true. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Fetch the Projection Geometry of this projector. * * @return Projection Geometry class. */ const CProjectionGeometry3D& getProjectionGeometry(); /** Fetch the Volume Geometry of this projector. * * @return Volume Geometry class. */ const CVolumeGeometry3D& getVolumeGeometry(); /** Compute the pixel weights for a single ray, from the source to a detector pixel. * * @param _iProjectionIndex Index of the projection. * @param _iSliceIndex Index of the detector pixel (1-d index). * @param _iDetectorIndex Index of the detector pixel (1-d index). * @param _pWeightedPixels Pointer to a pre-allocated array, consisting of _iMaxPixelCount elements * of type SPixelWeight. On return, this array contains a list of the index * and weight for all pixels on the ray. * @param _iMaxPixelCount Maximum number of pixels (and corresponding weights) that can be stored in _pWeightedPixels. * This number MUST be greater than the total number of pixels on the ray. * @param _iStoredPixelCount On return, this variable contains the total number of pixels on the * ray (that have been stored in the list _pWeightedPixels). */ virtual void computeSingleRayWeights(int _iProjectionIndex, int _iSliceIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount) = 0; /** Compute the pixel weights for all rays in a single projection, from the source to a each of the * detector pixels. All pixels and their weights are stored consecutively in the array _pWeightedPixels. * The array starts with all pixels on the first ray, followed by all pixels on the second ray, the third * ray, etc. Note that a pixel may occur in the list more than once, as it can be on several rays. * * @param _iProjectionIndex Index of the projection (zero-based). * @param _pfWeightedPixels Pointer to a pre-allocated array, consisting of getProjectionWeightsCount() * elements of type SPixelWeight. On return, this array contains a list of * the index and weight for all pixels on each of the rays. The elements for * every ray start at equal offsets (ray_index * _pWeightedPixels / ray_count). * @param _piRayStoredPixelCount Pointer to a pre-allocated array, containing a single integer for each * ray in the projection. On return, this array contains the number of * pixels on the ray, for each ray in the given projection. */ virtual void computeProjectionRayWeights(int _iProjectionIndex, SPixelWeight* _pfWeightedPixels, int* _piRayStoredPixelCount); /** Create a list of detectors that are influenced by point [_iRow, _iCol]. * * @param _iRow row of the point * @param _iCol column of the point * @return list of SDetector2D structs */ //virtual std::vector projectPoint(int _iRow, int _iCol) = 0; /** Returns the number of weights required for storage of all weights of one projection. * * @param _iProjectionIndex Index of the projection (zero-based). * @return Size of buffer (given in SWeightedPixel3D elements) needed to store weighted pixels. */ virtual int getProjectionWeightsCount(int _iProjectionIndex) = 0; /** Has the projector been initialized? * * @return initialized successfully */ bool isInitialized() const; /** get a description of the class * * @return description string */ virtual std::string description() const = 0; /** * Returns a string describing the projector type */ virtual std::string getType() = 0; private: //< For Config unused argument checking ConfigCheckData* configCheckData; friend class ConfigReader; }; // inline functions inline bool CProjector3D::isInitialized() const { return m_bIsInitialized; } inline const CProjectionGeometry3D& CProjector3D::getProjectionGeometry() { return *m_pProjectionGeometry; } inline const CVolumeGeometry3D& CProjector3D::getVolumeGeometry() { return *m_pVolumeGeometry; } } // namespace astra #endif /* INC_ASTRA_PROJECTOR3D */ astra-toolbox-2.3.0/include/astra/ProjectorTypelist.h000066400000000000000000000042451475635207100227440ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_PROJECTORTYPELIST #define _INC_ASTRA_PROJECTORTYPELIST #include "Projector2D.h" #include "TypeList.h" // Projector2D #include "Projector2D.h" #include "ParallelBeamLineKernelProjector2D.h" #include "ParallelBeamLinearKernelProjector2D.h" #include "ParallelBeamDistanceDrivenProjector2D.h" #include "ParallelBeamBlobKernelProjector2D.h" #include "ParallelBeamStripKernelProjector2D.h" #include "SparseMatrixProjector2D.h" #include "FanFlatBeamLineKernelProjector2D.h" #include "FanFlatBeamStripKernelProjector2D.h" #include "CudaProjector2D.h" namespace astra{ typedef TypeList< #ifdef ASTRA_CUDA CCudaProjector2D, #endif CFanFlatBeamLineKernelProjector2D, CFanFlatBeamStripKernelProjector2D, CParallelBeamDistanceDrivenProjector2D, CParallelBeamLinearKernelProjector2D, CParallelBeamLineKernelProjector2D, CParallelBeamBlobKernelProjector2D, CParallelBeamStripKernelProjector2D, CSparseMatrixProjector2D > Projector2DTypeList; } // Projector3D #include "Projector3D.h" #include "CudaProjector3D.h" namespace astra { #ifdef ASTRA_CUDA typedef TypeList Projector3DTypeList; #else typedef TypeList<> Projector3DTypeList; #endif } #endif astra-toolbox-2.3.0/include/astra/ReconstructionAlgorithm2D.h000066400000000000000000000161031475635207100243110ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_RECONSTRUCTIONALGORITHM2D #define _INC_ASTRA_RECONSTRUCTIONALGORITHM2D #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "Projector2D.h" #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" namespace astra { /** * This is a base class for the different implementations of 2D reconstruction algorithms. * * \par XML Configuration * \astra_xml_item{ProjectorId, integer, Identifier of a projector as it is stored in the ProjectorManager.} * \astra_xml_item{ProjectionDataId, integer, Identifier of a projection data object as it is stored in the DataManager.} * \astra_xml_item{ReconstructionDataId, integer, Identifier of a volume data object as it is stored in the DataManager.} * \astra_xml_item_option{ReconstructionMaskId, integer, not used, Identifier of a volume data object that acts as a reconstruction mask. 1 = reconstruct on this pixel. 0 = don't reconstruct on this pixel.} * \astra_xml_item_option{SinogramMaskId, integer, not used, Identifier of a projection data object that acts as a projection mask. 1 = reconstruct using this ray. 0 = don't use this ray while reconstructing.} * \astra_xml_item_option{UseMinConstraint, bool, false, Use minimum value constraint.} * \astra_xml_item_option{MinConstraintValue, float, 0, Minimum constraint value.} * \astra_xml_item_option{UseMaxConstraint, bool, false, Use maximum value constraint.} * \astra_xml_item_option{MaxConstraintValue, float, 255, Maximum constraint value.} */ class _AstraExport CReconstructionAlgorithm2D : public CAlgorithm { public: /** Default constructor, containing no code. */ CReconstructionAlgorithm2D(); /** Destructor. */ virtual ~CReconstructionAlgorithm2D(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. */ bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Clear this class. */ virtual void clear(); /** Add a min/max constraint to the reconstruction process * * @param _bUseMin True if the algorithm should use a min constraint. * @param _fMinValue Lower value to clip pixel values to. * @param _bUseMax True if the algorithm should use a max constraint. * @param _fMaxValue Upper value to clip pixel values to. */ void setConstraints(bool _bUseMin, float32 _fMinValue, bool _bUseMax, float32 _fMaxValue); /** Set a fixed reconstruction mask. A pixel will only be used in the reconstruction if the * corresponding value in the mask is 1. * * @param _pMask Volume Data object containing fixed reconstruction mask * @param _bEnable enable the use of this mask */ void setReconstructionMask(CFloat32VolumeData2D* _pMask, bool _bEnable = true); /** Set a fixed sinogram mask. A detector value will only be used in the reconstruction if the * corresponding value in the mask is 1. * * @param _pMask Projection Data object containing fixed sinogram mask * @param _bEnable enable the use of this mask */ void setSinogramMask(CFloat32ProjectionData2D* _pMask, bool _bEnable = true); /** Get projector object * * @return projector */ CProjector2D* getProjector() const; /** Get sinogram data object * * @return sinogram data object */ CFloat32ProjectionData2D* getSinogram() const; /** Get Reconstructed Data * * @return reconstruction */ CFloat32VolumeData2D* getReconstruction() const; /** Get Fixed Reconstruction Mask * * @return fixed reconstruction mask */ CFloat32VolumeData2D* getReconstructionMask() const; /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0) = 0; /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** Get the norm of the residual image. * Only a few algorithms support this method. * * @param _fNorm if supported, the norm is returned here * @return true if this operation is supported */ virtual bool getResidualNorm(float32& _fNorm) { return false; } protected: /** Check this object. * * @return object initialized */ bool _check(); /** Initial clearing. Only to be used by constructors. */ void _clear(); //< Projector object. CProjector2D* m_pProjector; //< ProjectionData2D object containing the sinogram. CFloat32ProjectionData2D* m_pSinogram; //< VolumeData2D object for storing the reconstruction volume. CFloat32VolumeData2D* m_pReconstruction; //< Use minimum value constraint? bool m_bUseMinConstraint; //< Minimum value constraint. float32 m_fMinValue; //< Use maximum value constraint? bool m_bUseMaxConstraint; //< Maximum value constraint. float32 m_fMaxValue; //< Dataobject containing fixed reconstruction mask (0 = don't reconstruct) CFloat32VolumeData2D* m_pReconstructionMask; //< Use the fixed reconstruction mask? bool m_bUseReconstructionMask; //< Dataobject containing fixed reconstruction mask (0 = don't reconstruct) CFloat32ProjectionData2D* m_pSinogramMask; //< Use the fixed reconstruction mask? bool m_bUseSinogramMask; //< Specify if initialize/check should check for a valid Projector virtual bool requiresProjector() const { return true; } }; // inline functions inline std::string CReconstructionAlgorithm2D::description() const { return "3D Reconstruction Algorithm"; }; inline CProjector2D* CReconstructionAlgorithm2D::getProjector() const { return m_pProjector; } inline CFloat32ProjectionData2D* CReconstructionAlgorithm2D::getSinogram() const { return m_pSinogram; } inline CFloat32VolumeData2D* CReconstructionAlgorithm2D::getReconstruction() const { return m_pReconstruction; } inline CFloat32VolumeData2D* CReconstructionAlgorithm2D::getReconstructionMask() const { return m_pReconstructionMask; } } // end namespace #endif astra-toolbox-2.3.0/include/astra/ReconstructionAlgorithm3D.h000066400000000000000000000156271475635207100243240ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_RECONSTRUCTIONALGORITHM3D #define _INC_ASTRA_RECONSTRUCTIONALGORITHM3D #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "Data3D.h" namespace astra { class CProjector3D; /** * This is a base class for the different implementations of 3D reconstruction algorithms. * * \par XML Configuration * \astra_xml_item{ProjectorId, integer, Identifier of a projector as it is stored in the ProjectorManager.} * \astra_xml_item{ProjectionDataId, integer, Identifier of a projection data object as it is stored in the DataManager.} * \astra_xml_item{ReconstructionDataId, integer, Identifier of a volume data object as it is stored in the DataManager.} * \astra_xml_item_option{ReconstructionMaskId, integer, not used, Identifier of a volume data object that acts as a reconstruction mask. 1 = reconstruct on this pixel. 0 = don't reconstruct on this pixel.} * \astra_xml_item_option{SinogramMaskId, integer, not used, Identifier of a projection data object that acts as a projection mask. 1 = reconstruct using this ray. 0 = don't use this ray while reconstructing.} * \astra_xml_item_option{UseMinConstraint, bool, false, Use minimum value constraint.} * \astra_xml_item_option{MinConstraintValue, float, 0, Minimum constraint value.} * \astra_xml_item_option{UseMaxConstraint, bool, false, Use maximum value constraint.} * \astra_xml_item_option{MaxConstraintValue, float, 255, Maximum constraint value.} */ class _AstraExport CReconstructionAlgorithm3D : public CAlgorithm { public: /** Default constructor, containing no code. */ CReconstructionAlgorithm3D(); /** Destructor. */ virtual ~CReconstructionAlgorithm3D(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData3D object containing the sinogram data. * @param _pReconstruction VolumeData3D object for storing the reconstructed volume. */ bool initialize(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pSinogram, CFloat32VolumeData3D* _pReconstruction); /** Clear this class. */ virtual void clear(); /** Add a min/max constraint to the reconstruction process * * @param _bUseMin True if the algorithm should use a min constraint. * @param _fMinValue Lower value to clip pixel values to. * @param _bUseMax True if the algorithm should use a max constraint. * @param _fMaxValue Upper value to clip pixel values to. */ void setConstraints(bool _bUseMin, float32 _fMinValue, bool _bUseMax, float32 _fMaxValue); /** Set a fixed reconstruction mask. A pixel will only be used in the reconstruction if the * corresponding value in the mask is 1. * * @param _pMask Volume Data object containing fixed reconstruction mask * @param _bEnable enable the use of this mask */ void setReconstructionMask(CFloat32VolumeData3D* _pMask, bool _bEnable = true); /** Set a fixed sinogram mask. A detector value will only be used in the reconstruction if the * corresponding value in the mask is 1. * * @param _pMask Projection Data object containing fixed sinogram mask * @param _bEnable enable the use of this mask */ void setSinogramMask(CFloat32ProjectionData3D* _pMask, bool _bEnable = true); /** Get projector object * * @return projector */ CProjector3D* getProjector() const; /** Get sinogram data object * * @return sinogram data object */ CFloat32ProjectionData3D* getSinogram() const; /** Get Reconstructed Data * * @return reconstruction */ CFloat32VolumeData3D* getReconstruction() const; /** Get Fixed Reconstruction Mask * * @return fixed reconstruction mask */ CFloat32VolumeData3D* getReconstructionMask() const; /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0) = 0; /** Get a description of the class. * * @return description string */ virtual std::string description() const; /** Get the norm of the residual image. * Only a few algorithms support this method. * * @param _fNorm if supported, the norm is returned here * @return true if this operation is supported */ virtual bool getResidualNorm(float32& _fNorm) { return false; } protected: /** Check this object. * * @return object initialized */ bool _check(); /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); //< Projector object. CProjector3D* m_pProjector; //< ProjectionData3D object containing the sinogram. CFloat32ProjectionData3D* m_pSinogram; //< VolumeData3D object for storing the reconstruction volume. CFloat32VolumeData3D* m_pReconstruction; //< Use minimum value constraint? bool m_bUseMinConstraint; //< Minimum value constraint. float32 m_fMinValue; //< Use maximum value constraint? bool m_bUseMaxConstraint; //< Maximum value constraint. float32 m_fMaxValue; //< Dataobject containing fixed reconstruction mask (0 = don't reconstruct) CFloat32VolumeData3D* m_pReconstructionMask; //< Use the fixed reconstruction mask? bool m_bUseReconstructionMask; //< Dataobject containing fixed reconstruction mask (0 = don't reconstruct) CFloat32ProjectionData3D* m_pSinogramMask; //< Use the fixed reconstruction mask? bool m_bUseSinogramMask; }; // inline functions inline std::string CReconstructionAlgorithm3D::description() const { return "3D Reconstruction Algorithm"; }; inline CProjector3D* CReconstructionAlgorithm3D::getProjector() const { return m_pProjector; } inline CFloat32ProjectionData3D* CReconstructionAlgorithm3D::getSinogram() const { return m_pSinogram; } inline CFloat32VolumeData3D* CReconstructionAlgorithm3D::getReconstruction() const { return m_pReconstruction; } inline CFloat32VolumeData3D* CReconstructionAlgorithm3D::getReconstructionMask() const { return m_pReconstructionMask; } } // end namespace #endif astra-toolbox-2.3.0/include/astra/SartAlgorithm.h000066400000000000000000000170071475635207100220170ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_SARTALGORITHM #define _INC_ASTRA_SARTALGORITHM #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "ReconstructionAlgorithm2D.h" #include "Projector2D.h" #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" #include "DataProjector.h" namespace astra { /** * \brief * This class contains the implementation of the SART (Simultaneous Algebraic Reconstruction Technique) algorithm. * * The update step of pixel \f$v_j\f$ for projection \f$phi\f$ and iteration \f$k\f$ is given by: * \f[ * v_j^{(k+1)} = v_j^{(k)} + \lambda \frac{\sum_{p_i \in P_\phi} \left( \frac{p_i - \sum_{r=1}^{N} w_{ir}v_r^{(k)}} {\sum_{r=1}^{N}w_{ir} } \right)} {\sum_{p_i \in P_\phi}w_{ij}} * \f] * * \par XML Configuration * \astra_xml_item{ProjectorId, integer, Identifier of a projector as it is stored in the ProjectorManager.} * \astra_xml_item{ProjectionDataId, integer, Identifier of a projection data object as it is stored in the DataManager.} * \astra_xml_item{ReconstructionDataId, integer, Identifier of a volume data object as it is stored in the DataManager.} * \astra_xml_item_option{ReconstructionMaskId, integer, not used, Identifier of a volume data object that acts as a reconstruction mask. 1 = reconstruct on this pixel. 0 = don't reconstruct on this pixel.} * \astra_xml_item_option{SinogramMaskId, integer, not used, Identifier of a projection data object that acts as a projection mask. 1 = reconstruct using this ray. 0 = don't use this ray while reconstructing.} * \astra_xml_item_option{UseMinConstraint, bool, false, Use minimum value constraint.} * \astra_xml_item_option{MinConstraintValue, float, 0, Minimum constraint value.} * \astra_xml_item_option{UseMaxConstraint, bool, false, Use maximum value constraint.} * \astra_xml_item_option{MaxConstraintValue, float, 255, Maximum constraint value.} * \astra_xml_item_option{ProjectionOrder, string, "sequential", the order in which the projections are updated. 'sequential', 'random' or 'custom'} * \astra_xml_item_option{ProjectionOrderList, vector of float, not used, if ProjectionOrder='custom': use this order.} * \astra_xml_item_option{Relaxation, float, 1, The relaxation parameter.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('SART');\n * cfg.ProjectorId = proj_id;\n * cfg.ProjectionDataId = sino_id;\n * cfg.ReconstructionDataId = recon_id;\n * cfg.option.MaskId = mask_id;\n * cfg.option.UseMinConstraint = 'yes';\n * cfg.option.UseMaxConstraint = 'yes';\n * cfg.option.MaxConstraintValue = 1024;\n * cfg.option.ProjectionOrder = 'custom';\n * cfg.option.ProjectionOrderList = randperm(100);\n * cfg.option.Relaxation = 1.0;\n * alg_id = astra_mex_algorithm('create'\, cfg);\n * astra_mex_algorithm('iterate'\, alg_id\, 10);\n * astra_mex_algorithm('delete'\, alg_id);\n * } */ class _AstraExport CSartAlgorithm : public CReconstructionAlgorithm2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - valid projector * - valid data objects * - projection order all within range */ virtual bool _check(); // temporary data objects CFloat32ProjectionData2D* m_pTotalRayLength; CFloat32VolumeData2D* m_pTotalPixelWeight; CFloat32ProjectionData2D* m_pDiffSinogram; unsigned int m_iIterationCount; public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "SART"; /** Default constructor, containing no code. */ CSartAlgorithm(); /** Constructor. * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. */ CSartAlgorithm(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Constructor. * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. * @param _piProjectionOrder array containing a projection order. * @param _iProjectionCount number of elements in _piProjectionOrder. */ CSartAlgorithm(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction, int* _piProjectionOrder, int _iProjectionCount); /** Destructor. */ virtual ~CSartAlgorithm(); /** Clear this class. */ virtual void clear(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class, no optionals, use sequential order. * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. * @return initialization successful? */ virtual bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Initialize class, use custom order. * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. * @param _piProjectionOrder array containing a projection order. * @param _iProjectionCount number of elements in _piProjectionOrder. * @return initialization successful? */ virtual bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction, int* _piProjectionOrder, int _iProjectionCount); /** Perform a number of iterations. Each iteration is a forward and backprojection of * a single projection index. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 1); /** Get a description of the class. * * @return description string */ virtual std::string description() const; protected: //< Order of the projections. std::vector m_piProjectionOrder; //< Relaxation parameter float m_fLambda; }; // inline functions inline std::string CSartAlgorithm::description() const { return CSartAlgorithm::type; }; } // end namespace #endif astra-toolbox-2.3.0/include/astra/SheppLogan.h000066400000000000000000000025021475635207100212710ustar00rootroot00000000000000 /* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_SHEPPLOGAN #define _INC_ASTRA_SHEPPLOGAN #include "Globals.h" #include "Config.h" namespace astra { class CFloat32VolumeData2D; class CFloat32VolumeData3D; _AstraExport void generateSheppLogan(CFloat32VolumeData2D *data, bool modified); _AstraExport void generateSheppLogan3D(CFloat32VolumeData3D *data, bool modified); } #endif astra-toolbox-2.3.0/include/astra/Singleton.h000066400000000000000000000051451475635207100212010ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_SINGLETON #define _INC_ASTRA_SINGLETON #include #ifndef _MSC_VER #include #endif namespace astra { /** * This singleton interface class ensures that any of its children can be instatiated only once. This is used by the ObjectFactories. **/ template class Singleton { public: // constructor Singleton() { } // destructor virtual ~Singleton() { assert(m_singleton); m_singleton = 0; } static void construct(); // get singleton static T& getSingleton() { if (!m_singleton) construct(); return *m_singleton; } static T* getSingletonPtr() { if (!m_singleton) construct(); return m_singleton; } private: // the singleton static T* m_singleton; }; // We specifically avoid defining construct() in the header. // That way, the call to new is always executed by code inside libastra. // This avoids the situation where a singleton gets created by a copy // of the constructor linked into an object file outside of libastra, such // as a .mex file, which would then also cause the vtable to be outside of // libastra. This situation would cause issues when .mex files are unloaded. #define DEFINE_SINGLETON(T) \ template<> T* Singleton::m_singleton = 0; \ template<> void Singleton::construct() { assert(!m_singleton); m_singleton = new T(); } // This is a hack to support statements like // DEFINE_SINGLETON2(CTemplatedClass); #define DEFINE_SINGLETON2(A,B) \ template<> A,B* Singleton::m_singleton = 0; \ template<> void Singleton::construct() { assert(!m_singleton); m_singleton = new A,B(); } } // end namespace #endif astra-toolbox-2.3.0/include/astra/SirtAlgorithm.h000066400000000000000000000156761475635207100220410ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_SIRTALGORITHM #define _INC_ASTRA_SIRTALGORITHM #include "Globals.h" #include "Config.h" #include "Algorithm.h" #include "ReconstructionAlgorithm2D.h" #include "Projector2D.h" #include "Float32ProjectionData2D.h" #include "Float32VolumeData2D.h" #include "DataProjector.h" namespace astra { /** * \brief * This class contains the implementation of the SIRT (Simultaneous Iterative Reconstruction Technique) algorithm. * * The update step of pixel \f$v_j\f$ for iteration \f$k\f$ is given by: * \f[ * v_j^{(k+1)} = v_j^{(k)} + \lambda \sum_{i=1}^{M} \left( \frac{w_{ij}\left( p_i - \sum_{r=1}^{N} w_{ir}v_r^{(k)}\right)}{\sum_{k=1}^{N} w_{ik}} \right) \frac{1}{\sum_{l=1}^{M}w_{lj}} * \f] * * \par XML Configuration * \astra_xml_item{ProjectorId, integer, Identifier of a projector as it is stored in the ProjectorManager.} * \astra_xml_item{ProjectionDataId, integer, Identifier of a projection data object as it is stored in the DataManager.} * \astra_xml_item{ReconstructionDataId, integer, Identifier of a volume data object as it is stored in the DataManager.} * \astra_xml_item_option{ReconstructionMaskId, integer, not used, Identifier of a volume data object that acts as a reconstruction mask. 1 = reconstruct on this pixel. 0 = don't reconstruct on this pixel.} * \astra_xml_item_option{SinogramMaskId, integer, not used, Identifier of a projection data object that acts as a projection mask. 1 = reconstruct using this ray. 0 = don't use this ray while reconstructing.} * \astra_xml_item_option{UseMinConstraint, bool, false, Use minimum value constraint.} * \astra_xml_item_option{MinConstraintValue, float, 0, Minimum constraint value.} * \astra_xml_item_option{UseMaxConstraint, bool, false, Use maximum value constraint.} * \astra_xml_item_option{MaxConstraintValue, float, 255, Maximum constraint value.} * \astra_xml_item_option{Relaxation, float, 1, The relaxation factor.} * * \par XML Example * \astra_code{ * <Algorithm type="SIRT">\n * <ProjectorID>proj_id</ProjectorID>\n * <ProjectionDataId>sino_id</ProjectionDataId>\n * <ReconstructionDataId>recon_id</ReconstructionDataId>\n * <Option key="ReconstructionMaskId" value="3"/>\n * <Option key="SinogramMaskId" value="4"/>\n * <Option key="UseMinConstraint" value="yes"/>\n * <Option key="UseMaxConstraint" value="yes"/>\n * <Option key="MaxConstraintValue" value="1024"/>\n * <Option key="Relaxation" value="1"/>\n * </Algorithm> * } * * \par MATLAB example * \astra_code{ * cfg = astra_struct('SIRT');\n * cfg.ProjectorId = proj_id;\n * cfg.ProjectionDataId = sino_id;\n * cfg.ReconstructionDataId = recon_id;\n * cfg.option.SinogramMaskId = smask_id;\n * cfg.option.ReconstructionMaskId = mask_id;\n * cfg.option.UseMinConstraint = 'yes';\n * cfg.option.UseMaxConstraint = 'yes';\n * cfg.option.MaxConstraintValue = 1024;\n * cfg.option.Relaxation = 1.0;\n * alg_id = astra_mex_algorithm('create'\, cfg);\n * astra_mex_algorithm('iterate'\, alg_id\, 10);\n * astra_mex_algorithm('delete'\, alg_id);\n * } * * \par References * [1] "Computational Analysis and Improvement of SIRT", J. Gregor, T. Benson, IEEE Transactions on Medical Imaging, Vol. 22, No. 7, July 2008. */ class _AstraExport CSirtAlgorithm : public CReconstructionAlgorithm2D { protected: /** Init stuff */ virtual void _init(); /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - valid projector * - valid data objects */ virtual bool _check(); /** Temporary data object for storing the total ray lengths */ CFloat32ProjectionData2D* m_pTotalRayLength; /** Temporary data object for storing the total pixel weigths */ CFloat32VolumeData2D* m_pTotalPixelWeight; /** Temporary data object for storing the difference between the forward projected * reconstruction, and the measured projection data */ CFloat32ProjectionData2D* m_pDiffSinogram; /** Temporary data object for storing volume data */ CFloat32VolumeData2D* m_pTmpVolume; /** The number of performed iterations */ int m_iIterationCount; /** Relaxation parameter */ float m_fLambda; public: // type of the algorithm, needed to register with CAlgorithmFactory static inline const char* const type = "SIRT"; /** Default constructor, containing no code. */ CSirtAlgorithm(); /** Default constructor * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. */ CSirtAlgorithm(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Destructor. */ virtual ~CSirtAlgorithm(); /** Clear this class. */ virtual void clear(); /** Initialize the algorithm with a config object. * * @param _cfg Configuration Object * @return Initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize class. * * @param _pProjector Projector Object. * @param _pSinogram ProjectionData2D object containing the sinogram data. * @param _pReconstruction VolumeData2D object for storing the reconstructed volume. * @return Initialization successful? */ bool initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction); /** Perform a number of iterations. * * @param _iNrIterations amount of iterations to perform. */ virtual bool run(int _iNrIterations = 0); /** Get a description of the class. * * @return description string */ virtual std::string description() const; }; // inline functions inline std::string CSirtAlgorithm::description() const { return CSirtAlgorithm::type; }; } // end namespace #endif astra-toolbox-2.3.0/include/astra/SparseMatrix.h000066400000000000000000000071761475635207100216670ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_SPARSEMATRIX #define _INC_ASTRA_SPARSEMATRIX namespace astra { /** This class implements a sparse matrix. It is stored as three arrays. * The values are stored row-by-row. * m_pfValues contains the values * m_piColIndices contains the col indices of the values * m_plRowStarts contains the start offsets of the rows */ class _AstraExport CSparseMatrix { public: CSparseMatrix(); // TODO: are ints large enough for width/height? CSparseMatrix(unsigned int _iHeight, unsigned int _iWidth, unsigned long _lSize); /** Initialize the matrix structure. * It does not initialize any values. * * @param _iHeight number of rows * @param _iWidth number of columns * @param _lSize maximum number of non-zero entries * @return initialization successful? */ bool initialize(unsigned int _iHeight, unsigned int _iWidth, unsigned long _lSize); /** Destructor. */ ~CSparseMatrix(); /** Has the matrix structure been initialized? * * @return initialized successfully */ bool isInitialized() const { return m_bInitialized; } /** get a description of the class * * @return description string */ std::string description() const; /** get the data for a single row. Entries are stored from left to right. * * @param _iRow the row * @param _iSize the returned number of elements in the row * @param _pfValues the values of the non-zero entries in the row * @param _piColIndices the column indices of the non-zero entries */ void getRowData(unsigned int _iRow, unsigned int& _iSize, const float32*& _pfValues, const unsigned int*& _piColIndices) const { assert(_iRow < m_iHeight); unsigned long lStart = m_plRowStarts[_iRow]; _iSize = m_plRowStarts[_iRow+1] - lStart; _pfValues = &m_pfValues[lStart]; _piColIndices = &m_piColIndices[lStart]; } /** get the number of elements in a row * * @param _iRow the row * @return number of stored entries in the row */ unsigned int getRowSize(unsigned int _iRow) const { assert(_iRow < m_iHeight); return m_plRowStarts[_iRow+1] - m_plRowStarts[_iRow]; } /** Matrix width */ unsigned int m_iHeight; /** Matrix height */ unsigned int m_iWidth; /** Maximum number of non-zero entries */ unsigned long m_lSize; /** Contains the numeric values of all non-zero elements */ float32* m_pfValues; /** Contains the colon index of all non-zero elements */ unsigned int* m_piColIndices; /** The indices in this array point to the first element of each row in the m_pfValues array */ unsigned long* m_plRowStarts; protected: /** Is the class initialized? */ bool m_bInitialized; }; } #endif astra-toolbox-2.3.0/include/astra/SparseMatrixProjectionGeometry2D.h000066400000000000000000000120211475635207100256070ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_SPARSEMATRIXPROJECTIONGEOMETRY2D #define _INC_ASTRA_SPARSEMATRIXPROJECTIONGEOMETRY2D #include "ProjectionGeometry2D.h" namespace astra { class CSparseMatrix; /** * This class defines a projection geometry determined by an arbitrary * sparse matrix. * * The projection data is assumed to be grouped by 'angle' and 'detector pixel'. * This does not have any effect on the algorithms, but only on the * way the projection data is stored and accessed. */ class _AstraExport CSparseMatrixProjectionGeometry2D : public CProjectionGeometry2D { public: /** Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the init() methods before the object can be used. Any use before calling init() is not allowed, * except calling the member function isInitialized(). * */ CSparseMatrixProjectionGeometry2D(); /** Constructor. Create an instance of the CSparseMatrixProjectionGeometry2D class. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle. * @param _pMatrix Pointer to a CSparseMatrix. The caller is responsible for keeping this matrix valid until it is no longer required. */ CSparseMatrixProjectionGeometry2D(int _iProjectionAngleCount, int _iDetectorCount, const CSparseMatrix* _pMatrix); /** Copy constructor. */ CSparseMatrixProjectionGeometry2D(const CSparseMatrixProjectionGeometry2D& _projGeom); /** Destructor. */ ~CSparseMatrixProjectionGeometry2D(); /** Assignment operator. */ CSparseMatrixProjectionGeometry2D& operator=(const CSparseMatrixProjectionGeometry2D& _other); /** Initialize the geometry with a config object. This does not allow * setting a matrix. Use the setMatrix() method for that afterwards. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialization. Initializes an instance of the CProjectionGeometry2D class. If the object has been * initialized before, the object is reinitialized and memory is freed and reallocated if necessary. * * @param _iProjectionAngleCount Number of projection angles. * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle. * @param _pMatrix Pointer to a CSparseMatrix. The caller is responsible for keeping this matrix valid until it is no longer required. */ bool initialize(int _iProjectionAngleCount, int _iDetectorCount, const CSparseMatrix* _pMatrix); /** Set the associated sparse matrix. The previous one is deleted. * * @param _pMatrix Pointer to a CSparseMatrix. The caller is responsible for keeping this matrix valid until it is no longer required. * @return initialization successful? */ bool setMatrix(CSparseMatrix* _pMatrix); /** Get a pointer to the associated sparse matrix. * @return the associated sparse matrix */ const CSparseMatrix* getMatrix() const { return m_pMatrix; } /** Create a hard copy. */ virtual CProjectionGeometry2D* clone() const override; /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CProjectionGeometry2D&) const override; /** Returns true if the type of geometry defined in this class is the one specified in _sType. * * @param _sType geometry type to compare to. * @return true if _sType == "parallel". */ virtual bool isOfType(const std::string& _sType); /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const; protected: /** Check this object. * * @return object initialized */ bool _check(); const CSparseMatrix* m_pMatrix; }; } // namespace astra #endif /* _INC_ASTRA_SPARSEMATRIXPROJECTIONGEOMETRY2D */ astra-toolbox-2.3.0/include/astra/SparseMatrixProjector2D.h000066400000000000000000000164501475635207100237400ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_SPARSEMATRIXPROJECTOR2D #define _INC_ASTRA_SPARSEMATRIXPROJECTOR2D #include "SparseMatrixProjectionGeometry2D.h" #include "SparseMatrix.h" #include "Float32Data2D.h" #include "Projector2D.h" namespace astra { /** This class implements a two-dimensional projector using a projection geometry defined by an arbitrary sparse matrix. * * \par XML Configuration * \astra_xml_item{ProjectionGeometry, xml node, The geometry of the projection.} * \astra_xml_item{VolumeGeometry, xml node, The geometry of the volume.} * * \par MATLAB example * \astra_code{ * cfg = astra_struct('sparse_matrix');\n * cfg.ProjectionGeometry = proj_geom;\n * cfg.VolumeGeometry = vol_geom;\n * proj_id = astra_mex_projector('create'\, cfg);\n * } */ class _AstraExport CSparseMatrixProjector2D : public CProjector2D { protected: /** Initial clearing. Only to be used by constructors. */ virtual void _clear(); /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - no NULL pointers * - all sub-objects are initialized properly * - matrix dimensions match volume geometry */ virtual bool _check(); public: // type of the projector, needed to register with CProjectorFactory static inline const char* const type = "sparse_matrix"; /** Default constructor. */ CSparseMatrixProjector2D(); /** Constructor. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. */ CSparseMatrixProjector2D(const CSparseMatrixProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); /** Destructor, is virtual to show that we are aware subclass destructor are called. */ ~CSparseMatrixProjector2D(); /** Initialize the projector with a config object. * * @param _cfg Configuration Object * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialize the projector. * * @param _pProjectionGeometry Information class about the geometry of the projection. Will be HARDCOPIED. * @param _pReconstructionGeometry Information class about the geometry of the reconstruction volume. Will be HARDCOPIED. * @return initialization successful? */ bool initialize(const CSparseMatrixProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry); /** Clear this class. */ virtual void clear(); /** Returns the number of weights required for storage of all weights of one projection. * * @param _iProjectionIndex Index of the projection (zero-based). * @return Size of buffer (given in SPixelWeight elements) needed to store weighted pixels. */ virtual int getProjectionWeightsCount(int _iProjectionIndex); /** Compute the pixel weights for a single ray, from the source to a detector pixel. * * @param _iProjectionIndex Index of the projection * @param _iDetectorIndex Index of the detector pixel * @param _pWeightedPixels Pointer to a pre-allocated array, consisting of _iMaxPixelCount elements * of type SPixelWeight. On return, this array contains a list of the index * and weight for all pixels on the ray. * @param _iMaxPixelCount Maximum number of pixels (and corresponding weights) that can be stored in _pWeightedPixels. * This number MUST be greater than the total number of pixels on the ray. * @param _iStoredPixelCount On return, this variable contains the total number of pixels on the * ray (that have been stored in the list _pWeightedPixels). */ virtual void computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount); /** Policy-based projection of all rays. This function will calculate each non-zero projection * weight and use this value for a task provided by the policy object. * * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void project(Policy& _policy); /** Policy-based projection of all rays of a single projection. This function will calculate * each non-zero projection weight and use this value for a task provided by the policy object. * * @param _iProjection Wwhich projection should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleProjection(int _iProjection, Policy& _policy); /** Policy-based projection of a single ray. This function will calculate each non-zero * projection weight and use this value for a task provided by the policy object. * * @param _iProjection Which projection should be projected? * @param _iDetector Which detector should be projected? * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleRay(int _iProjection, int _iDetector, Policy& _policy); /** Policy-based voxel-projection of a single pixel. This function will calculate * each non-zero projection weight and use this value for a task provided by the policy object. * * @param _iRow * @param _iCol * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectSingleVoxel(int _iRow, int _iCol, Policy& _policy) {} /** Policy-based voxel-projection of all voxels. This function will calculate * each non-zero projection weight and use this value for a task provided by the policy object. * * @param _policy Policy object. Should contain prior, addWeight and posterior function. */ template void projectAllVoxels(Policy& _policy) {} protected: /** Return the type of this projector. * * @return identification type of this projector */ virtual std::string getType(); }; //---------------------------------------------------------------------------------------- inline std::string CSparseMatrixProjector2D::getType() { return type; } } // namespace astra #endif astra-toolbox-2.3.0/include/astra/SparseMatrixProjector2D.inl000066400000000000000000000054001475635207100242640ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ //---------------------------------------------------------------------------------------- // PROJECT ALL template void CSparseMatrixProjector2D::project(Policy& p) { ASTRA_ASSERT(m_bIsInitialized); for (int i = 0; i < m_pProjectionGeometry->getProjectionAngleCount(); ++i) for (int j = 0; j < m_pProjectionGeometry->getDetectorCount(); ++j) projectSingleRay(i, j, p); } //---------------------------------------------------------------------------------------- // PROJECT SINGLE PROJECTION template void CSparseMatrixProjector2D::projectSingleProjection(int _iProjection, Policy& p) { ASTRA_ASSERT(m_bIsInitialized); for (int j = 0; j < m_pProjectionGeometry->getDetectorCount(); ++j) projectSingleRay(_iProjection, j, p); } //---------------------------------------------------------------------------------------- // PROJECT SINGLE RAY template void CSparseMatrixProjector2D::projectSingleRay(int _iProjection, int _iDetector, Policy& p) { ASTRA_ASSERT(m_bIsInitialized); int iRayIndex = _iProjection * m_pProjectionGeometry->getDetectorCount() + _iDetector; const CSparseMatrix* pMatrix = dynamic_cast(m_pProjectionGeometry)->getMatrix(); // POLICY: RAY PRIOR if (!p.rayPrior(iRayIndex)) return; const unsigned int* piColIndices; const float32* pfValues; unsigned int iSize; pMatrix->getRowData(iRayIndex, iSize, pfValues, piColIndices); for (unsigned int i = 0; i < iSize; ++i) { unsigned int iVolumeIndex = piColIndices[i]; // POLICY: PIXEL PRIOR if (p.pixelPrior(iVolumeIndex)) { // POLICY: ADD p.addWeight(iRayIndex, iVolumeIndex, pfValues[i]); // POLICY: PIXEL POSTERIOR p.pixelPosterior(iVolumeIndex); } } // POLICY: RAY POSTERIOR p.rayPosterior(iRayIndex); } astra-toolbox-2.3.0/include/astra/TypeList.h000066400000000000000000000031631475635207100210120ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_TYPELIST #define _INC_ASTRA_TYPELIST #include "Globals.h" #include namespace astra { template struct TypeList { }; template Base* createObject_internal(const std::string &name) { if (name == T::type) return new T(); if constexpr (sizeof...(Ts) > 0) return createObject_internal(name); else return nullptr; } template Base* createObject(const std::string &name, TypeList) { if constexpr (sizeof...(Ts) > 0) return createObject_internal(name); else return nullptr; } } // end namespace astra #endif astra-toolbox-2.3.0/include/astra/Utilities.h000066400000000000000000000061341475635207100212110ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_UTILIES #define _INC_ASTRA_UTILIES #include #include #include #include #include "Globals.h" namespace astra { namespace StringUtil { // Exception thrown by functions below class bad_cast : public std::exception { public: bad_cast() { } }; //< Format a string, returning it as a std::string _AstraExport std::string vformat(const char *fmt, va_list ap); //< Format a string, returning it as a std::string _AstraExport std::string format(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2); //< Parse string as int. //< Throw exception on failure. _AstraExport int stringToInt(const std::string& s); //< Parse string as int. //< Return fallback on failure. _AstraExport int stringToInt(const std::string& s, int fallback); //< Parse string as float. //< Throw exception on failure. _AstraExport float stringToFloat(const std::string& s); //< Parse string as double. //< Throw exception on failure. _AstraExport double stringToDouble(const std::string& s); template _AstraExport T stringTo(const std::string& s); //< Parse comma/semicolon-separated string as float vector. //< Throw exception on failure. _AstraExport std::vector stringToFloatVector(const std::string& s); //< Parse comma/semicolon-separated string as double vector. //< Throw exception on failure. _AstraExport std::vector stringToDoubleVector(const std::string& s); template _AstraExport std::vector stringToVector(const std::string& s); //< Generate string from float. _AstraExport std::string floatToString(float f); //< Generate string from double. _AstraExport std::string doubleToString(double f); template _AstraExport std::string toString(T f); _AstraExport void splitString(std::vector &items, const std::string& s, const char *delim); } template std::map mergeMap(std::map _mMap1, std::map _mMap2) { std::map result = _mMap1; for (typename std::map::iterator it = _mMap2.begin(); it != _mMap2.end(); it++) { result[(*it).first] = (*it).second; } return result; } } // end namespace #endif astra-toolbox-2.3.0/include/astra/Vector3D.h000066400000000000000000000047141475635207100206710ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_VECTOR3D #define _INC_ASTRA_VECTOR3D #include "Globals.h" namespace astra { /** * This class defines a three-dimensional vector type. */ class CVector3D { float32 m_fX; ///< X Coordinate float32 m_fY; ///< Y Coordinate float32 m_fZ; ///< Z Coordinate public: /** * Default constructor */ CVector3D(); /** * Constructor initializing member variables */ CVector3D(float32 _fX, float32 _fY, float32 _fZ); /** * Returns the X-coordinate stored in this vector */ float32 getX() const; /** * Returns the Y-coordinate stored in this vector */ float32 getY() const; /** * Returns the Z-coordinate stored in this vector */ float32 getZ() const; /** * Sets the X-coordinate stored in this vector */ void setX(float32 _fX); /** * Sets the X-coordinate stored in this vector */ void setY(float32 _fY); /** * Sets the X-coordinate stored in this vector */ void setZ(float32 _fZ); }; inline CVector3D::CVector3D() { m_fX = m_fY = m_fZ = 0.0f; } inline CVector3D::CVector3D(float32 _fX, float32 _fY, float32 _fZ) { m_fX = _fX; m_fY = _fY; m_fZ = _fZ; } inline float32 CVector3D::getX() const { return m_fX; } inline float32 CVector3D::getY() const { return m_fY; } inline float32 CVector3D::getZ() const { return m_fZ; } inline void CVector3D::setX(float32 _fX) { m_fX = _fX; } inline void CVector3D::setY(float32 _fY) { m_fY = _fY; } inline void CVector3D::setZ(float32 _fZ) { m_fZ = _fZ; } } #endif /* _INC_ASTRA_VECTOR3D */ astra-toolbox-2.3.0/include/astra/VolumeGeometry2D.h000066400000000000000000000471441475635207100224150ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_VOLUMEGEOMETRY2D #define _INC_ASTRA_VOLUMEGEOMETRY2D #include "Globals.h" #include "Config.h" namespace astra { /** * This class represents a pixel grid that is placed in the geometry. It defines a rectangular volume window. * * \par XML Configuration * \astra_xml_item{GridColCount, integer, Number of columns in this geometry.} * \astra_xml_item{GridRowCount, integer, Number of rows in this geometry.} * \astra_xml_item_option{WindowMinX, float, Minimal X-coordinate in the volume window.} * \astra_xml_item_option{WindowMaxX, float, Maximal X-coordinate in the volume window.} * \astra_xml_item_option{WindowMinY, float, Minimal Y-coordinate in the volume window.} * \astra_xml_item_option{WindowMaxY, float, Maximal Y-coordinate in the volume window.} * * \par MATLAB example * \astra_code{ * vol_geom = struct();\n * vol_geom.GridColCount = 1024;\n * vol_geom.GridRowCount = 768;\n * vol_geom.option.WindowMinX = -512;\n * vol_geom.option.WindowMaxX = -384;\n * vol_geom.option.WindowMinY = 512;\n * vol_geom.option.WindowMaxY = 384;\n * } */ class _AstraExport CVolumeGeometry2D { protected: bool m_bInitialized; ///< Has this object been initialized? int m_iGridColCount; ///< number of columns in the volume grid. int m_iGridRowCount; ///< number of rows in the volume grid. int m_iGridTotCount; ///< total number of pixels in the volume grid (= m_iGridColCount * m_iGridRowCount). /** Width of the volume window, in unit lengths. * * Note that this width is independent of the number of pixels in the X-direction, as the width of a pixel can * be different from 1. */ float32 m_fWindowLengthX; /** Height of the volume window, in unit lengths. * * Note that this height is independent of the number of pixels in the Y-direction, as the height of a pixel can * be different from 1. */ float32 m_fWindowLengthY; float32 m_fWindowArea; ///< Total area of the volume window, in unit lengths squared. float32 m_fPixelLengthX; ///< Width of a single pixel, in unit lengths. float32 m_fPixelLengthY; ///< Height of a single pixel, in unit lengths. float32 m_fPixelArea; ///< Area of a single pixel, in unit lengths squared. float32 m_fDivPixelLengthX; ///< 1/m_fPixelLengthX, used for fast division. float32 m_fDivPixelLengthY; ///< 1/m_fPixelLengthY, used for fast division. float32 m_fWindowMinX; ///< Minimal X-coordinate in the volume window. float32 m_fWindowMinY; ///< Maximal X-coordinate in the volume window. float32 m_fWindowMaxX; ///< Minimal Y-coordinate in the volume window. float32 m_fWindowMaxY; ///< Maximal Y-coordinate in the volume window. /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - number of rows and columns is larger than zero * - window minima is smaller than window maxima * - m_iGridTotCount, m_fWindowLengthX, m_fWindowLengthY, m_fWindowArea, m_fPixelLengthX, * m_fPixelLengthY, m_fPixelArea, m_fDivPixelLengthX and m_fDivPixelLengthY are initialized ok */ bool _check(); /** Calculate values of all member variables from m_iGridRow/ColCount, m_fWindow* */ void _calculateDependents(); public: /** Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the init() methods before the object can be used. Any use before calling init() is not allowed, * except calling the member function isInitialized(). */ CVolumeGeometry2D(); /** Constructor. Create an instance of the CVolumeGeometry2D class. * The minimal and coordinates values of the geometry will be set to -/+ the number of rows/columns. * * @param _iGridCountX Number of columns in the volume grid. * @param _iGridCountY Number of rows in the volume grid. */ CVolumeGeometry2D(int _iGridCountX, int _iGridCountY); /** Constructor. Create an instance of the CVolumeGeometry2D class. * * @param _iGridCountX Number of columns in the volume grid. * @param _iGridCountY Number of rows in the volume grid. * @param _fWindowMinX Minimal X-coordinate in the volume window. * @param _fWindowMinY Minimal Y-coordinate in the volume window. * @param _fWindowMaxX Maximal X-coordinate in the volume window. * @param _fWindowMaxY Maximal Y-coordinate in the volume window. */ CVolumeGeometry2D(int _iGridCountX, int _iGridCountY, float32 _fWindowMinX, float32 _fWindowMinY, float32 _fWindowMaxX, float32 _fWindowMaxY); /** Destructor. */ virtual ~CVolumeGeometry2D(); /** Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. */ void clear(); /** Create a hard copy. */ CVolumeGeometry2D* clone() const; /** Initialize the volume geometry with a config object. * * @param _cfg Configuration Object. * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialization. Initializes an instance of the CVolumeGeometry2D class. * The minimal and maximal coordinates of the geometry will be set to -/+ half the number of rows/columns. * * If the object has been initialized before, the object is reinitialized and * memory is freed and reallocated if necessary. * * @param _iGridColCount Number of columns in the volume grid. * @param _iGridRowCount Number of rows in the volume grid. * @return initialization successful */ bool initialize(int _iGridColCount, int _iGridRowCount); /** Initialization. Initializes an instance of the CVolumeGeometry2D class. * * If the object has been initialized before, the object is reinitialized and * memory is freed and reallocated if necessary. * * @param _iGridColCount Number of columns in the volume grid. * @param _iGridRowCount Number of rows in the volume grid. * @param _fWindowMinX Minimal X-coordinate in the volume window. * @param _fWindowMinY Minimal Y-coordinate in the volume window. * @param _fWindowMaxX Maximal X-coordinate in the volume window. * @param _fWindowMaxY Maximal Y-coordinate in the volume window. * @return initialization successful */ bool initialize(int _iGridColCount, int _iGridRowCount, float32 _fWindowMinX, float32 _fWindowMinY, float32 _fWindowMaxX, float32 _fWindowMaxY); /** Get the initialization state of the object. * * @return true iff the object has been initialized. */ bool isInitialized() const; /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CVolumeGeometry2D&) const; /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const; /** Get the number of columns in the volume grid. * * @return Number of columns in the volume grid. */ int getGridColCount() const; /** Get the number of rows in the volume grid. * * @return Number of rows in the volume grid. */ int getGridRowCount() const; /** Get the total number of pixels in the volume grid. * * @return Total number of pixels. */ int getGridTotCount() const; /** Get the horizontal length of the volume window, in unit lengths. * * @return Horizontal length of the volume window. */ float32 getWindowLengthX() const; /** Get the vertical length of the volume window, in unit lengths. * * @return Vertical length of the volume window. */ float32 getWindowLengthY() const; /** Get the total area of the volume window, in unit lengths squared. * * @return Total area of the volume window. */ float32 getWindowArea() const; /** Get the horizontal length of a single pixel (i.e., width), in unit lengths. * * @return Horizontal length of a single pixel. */ float32 getPixelLengthX() const; /** Get the vertical length of a single pixel (i.e., height), in unit lengths. * * @return Vertical length of a single pixel. */ float32 getPixelLengthY() const; /** Get the area of a single pixel (width*height), in unit lengths squared. * * @return Area of a single pixel. */ float32 getPixelArea() const; /** Get the minimal X-coordinate in the volume window. * * @return Minimal X-coordinate in the volume window. */ float32 getWindowMinX() const; /** Get the minimal Y-coordinate in the volume window. * * @return Minimal Y-coordinate in the volume window. */ float32 getWindowMinY() const; /** Get the maximal X-coordinate in the volume window. * * @return Maximal X-coordinate in the volume window. */ float32 getWindowMaxX() const; /** Get the maximal Y-coordinate in the volume window. * * @return Maximal Y-coordinate in the volume window. */ float32 getWindowMaxY() const; /** Convert column and row index of a pixel to a single index in the interval [0..getGridTotCount()-1]. * * @param _iPixelCol Column index of the pixel, in the interval [0..getGridColCount()-1]. * @param _iPixelRow Row index of the pixel, in the interval [0..getGridRowCount()-1]. * @return Computed index of the pixel, in the interval [0..getGridTotCount()-1]. */ int pixelRowColToIndex(int _iPixelRow, int _iPixelCol) const; /** Convert a pixel index (from the interval [0..getGridTotCount()-1] to a column and row index. * * @param _iPixelIndex Index of the pixel, in the interval [0..getGridTotCount()-1]. * @param _iPixelRow Computed row index of the pixel, in the interval [0..getGridRowCount()-1]. * @param _iPixelCol Computed column index of the pixel, in the interval [0..getGridColCount()-1]. */ void pixelIndexToRowCol(int _iPixelIndex, int &_iPixelRow, int &_iPixelCol) const; /** Convert a pixel column index to the X-coordinate of its center. * * @param _iPixelCol Column index of the pixel. * @return X-coordinate of the pixel center. */ float32 pixelColToCenterX(int _iPixelCol) const; /** Convert a pixel column index to the minimum X-coordinate of points in that column. * * @param _iPixelCol Column index of the pixel. * @return Minimum X-coordinate. */ float32 pixelColToMinX(int _iPixelCol) const; /** Convert a pixel column index to the maximum X-coordinate of points in that column. * * @param _iPixelCol Column index of the pixel. * @return Maximum X-coordinate. */ float32 pixelColToMaxX(int _iPixelCol) const; /** Convert a pixel row index to the Y-coordinate of its center. * * @param _iPixelRow Row index of the pixel. * @return Y-coordinate of the pixel center. */ float32 pixelRowToCenterY(int _iPixelRow) const; /** Convert a pixel row index to the minimum Y-coordinate of points in that row. * * @param _iPixelRow Row index of the pixel. * @return Minimum Y-coordinate. */ float32 pixelRowToMinY(int _iPixelRow) const; /** Convert a pixel row index to the maximum Y-coordinate of points in that row. * * @param _iPixelRow Row index of the pixel. * @return Maximum Y-coordinate. */ float32 pixelRowToMaxY(int _iPixelRow) const; /** Convert an X-coordinate to a column index in the volume grid. * * @param _fCoordX X-coordinate. * @return If the X-coordinate falls within a column of the volume grid, the column index is returned. * Otherwise, a value of -1 is returned. */ int coordXToCol(float32 _fCoordX) const; /** Convert a Y-coordinate to a row index in the volume grid. * * @param _fCoordY Y-coordinate * @return If the Y-coordinate falls within a row of the volume grid, the row index is returned. * Otherwise, a value of -1 is returned. */ int coordYToRow(float32 _fCoordY) const; /** Convert an X-coordinate to an offset in the volume grid. * WindowMinX is converted to 0. * * @param _fCoordX X-coordinate. * @return The corresponding offset in the volume grid */ float coordXToColF(float32 _fCoordX) const; /** Convert a Y-coordinate to an offset in the volume grid. * WindowMaxY is converted to 0. * * @param _fCoordY Y-coordinate * @return The corresponding offset in the volume grid */ float coordYToRowF(float32 _fCoordY) const; private: //< For Config unused argument checking ConfigCheckData* configCheckData; friend class ConfigReader; }; // Get the initialization state of the object. inline bool CVolumeGeometry2D::isInitialized() const { return m_bInitialized; } // Get the number of columns in the volume grid. inline int CVolumeGeometry2D::getGridColCount() const { ASTRA_ASSERT(m_bInitialized); return m_iGridColCount; } // Get the number of rows in the volume grid. inline int CVolumeGeometry2D::getGridRowCount() const { ASTRA_ASSERT(m_bInitialized); return m_iGridRowCount; } // Get the total number of pixels in the volume window. inline int CVolumeGeometry2D::getGridTotCount() const { ASTRA_ASSERT(m_bInitialized); return m_iGridTotCount; } // Get the horizontal length of the volume window, in unit lengths. inline float32 CVolumeGeometry2D::getWindowLengthX() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowLengthX; } // Get the vertical length of the volume window, in unit lengths. inline float32 CVolumeGeometry2D::getWindowLengthY() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowLengthY; } // Get the total area of the volume window, in unit lengths squared. inline float32 CVolumeGeometry2D::getWindowArea() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowArea; } // Get the horizontal length of a single pixel (i.e., width), in unit lengths. inline float32 CVolumeGeometry2D::getPixelLengthX() const { ASTRA_ASSERT(m_bInitialized); return m_fPixelLengthX; } // Get the vertical length of a single pixel (i.e., height), in unit lengths. inline float32 CVolumeGeometry2D::getPixelLengthY() const { ASTRA_ASSERT(m_bInitialized); return m_fPixelLengthY; } // Get the area of a single pixel (width*height), in unit lengths squared. inline float32 CVolumeGeometry2D::getPixelArea() const { ASTRA_ASSERT(m_bInitialized); return m_fPixelArea; } // Get the minimal X-coordinate in the volume window. inline float32 CVolumeGeometry2D::getWindowMinX() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowMinX; } // Get the minimal Y-coordinate in the volume window. inline float32 CVolumeGeometry2D::getWindowMinY() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowMinY; } // Get the maximal X-coordinate in the volume window. inline float32 CVolumeGeometry2D::getWindowMaxX() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowMaxX; } // Get the maximal Y-coordinate in the volume window. inline float32 CVolumeGeometry2D::getWindowMaxY() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowMaxY; } // Convert column and row index of a pixel to a single index in the interval [0..getGridCountTot()-1]. inline int CVolumeGeometry2D::pixelRowColToIndex(int _iPixelRow, int _iPixelCol) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelCol >= 0); ASTRA_ASSERT(_iPixelCol < m_iGridColCount); ASTRA_ASSERT(_iPixelRow >= 0); ASTRA_ASSERT(_iPixelRow < m_iGridRowCount); return (_iPixelRow * m_iGridColCount + _iPixelCol); } // Convert a pixel index (from the interval [0..getGridCountTot()-1] to a column and row index. inline void CVolumeGeometry2D::pixelIndexToRowCol(int _iPixelIndex, int &_iPixelRow, int &_iPixelCol) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelIndex >= 0); ASTRA_ASSERT(_iPixelIndex < m_iGridTotCount); _iPixelCol = (_iPixelIndex % m_iGridColCount); _iPixelRow = (_iPixelIndex / m_iGridColCount); } // Convert a pixel column index to the X-coordinate of its center inline float32 CVolumeGeometry2D::pixelColToCenterX(int _iPixelCol) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelCol >= 0); ASTRA_ASSERT(_iPixelCol < m_iGridColCount); return (m_fWindowMinX + (float32(_iPixelCol) + 0.5f) * m_fPixelLengthX); } // Convert a pixel column index to the minimum X-coordinate of points in that column inline float32 CVolumeGeometry2D::pixelColToMinX(int _iPixelCol) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelCol >= 0); ASTRA_ASSERT(_iPixelCol < m_iGridColCount); return (m_fWindowMinX + float32(_iPixelCol) * m_fPixelLengthX); } // Convert a pixel column index to the maximum X-coordinate of points in that column inline float32 CVolumeGeometry2D::pixelColToMaxX(int _iPixelCol) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelCol >= 0); ASTRA_ASSERT(_iPixelCol < m_iGridColCount); return (m_fWindowMinX + (float32(_iPixelCol) + 1.0f) * m_fPixelLengthX); } // Convert a pixel row index to the Y-coordinate of its center inline float32 CVolumeGeometry2D::pixelRowToCenterY(int _iPixelRow) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelRow >= 0); ASTRA_ASSERT(_iPixelRow < m_iGridRowCount); return (m_fWindowMaxY - (float32(_iPixelRow) + 0.5f) * m_fPixelLengthY); } // Convert a pixel row index to the minimum Y-coordinate of points in that row inline float32 CVolumeGeometry2D::pixelRowToMinY(int _iPixelRow) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelRow >= 0); ASTRA_ASSERT(_iPixelRow < m_iGridRowCount); return (m_fWindowMaxY - (float32(_iPixelRow) + 1.0f) * m_fPixelLengthY); } // Convert a pixel row index to the maximum Y-coordinate of points in that row inline float32 CVolumeGeometry2D::pixelRowToMaxY(int _iPixelRow) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelRow >= 0); ASTRA_ASSERT(_iPixelRow < m_iGridRowCount); return (m_fWindowMaxY - (float32(_iPixelRow) * m_fPixelLengthY)); } // Convert an X-coordinate to a column index in the volume grid inline int CVolumeGeometry2D::coordXToCol(float32 _fCoordX) const { if (_fCoordX < m_fWindowMinX) return -1; if (_fCoordX > m_fWindowMaxX) return -1; int iCol = int((_fCoordX - m_fWindowMinX) * m_fDivPixelLengthX); ASTRA_ASSERT(iCol >= 0); ASTRA_ASSERT(iCol < m_iGridColCount); return iCol; } // Convert a Y-coordinate to a row index in the volume grid inline int CVolumeGeometry2D::coordYToRow(float32 _fCoordY) const { if (_fCoordY < m_fWindowMinY) return -1; if (_fCoordY > m_fWindowMaxY) return -1; int iRow = int((m_fWindowMaxY - _fCoordY) * m_fDivPixelLengthY); ASTRA_ASSERT(iRow >= 0); ASTRA_ASSERT(iRow < m_iGridRowCount); return iRow; } // Convert an X-coordinate to an offset in the volume grid // (WindowMinX is converted to 0) inline float CVolumeGeometry2D::coordXToColF(float32 _fCoordX) const { return (_fCoordX - m_fWindowMinX) * m_fDivPixelLengthX; } // Convert a Y-coordinate to an offset in the volume grid // (WindowMaxY is converted to 0) inline float CVolumeGeometry2D::coordYToRowF(float32 _fCoordY) const { return (m_fWindowMaxY - _fCoordY) * m_fDivPixelLengthY; } } // end namespace astra #endif /* _INC_ASTRA_VOLUMEGEOMETRY2D */ astra-toolbox-2.3.0/include/astra/VolumeGeometry3D.h000066400000000000000000000740571475635207100224210ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_VOLUMEGEOMETRY3D #define _INC_ASTRA_VOLUMEGEOMETRY3D #include "Globals.h" #include "Config.h" #include "VolumeGeometry2D.h" namespace astra { /** * This class represents a 3D pixel grid that is placed in the geometry. It defines a rectangular volume window. * * \par XML Configuration * \astra_xml_item{GridColCount, integer, Number of columns in this geometry.} * \astra_xml_item{GridRowCount, integer, Number of rows in this geometry.} * \astra_xml_item{GridSliceCount, integer, Number of slices in this geometry.} * \astra_xml_item_option{WindowMinX, float, -GridColCount/2, Minimal X-coordinate in the volume window.} * \astra_xml_item_option{WindowMaxX, float, GridColCount/2, Maximal X-coordinate in the volume window.} * \astra_xml_item_option{WindowMinY, float, -GridRowCount/2, Minimal Y-coordinate in the volume window.} * \astra_xml_item_option{WindowMaxY, float, GridRowCount/2, Maximal Y-coordinate in the volume window.} * \astra_xml_item_option{WindowMinZ, float, -GridSliceCount/2, Minimal Z-coordinate in the volume window.} * \astra_xml_item_option{WindowMaxZ, float, GridSliceCount/2, Maximal Z-coordinate in the volume window.} * * \par MATLAB example * \astra_code{ * vol_geom = struct();\n * vol_geom.GridColCount = 1024;\n * vol_geom.GridRowCount = 768;\n * vol_geom.GridSliceCount = 300;\n * vol_geom.option.WindowMinX = -512;\n * vol_geom.option.WindowMaxX = -384;\n * vol_geom.option.WindowMinY = 512;\n * vol_geom.option.WindowMaxY = 384;\n * vol_geom.option.WindowMinZ = -150;\n * vol_geom.option.WindowMaxZ = 150;\n * } */ class _AstraExport CVolumeGeometry3D { protected: bool m_bInitialized; ///< Has this object been initialized? int m_iGridColCount; ///< number of columns in the volume grid. int m_iGridRowCount; ///< number of rows in the volume grid. int m_iGridSliceCount; ///< number of slices in the volume grid. size_t m_iGridTotCount; ///< total number of pixels in the volume grid (= m_iGridColCount * m_iGridRowCount * m_iGridSliceCount). /** Width of the volume window, in unit lengths. * * Note that this width is independent of the number of pixels in the X-direction, as the width of a pixel can * be different from 1. */ float32 m_fWindowLengthX; /** Height of the volume window, in unit lengths. * * Note that this height is independent of the number of pixels in the Y-direction, as the height of a pixel can * be different from 1. */ float32 m_fWindowLengthY; /** Depth of the volume window, in unit lengths. * * Note that this depth is independent of the number of pixels in the Z-direction, as the depth of a pixel can * be different from 1. */ float32 m_fWindowLengthZ; /** Total area of the volume window, in unit lengths squared. */ float32 m_fWindowArea; float32 m_fPixelLengthX; ///< Width of a single pixel, in unit lengths. float32 m_fPixelLengthY; ///< Height of a single pixel, in unit lengths. float32 m_fPixelLengthZ; ///< Depth of a single pixel, in unit lengths. float32 m_fPixelArea; ///< Area of a single pixel, in unit lengths squared. float32 m_fDivPixelLengthX; ///< 1/m_fPixelLengthX, used for fast division. float32 m_fDivPixelLengthY; ///< 1/m_fPixelLengthY, used for fast division. float32 m_fDivPixelLengthZ; ///< 1/m_fPixelLengthZ, used for fast division. float32 m_fWindowMinX; ///< Minimal X-coordinate in the volume window. float32 m_fWindowMinY; ///< Minimal Y-coordinate in the volume window. float32 m_fWindowMinZ; ///< Minimal Z-coordinate in the volume window. float32 m_fWindowMaxX; ///< Maximal X-coordinate in the volume window. float32 m_fWindowMaxY; ///< Maximal Y-coordinate in the volume window. float32 m_fWindowMaxZ; ///< Maximal Z-coordinate in the volume window. /** Check the values of this object. If everything is ok, the object can be set to the initialized state. * The following statements are then guaranteed to hold: * - number of rows, columns and slices is larger than zero * - window minima is smaller than window maxima * - m_iGridTotCount, m_fWindowLengthX, m_fWindowLengthY, m_fWindowLengthZ, m_fWindowArea, m_fPixelLengthX, * m_fPixelLengthY, m_fPixelLengthZ, m_fPixelArea, m_fDivPixelLengthX, m_fDivPixelLengthY * and m_fDivPixelLengthZ are initialized ok */ bool _check(); public: /** Default constructor. Sets all numeric member variables to 0 and all pointer member variables to NULL. * * If an object is constructed using this default constructor, it must always be followed by a call * to one of the init() methods before the object can be used. Any use before calling init() is not allowed, * except calling the member function isInitialized(). */ CVolumeGeometry3D(); /** Constructor. Create an instance of the CVolumeGeometry2D class. * The minimal and coordinates values of the geometry will be set to -/+ the number of rows/columns. * * @param _iGridCountX Number of columns in the volume grid. * @param _iGridCountY Number of rows in the volume grid. * @param _iGridCountZ Number of slices in the volume grid. */ CVolumeGeometry3D(int _iGridCountX, int _iGridCountY, int _iGridCountZ); /** Constructor. Create an instance of the CVolumeGeometry2D class. * * @param _iGridCountX Number of columns in the volume grid. * @param _iGridCountY Number of rows in the volume grid. * @param _iGridCountZ Number of slices in the volume grid. * @param _fWindowMinX Minimal X-coordinate in the volume window. * @param _fWindowMinY Minimal Y-coordinate in the volume window. * @param _fWindowMinZ Minimal Z-coordinate in the volume window. * @param _fWindowMaxX Maximal X-coordinate in the volume window. * @param _fWindowMaxY Maximal Y-coordinate in the volume window. * @param _fWindowMaxZ Maximal Z-coordinate in the volume window. */ CVolumeGeometry3D(int _iGridCountX, int _iGridCountY, int _iGridCountZ, float32 _fWindowMinX, float32 _fWindowMinY, float32 _fWindowMinZ, float32 _fWindowMaxX, float32 _fWindowMaxY, float32 _fWindowMaxZ); /** * Copy constructor */ CVolumeGeometry3D(const CVolumeGeometry3D& _other); /** * Assignment operator */ CVolumeGeometry3D& operator=(const CVolumeGeometry3D& _other); /** Destructor. */ virtual ~CVolumeGeometry3D(); /** Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. */ void clear(); /** Create a hard copy. */ CVolumeGeometry3D* clone() const; /** Initialize the volume geometry with a config object. * * @param _cfg Configuration Object. * @return initialization successful? */ virtual bool initialize(const Config& _cfg); /** Initialization. Initializes an instance of the CVolumeGeometry3D class. * The minimal and maximal coordinates of the geometry will be set to -/+ half the number of rows/columns/slices. * * If the object has been initialized before, the object is reinitialized and * memory is freed and reallocated if necessary. * * @param _iGridColCount Number of columns in the volume grid. * @param _iGridRowCount Number of rows in the volume grid. * @param _iGridSliceCount Number of slices in the volume grid. * @return initialization successful */ bool initialize(int _iGridColCount, int _iGridRowCount, int _iGridSliceCount); /** Initialization. Initializes an instance of the CVolumeGeometry3D class. * * If the object has been initialized before, the object is reinitialized and * memory is freed and reallocated if necessary. * * @param _iGridColCount Number of columns in the volume grid. * @param _iGridRowCount Number of rows in the volume grid. * @param _iGridSliceCount Number of slices in the volume grid. * @param _fWindowMinX Minimal X-coordinate in the volume window. * @param _fWindowMinY Minimal Y-coordinate in the volume window. * @param _fWindowMinZ Minimal Z-coordinate in the volume window. * @param _fWindowMaxX Maximal X-coordinate in the volume window. * @param _fWindowMaxY Maximal Y-coordinate in the volume window. * @param _fWindowMaxZ Maximal Z-coordinate in the volume window. * @return initialization successful */ bool initialize(int _iGridColCount, int _iGridRowCount, int _iGridSliceCount, float32 _fWindowMinX, float32 _fWindowMinY, float32 _fWindowMinZ, float32 _fWindowMaxX, float32 _fWindowMaxY, float32 _fWindowMaxZ); /** Get the initialization state of the object. * * @return true iff the object has been initialized. */ bool isInitialized() const; /** Return true if this geometry instance is the same as the one specified. * * @return true if this geometry instance is the same as the one specified. */ virtual bool isEqual(const CVolumeGeometry3D*) const; /** Get all settings in a Config object. * * @return Configuration Object. */ virtual Config* getConfiguration() const; /** Get the number of columns in the volume grid. * * @return Number of columns in the volume grid. */ int getGridColCount() const; /** Get the number of rows in the volume grid. * * @return Number of rows in the volume grid. */ int getGridRowCount() const; /** Get the number of slices in the volume grid. * * @return Number of slices in the volume grid. */ int getGridSliceCount() const; /** Get the total number of pixels in the volume grid. * * @return Total number of pixels. */ size_t getGridTotCount() const; /** Get the horizontal length of the volume window, in unit lengths. * * @return Horizontal length of the volume window. */ float32 getWindowLengthX() const; /** Get the vertical length of the volume window, in unit lengths. * * @return Vertical length of the volume window. */ float32 getWindowLengthY() const; /** Get the depth of the volume window, in unit lengths. * * @return Depth of the volume window. */ float32 getWindowLengthZ() const; /** Get the total area of the volume window, in unit lengths squared. * * @return Total area of the volume window. */ float32 getWindowArea() const; /** Get the horizontal length of a single pixel (i.e., width), in unit lengths. * * @return Horizontal length of a single pixel. */ float32 getPixelLengthX() const; /** Get the vertical length of a single pixel (i.e., height), in unit lengths. * * @return Vertical length of a single pixel. */ float32 getPixelLengthY() const; /** Get the depth of a single pixel in unit lengths. * * @return Depth of a single pixel. */ float32 getPixelLengthZ() const; /** Get the area of a single pixel (width*height*depth), in unit lengths squared. * * @return Area of a single pixel. */ float32 getPixelArea() const; /** Get the minimal X-coordinate in the volume window. * * @return Minimal X-coordinate in the volume window. */ float32 getWindowMinX() const; /** Get the minimal Y-coordinate in the volume window. * * @return Minimal Y-coordinate in the volume window. */ float32 getWindowMinY() const; /** Get the minimal Z-coordinate in the volume window. * * @return Minimal Z-coordinate in the volume window. */ float32 getWindowMinZ() const; /** Get the maximal X-coordinate in the volume window. * * @return Maximal X-coordinate in the volume window. */ float32 getWindowMaxX() const; /** Get the maximal Y-coordinate in the volume window. * * @return Maximal Y-coordinate in the volume window. */ float32 getWindowMaxY() const; /** Get the maximal Z-coordinate in the volume window. * * @return Maximal Z-coordinate in the volume window. */ float32 getWindowMaxZ() const; /** Convert row, column and slice index of a pixel to a single index in the interval [0..getGridTotCount()-1]. * * @param _iPixelRow Row index of the pixel, in the interval [0..getGridRowCount()-1]. * @param _iPixelCol Column index of the pixel, in the interval [0..getGridColCount()-1]. * @param _iPixelSlice Slice index of the pixel, in the interval [0..getGridSliceCount()-1]. * @return Computed index of the pixel, in the interval [0..getGridTotCount()-1]. */ size_t pixelRowColSliceToIndex(int _iPixelRow, int _iPixelCol, int _iPixelSlice) const; /** Convert a pixel index (from the interval [0..getGridTotCount()-1] to row, column and slice index. * * @param _iPixelIndex Index of the pixel, in the interval [0..getGridTotCount()-1]. * @param _iPixelRow Computed row index of the pixel, in the interval [0..getGridRowCount()-1]. * @param _iPixelCol Computed column index of the pixel, in the interval [0..getGridColCount()-1]. * @param _iPixelSlice Computed slice index of the pixel, in the interval [0..getGridSliceCount()-1]. */ void pixelIndexToRowColSlice(size_t _iPixelIndex, int &_iPixelRow, int &_iPixelCol, int &_iPixelSlice) const; /** Convert a pixel column index to the X-coordinate of its center. * * @param _iPixelCol Column index of the pixel. * @return X-coordinate of the pixel center. */ float32 pixelColToCenterX(int _iPixelCol) const; /** Convert a pixel column index to the minimum X-coordinate of points in that column. * * @param _iPixelCol Column index of the pixel. * @return Minimum X-coordinate. */ float32 pixelColToMinX(int _iPixelCol) const; /** Convert a pixel column index to the maximum X-coordinate of points in that column. * * @param _iPixelCol Column index of the pixel. * @return Maximum X-coordinate. */ float32 pixelColToMaxX(int _iPixelCol) const; /** Convert a pixel row index to the Y-coordinate of its center. * * @param _iPixelRow Row index of the pixel. * @return Y-coordinate of the pixel center. */ float32 pixelRowToCenterY(int _iPixelRow) const; /** Convert a pixel row index to the minimum Y-coordinate of points in that row. * * @param _iPixelRow Row index of the pixel. * @return Minimum Y-coordinate. */ float32 pixelRowToMinY(int _iPixelRow) const; /** Convert a pixel row index to the maximum Y-coordinate of points in that row. * * @param _iPixelRow Row index of the pixel. * @return Maximum Y-coordinate. */ float32 pixelRowToMaxY(int _iPixelRow) const; /** Convert a pixel slice index to the Z-coordinate of its center. * * @param _iPixelSlice Slice index of the pixel. * @return Z-coordinate of the pixel center. */ float32 pixelSliceToCenterZ(int _iPixelSlice) const; /** Convert a pixel slice index to the minimum Z-coordinate of points in that slice. * * @param _iPixelSlice Slice index of the pixel. * @return Minimum Z-coordinate. */ float32 pixelSliceToMinZ(int _iPixelSlice) const; /** Convert a pixel slice index to the maximum Z-coordinate of points in that slice. * * @param _iPixelSlice Slice index of the pixel. * @return Maximum Z-coordinate. */ float32 pixelSliceToMaxZ(int _iPixelSlice) const; /** Convert an X-coordinate to a column index in the volume grid. * * @param _fCoordX X-coordinate. * @return If the X-coordinate falls within a column of the volume grid, the column index is returned. * Otherwise, a value of -1 is returned. */ int coordXToCol(float32 _fCoordX) const; /** Convert a Y-coordinate to a row index in the volume grid. * * @param _fCoordY Y-coordinate * @return If the Y-coordinate falls within a row of the volume grid, the row index is returned. * Otherwise, a value of -1 is returned. */ int coordYToRow(float32 _fCoordY) const; /** Convert a Z-coordinate to a slice index in the volume grid. * * @param _fCoordZ Z-coordinate * @return If the Z-coordinate falls within a slice of the volume grid, the slice index is returned. * Otherwise, a value of -1 is returned. */ int coordZToSlice(float32 _fCoordZ) const; /** Convert an X-coordinate to a column index in the volume grid. * * @param _fCoordX X-coordinate. * @return If the X-coordinate falls within a column of the volume grid, the column index is returned. * Otherwise, a value of -1 is returned. */ float32 coordXToColFloat(float32 _fCoordX) const; /** Convert a Y-coordinate to a row index in the volume grid. * * @param _fCoordY Y-coordinate * @return If the Y-coordinate falls within a row of the volume grid, the row index is returned. * Otherwise, a value of -1 is returned. */ float32 coordYToRowFloat(float32 _fCoordY) const; /** Convert a Z-coordinate to a slice index in the volume grid. * * @param _fCoordZ Z-coordinate * @return If the Z-coordinate falls within a slice of the volume grid, the slice index is returned. * Otherwise, a value of -1 is returned. */ float32 coordZToSliceFloat(float32 _fCoordZ) const; CVolumeGeometry2D * createVolumeGeometry2D() const; private: //< For Config unused argument checking ConfigCheckData* configCheckData; friend class ConfigReader; }; //---------------------------------------------------------------------------------------- // Get the initialization state of the object. inline bool CVolumeGeometry3D::isInitialized() const { return m_bInitialized; } //---------------------------------------------------------------------------------------- // Get the number of columns in the volume grid. inline int CVolumeGeometry3D::getGridColCount() const { ASTRA_ASSERT(m_bInitialized); return m_iGridColCount; } //---------------------------------------------------------------------------------------- // Get the number of rows in the volume grid. inline int CVolumeGeometry3D::getGridRowCount() const { ASTRA_ASSERT(m_bInitialized); return m_iGridRowCount; } //---------------------------------------------------------------------------------------- // Get the number of rows in the volume grid. inline int CVolumeGeometry3D::getGridSliceCount() const { ASTRA_ASSERT(m_bInitialized); return m_iGridSliceCount; } //---------------------------------------------------------------------------------------- // Get the total number of pixels in the volume window. inline size_t CVolumeGeometry3D::getGridTotCount() const { ASTRA_ASSERT(m_bInitialized); return m_iGridTotCount; } //---------------------------------------------------------------------------------------- // Get the horizontal length of the volume window, in unit lengths. inline float32 CVolumeGeometry3D::getWindowLengthX() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowLengthX; } //---------------------------------------------------------------------------------------- // Get the vertical length of the volume window, in unit lengths. inline float32 CVolumeGeometry3D::getWindowLengthY() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowLengthY; } //---------------------------------------------------------------------------------------- // Get the vertical length of the volume window, in unit lengths. inline float32 CVolumeGeometry3D::getWindowLengthZ() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowLengthZ; } //---------------------------------------------------------------------------------------- // Get the total area of the volume window, in unit lengths squared. inline float32 CVolumeGeometry3D::getWindowArea() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowArea; } //---------------------------------------------------------------------------------------- // Get the horizontal length of a single pixel (i.e., width), in unit lengths. inline float32 CVolumeGeometry3D::getPixelLengthX() const { ASTRA_ASSERT(m_bInitialized); return m_fPixelLengthX; } //---------------------------------------------------------------------------------------- // Get the vertical length of a single pixel (i.e., height), in unit lengths. inline float32 CVolumeGeometry3D::getPixelLengthY() const { ASTRA_ASSERT(m_bInitialized); return m_fPixelLengthY; } //---------------------------------------------------------------------------------------- // Get the depth of a single pixel in unit lengths. inline float32 CVolumeGeometry3D::getPixelLengthZ() const { ASTRA_ASSERT(m_bInitialized); return m_fPixelLengthZ; } //---------------------------------------------------------------------------------------- // Get the area of a single pixel (width*height), in unit lengths squared. inline float32 CVolumeGeometry3D::getPixelArea() const { ASTRA_ASSERT(m_bInitialized); return m_fPixelArea; } //---------------------------------------------------------------------------------------- // Get the minimal X-coordinate in the volume window. inline float32 CVolumeGeometry3D::getWindowMinX() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowMinX; } //---------------------------------------------------------------------------------------- // Get the minimal Y-coordinate in the volume window. inline float32 CVolumeGeometry3D::getWindowMinY() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowMinY; } //---------------------------------------------------------------------------------------- // Get the minimal Y-coordinate in the volume window. inline float32 CVolumeGeometry3D::getWindowMinZ() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowMinZ; } //---------------------------------------------------------------------------------------- // Get the maximal X-coordinate in the volume window. inline float32 CVolumeGeometry3D::getWindowMaxX() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowMaxX; } //---------------------------------------------------------------------------------------- // Get the maximal Y-coordinate in the volume window. inline float32 CVolumeGeometry3D::getWindowMaxY() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowMaxY; } //---------------------------------------------------------------------------------------- // Get the maximal Z-coordinate in the volume window. inline float32 CVolumeGeometry3D::getWindowMaxZ() const { ASTRA_ASSERT(m_bInitialized); return m_fWindowMaxZ; } //---------------------------------------------------------------------------------------- // Convert row, column and slice index of a pixel to a single index in the interval [0..getGridCountTot()-1]. inline size_t CVolumeGeometry3D::pixelRowColSliceToIndex(int _iPixelRow, int _iPixelCol, int _iPixelSlice) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelCol >= 0); ASTRA_ASSERT(_iPixelCol < m_iGridColCount); ASTRA_ASSERT(_iPixelRow >= 0); ASTRA_ASSERT(_iPixelRow < m_iGridRowCount); ASTRA_ASSERT(_iPixelSlice >= 0); ASTRA_ASSERT(_iPixelSlice < m_iGridSliceCount); return ((size_t)m_iGridColCount*m_iGridRowCount*_iPixelSlice + (size_t)_iPixelRow * m_iGridColCount + _iPixelCol); } //---------------------------------------------------------------------------------------- // Convert a pixel index (from the interval [0..getGridCountTot()-1] to a row, column and slice index. inline void CVolumeGeometry3D::pixelIndexToRowColSlice(size_t _iPixelIndex, int &_iPixelRow, int &_iPixelCol, int &_iPixelSlice) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelIndex < m_iGridTotCount); _iPixelSlice = _iPixelIndex / ((size_t)m_iGridRowCount*m_iGridColCount); _iPixelRow = (_iPixelIndex-(size_t)_iPixelSlice*m_iGridRowCount*m_iGridColCount) / m_iGridColCount; _iPixelCol = (_iPixelIndex-(size_t)_iPixelSlice*m_iGridRowCount*m_iGridColCount) % m_iGridColCount; } //---------------------------------------------------------------------------------------- // Convert a pixel column index to the X-coordinate of its center inline float32 CVolumeGeometry3D::pixelColToCenterX(int _iPixelCol) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelCol >= 0); ASTRA_ASSERT(_iPixelCol < m_iGridColCount); return (m_fWindowMinX + (float32(_iPixelCol) + 0.5f) * m_fPixelLengthX); } //---------------------------------------------------------------------------------------- // Convert a pixel column index to the minimum X-coordinate of points in that column inline float32 CVolumeGeometry3D::pixelColToMinX(int _iPixelCol) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelCol >= 0); ASTRA_ASSERT(_iPixelCol < m_iGridColCount); return (m_fWindowMinX + float32(_iPixelCol) * m_fPixelLengthX); } //---------------------------------------------------------------------------------------- // Convert a pixel column index to the maximum X-coordinate of points in that column inline float32 CVolumeGeometry3D::pixelColToMaxX(int _iPixelCol) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelCol >= 0); ASTRA_ASSERT(_iPixelCol < m_iGridColCount); return (m_fWindowMaxX + (float32(_iPixelCol) + 1.0f) * m_fPixelLengthX); } //---------------------------------------------------------------------------------------- // Convert a pixel row index to the Y-coordinate of its center inline float32 CVolumeGeometry3D::pixelRowToCenterY(int _iPixelRow) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelRow >= 0); ASTRA_ASSERT(_iPixelRow < m_iGridRowCount); return (m_fWindowMaxY - (float32(_iPixelRow) + 0.5f) * m_fPixelLengthY); } //---------------------------------------------------------------------------------------- // Convert a pixel row index to the minimum Y-coordinate of points in that row inline float32 CVolumeGeometry3D::pixelRowToMinY(int _iPixelRow) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelRow >= 0); ASTRA_ASSERT(_iPixelRow < m_iGridRowCount); return (m_fWindowMaxY - (float32(_iPixelRow) + 1.0f) * m_fPixelLengthY); } //---------------------------------------------------------------------------------------- // Convert a pixel row index to the maximum Y-coordinate of points in that row inline float32 CVolumeGeometry3D::pixelRowToMaxY(int _iPixelRow) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelRow >= 0); ASTRA_ASSERT(_iPixelRow < m_iGridRowCount); return (m_fWindowMaxY - (float32(_iPixelRow) * m_fPixelLengthY)); } //---------------------------------------------------------------------------------------- // Convert a pixel slice index to the Z-coordinate of its center inline float32 CVolumeGeometry3D::pixelSliceToCenterZ(int _iPixelSlice) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelSlice >= 0); ASTRA_ASSERT(_iPixelSlice < m_iGridSliceCount); return (m_fWindowMaxZ - (float32(_iPixelSlice) + 0.5f) * m_fPixelLengthZ); } //---------------------------------------------------------------------------------------- // Convert a pixel slice index to the minimum Z-coordinate of points in that slice inline float32 CVolumeGeometry3D::pixelSliceToMinZ(int _iPixelSlice) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelSlice >= 0); ASTRA_ASSERT(_iPixelSlice < m_iGridSliceCount); return (m_fWindowMaxZ - (float32(_iPixelSlice) + 1.0f) * m_fPixelLengthZ); } //---------------------------------------------------------------------------------------- // Convert a pixel slice index to the maximum Z-coordinate of points in that slice inline float32 CVolumeGeometry3D::pixelSliceToMaxZ(int _iPixelSlice) const { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_iPixelSlice >= 0); ASTRA_ASSERT(_iPixelSlice < m_iGridSliceCount); return (m_fWindowMaxZ - (float32(_iPixelSlice) * m_fPixelLengthZ)); } //---------------------------------------------------------------------------------------- // Convert an X-coordinate to a column index in the volume grid inline int CVolumeGeometry3D::coordXToCol(float32 _fCoordX) const { if (_fCoordX < m_fWindowMinX) return -1; if (_fCoordX > m_fWindowMaxX) return -1; int iCol = int((_fCoordX - m_fWindowMinX) * m_fDivPixelLengthX); ASTRA_ASSERT(iCol >= 0); ASTRA_ASSERT(iCol < m_iGridColCount); return iCol; } //---------------------------------------------------------------------------------------- // Convert a Y-coordinate to a row index in the volume grid inline int CVolumeGeometry3D::coordYToRow(float32 _fCoordY) const { if (_fCoordY < m_fWindowMinY) return -1; if (_fCoordY > m_fWindowMaxY) return -1; int iRow = int((m_fWindowMaxY - _fCoordY) * m_fDivPixelLengthY); ASTRA_ASSERT(iRow >= 0); ASTRA_ASSERT(iRow < m_iGridRowCount); return iRow; } //---------------------------------------------------------------------------------------- // Convert a Z-coordinate to a slice index in the volume grid inline int CVolumeGeometry3D::coordZToSlice(float32 _fCoordZ) const { if (_fCoordZ < m_fWindowMinZ) return -1; if (_fCoordZ > m_fWindowMaxZ) return -1; int iSlice = int((m_fWindowMaxZ - _fCoordZ) * m_fDivPixelLengthZ); ASTRA_ASSERT(iSlice >= 0); ASTRA_ASSERT(iSlice < m_iGridSliceCount); return iSlice; } //---------------------------------------------------------------------------------------- // Convert an X-coordinate to a column index in the volume grid inline float32 CVolumeGeometry3D::coordXToColFloat(float32 _fCoordX) const { ASTRA_ASSERT(m_bInitialized); return (_fCoordX - m_fWindowMinX) * m_fDivPixelLengthX; } //---------------------------------------------------------------------------------------- // Convert a Y-coordinate to a row index in the volume grid inline float32 CVolumeGeometry3D::coordYToRowFloat(float32 _fCoordY) const { ASTRA_ASSERT(m_bInitialized); return (m_fWindowMaxY - _fCoordY) * m_fDivPixelLengthY; } //---------------------------------------------------------------------------------------- // Convert a Z-coordinate to a slice index in the volume grid inline float32 CVolumeGeometry3D::coordZToSliceFloat(float32 _fCoordZ) const { ASTRA_ASSERT(m_bInitialized); return (m_fWindowMaxZ - _fCoordZ) * m_fDivPixelLengthZ; } //---------------------------------------------------------------------------------------- } // end namespace astra #endif /* _INC_ASTRA_VOLUMEGEOMETRY2D */ astra-toolbox-2.3.0/include/astra/XMLConfig.h000066400000000000000000000064021475635207100210220ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_XMLCONFIG #define _INC_ASTRA_XMLCONFIG #include "Globals.h" #include "XMLNode.h" #include "XMLDocument.h" #include "Config.h" namespace astra { class _AstraExport XMLConfig : public Config { public: XMLConfig(XMLNode _node); XMLConfig(const std::string &rootname); virtual ~XMLConfig(); private: template friend class ConfigReader; virtual bool has(const std::string &name) const; virtual bool hasOption(const std::string &name) const; virtual bool getSubConfig(const std::string &name, Config *&_cfg, std::string &type) const; virtual bool getInt(const std::string &name, int &iValue) const; virtual bool getFloat(const std::string &name, float &fValue) const; virtual bool getDoubleArray(const std::string &name, std::vector &values) const; virtual bool getIntArray(const std::string &name, std::vector &values) const; virtual bool getString(const std::string &name, std::string &sValue) const; virtual bool getOptionFloat(const std::string &name, float &fValue) const; virtual bool getOptionInt(const std::string &name, int &iValue) const; virtual bool getOptionUInt(const std::string &name, unsigned int &iValue) const; virtual bool getOptionBool(const std::string &name, bool &bValue) const; virtual bool getOptionString(const std::string &name, std::string &sValue) const; virtual bool getOptionIntArray(const std::string &name, std::vector &values) const; virtual std::list checkUnparsed(const ConfigCheckData &data) const; private: friend class ConfigWriter; XMLDocument *_doc; public: // TODO: Make this private once python/matlab interfaces can handle that XMLNode self; }; class _AstraExport ConfigWriter { public: ConfigWriter(const std::string &name); ConfigWriter(const std::string &name, const std::string &type); ~ConfigWriter(); Config* getConfig(); void addInt(const std::string &name, int iValue); void addNumerical(const std::string &name, double fValue); void addNumericalArray(const std::string &name, const float *pfValues, int iCount); void addNumericalMatrix(const std::string &name, const double *pfValues, int iHeight, int iWidth); void addID(const std::string &name, int iValue); void addOptionNumerical(const std::string &name, double fValue); private: XMLConfig *cfg; }; } // end namespace #endif astra-toolbox-2.3.0/include/astra/XMLDocument.h000066400000000000000000000045121475635207100213730ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_XMLDOCUMENT #define _INC_ASTRA_XMLDOCUMENT #include #if 1 namespace rapidxml { template class xml_document; } #else #include "rapidxml.hpp" #endif #include "Globals.h" #include "XMLNode.h" namespace astra { /** This class encapsulates an XML Document of the Xerces DOM Parser. */ class _AstraExport XMLDocument { public: /** Default Constructor */ XMLDocument(); /** Destructor */ ~XMLDocument(); /** Construct an XML DOM tree and Document from an XML file * * @param sFilename Location of the XML file. * @return XML Document containing the DOM tree */ static XMLDocument* readFromFile(std::string sFilename); /** Construct an empty XML DOM tree with a specific root tag. * * @param sRootName Element name of the root tag. * @return XML Document with an empty root node */ static XMLDocument* createDocument(std::string sRootName); /** Get the rootnode of the XML document * * @return first XML node of the document */ XMLNode getRootNode(); /** Save an XML DOM tree to an XML file * * @param sFilename Location of the XML file. */ void saveToFile(std::string sFilename); /** convert and XML DOM tree to a string */ std::string toString(); private: //!< Document of rapidxml rapidxml::xml_document* fDOMDocument; std::string fBuf; }; } // end namespace #endif astra-toolbox-2.3.0/include/astra/XMLNode.h000066400000000000000000000232311475635207100205010ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_XMLNODE #define _INC_ASTRA_XMLNODE #include #include #include #if 1 namespace rapidxml { template class xml_node; } #else #include "rapidxml.hpp" #endif #include "Globals.h" #include "Utilities.h" namespace astra { /** * This class encapsulates an XML Node of the Xerces DOM Parser. */ class _AstraExport XMLNode { friend class XMLDocument; public: /** Default Constructor */ XMLNode(); /** Deconstructor */ ~XMLNode(); /** Check validity */ operator bool() const { return fDOMElement != 0; } /** Get a single child XML node. If there are more, the first one is returned * * @param _sName tagname of the requested child node * @return first child node with the correct tagname, null pointer if it doesn't exist */ XMLNode getSingleNode(std::string _sName) const; /** Get all child XML nodes that have the tagname name * * @param _sName tagname of the requested child nodes * @return list with all child nodes with the correct tagname */ std::list getNodes(std::string _sName) const; /** Get all child XML nodes * * @return list with all child nodes */ std::list getNodes() const; /** Get the name of this node * * @return name of node */ std::string getName() const; /** Get the content of the XML node as a single string. * * @return node content */ std::string getContent() const; /** Get the content of the XML node as an integer * * @return node content */ int getContentInt() const; /** Get the content of the XML node as a numerical. * * @return node content */ float32 getContentNumerical() const; /** Get the content of the XML node as a boolean. * * @return node content */ bool getContentBool() const; /** Get the content of the XML node as a vector of strings. * * @return node content */ std::vector getContentArray() const; /** Get the content of the XML node as a stl container of float32 data. * NB: A 2D matrix is returned as a linear list * * @return node content */ std::vector getContentNumericalArray() const; std::vector getContentNumericalArrayDouble() const; /** Does this node contain an attribute with a certain name? * * @param _sName of the attribute. * @return attribute value, empty string if it doesn't exist. */ bool hasAttribute(std::string _sName) const; /** Get the value of an attribute. * * @param _sName of the attribute. * @param _sDefaultValue value to return if the attribute isn't found * @return attribute value, _sDefaultValue if it doesn't exist. */ std::string getAttribute(std::string _sName, std::string _sDefaultValue = "") const; /** Get the value of a numerical attribute. * * @param _sName of the attribute. * @param _fDefaultValue value to return if the attribute isn't found * @return attribute value, _fDefaultValue if it doesn't exist. */ float32 getAttributeNumerical(std::string _sName, float32 _fDefaultValue = 0) const; double getAttributeNumericalDouble(std::string _sName, double _fDefaultValue = 0) const; int getAttributeInt(std::string _sName, int _fDefaultValue = 0) const; /** Get the value of a boolean attribute. * * @param _sName of the attribute. * @param _bDefaultValue value to return if the attribute isn't found * @return attribute value, _bDefaultValue if it doesn't exist. */ bool getAttributeBool(std::string _sName, bool _bDefaultValue = false) const; /** Does this node contain an option with a certain key? * * @param _sKey option key * @return true if option does exist */ bool hasOption(std::string _sKey) const; /** Get the value of an option within this XML Node * * @param _sKey option key * @param _sDefaultValue value to return if key isn't found * @return option value, _sDefaultValue if the option doesn't exist */ std::string getOption(std::string _sKey, std::string _sDefaultValue = "") const; /** Get the value of an option within this XML Node * * @param _sKey option key * @param _fDefaultValue value to return if key isn't found * @return option value, _fDefaultValue if the option doesn't exist */ float32 getOptionNumerical(std::string _sKey, float32 _fDefaultValue = 0) const; int getOptionInt(std::string _sKey, int _fDefaultValue = 0) const; /** Get the value of an option within this XML Node * * @param _sKey option key * @param _bDefaultValue value to return if key isn't found * @return option value, _bDefaultValue if the option doesn't exist */ bool getOptionBool(std::string _sKey, bool _bDefaultValue = false) const; /** Get the value of an option within this XML Node * * @param _sKey option key * @return numerical array */ std::vector getOptionNumericalArray(std::string _sKey) const; /** Create a new XML node as a child to this one: <...><_sNodeName/></...> * * @param _sNodeName the name of the new childnode * @param _sValue some node content * @return new child node */ XMLNode addChildNode(std::string _sNodeName, std::string _sValue); /** Create a new XML node as a child to this one, also add some numerical content: * <...><_sNodeName>_sValue</_sNodeName></...> * * @param _sNodeName the name of the new childnode * @param _fValue some node content * @return new child node */ XMLNode addChildNode(std::string _sNodeName, float32 _fValue); /** Create a new XML node as a child to this one, also add a list of numerical content: * <...><_sNodeName>_sValue</_sNodeName></...> * * @param _sNodeName the name of the new childnode * @param _pfList list data * @param _iSize number of elements in _pfList * @return new child node */ XMLNode addChildNode(std::string _sNodeName, float32* _pfList, int _iSize); /** Add some text to the node: <...>_sText</...> * * @param _sText text to insert */ void setContent(std::string _sText); /** Add a number to the node: <...>_sText</...> * * @param _fValue number to insert */ void setContent(float32 _fValue); /** Add a list of numerical data to the node * * @param _pfList data * @param _iSize number of elements in the list */ void setContent(const float32* _pfList, int _iSize); /** Add a list of numerical data to the node * * @param _pfList data * @param _iSize number of elements in the list */ void setContent(const double* _pfList, int _iSize); /** Add a (2D) matrix of numerical data to the node * * @param _pfMatrix data * @param _iWidth width of the matrix * @param _iHeight height of the matrix * @param transposed true is C order, false is Fortran order */ void setContent(const float32* _pfMatrix, int _iWidth, int _iHeight, bool transposed); /** Add a (2D) matrix of numerical data to the node * * @param _pfMatrix data * @param _iWidth width of the matrix * @param _iHeight height of the matrix * @param transposed true is C order, false is Fortran order */ void setContent(const double* _pfMatrix, int _iWidth, int _iHeight, bool transposed); /** Add an attribute to this node: <... _sName="_sValue"> * * @param _sName name of the attribute * @param _sValue value of the attribute */ void addAttribute(std::string _sName, std::string _sValue); /** Add an attribute with numerical data to this node: <... _sName="_fValue"> * * @param _sName name of the attribute * @param _sValue value of the attribute */ void addAttribute(std::string _sName, float32 _fValue); /** Add an option node as a child: <Option key="<_sKey>" value="<_sValue>"/> * * @param _sKey option key * @param _sValue option value */ void addOption(std::string _sKey, std::string _sValue); /** Add an option node as a child: <Option key="<_sKey>" value="<_sValue>"/> * * @param _sKey option key * @param _sValue option value */ void addOption(std::string _sKey, float32 _fValue); /** Print to String */ std::string toString() const; /** Print the node */ void print() const; protected: /** Private Constructor. * * @param n rapidxml node */ XMLNode(rapidxml::xml_node* n); /** Link this object to a rapidxml node * @param n object of the Xerces C++ library */ void setDOMNode(rapidxml::xml_node* n); // todo: rename "fDOMElement" to "m_fDOMElement"? //!< Node of rapidxml rapidxml::xml_node* fDOMElement; }; } // end namespace #endif astra-toolbox-2.3.0/include/astra/clog.h000066400000000000000000000422211475635207100201570ustar00rootroot00000000000000/* clog: Extremely simple logger for C. * * Features: * - Implemented purely as a single header file. * - Create multiple loggers. * - Four log levels (debug, info, warn, error). * - Custom formats. * - Fast. * * Dependencies: * - Should conform to C89, C++98 (but requires vsnprintf, unfortunately). * - POSIX environment. * * USAGE: * * Include this header in any file that wishes to write to logger(s). In * exactly one file (per executable), define CLOG_MAIN first (e.g. in your * main .c file). * * #define CLOG_MAIN * #include "clog.h" * * This will define the actual objects that all the other units will use. * * Loggers are identified by integers (0 - 15). It's expected that you'll * create meaningful constants and then refer to the loggers as such. * * Example: * * const int MY_LOGGER = 0; * * int main() { * int r; * r = clog_init_path(MY_LOGGER, "my_log.txt"); * if (r != 0) { * fprintf(stderr, "Logger initialization failed.\n"); * return 1; * } * clog_info(CLOG(MY_LOGGER), "Hello, world!"); * clog_free(MY_LOGGER); * return 0; * } * * The CLOG macro used in the call to clog_info is a helper that passes the * __FILE__ and __LINE__ parameters for you, so you don't have to type them * every time. (It could be prettier with variadic macros, but that requires * C99 or C++11 to be standards compliant.) * * Errors encountered by clog will be printed to stderr. You can suppress * these by defining a macro called CLOG_SILENT before including clog.h. * * License: Do whatever you want. It would be nice if you contribute * improvements as pull requests here: * * https://github.com/mmueller/clog * * Copyright 2013 Mike Mueller . * * As is; no warranty is provided; use at your own risk. */ #ifndef __CLOG_H__ #define __CLOG_H__ #include #include #include #include #include #include #include #include #include #ifndef _MSC_VER #include #else #define WIN32_LEAN_AND_MEAN #include #include #define open _open #define close _close #define write _write #define snprintf _snprintf #endif /* Number of loggers that can be defined. */ #define CLOG_MAX_LOGGERS 16 /* Format strings cannot be longer than this. */ #define CLOG_FORMAT_LENGTH 256 /* Formatted times and dates should be less than this length. If they are not, * they will not appear in the log. */ #define CLOG_DATETIME_LENGTH 256 /* Default format strings. */ #define CLOG_DEFAULT_FORMAT "%d %t %f(%n): %l: %m\n" #define CLOG_DEFAULT_DATE_FORMAT "%Y-%m-%d" #define CLOG_DEFAULT_TIME_FORMAT "%H:%M:%S" #ifdef __cplusplus extern "C" { #endif enum clog_level { CLOG_DEBUG, CLOG_INFO, CLOG_WARN, CLOG_ERROR }; struct clog; /** * Create a new logger writing to the given file path. The file will always * be opened in append mode. * * @param id * A constant integer between 0 and 15 that uniquely identifies this logger. * * @param path * Path to the file where log messages will be written. * * @return * Zero on success, non-zero on failure. */ int clog_init_path(int id, const char *const path); /** * Create a new logger writing to a file descriptor. * * @param id * A constant integer between 0 and 15 that uniquely identifies this logger. * * @param fd * The file descriptor where log messages will be written. * * @return * Zero on success, non-zero on failure. */ int clog_init_fd(int id, int fd); /** * Destroy (clean up) a logger. You should do this at the end of execution, * or when you are done using the logger. * * @param id * The id of the logger to destroy. */ void clog_free(int id); #define CLOG(id) __FILE__, __LINE__, id /** * Log functions (one per level). Call these to write messages to the log * file. The first three arguments can be replaced with a call to the CLOG * macro defined above, e.g.: * * clog_debug(CLOG(MY_LOGGER_ID), "This is a log message."); * * @param sfile * The name of the source file making this log call (e.g. __FILE__). * * @param sline * The line number of the call in the source code (e.g. __LINE__). * * @param id * The id of the logger to write to. * * @param fmt * The format string for the message (printf formatting). * * @param ... * Any additional format arguments. */ void clog_debug(const char *sfile, int sline, int id, const char *fmt, va_list ap); void clog_info(const char *sfile, int sline, int id, const char *fmt, va_list ap); void clog_warn(const char *sfile, int sline, int id, const char *fmt, va_list ap); void clog_error(const char *sfile, int sline, int id, const char *fmt, va_list ap); /** * Set the minimum level of messages that should be written to the log. * Messages below this level will not be written. By default, loggers are * created with level == CLOG_DEBUG. * * @param id * The identifier of the logger. * * @param level * The new minimum log level. * * @return * Zero on success, non-zero on failure. */ int clog_set_level(int id, enum clog_level level); /** * Set the format string used for times. See strftime(3) for how this string * should be defined. The default format string is CLOG_DEFAULT_TIME_FORMAT. * * @param fmt * The new format string, which must be less than CLOG_FORMAT_LENGTH bytes. * * @return * Zero on success, non-zero on failure. */ int clog_set_time_fmt(int id, const char *fmt); /** * Set the format string used for dates. See strftime(3) for how this string * should be defined. The default format string is CLOG_DEFAULT_DATE_FORMAT. * * @param fmt * The new format string, which must be less than CLOG_FORMAT_LENGTH bytes. * * @return * Zero on success, non-zero on failure. */ int clog_set_date_fmt(int id, const char *fmt); /** * Set the format string for log messages. Here are the substitutions you may * use: * * %f: Source file name generating the log call. * %n: Source line number where the log call was made. * %m: The message text sent to the logger (after printf formatting). * %d: The current date, formatted using the logger's date format. * %t: The current time, formatted using the logger's time format. * %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR"). * %%: A literal percent sign. * * The default format string is CLOG_DEFAULT_FORMAT. * * @param fmt * The new format string, which must be less than CLOG_FORMAT_LENGTH bytes. * You probably will want to end this with a newline (\n). * * @return * Zero on success, non-zero on failure. */ int clog_set_fmt(int id, const char *fmt); /** * Set the callback function. * * @param cb * The new callback function. * * @return * Zero on success, non-zero on failure. */ int clog_set_cb(int id, void (*cb)(const char *msg, size_t len)); /** * Set the file descriptor. * * @param id * The identifier of the logger. * * @param fd * The new file descriptor. * * @return * Zero on success, non-zero on failure. */ int clog_set_fd(int id, int fd); /* * No need to read below this point. */ /** * The C logger structure. */ struct clog { /* The current level of this logger. Messages below it will be dropped. */ enum clog_level level; /* The file being written. */ int fd; /* The format specifier. */ char fmt[CLOG_FORMAT_LENGTH]; /* Date format */ char date_fmt[CLOG_FORMAT_LENGTH]; /* Time format */ char time_fmt[CLOG_FORMAT_LENGTH]; /* Tracks whether the fd needs to be closed eventually. */ int opened; /* Callback function for each log message. */ void (*cb)(const char *msg, size_t len); }; void _clog_err(const char *fmt, ...); #ifdef CLOG_MAIN struct clog *_clog_loggers[CLOG_MAX_LOGGERS] = { 0 }; #else extern struct clog *_clog_loggers[CLOG_MAX_LOGGERS]; #endif #ifdef CLOG_MAIN const char *const CLOG_LEVEL_NAMES[] = { "Debug", "Info", "Warning", "Error", }; int clog_init_path(int id, const char *const path) { int fd = open(path, O_CREAT | O_WRONLY | O_APPEND, 0666); if (fd == -1) { _clog_err("Unable to open %s: %s\n", path, strerror(errno)); return 1; } if (clog_init_fd(id, fd)) { close(fd); return 1; } _clog_loggers[id]->opened = 1; return 0; } int clog_init_fd(int id, int fd) { struct clog *logger; if (_clog_loggers[id] != NULL) { _clog_err("Logger %d already initialized.\n", id); return 1; } logger = (struct clog *) malloc(sizeof(struct clog)); if (logger == NULL) { _clog_err("Failed to allocate logger: %s\n", strerror(errno)); return 1; } logger->level = CLOG_DEBUG; logger->fd = fd; logger->opened = 0; strcpy(logger->fmt, CLOG_DEFAULT_FORMAT); strcpy(logger->date_fmt, CLOG_DEFAULT_DATE_FORMAT); strcpy(logger->time_fmt, CLOG_DEFAULT_TIME_FORMAT); logger->cb = NULL; _clog_loggers[id] = logger; return 0; } void clog_free(int id) { if (_clog_loggers[id]) { if (_clog_loggers[id]->opened) { close(_clog_loggers[id]->fd); } free(_clog_loggers[id]); _clog_loggers[id]=NULL; } } int clog_set_level(int id, enum clog_level level) { if (_clog_loggers[id] == NULL) { return 1; } if ((unsigned) level > CLOG_ERROR) { return 1; } _clog_loggers[id]->level = level; return 0; } int clog_set_fd(int id, int fd) { if (_clog_loggers[id] == NULL) { return 1; } _clog_loggers[id]->fd = fd; return 0; } int clog_set_time_fmt(int id, const char *fmt) { struct clog *logger = _clog_loggers[id]; if (logger == NULL) { _clog_err("clog_set_time_fmt: No such logger: %d\n", id); return 1; } if (strlen(fmt) >= CLOG_FORMAT_LENGTH) { _clog_err("clog_set_time_fmt: Format specifier too long.\n"); return 1; } strcpy(logger->time_fmt, fmt); return 0; } int clog_set_date_fmt(int id, const char *fmt) { struct clog *logger = _clog_loggers[id]; if (logger == NULL) { _clog_err("clog_set_date_fmt: No such logger: %d\n", id); return 1; } if (strlen(fmt) >= CLOG_FORMAT_LENGTH) { _clog_err("clog_set_date_fmt: Format specifier too long.\n"); return 1; } strcpy(logger->date_fmt, fmt); return 0; } int clog_set_fmt(int id, const char *fmt) { struct clog *logger = _clog_loggers[id]; if (logger == NULL) { _clog_err("clog_set_fmt: No such logger: %d\n", id); return 1; } if (strlen(fmt) >= CLOG_FORMAT_LENGTH) { _clog_err("clog_set_fmt: Format specifier too long.\n"); return 1; } strcpy(logger->fmt, fmt); return 0; } int clog_set_cb(int id, void (*cb)(const char *msg, size_t len)) { struct clog *logger = _clog_loggers[id]; if (logger == NULL) { _clog_err("clog_set_cb: No such logger: %d\n", id); return 1; } logger->cb = cb; return 0; } /* Internal functions */ size_t _clog_append_str(char **dst, char *orig_buf, const char *src, size_t cur_size) { size_t new_size = cur_size; while (strlen(*dst) + strlen(src) >= new_size) { new_size *= 2; } if (new_size != cur_size) { if (*dst == orig_buf) { *dst = (char *) malloc(new_size); strcpy(*dst, orig_buf); } else { *dst = (char *) realloc(*dst, new_size); } } strcat(*dst, src); return new_size; } size_t _clog_append_int(char **dst, char *orig_buf, long int d, size_t cur_size) { char buf[40]; /* Enough for 128-bit decimal */ if (snprintf(buf, 40, "%ld", d) >= 40) { return cur_size; } return _clog_append_str(dst, orig_buf, buf, cur_size); } size_t _clog_append_time(char **dst, char *orig_buf, struct tm *lt, const char *fmt, size_t cur_size) { char buf[CLOG_DATETIME_LENGTH]; size_t result = strftime(buf, CLOG_DATETIME_LENGTH, fmt, lt); if (result > 0) { return _clog_append_str(dst, orig_buf, buf, cur_size); } return cur_size; } const char * _clog_basename(const char *path) { const char *slash = strrchr(path, '/'); if (slash) { path = slash + 1; } #ifdef _WIN32 slash = strrchr(path, '\\'); if (slash) { path = slash + 1; } #endif return path; } char * _clog_format(const struct clog *logger, char buf[], size_t buf_size, const char *sfile, int sline, const char *level, const char *message) { size_t cur_size = buf_size; char *result = buf; enum { NORMAL, SUBST } state = NORMAL; size_t fmtlen = strlen(logger->fmt); size_t i; time_t t = time(NULL); struct tm *lt = localtime(&t); sfile = _clog_basename(sfile); result[0] = 0; for (i = 0; i < fmtlen; ++i) { if (state == NORMAL) { if (logger->fmt[i] == '%') { state = SUBST; } else { char str[2] = { 0 }; str[0] = logger->fmt[i]; cur_size = _clog_append_str(&result, buf, str, cur_size); } } else { switch (logger->fmt[i]) { case '%': cur_size = _clog_append_str(&result, buf, "%", cur_size); break; case 't': cur_size = _clog_append_time(&result, buf, lt, logger->time_fmt, cur_size); break; case 'd': cur_size = _clog_append_time(&result, buf, lt, logger->date_fmt, cur_size); break; case 'l': cur_size = _clog_append_str(&result, buf, level, cur_size); break; case 'n': cur_size = _clog_append_int(&result, buf, sline, cur_size); break; case 'f': cur_size = _clog_append_str(&result, buf, sfile, cur_size); break; case 'm': cur_size = _clog_append_str(&result, buf, message, cur_size); break; } state = NORMAL; } } return result; } void _clog_log(const char *sfile, int sline, enum clog_level level, int id, const char *fmt, va_list ap) { /* For speed: Use a stack buffer until message exceeds 4096, then switch * to dynamically allocated. This should greatly reduce the number of * memory allocations (and subsequent fragmentation). */ char buf[4096]; size_t buf_size = 4096; char *dynbuf = buf; char *message; int result; struct clog *logger = _clog_loggers[id]; if (!logger) { _clog_err("No such logger: %d\n", id); return; } if (level < logger->level) { return; } /* Format the message text with the argument list. */ result = vsnprintf(dynbuf, buf_size, fmt, ap); if ((size_t) result >= buf_size) { buf_size = result + 1; dynbuf = (char *) malloc(buf_size); result = vsnprintf(dynbuf, buf_size, fmt, ap); if ((size_t) result >= buf_size) { /* Formatting failed -- too large */ _clog_err("Formatting failed (1).\n"); free(dynbuf); return; } } /* Format according to log format and write to log */ { char message_buf[4096]; message = _clog_format(logger, message_buf, 4096, sfile, sline, CLOG_LEVEL_NAMES[level], dynbuf); if (!message) { _clog_err("Formatting failed (2).\n"); if (dynbuf != buf) { free(dynbuf); } return; } result = write(logger->fd, message, strlen(message)); if (logger->cb) logger->cb(message,strlen(message)); if (result == -1) { _clog_err("Unable to write to log file: %s\n", strerror(errno)); } if (message != message_buf) { free(message); } if (dynbuf != buf) { free(dynbuf); } #ifndef _MSC_VER fsync(logger->fd); #else HANDLE h = (HANDLE) _get_osfhandle(logger->fd); if (h != INVALID_HANDLE_VALUE) { // This call will fail on a console fd, but that's ok. FlushFileBuffers(h); } #endif } } void clog_debug(const char *sfile, int sline, int id, const char *fmt, va_list ap) { _clog_log(sfile, sline, CLOG_DEBUG, id, fmt, ap); } void clog_info(const char *sfile, int sline, int id, const char *fmt, va_list ap) { _clog_log(sfile, sline, CLOG_INFO, id, fmt, ap); } void clog_warn(const char *sfile, int sline, int id, const char *fmt, va_list ap) { _clog_log(sfile, sline, CLOG_WARN, id, fmt, ap); } void clog_error(const char *sfile, int sline, int id, const char *fmt, va_list ap) { _clog_log(sfile, sline, CLOG_ERROR, id, fmt, ap); } void _clog_err(const char *fmt, ...) { #ifdef CLOG_SILENT (void) fmt; #else va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); #endif } #endif /* CLOG_MAIN */ #ifdef __cplusplus } /* extern "C" */ #endif #endif /* __CLOG_H__ */ astra-toolbox-2.3.0/include/astra/cuda/000077500000000000000000000000001475635207100177755ustar00rootroot00000000000000astra-toolbox-2.3.0/include/astra/cuda/2d/000077500000000000000000000000001475635207100203025ustar00rootroot00000000000000astra-toolbox-2.3.0/include/astra/cuda/2d/algo.h000066400000000000000000000120261475635207100213760ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_ALGO_H #define _CUDA_ALGO_H #include "astra/Globals.h" #include "dims.h" namespace astra { class CParallelProjectionGeometry2D; class CParallelVecProjectionGeometry2D; class CFanFlatProjectionGeometry2D; class CFanFlatVecProjectionGeometry2D; class CVolumeGeometry2D; class CProjectionGeometry2D; } namespace astraCUDA { class _AstraExport ReconAlgo { public: ReconAlgo(); virtual ~ReconAlgo(); bool setGPUIndex(int iGPUIndex); bool setGeometry(const astra::CVolumeGeometry2D* pVolGeom, const astra::CProjectionGeometry2D* pProjGeom); bool setSuperSampling(int raysPerDet, int raysPerPixelDim); // Scale the final reconstruction. // May be called at any time after setGeometry and before iterate(). Multiple calls stack. bool setReconstructionScale(float fScale); virtual bool enableVolumeMask(); virtual bool enableSinogramMask(); // init should be called after setting all geometry virtual bool init() = 0; // setVolumeMask should be called after init and before iterate, // but only if enableVolumeMask was called before init. // It may be called again after iterate. bool setVolumeMask(float* D_maskData, unsigned int maskPitch); // setSinogramMask should be called after init and before iterate, // but only if enableSinogramMask was called before init. // It may be called again after iterate. bool setSinogramMask(float* D_smaskData, unsigned int smaskPitch); // setBuffers should be called after init and before iterate. // It may be called again after iterate. virtual bool setBuffers(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch); // instead of calling setBuffers, you can also call allocateBuffers // to let ReconAlgo manage its own GPU memory virtual bool allocateBuffers(); // copy data to GPU. This must be called after allocateBuffers. // pfSinogram, pfReconstruction, pfVolMask, pfSinoMask are the // sinogram, reconstruction, volume mask and sinogram mask in system RAM, // respectively. The corresponding pitch variables give the pitches // of these buffers, measured in floats. virtual bool copyDataToGPU(const float* pfSinogram, unsigned int iSinogramPitch, const float* pfReconstruction, unsigned int iReconstructionPitch, const float* pfVolMask, unsigned int iVolMaskPitch, const float* pfSinoMask, unsigned int iSinoMaskPitch); // set Min/Max constraints. They may be called at any time, and will affect // any iterate() calls afterwards. virtual bool setMinConstraint(float fMin); virtual bool setMaxConstraint(float fMax); // iterate should be called after init and setBuffers. // It may be called multiple times. virtual bool iterate(unsigned int iterations) = 0; // Compute the norm of the difference of the FP of the current // reconstruction and the sinogram. (This performs one FP.) // It can be called after iterate. virtual float computeDiffNorm() = 0; // TODO: computeDiffNorm shouldn't be virtual, but for it to be // implemented in ReconAlgo, it needs a way to get a suitable // temporary sinogram buffer. bool getReconstruction(float* pfReconstruction, unsigned int iReconstructionPitch) const; protected: void reset(); bool callFP(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, float outputScale); bool callBP(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, float outputScale); SDimensions dims; SParProjection* parProjs; SFanProjection* fanProjs; float fProjectorScale; bool freeGPUMemory; // Input/output float* D_sinoData; unsigned int sinoPitch; float* D_volumeData; unsigned int volumePitch; // Masks bool useVolumeMask; bool useSinogramMask; float* D_maskData; unsigned int maskPitch; float* D_smaskData; unsigned int smaskPitch; // Min/max bool useMinConstraint; bool useMaxConstraint; float fMinConstraint; float fMaxConstraint; }; } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/arith.h000066400000000000000000000073251475635207100215710ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_ARITH_H #define _CUDA_ARITH_H #include #include namespace astraCUDA { struct opAddScaled; struct opScaleAndAdd; struct opAddMulScaled; struct opAddMul; struct opAdd; struct opAdd2; struct opMul; struct opDiv; struct opMul2; struct opDividedBy; struct opInvert; struct opSet; struct opClampMin; struct opClampMax; struct opClampMinMask; struct opClampMaxMask; struct opSegmentAndMask; struct opSetMaskedValues; struct opMulMask; template bool processVol(float* out, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processVol(float* out, float fParam, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processVol(float* out1, float* out2, float fParam1, float fParam2, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processVol(float* out, const float* in, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processVol(float* out, const float* in, float fParam, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processVol(float* out, const float* in1, const float* in2, float fParam, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processVol(float* out, const float* in1, const float* in2, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processSino(float* out, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processSino(float* out, float fParam, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processSino(float* out1, float* out2, float fParam1, float fParam2, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processSino(float* out, const float* in, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processSino(float* out, const float* in, float fParam, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processSino(float* out, const float* in1, const float* in2, float fParam, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); template bool processSino(float* out, const float* in1, const float* in2, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/astra.h000066400000000000000000000123121475635207100215640ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_ASTRA_H #define _CUDA_ASTRA_H #include "dims.h" #include "algo.h" using astraCUDA::SFanProjection; namespace astra { enum Cuda2DProjectionKernel { ker2d_default = 0 }; class CParallelProjectionGeometry2D; class CParallelVecProjectionGeometry2D; class CFanFlatProjectionGeometry2D; class CFanFlatVecProjectionGeometry2D; class CVolumeGeometry2D; class CProjectionGeometry2D; class _AstraExport BPalgo : public astraCUDA::ReconAlgo { public: BPalgo(); ~BPalgo(); virtual bool init(); virtual bool iterate(unsigned int iterations); virtual float computeDiffNorm(); }; // TODO: Clean up this interface to FP // Do a single forward projection _AstraExport bool astraCudaFP(const float* pfVolume, float* pfSinogram, unsigned int iVolWidth, unsigned int iVolHeight, unsigned int iProjAngles, unsigned int iProjDets, const SParProjection *pAngles, unsigned int iDetSuperSampling = 1, float fOutputScale = 1.0f, int iGPUIndex = 0); _AstraExport bool astraCudaFanFP(const float* pfVolume, float* pfSinogram, unsigned int iVolWidth, unsigned int iVolHeight, unsigned int iProjAngles, unsigned int iProjDets, const SFanProjection *pAngles, unsigned int iDetSuperSampling = 1, float fOutputScale = 1.0f, int iGPUIndex = 0); _AstraExport bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom, const CParallelProjectionGeometry2D* pProjGeom, astraCUDA::SParProjection*& pProjs, float& fOutputScale); _AstraExport bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom, const CParallelVecProjectionGeometry2D* pProjGeom, astraCUDA::SParProjection*& pProjs, float& fOutputScale); _AstraExport bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom, const CFanFlatProjectionGeometry2D* pProjGeom, astraCUDA::SFanProjection*& pProjs, float& outputScale); _AstraExport bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom, const CFanFlatVecProjectionGeometry2D* pProjGeom, astraCUDA::SFanProjection*& pProjs, float& outputScale); _AstraExport bool convertAstraGeometry_dims(const CVolumeGeometry2D* pVolGeom, const CProjectionGeometry2D* pProjGeom, astraCUDA::SDimensions& dims); _AstraExport bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom, const CProjectionGeometry2D* pProjGeom, astraCUDA::SParProjection*& pParProjs, astraCUDA::SFanProjection*& pFanProjs, float& outputScale); } namespace astraCUDA { // Return string with CUDA device number, name and memory size. // Use device == -1 to get info for the current device. _AstraExport std::string getCudaDeviceString(int device); _AstraExport bool setGPUIndex(int index); _AstraExport size_t availableGPUMemory(); struct opAddScaled; struct opScaleAndAdd; struct opAddMulScaled; struct opAddMul; struct opAdd; struct opAdd2; struct opMul; struct opDiv; struct opMul2; struct opDividedBy; struct opInvert; struct opSet; struct opClampMin; struct opClampMax; struct opClampMinMask; struct opClampMaxMask; struct opSegmentAndMask; struct opSetMaskedValues; struct opMulMask; template bool processVolCopy(float* out, const SDimensions& dims); template bool processVolCopy(float* out, float param, const SDimensions& dims); template bool processVolCopy(float* out1, float* out2, float param1, float param2, const SDimensions& dims); template bool processVolCopy(float* out, const float* in, const SDimensions& dims); template bool processVolCopy(float* out, const float* in, float param, const SDimensions& dims); template bool processVolCopy(float* out, const float* in1, const float* in2, const SDimensions& dims); template bool processVolCopy(float* out, const float* in1, const float* in2, float param, const SDimensions& dims); } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/cgls.h000066400000000000000000000045711475635207100214120ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_CGLS_H #define _CUDA_CGLS_H #include "algo.h" namespace astraCUDA { class _AstraExport CGLS : public ReconAlgo { public: CGLS(); virtual ~CGLS(); // disable some features virtual bool enableSinogramMask() { return false; } virtual bool setMinConstraint(float) { return false; } virtual bool setMaxConstraint(float) { return false; } virtual bool init(); virtual bool setBuffers(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch); virtual bool copyDataToGPU(const float* pfSinogram, unsigned int iSinogramPitch, const float* pfReconstruction, unsigned int iReconstructionPitch, const float* pfVolMask, unsigned int iVolMaskPitch, const float* pfSinoMask, unsigned int iSinoMaskPitch); virtual bool iterate(unsigned int iterations); virtual float computeDiffNorm(); protected: void reset(); bool sliceInitialized; // Buffers float* D_r; unsigned int rPitch; float* D_w; unsigned int wPitch; float* D_z; unsigned int zPitch; float* D_p; unsigned int pPitch; float gamma; }; _AstraExport bool doCGLS(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const float* angles, const float* TOffsets, unsigned int iterations); } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/darthelper.h000066400000000000000000000026671475635207100226200ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_DARTHELPER_H #define _CUDA_DARTHELPER_H #include "astra/Globals.h" namespace astraCUDA { void roiSelect(float* out, float radius, unsigned int width, unsigned int height); void dartMask(float* out, const float* in, unsigned int conn, unsigned int radius, unsigned int threshold, unsigned int width, unsigned int height); void dartSmoothing(float* out, const float* in, float b, unsigned int radius, unsigned int width, unsigned int height); } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/dims.h000066400000000000000000000035011475635207100214060ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_DIMS_H #define _CUDA_DIMS_H #include "astra/GeometryUtil2D.h" namespace astraCUDA { using astra::SParProjection; using astra::SFanProjection; struct SDimensions { // Width, height of reconstruction volume unsigned int iVolWidth; unsigned int iVolHeight; // Number of projection angles unsigned int iProjAngles; // Number of detector pixels unsigned int iProjDets; // in FP, number of rays to trace per detector pixel. // This should usually be set to 1. // If fDetScale > 1, this should be set to an integer of roughly // the same size as fDetScale. unsigned int iRaysPerDet; // in BP, square root of number of rays to trace per volume pixel // This should usually be set to 1. // If fDetScale < 1, this should be set to an integer of roughly // the same size as 1 / fDetScale. unsigned int iRaysPerPixelDim; }; } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/em.h000066400000000000000000000037251475635207100210630ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_EM_H #define _CUDA_EM_H #include "algo.h" namespace astraCUDA { class _AstraExport EM : public ReconAlgo { public: EM(); virtual ~EM(); // disable some features virtual bool enableSinogramMask() { return false; } virtual bool enableVolumeMask() { return false; } virtual bool setMinConstraint(float) { return false; } virtual bool setMaxConstraint(float) { return false; } virtual bool init(); virtual bool iterate(unsigned int iterations); virtual float computeDiffNorm(); protected: void reset(); bool precomputeWeights(); // Temporary buffers float* D_projData; unsigned int projPitch; float* D_tmpData; unsigned int tmpPitch; // Geometry-specific precomputed data float* D_pixelWeight; unsigned int pixelPitch; }; _AstraExport bool doEM(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const float* angles, const float* TOffsets, unsigned int iterations); } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/fan_bp.h000066400000000000000000000034241475635207100217030ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_FAN_BP_H #define _CUDA_FAN_BP_H namespace astraCUDA { _AstraExport bool FanBP(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SFanProjection* angles, float fOutputScale); _AstraExport bool FanBP_SART(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, unsigned int angle, const SDimensions& dims, const SFanProjection* angles, float fOutputScale); _AstraExport bool FanBP_FBPWeighted(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SFanProjection* angles, float fOutputScale); } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/fan_fp.h000066400000000000000000000024161475635207100217070ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_FAN_FP_H #define _CUDA_FAN_FP_H namespace astraCUDA { _AstraExport bool FanFP(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SFanProjection* angles, float outputScale); } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/fbp.h000066400000000000000000000065661475635207100212370ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "algo.h" #include "astra/Filters.h" namespace astraCUDA { class _AstraExport FBP : public ReconAlgo { public: FBP(); ~FBP(); virtual bool useSinogramMask() { return false; } virtual bool useVolumeMask() { return false; } // Returns the required size of a filter in the fourier domain // when multiplying it with the fft of the projection data. // Its value is equal to the smallest power of two larger than // or equal to twice the number of detectors in the spatial domain. // // _iDetectorCount is the number of detectors in the spatial domain. static int calcFourierFilterSize(int _iDetectorCount); // Sets the filter type. Some filter types require the user to supply an // array containing the filter. // The number of elements in a filter in the fourier domain should be equal // to the value returned by calcFourierFilterSize(). // The following types require a filter: // // - FILTER_PROJECTION: // The filter size should be equal to the output of // calcFourierFilterSize(). The filtered sinogram is // multiplied with the supplied filter. // // - FILTER_SINOGRAM: // Same as FILTER_PROJECTION, but now the filter should contain a row for // every projection direction. // // - FILTER_RPROJECTION: // The filter should now contain one kernel (= ifft of filter), with the // peak in the center. The filter width // can be any value. If odd, the peak is assumed to be in the center, if // even, it is assumed to be at floor(filter-width/2). // // - FILTER_RSINOGRAM // Same as FILTER_RPROJECTION, but now the supplied filter should contain a // row for every projection direction. // // A large number of other filters (FILTER_RAMLAK, FILTER_SHEPPLOGAN, // FILTER_COSINE, FILTER_HAMMING, and FILTER_HANN) // have a D variable, which gives the cutoff point in the frequency domain. // Setting this value to 1.0 will include the whole filter bool setFilter(const astra::SFilterConfig &_cfg); bool setShortScan(bool ss) { m_bShortScan = ss; return true; } // Scale the final reconstruction. // May be called at any time before iterate(). bool setReconstructionScale(float fScale); virtual bool init(); virtual bool iterate(unsigned int iterations); virtual float computeDiffNorm() { return 0.0f; } // TODO protected: void reset(); void* D_filter; // cufftComplex* bool m_bSingleFilter; // Same filter for every projection? bool m_bShortScan; float fReconstructionScale; }; } astra-toolbox-2.3.0/include/astra/cuda/2d/fft.h000066400000000000000000000065451475635207100212440ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_FFT_H #define _CUDA_FFT_H #include #include #include #include "astra/Filters.h" namespace astraCUDA { // Functions taking an std::optional will be // synchronous when not passed a stream. If they do get a stream, // the cuda parts might be partially or fully asynchronous. bool allocateComplexOnDevice(int _iProjectionCount, int _iDetectorCount, cufftComplex ** _ppDevComplex); bool freeComplexOnDevice(cufftComplex * _pDevComplex); bool uploadComplexArrayToDevice(int _iProjectionCount, int _iDetectorCount, cufftComplex * _pHostComplexSource, cufftComplex * _pDevComplexTarget, std::optional _stream = {}); bool runCudaFFT(int _iProjectionCount, const float * D_pfSource, int _iSourcePitch, int _iProjDets, int _iPaddedSize, cufftComplex * D_pcTarget, std::optional _stream = {}); bool runCudaIFFT(int _iProjectionCount, const cufftComplex* D_pcSource, float * D_pfTarget, int _iTargetPitch, int _iProjDets, int _iPaddedSize, std::optional _stream = {}); bool applyFilter(int _iProjectionCount, int _iFreqBinCount, cufftComplex * _pSinogram, cufftComplex * _pFilter, bool singleFilter = false, std::optional _stream = {}); void genCuFFTFilter(const astra::SFilterConfig &_cfg, int _iProjectionCount, cufftComplex * _pFilter, int _iFFTRealDetectorCount, int _iFFTFourierDetectorCount); bool prepareCuFFTFilter(const astra::SFilterConfig &cfg, cufftComplex *&D_filter, bool &singleFilter, int iProjectionCount, int iDetectorCount, std::optional _stream = {}); void genIdenFilter(int _iProjectionCount, cufftComplex * _pFilter, int _iFFTRealDetectorCount, int _iFFTFourierDetectorCount); bool rescaleInverseFourier(int _iProjectionCount, int _iDetectorCount, float * _pfInFourierOutput, std::optional _stream = {}); } #endif /* FFT_H */ astra-toolbox-2.3.0/include/astra/cuda/2d/par_bp.h000066400000000000000000000030221475635207100217130ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_PAR_BP_H #define _CUDA_PAR_BP_H #include "dims.h" namespace astraCUDA { _AstraExport bool BP(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SParProjection* angles, float fOutputScale); _AstraExport bool BP_SART(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, unsigned int angle, const SDimensions& dims, const SParProjection* angles, float fOutputScale); } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/par_fp.h000066400000000000000000000024031475635207100217210ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_PAR_FP_H #define _CUDA_PAR_FP_H namespace astraCUDA { _AstraExport bool FP(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, const SDimensions& dims, const SParProjection* angles, float fOutputScale); } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/sart.h000066400000000000000000000043431475635207100214300ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_SART_H #define _CUDA_SART_H #include "algo.h" namespace astraCUDA { class _AstraExport SART : public ReconAlgo { public: SART(); ~SART(); // disable some features virtual bool enableSinogramMask() { return false; } virtual bool init(); virtual bool setProjectionOrder(int* projectionOrder, int projectionCount); virtual bool iterate(unsigned int iterations); virtual float computeDiffNorm(); void setRelaxation(float r) { fRelaxation = r; } protected: void reset(); bool precomputeWeights(); bool callFP_SART(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, unsigned int angle, float outputScale); bool callBP_SART(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, unsigned int angle, float outputScale); // projection angle variables bool customOrder; int* projectionOrder; int projectionCount; int iteration; // Temporary buffers float* D_projData; unsigned int projPitch; float* D_tmpData; // Only used when there's a volume mask unsigned int tmpPitch; // Geometry-specific precomputed data float* D_lineWeight; unsigned int linePitch; float fRelaxation; }; } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/sirt.h000066400000000000000000000050051475635207100214340ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_SIRT_H #define _CUDA_SIRT_H #include "algo.h" namespace astraCUDA { class _AstraExport SIRT : public ReconAlgo { public: SIRT(); ~SIRT(); virtual bool init(); // Do optional long-object compensation. See the comments in sirt.cu. // Call this after init(). It can not be used in combination with masks. bool doSlabCorrections(); // Set min/max masks to existing GPU memory buffers bool setMinMaxMasks(float* D_minMaskData, float* D_maxMaskData, unsigned int pitch); // Set min/max masks from RAM buffers bool uploadMinMaxMasks(const float* minMaskData, const float* maxMaskData, unsigned int pitch); void setRelaxation(float r) { fRelaxation = r; } virtual bool iterate(unsigned int iterations); virtual float computeDiffNorm(); protected: void reset(); bool precomputeWeights(); // Temporary buffers float* D_projData; unsigned int projPitch; float* D_tmpData; unsigned int tmpPitch; // Geometry-specific precomputed data float* D_lineWeight; unsigned int linePitch; float* D_pixelWeight; unsigned int pixelPitch; // Masks bool freeMinMaxMasks; float* D_minMaskData; unsigned int minMaskPitch; float* D_maxMaskData; unsigned int maxMaskPitch; float fRelaxation; }; bool doSIRT(float* D_volumeData, unsigned int volumePitch, float* D_projData, unsigned int projPitch, float* D_maskData, unsigned int maskPitch, const SDimensions& dims, const float* angles, const float* TOffsets, unsigned int iterations); } #endif astra-toolbox-2.3.0/include/astra/cuda/2d/util.h000066400000000000000000000121711475635207100214320ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_UTIL_H #define _CUDA_UTIL_H #include #include #include #include #include #include "astra/Globals.h" #include "dims.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif namespace astraCUDA { bool copyVolumeToDevice(const float* in_data, unsigned int in_pitch, const SDimensions& dims, float* outD_data, unsigned int out_pitch); bool copyVolumeFromDevice(float* out_data, unsigned int out_pitch, const SDimensions& dims, float* inD_data, unsigned int in_pitch); bool copySinogramFromDevice(float* out_data, unsigned int out_pitch, const SDimensions& dims, float* inD_data, unsigned int in_pitch); bool copySinogramToDevice(const float* in_data, unsigned int in_pitch, const SDimensions& dims, float* outD_data, unsigned int out_pitch); bool allocateVolume(float*& D_ptr, unsigned int width, unsigned int height, unsigned int& pitch); bool zeroVolume(float* D_data, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream = {}); bool allocateVolumeData(float*& D_ptr, unsigned int& pitch, const SDimensions& dims); bool allocateProjectionData(float*& D_ptr, unsigned int& pitch, const SDimensions& dims); bool zeroVolumeData(float* D_ptr, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); bool zeroProjectionData(float* D_ptr, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); bool duplicateVolumeData(float* D_dst, float* D_src, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); bool duplicateProjectionData(float* D_dst, float* D_src, unsigned int pitch, const SDimensions& dims, std::optional _stream = {}); bool createArrayAndTextureObject2D(float* data, cudaArray*& dataArray, cudaTextureObject_t& texObj, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream = {}); bool createTextureObjectPitch2D(float* D_data, cudaTextureObject_t& texObj, unsigned int pitch, unsigned int width, unsigned int height, cudaTextureAddressMode mode = cudaAddressModeBorder); bool checkCuda(cudaError_t err, const char *msg); float dotProduct2D(float* D_data, unsigned int pitch, unsigned int width, unsigned int height, std::optional _stream = {}); // Helper class for functions taking a std::optional argument. // If a stream isn't passed to us, create a new stream and destroy that in our destructor. class StreamHelper { public: StreamHelper(std::optional _stream) { if (_stream) { ok = true; ownsStream = false; stream = _stream.value(); } else { ok = true; ownsStream = true; stream = 0; ok &= checkCuda(cudaStreamCreate(&stream), "StreamHelper create"); } } ~StreamHelper() { if (ownsStream) cudaStreamDestroy(stream); } cudaStream_t operator()() const { return stream; } operator bool() const { return ok; } // Sync on stream if not using an existing stream bool syncIfSync(const char *msg) { if (ownsStream) return sync(msg); else return ok; } bool sync(const char *msg) { ok &= checkCuda(cudaStreamSynchronize(stream), msg); return ok; } private: bool ok; bool ownsStream; cudaStream_t stream; }; // A utility class storing a tuple of vectors (of arbitrary types) of a given // size, to be used as a buffer. // The cudaEvent_t can be used to synchronize access to the buffer. template class TransferConstantsBuffer_t { public: TransferConstantsBuffer_t(size_t count) // Slightly hackish way to construct each vector in the tuple with the count argument. // To be able to use the '...' expansion on count, T needs to appear in the expression, // so we use a comma operator with void(sizeof(T)) which has no effect. : d{ (void(sizeof(T)), count)... } { checkCuda(cudaEventCreateWithFlags(&event, cudaEventDisableTiming), "TransferConstantsBuffer event create"); } ~TransferConstantsBuffer_t() { cudaEventDestroy(event); } std::tuple...> d; cudaEvent_t event; }; } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/000077500000000000000000000000001475635207100203035ustar00rootroot00000000000000astra-toolbox-2.3.0/include/astra/cuda/3d/algo3d.h000066400000000000000000000034361475635207100216330ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_ALGO3D_H #define _CUDA_ALGO3D_H #include "dims3d.h" #include "util3d.h" namespace astraCUDA3d { class _AstraExport ReconAlgo3D { public: ReconAlgo3D(); ~ReconAlgo3D(); bool setConeGeometry(const SDimensions3D& dims, const SConeProjection* projs, const SProjectorParams3D& params); bool setPar3DGeometry(const SDimensions3D& dims, const SPar3DProjection* projs, const SProjectorParams3D& params); protected: void reset(); bool callFP(cudaPitchedPtr& D_volumeData, cudaPitchedPtr& D_projData, float outputScale); bool callBP(cudaPitchedPtr& D_volumeData, cudaPitchedPtr& D_projData, float outputScale); SDimensions3D dims; SProjectorParams3D params; SConeProjection* coneProjs; SPar3DProjection* par3DProjs; float fOutputScale; }; } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/arith3d.h000066400000000000000000000063631475635207100220220ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_ARITH3D_H #define _CUDA_ARITH3D_H #include #include namespace astraCUDA3d { struct opAddScaled; struct opScaleAndAdd; struct opAddMulScaled; struct opAddMul; struct opAdd; struct opMul; struct opMul2; struct opDividedBy; struct opInvert; struct opSet; struct opClampMin; struct opClampMax; template bool processVol3D(cudaPitchedPtr& out, const SDimensions3D& dims, std::optional _stream = {}); template bool processVol3D(cudaPitchedPtr& out, float fParam, const SDimensions3D& dims, std::optional _stream = {}); template bool processVol3D(cudaPitchedPtr& out, const cudaPitchedPtr& in, const SDimensions3D& dims, std::optional _stream = {}); template bool processVol3D(cudaPitchedPtr& out, const cudaPitchedPtr& in, float fParam, const SDimensions3D& dims, std::optional _stream = {}); template bool processVol3D(cudaPitchedPtr& out, const cudaPitchedPtr& in1, const cudaPitchedPtr& in2, float fParam, const SDimensions3D& dims, std::optional _stream = {}); template bool processVol3D(cudaPitchedPtr& out, const cudaPitchedPtr& in1, const cudaPitchedPtr& in2, const SDimensions3D& dims, std::optional _stream = {}); template bool processSino3D(cudaPitchedPtr& out, const SDimensions3D& dims, std::optional _stream = {}); template bool processSino3D(cudaPitchedPtr& out, float fParam, const SDimensions3D& dims, std::optional _stream = {}); template bool processSino3D(cudaPitchedPtr& out, const cudaPitchedPtr& in, const SDimensions3D& dims, std::optional _stream = {}); template bool processSino3D(cudaPitchedPtr& out, const cudaPitchedPtr& in, float fParam, const SDimensions3D& dims, std::optional _stream = {}); template bool processSino3D(cudaPitchedPtr& out, const cudaPitchedPtr& in1, const cudaPitchedPtr& in2, float fParam, const SDimensions3D& dims, std::optional _stream = {}); template bool processSino3D(cudaPitchedPtr& out, const cudaPitchedPtr& in1, const cudaPitchedPtr& in2, const SDimensions3D& dims, std::optional _stream = {}); } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/astra3d.h000066400000000000000000000276661475635207100220360ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_ASTRA3D_H #define _CUDA_ASTRA3D_H #include "dims3d.h" #include #include namespace astra { // TODO: Switch to a class hierarchy as with the 2D algorithms class CProjectionGeometry3D; class CParallelProjectionGeometry3D; class CParallelVecProjectionGeometry3D; class CConeProjectionGeometry3D; class CConeVecProjectionGeometry3D; class CVolumeGeometry3D; class CFloat32ProjectionData3D; class AstraSIRT3d_internal; using astraCUDA3d::Cuda3DProjectionKernel; using astraCUDA3d::ker3d_default; using astraCUDA3d::ker3d_sum_square_weights; using astraCUDA3d::ker3d_fdk_weighting; using astraCUDA3d::ker3d_2d_weighting; using astraCUDA3d::SPar3DProjection; using astraCUDA3d::SConeProjection; class _AstraExport Geometry3DParameters { public: using variant_t = std::variant, std::vector>; Geometry3DParameters() {} Geometry3DParameters(variant_t && p) : projs(p) { } bool isValid() const { return !std::holds_alternative(projs); } bool isParallel() const { return std::holds_alternative>(projs); } bool isCone() const { return std::holds_alternative>(projs); } const SPar3DProjection *getParallel() const { if (!std::holds_alternative>(projs)) return nullptr; return &std::get>(projs)[0]; } const SConeProjection *getCone() const { if (!std::holds_alternative>(projs)) return nullptr; return &std::get>(projs)[0]; } private: variant_t projs; }; class _AstraExport AstraSIRT3d { public: AstraSIRT3d(); ~AstraSIRT3d(); // Set the volume and projection geometry bool setGeometry(const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom); // Enable supersampling. // // The number of rays used in FP is the square of iDetectorSuperSampling. // The number of rays used in BP is the cube of iVoxelSuperSampling. bool enableSuperSampling(unsigned int iVoxelSuperSampling, unsigned int iDetectorSuperSampling); bool setRelaxation(float r); // Enable volume/sinogram masks // // This may optionally be called before init(). // If it is called, setVolumeMask()/setSinogramMask() must be called between // setSinogram() and iterate(). bool enableVolumeMask(); bool enableSinogramMask(); // Set GPU index // // This should be called before init(). Note that setting the GPU index // in a thread which has already used the GPU may not work. bool setGPUIndex(int index); // Allocate GPU buffers and // precompute geometry-specific data. // // This must be called after calling setReconstructionGeometry() and // setProjectionGeometry() or setFanProjectionGeometry(). bool init(); // Setup input sinogram for a slice. // pfSinogram must be a float array of size XXX // NB: iSinogramPitch is measured in floats, not in bytes. // // This must be called after init(), and before iterate(). It may be // called again after iterate()/getReconstruction() to start a new slice. // // pfSinogram will only be read from during this call. bool setSinogram(const float* pfSinogram, unsigned int iSinogramPitch); // Setup volume mask for a slice. // pfMask must be a float array of size XXX // NB: iMaskPitch is measured in floats, not in bytes. // // It may only contain the exact values 0.0f and 1.0f. Only volume pixels // for which pfMask[z] is 1.0f are processed. bool setVolumeMask(const float* pfMask, unsigned int iMaskPitch); // Setup sinogram mask for a slice. // pfMask must be a float array of size XXX // NB: iMaskPitch is measured in floats, not in bytes. // // It may only contain the exact values 0.0f and 1.0f. Only sinogram pixels // for which pfMask[z] is 1.0f are processed. bool setSinogramMask(const float* pfMask, unsigned int iMaskPitch); // Set the starting reconstruction for SIRT. // pfReconstruction must be a float array of size XXX // NB: iReconstructionPitch is measured in floats, not in bytes. // // This may be called between setSinogram() and iterate(). // If this function is not called before iterate(), SIRT will start // from a zero reconstruction. // // pfReconstruction will only be read from during this call. bool setStartReconstruction(const float* pfReconstruction, unsigned int iReconstructionPitch); // Enable min/max constraint. // // These may optionally be called between init() and iterate() bool setMinConstraint(float fMin); bool setMaxConstraint(float fMax); // Perform a number of (additive) SIRT iterations. // This must be called after setSinogram(). // // If called multiple times, without calls to setSinogram() or // setStartReconstruction() in between, iterate() will continue from // the result of the previous call. // Calls to getReconstruction() are allowed between calls to iterate() and // do not change the state. bool iterate(unsigned int iIterations); // Get the reconstructed slice. // pfReconstruction must be a float array of size XXX // NB: iReconstructionPitch is measured in floats, not in bytes. // // This may be called after iterate(). bool getReconstruction(float* pfReconstruction, unsigned int iReconstructionPitch) const; // Compute the norm of the difference of the FP of the current // reconstruction and the sinogram. (This performs one FP.) // It can be called after iterate(). float computeDiffNorm(); protected: AstraSIRT3d_internal *pData; }; class AstraCGLS3d_internal; class _AstraExport AstraCGLS3d { public: AstraCGLS3d(); ~AstraCGLS3d(); // Set the volume and projection geometry bool setGeometry(const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom); // Enable supersampling. // // The number of rays used in FP is the square of iDetectorSuperSampling. // The number of rays used in BP is the cube of iVoxelSuperSampling. bool enableSuperSampling(unsigned int iVoxelSuperSampling, unsigned int iDetectorSuperSampling); // Enable volume/sinogram masks // // This may optionally be called before init(). // If it is called, setVolumeMask()/setSinogramMask() must be called between // setSinogram() and iterate(). bool enableVolumeMask(); //bool enableSinogramMask(); // Set GPU index // // This should be called before init(). Note that setting the GPU index // in a thread which has already used the GPU may not work. bool setGPUIndex(int index); // Allocate GPU buffers and // precompute geometry-specific data. // // This must be called after calling setReconstructionGeometry() and // setProjectionGeometry() or setFanProjectionGeometry(). bool init(); // Setup input sinogram for a slice. // pfSinogram must be a float array of size XXX // NB: iSinogramPitch is measured in floats, not in bytes. // // This must be called after init(), and before iterate(). It may be // called again after iterate()/getReconstruction() to start a new slice. // // pfSinogram will only be read from during this call. bool setSinogram(const float* pfSinogram, unsigned int iSinogramPitch); // Setup volume mask for a slice. // pfMask must be a float array of size XXX // NB: iMaskPitch is measured in floats, not in bytes. // // It may only contain the exact values 0.0f and 1.0f. Only volume pixels // for which pfMask[z] is 1.0f are processed. bool setVolumeMask(const float* pfMask, unsigned int iMaskPitch); // Setup sinogram mask for a slice. // pfMask must be a float array of size XXX // NB: iMaskPitch is measured in floats, not in bytes. // // It may only contain the exact values 0.0f and 1.0f. Only sinogram pixels // for which pfMask[z] is 1.0f are processed. //bool setSinogramMask(const float* pfMask, unsigned int iMaskPitch); // Set the starting reconstruction for SIRT. // pfReconstruction must be a float array of size XXX // NB: iReconstructionPitch is measured in floats, not in bytes. // // This may be called between setSinogram() and iterate(). // If this function is not called before iterate(), SIRT will start // from a zero reconstruction. // // pfReconstruction will only be read from during this call. bool setStartReconstruction(const float* pfReconstruction, unsigned int iReconstructionPitch); // Enable min/max constraint. // // These may optionally be called between init() and iterate() //bool setMinConstraint(float fMin); //bool setMaxConstraint(float fMax); // Perform a number of (additive) SIRT iterations. // This must be called after setSinogram(). // // If called multiple times, without calls to setSinogram() or // setStartReconstruction() in between, iterate() will continue from // the result of the previous call. // Calls to getReconstruction() are allowed between calls to iterate() and // do not change the state. bool iterate(unsigned int iIterations); // Get the reconstructed slice. // pfReconstruction must be a float array of size XXX // NB: iReconstructionPitch is measured in floats, not in bytes. // // This may be called after iterate(). bool getReconstruction(float* pfReconstruction, unsigned int iReconstructionPitch) const; // Compute the norm of the difference of the FP of the current // reconstruction and the sinogram. (This performs one FP.) // It can be called after iterate(). float computeDiffNorm(); protected: AstraCGLS3d_internal *pData; }; bool convertAstraGeometry_dims(const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom, astraCUDA3d::SDimensions3D& dims); Geometry3DParameters convertAstraGeometry(const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom, astraCUDA3d::SProjectorParams3D& params); _AstraExport bool astraCudaFP(const float* pfVolume, float* pfProjections, const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom, int iGPUIndex, int iDetectorSuperSampling, Cuda3DProjectionKernel projKernel); _AstraExport bool astraCudaBP(float* pfVolume, const float* pfProjections, const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom, int iGPUIndex, int iVoxelSuperSampling); _AstraExport bool astraCudaBP_SIRTWeighted(float* pfVolume, const float* pfProjections, const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom, int iGPUIndex, int iVoxelSuperSampling); _AstraExport bool uploadMultipleProjections(CFloat32ProjectionData3D *proj, const float *data, unsigned int y_min, unsigned int y_max); } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/cgls3d.h000066400000000000000000000062331475635207100216370ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_CGLS3D_H #define _CUDA_CGLS3D_H #include "algo3d.h" namespace astraCUDA3d { class _AstraExport CGLS : public ReconAlgo3D { public: CGLS(); ~CGLS(); // bool setConeGeometry(const SDimensions3D& dims, const SConeProjection* projs); bool enableVolumeMask(); bool enableSinogramMask(); // init should be called after setting all geometry bool init(); // setVolumeMask should be called after init and before iterate, // but only if enableVolumeMask was called before init. // It may be called again after iterate. bool setVolumeMask(cudaPitchedPtr& D_maskData); // setSinogramMask should be called after init and before iterate, // but only if enableSinogramMask was called before init. // It may be called again after iterate. bool setSinogramMask(cudaPitchedPtr& D_smaskData); // setBuffers should be called after init and before iterate. // It may be called again after iterate. bool setBuffers(cudaPitchedPtr& D_volumeData, cudaPitchedPtr& D_projData); // set Min/Max constraints. They may be called at any time, and will affect // any iterate() calls afterwards. bool setMinConstraint(float fMin) { return false; } bool setMaxConstraint(float fMax) { return false; } // iterate should be called after init and setBuffers. // It may be called multiple times. bool iterate(unsigned int iterations); // Compute the norm of the difference of the FP of the current reconstruction // and the sinogram. (This performs one FP.) // It can be called after iterate. float computeDiffNorm(); protected: void reset(); bool useVolumeMask; bool useSinogramMask; cudaPitchedPtr D_maskData; cudaPitchedPtr D_smaskData; // Input/output cudaPitchedPtr D_sinoData; cudaPitchedPtr D_volumeData; // Temporary buffers cudaPitchedPtr D_r; cudaPitchedPtr D_w; cudaPitchedPtr D_z; cudaPitchedPtr D_p; float gamma; bool sliceInitialized; }; _AstraExport bool doCGLS(cudaPitchedPtr D_volumeData, unsigned int volumePitch, cudaPitchedPtr D_projData, unsigned int projPitch, cudaPitchedPtr D_maskData, unsigned int maskPitch, const SDimensions3D& dims, const SConeProjection* projs, unsigned int iterations); } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/cone_bp.h000066400000000000000000000027671475635207100220750ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_CONE_BP_H #define _CUDA_CONE_BP_H namespace astraCUDA3d { _AstraExport bool ConeBP_Array(cudaPitchedPtr D_volumeData, cudaArray *D_projArray, const SDimensions3D& dims, const SConeProjection* angles, const SProjectorParams3D& params); _AstraExport bool ConeBP(cudaPitchedPtr D_volumeData, cudaPitchedPtr D_projData, const SDimensions3D& dims, const SConeProjection* angles, const SProjectorParams3D& params); } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/cone_fp.h000066400000000000000000000027531475635207100220740ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_CONE_FP_H #define _CUDA_CONE_FP_H namespace astraCUDA3d { _AstraExport bool ConeFP_Array(cudaArray *D_volArray, cudaPitchedPtr D_projData, const SDimensions3D& dims, const SConeProjection* angles, const SProjectorParams3D& params); _AstraExport bool ConeFP(cudaPitchedPtr D_volumeData, cudaPitchedPtr D_projData, const SDimensions3D& dims, const SConeProjection* angles, const SProjectorParams3D& params); } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/darthelper3d.h000066400000000000000000000025301475635207100230350ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_DARTHELPER3_H #define _CUDA_DARTHELPER3_H #include "dims3d.h" namespace astraCUDA3d { void dartSmoothing(float* out, const float* in, float b, unsigned int radius, SDimensions3D dims); void dartMasking(float* out, const float* in, unsigned int conn, unsigned int radius, unsigned int threshold, SDimensions3D dims); bool setGPUIndex(int index); } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/dims3d.h000066400000000000000000000036241475635207100216440ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_DIMS3D_H #define _CUDA_DIMS3D_H #include "astra/GeometryUtil3D.h" namespace astraCUDA3d { using astra::SConeProjection; using astra::SPar3DProjection; enum Cuda3DProjectionKernel { ker3d_default = 0, ker3d_sum_square_weights, ker3d_fdk_weighting, ker3d_2d_weighting }; struct SDimensions3D { unsigned int iVolX; unsigned int iVolY; unsigned int iVolZ; unsigned int iProjAngles; unsigned int iProjU; // number of detectors in the U direction unsigned int iProjV; // number of detectors in the V direction }; struct SProjectorParams3D { SProjectorParams3D() : iRaysPerDetDim(1), iRaysPerVoxelDim(1), fOutputScale(1.0f), fVolScaleX(1.0f), fVolScaleY(1.0f), fVolScaleZ(1.0f), projKernel(ker3d_default) { } unsigned int iRaysPerDetDim; unsigned int iRaysPerVoxelDim; float fOutputScale; float fVolScaleX; float fVolScaleY; float fVolScaleZ; Cuda3DProjectionKernel projKernel; }; } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/fdk.h000066400000000000000000000033671475635207100212310ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_FDK_H #define _CUDA_FDK_H #include "dims3d.h" namespace astra { struct SFilterConfig; } namespace astraCUDA3d { bool FDK_PreWeight(cudaPitchedPtr D_projData, float fSrcOrigin, float fDetOrigin, float fZShift, float fDetUSize, float fDetVSize, bool bShortScan, const SDimensions3D& dims, const float* angles); bool FDK_Filter(cudaPitchedPtr D_projData, const float *pfFilter, const SDimensions3D& dims); bool FDK(cudaPitchedPtr D_volumeData, cudaPitchedPtr D_projData, const SConeProjection* angles, const SDimensions3D& dims, SProjectorParams3D params, bool bShortScan, const astra::SFilterConfig& filterConfig); } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/mem3d.h000066400000000000000000000072401475635207100214640ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_MEM3D_H #define _CUDA_MEM3D_H #include "astra3d.h" #include namespace astra { class CVolumeGeometry3D; class CProjectionGeometry3D; struct SFilterConfig; } // MemHandle3D defines a very basic opaque interface to GPU memory pointers. // Its intended use is allowing ASTRA code to pass around GPU pointers without // requiring CUDA headers. // // It generally wraps CUDA linear global memory. // // As a very basic extension, it also allows wrapping a CUDA 3D array. // This extension (only) allows creating a CUDA 3D array, copying projection // data into it, performing a BP from the array, and freeing the array. namespace astraCUDA3d { // TODO: Make it possible to delete these handles when they're no longer // necessary inside the FP/BP // // TODO: Add functions for querying capacity struct SMemHandle3D_internal; struct MemHandle3D { std::shared_ptr d; operator bool() const { return (bool)d; } }; struct SSubDimensions3D { unsigned int nx; unsigned int ny; unsigned int nz; unsigned int pitch; unsigned int subnx; unsigned int subny; unsigned int subnz; unsigned int subx; unsigned int suby; unsigned int subz; }; /* // Useful or not? enum Mem3DCopyMode { MODE_SET, MODE_ADD }; */ enum Mem3DZeroMode { INIT_NO, INIT_ZERO }; int maxBlockDimension(); _AstraExport MemHandle3D wrapHandle(float *D_ptr, unsigned int x, unsigned int y, unsigned int z, unsigned int pitch); MemHandle3D createProjectionArrayHandle(const float *ptr, unsigned int x, unsigned int y, unsigned int z); MemHandle3D allocateGPUMemory(unsigned int x, unsigned int y, unsigned int z, Mem3DZeroMode zero); bool copyToGPUMemory(const float *src, MemHandle3D &dst, const SSubDimensions3D &pos); bool copyFromGPUMemory(float *dst, MemHandle3D &src, const SSubDimensions3D &pos); bool freeGPUMemory(MemHandle3D &handle); bool zeroGPUMemory(MemHandle3D &handle, unsigned int x, unsigned int y, unsigned int z); bool setGPUIndex(int index); bool copyIntoArray(MemHandle3D &handle, MemHandle3D &subdata, const SSubDimensions3D &pos); bool FP(const astra::CProjectionGeometry3D* pProjGeom, MemHandle3D &projData, const astra::CVolumeGeometry3D* pVolGeom, MemHandle3D &volData, int iDetectorSuperSampling, astra::Cuda3DProjectionKernel projKernel); bool BP(const astra::CProjectionGeometry3D* pProjGeom, MemHandle3D &projData, const astra::CVolumeGeometry3D* pVolGeom, MemHandle3D &volData, int iVoxelSuperSampling, astra::Cuda3DProjectionKernel projKernel); bool FDK(const astra::CProjectionGeometry3D* pProjGeom, MemHandle3D &projData, const astra::CVolumeGeometry3D* pVolGeom, MemHandle3D &volData, bool bShortScan, const astra::SFilterConfig &filterConfig, float fOutputScale = 1.0f); } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/par3d_bp.h000066400000000000000000000030041475635207100221430ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_PAR3D_BP_H #define _CUDA_PAR3D_BP_H namespace astraCUDA3d { _AstraExport bool Par3DBP_Array(cudaPitchedPtr D_volumeData, cudaArray *D_projArray, const SDimensions3D& dims, const SPar3DProjection* angles, const SProjectorParams3D& params); _AstraExport bool Par3DBP(cudaPitchedPtr D_volumeData, cudaPitchedPtr D_projData, const SDimensions3D& dims, const SPar3DProjection* angles, const SProjectorParams3D& params); } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/par3d_fp.h000066400000000000000000000033501475635207100221530ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_PAR3D_FP_H #define _CUDA_PAR3D_FP_H namespace astraCUDA3d { _AstraExport bool Par3DFP_Array(cudaArray *D_volArray, cudaPitchedPtr D_projData, const SDimensions3D& dims, const SPar3DProjection* angles, const SProjectorParams3D& params); _AstraExport bool Par3DFP(cudaPitchedPtr D_volumeData, cudaPitchedPtr D_projData, const SDimensions3D& dims, const SPar3DProjection* angles, const SProjectorParams3D& params); _AstraExport bool Par3DFP_SumSqW(cudaPitchedPtr D_volumeData, cudaPitchedPtr D_projData, const SDimensions3D& dims, const SPar3DProjection* angles, const SProjectorParams3D& params); } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/sirt3d.h000066400000000000000000000066121475635207100216710ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_SIRT3D_H #define _CUDA_SIRT3D_H #include "algo3d.h" namespace astraCUDA3d { class _AstraExport SIRT : public ReconAlgo3D { public: SIRT(); ~SIRT(); // bool setConeGeometry(const SDimensions3D& dims, const SConeProjection* projs); bool enableVolumeMask(); bool enableSinogramMask(); // Set relaxation factor. This must be called before init. void setRelaxation(float r) { fRelaxation = r; } // init should be called after setting all geometry bool init(); // setVolumeMask should be called after init and before iterate, // but only if enableVolumeMask was called before init. // It may be called again after iterate. bool setVolumeMask(cudaPitchedPtr& D_maskData); // setSinogramMask should be called after init and before iterate, // but only if enableSinogramMask was called before init. // It may be called again after iterate. bool setSinogramMask(cudaPitchedPtr& D_smaskData); // setBuffers should be called after init and before iterate. // It may be called again after iterate. bool setBuffers(cudaPitchedPtr& D_volumeData, cudaPitchedPtr& D_projData); // set Min/Max constraints. They may be called at any time, and will affect // any iterate() calls afterwards. bool setMinConstraint(float fMin); bool setMaxConstraint(float fMax); // iterate should be called after init and setBuffers. // It may be called multiple times. bool iterate(unsigned int iterations); // Compute the norm of the difference of the FP of the current reconstruction // and the sinogram. (This performs one FP.) // It can be called after iterate. float computeDiffNorm(); protected: void reset(); bool precomputeWeights(); bool useVolumeMask; bool useSinogramMask; bool useMinConstraint; bool useMaxConstraint; float fMinConstraint; float fMaxConstraint; float fRelaxation; cudaPitchedPtr D_maskData; cudaPitchedPtr D_smaskData; // Input/output cudaPitchedPtr D_sinoData; cudaPitchedPtr D_volumeData; // Temporary buffers cudaPitchedPtr D_projData; cudaPitchedPtr D_tmpData; // Geometry-specific precomputed data cudaPitchedPtr D_lineWeight; cudaPitchedPtr D_pixelWeight; }; bool doSIRT(cudaPitchedPtr D_volumeData, unsigned int volumePitch, cudaPitchedPtr D_projData, unsigned int projPitch, cudaPitchedPtr D_maskData, unsigned int maskPitch, const SDimensions3D& dims, const SConeProjection* projs, unsigned int iterations); } #endif astra-toolbox-2.3.0/include/astra/cuda/3d/util3d.h000066400000000000000000000103261475635207100216620ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _CUDA_UTIL3D_H #define _CUDA_UTIL3D_H #include #include #include "dims3d.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #include "../2d/util.h" namespace astraCUDA3d { using astraCUDA::checkCuda; using astraCUDA::TransferConstantsBuffer_t; using astraCUDA::StreamHelper; cudaPitchedPtr allocateVolumeData(const SDimensions3D& dims); cudaPitchedPtr allocateProjectionData(const SDimensions3D& dims); bool zeroVolumeData(cudaPitchedPtr& D_data, const SDimensions3D& dims); bool zeroProjectionData(cudaPitchedPtr& D_data, const SDimensions3D& dims); bool copyVolumeToDevice(const float* data, cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch = 0); bool copyProjectionsToDevice(const float* data, cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch = 0); bool copyVolumeFromDevice(float* data, const cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch = 0); bool copyProjectionsFromDevice(float* data, const cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch = 0); bool duplicateVolumeData(cudaPitchedPtr& D_dest, const cudaPitchedPtr& D_src, const SDimensions3D& dims); bool duplicateProjectionData(cudaPitchedPtr& D_dest, const cudaPitchedPtr& D_src, const SDimensions3D& dims); bool transferProjectionsToArray(cudaPitchedPtr D_projData, cudaArray* array, const SDimensions3D& dims, std::optional _stream = {}); bool transferHostProjectionsToArray(const float *projData, cudaArray* array, const SDimensions3D& dims); bool transferVolumeToArray(cudaPitchedPtr D_volumeData, cudaArray* array, const SDimensions3D& dims, std::optional _stream = {}); bool zeroProjectionArray(cudaArray* array, const SDimensions3D& dims); bool zeroVolumeArray(cudaArray* array, const SDimensions3D& dims); cudaArray* allocateProjectionArray(const SDimensions3D& dims); cudaArray* allocateVolumeArray(const SDimensions3D& dims); bool createTextureObject3D(cudaArray* array, cudaTextureObject_t& texObj); float dotProduct3D(cudaPitchedPtr data, unsigned int x, unsigned int y, unsigned int z); int calcNextPowerOfTwo(int _iValue); struct Vec3 { double x; double y; double z; Vec3(double x_, double y_, double z_) : x(x_), y(y_), z(z_) { } Vec3 operator+(const Vec3 &b) const { return Vec3(x + b.x, y + b.y, z + b.z); } Vec3 operator-(const Vec3 &b) const { return Vec3(x - b.x, y - b.y, z - b.z); } Vec3 operator-() const { return Vec3(-x, -y, -z); } Vec3 operator*(double s) { return Vec3(s*x, s*y, s*z); } double norm() const { return sqrt(x*x + y*y + z*z); } }; static double det3x(const Vec3 &b, const Vec3 &c) { return (b.y * c.z - b.z * c.y); } static double det3y(const Vec3 &b, const Vec3 &c) { return -(b.x * c.z - b.z * c.x); } static double det3z(const Vec3 &b, const Vec3 &c) { return (b.x * c.y - b.y * c.x); } static double det3(const Vec3 &a, const Vec3 &b, const Vec3 &c) { return a.x * det3x(b,c) + a.y * det3y(b,c) + a.z * det3z(b,c); } static Vec3 cross3(const Vec3 &a, const Vec3 &b) { return Vec3(det3x(a,b), det3y(a,b), det3z(a,b)); } static Vec3 scaled_cross3(const Vec3 &a, const Vec3 &b, const Vec3 &sc) { Vec3 ret = cross3(a, b); ret.x *= sc.y * sc.z; ret.y *= sc.x * sc.z; ret.z *= sc.x * sc.y; return ret; } } #endif astra-toolbox-2.3.0/lib/000077500000000000000000000000001475635207100150725ustar00rootroot00000000000000astra-toolbox-2.3.0/lib/include/000077500000000000000000000000001475635207100165155ustar00rootroot00000000000000astra-toolbox-2.3.0/lib/include/dlpack/000077500000000000000000000000001475635207100177535ustar00rootroot00000000000000astra-toolbox-2.3.0/lib/include/dlpack/dlpack.h000066400000000000000000000240221475635207100213620ustar00rootroot00000000000000/*! * Copyright (c) 2017 by Contributors * \file dlpack.h * \brief The common header of DLPack. */ #ifndef DLPACK_DLPACK_H_ #define DLPACK_DLPACK_H_ /** * \brief Compatibility with C++ */ #ifdef __cplusplus #define DLPACK_EXTERN_C extern "C" #else #define DLPACK_EXTERN_C #endif /*! \brief The current major version of dlpack */ #define DLPACK_MAJOR_VERSION 1 /*! \brief The current minor version of dlpack */ #define DLPACK_MINOR_VERSION 0 /*! \brief DLPACK_DLL prefix for windows */ #ifdef _WIN32 #ifdef DLPACK_EXPORTS #define DLPACK_DLL __declspec(dllexport) #else #define DLPACK_DLL __declspec(dllimport) #endif #else #define DLPACK_DLL #endif #include #include #ifdef __cplusplus extern "C" { #endif /*! * \brief The DLPack version. * * A change in major version indicates that we have changed the * data layout of the ABI - DLManagedTensorVersioned. * * A change in minor version indicates that we have added new * code, such as a new device type, but the ABI is kept the same. * * If an obtained DLPack tensor has a major version that disagrees * with the version number specified in this header file * (i.e. major != DLPACK_MAJOR_VERSION), the consumer must call the deleter * (and it is safe to do so). It is not safe to access any other fields * as the memory layout will have changed. * * In the case of a minor version mismatch, the tensor can be safely used as * long as the consumer knows how to interpret all fields. Minor version * updates indicate the addition of enumeration values. */ typedef struct { /*! \brief DLPack major version. */ uint32_t major; /*! \brief DLPack minor version. */ uint32_t minor; } DLPackVersion; /*! * \brief The device type in DLDevice. */ #ifdef __cplusplus typedef enum : int32_t { #else typedef enum { #endif /*! \brief CPU device */ kDLCPU = 1, /*! \brief CUDA GPU device */ kDLCUDA = 2, /*! * \brief Pinned CUDA CPU memory by cudaMallocHost */ kDLCUDAHost = 3, /*! \brief OpenCL devices. */ kDLOpenCL = 4, /*! \brief Vulkan buffer for next generation graphics. */ kDLVulkan = 7, /*! \brief Metal for Apple GPU. */ kDLMetal = 8, /*! \brief Verilog simulator buffer */ kDLVPI = 9, /*! \brief ROCm GPUs for AMD GPUs */ kDLROCM = 10, /*! * \brief Pinned ROCm CPU memory allocated by hipMallocHost */ kDLROCMHost = 11, /*! * \brief Reserved extension device type, * used for quickly test extension device * The semantics can differ depending on the implementation. */ kDLExtDev = 12, /*! * \brief CUDA managed/unified memory allocated by cudaMallocManaged */ kDLCUDAManaged = 13, /*! * \brief Unified shared memory allocated on a oneAPI non-partititioned * device. Call to oneAPI runtime is required to determine the device * type, the USM allocation type and the sycl context it is bound to. * */ kDLOneAPI = 14, /*! \brief GPU support for next generation WebGPU standard. */ kDLWebGPU = 15, /*! \brief Qualcomm Hexagon DSP */ kDLHexagon = 16, /*! \brief Microsoft MAIA devices */ kDLMAIA = 17, } DLDeviceType; /*! * \brief A Device for Tensor and operator. */ typedef struct { /*! \brief The device type used in the device. */ DLDeviceType device_type; /*! * \brief The device index. * For vanilla CPU memory, pinned memory, or managed memory, this is set to 0. */ int32_t device_id; } DLDevice; /*! * \brief The type code options DLDataType. */ typedef enum { /*! \brief signed integer */ kDLInt = 0U, /*! \brief unsigned integer */ kDLUInt = 1U, /*! \brief IEEE floating point */ kDLFloat = 2U, /*! * \brief Opaque handle type, reserved for testing purposes. * Frameworks need to agree on the handle data type for the exchange to be well-defined. */ kDLOpaqueHandle = 3U, /*! \brief bfloat16 */ kDLBfloat = 4U, /*! * \brief complex number * (C/C++/Python layout: compact struct per complex number) */ kDLComplex = 5U, /*! \brief boolean */ kDLBool = 6U, } DLDataTypeCode; /*! * \brief The data type the tensor can hold. The data type is assumed to follow the * native endian-ness. An explicit error message should be raised when attempting to * export an array with non-native endianness * * Examples * - float: type_code = 2, bits = 32, lanes = 1 * - float4(vectorized 4 float): type_code = 2, bits = 32, lanes = 4 * - int8: type_code = 0, bits = 8, lanes = 1 * - std::complex: type_code = 5, bits = 64, lanes = 1 * - bool: type_code = 6, bits = 8, lanes = 1 (as per common array library convention, the underlying storage size of bool is 8 bits) */ typedef struct { /*! * \brief Type code of base types. * We keep it uint8_t instead of DLDataTypeCode for minimal memory * footprint, but the value should be one of DLDataTypeCode enum values. * */ uint8_t code; /*! * \brief Number of bits, common choices are 8, 16, 32. */ uint8_t bits; /*! \brief Number of lanes in the type, used for vector types. */ uint16_t lanes; } DLDataType; /*! * \brief Plain C Tensor object, does not manage memory. */ typedef struct { /*! * \brief The data pointer points to the allocated data. This will be CUDA * device pointer or cl_mem handle in OpenCL. It may be opaque on some device * types. This pointer is always aligned to 256 bytes as in CUDA. The * `byte_offset` field should be used to point to the beginning of the data. * * Note that as of Nov 2021, multiply libraries (CuPy, PyTorch, TensorFlow, * TVM, perhaps others) do not adhere to this 256 byte aligment requirement * on CPU/CUDA/ROCm, and always use `byte_offset=0`. This must be fixed * (after which this note will be updated); at the moment it is recommended * to not rely on the data pointer being correctly aligned. * * For given DLTensor, the size of memory required to store the contents of * data is calculated as follows: * * \code{.c} * static inline size_t GetDataSize(const DLTensor* t) { * size_t size = 1; * for (tvm_index_t i = 0; i < t->ndim; ++i) { * size *= t->shape[i]; * } * size *= (t->dtype.bits * t->dtype.lanes + 7) / 8; * return size; * } * \endcode * * Note that if the tensor is of size zero, then the data pointer should be * set to `NULL`. */ void* data; /*! \brief The device of the tensor */ DLDevice device; /*! \brief Number of dimensions */ int32_t ndim; /*! \brief The data type of the pointer*/ DLDataType dtype; /*! \brief The shape of the tensor */ int64_t* shape; /*! * \brief strides of the tensor (in number of elements, not bytes) * can be NULL, indicating tensor is compact and row-majored. */ int64_t* strides; /*! \brief The offset in bytes to the beginning pointer to data */ uint64_t byte_offset; } DLTensor; /*! * \brief C Tensor object, manage memory of DLTensor. This data structure is * intended to facilitate the borrowing of DLTensor by another framework. It is * not meant to transfer the tensor. When the borrowing framework doesn't need * the tensor, it should call the deleter to notify the host that the resource * is no longer needed. * * \note This data structure is used as Legacy DLManagedTensor * in DLPack exchange and is deprecated after DLPack v0.8 * Use DLManagedTensorVersioned instead. * This data structure may get renamed or deleted in future versions. * * \sa DLManagedTensorVersioned */ typedef struct DLManagedTensor { /*! \brief DLTensor which is being memory managed */ DLTensor dl_tensor; /*! \brief the context of the original host framework of DLManagedTensor in * which DLManagedTensor is used in the framework. It can also be NULL. */ void * manager_ctx; /*! * \brief Destructor - this should be called * to destruct the manager_ctx which backs the DLManagedTensor. It can be * NULL if there is no way for the caller to provide a reasonable destructor. * The destructor deletes the argument self as well. */ void (*deleter)(struct DLManagedTensor * self); } DLManagedTensor; // bit masks used in in the DLManagedTensorVersioned /*! \brief bit mask to indicate that the tensor is read only. */ #define DLPACK_FLAG_BITMASK_READ_ONLY (1UL << 0UL) /*! * \brief bit mask to indicate that the tensor is a copy made by the producer. * * If set, the tensor is considered solely owned throughout its lifetime by the * consumer, until the producer-provided deleter is invoked. */ #define DLPACK_FLAG_BITMASK_IS_COPIED (1UL << 1UL) /*! * \brief A versioned and managed C Tensor object, manage memory of DLTensor. * * This data structure is intended to facilitate the borrowing of DLTensor by * another framework. It is not meant to transfer the tensor. When the borrowing * framework doesn't need the tensor, it should call the deleter to notify the * host that the resource is no longer needed. * * \note This is the current standard DLPack exchange data structure. */ struct DLManagedTensorVersioned { /*! * \brief The API and ABI version of the current managed Tensor */ DLPackVersion version; /*! * \brief the context of the original host framework. * * Stores DLManagedTensorVersioned is used in the * framework. It can also be NULL. */ void *manager_ctx; /*! * \brief Destructor. * * This should be called to destruct manager_ctx which holds the DLManagedTensorVersioned. * It can be NULL if there is no way for the caller to provide a reasonable * destructor. The destructor deletes the argument self as well. */ void (*deleter)(struct DLManagedTensorVersioned *self); /*! * \brief Additional bitmask flags information about the tensor. * * By default the flags should be set to 0. * * \note Future ABI changes should keep everything until this field * stable, to ensure that deleter can be correctly called. * * \sa DLPACK_FLAG_BITMASK_READ_ONLY * \sa DLPACK_FLAG_BITMASK_IS_COPIED */ uint64_t flags; /*! \brief DLTensor which is being memory managed */ DLTensor dl_tensor; }; #ifdef __cplusplus } // DLPACK_EXTERN_C #endif #endif // DLPACK_DLPACK_H_ astra-toolbox-2.3.0/lib/include/rapidxml/000077500000000000000000000000001475635207100203355ustar00rootroot00000000000000astra-toolbox-2.3.0/lib/include/rapidxml/rapidxml.hpp000066400000000000000000003471111475635207100226750ustar00rootroot00000000000000#ifndef RAPIDXML_HPP_INCLUDED #define RAPIDXML_HPP_INCLUDED // Copyright (C) 2006, 2009 Marcin Kalicinski // Version 1.13 // Revision $DateTime: 2009/05/13 01:46:17 $ //! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation // If standard library is disabled, user must provide implementations of required functions and typedefs #if !defined(RAPIDXML_NO_STDLIB) #include // For std::size_t #include // For assert #include // For placement new #endif // On MSVC, disable "conditional expression is constant" warning (level 4). // This warning is almost impossible to avoid with certain types of templated code #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4127) // Conditional expression is constant #endif /////////////////////////////////////////////////////////////////////////// // RAPIDXML_PARSE_ERROR #if defined(RAPIDXML_NO_EXCEPTIONS) #define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } namespace rapidxml { //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, //! this function is called to notify user about the error. //! It must be defined by the user. //!

//! This function cannot return. If it does, the results are undefined. //!

//! A very simple definition might look like that: //!

    //! void %rapidxml::%parse_error_handler(const char *what, void *where)
    //! {
    //!     std::cout << "Parse error: " << what << "\n";
    //!     std::abort();
    //! }
    //! 
//! \param what Human readable description of the error. //! \param where Pointer to character data where error was detected. void parse_error_handler(const char *what, void *where); } #else #include // For std::exception #define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) namespace rapidxml { //! Parse error exception. //! This exception is thrown by the parser when an error occurs. //! Use what() function to get human-readable error message. //! Use where() function to get a pointer to position within source text where error was detected. //!

//! If throwing exceptions by the parser is undesirable, //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. //! This function must be defined by the user. //!

//! This class derives from std::exception class. class parse_error: public std::exception { public: //! Constructs parse error parse_error(const char *what_, void *where_) : m_what(what_) , m_where(where_) { } //! Gets human readable description of error. //! \return Pointer to null terminated description of the error. virtual const char *what() const throw() { return m_what; } //! Gets pointer to character data where error happened. //! Ch should be the same as char type of xml_document that produced the error. //! \return Pointer to location within the parsed string where error occured. template Ch *where() const { return reinterpret_cast(m_where); } private: const char *m_what; void *m_where; }; } #endif /////////////////////////////////////////////////////////////////////////// // Pool sizes #ifndef RAPIDXML_STATIC_POOL_SIZE // Size of static memory block of memory_pool. // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. // No dynamic memory allocations are performed by memory_pool until static memory is exhausted. #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) #endif #ifndef RAPIDXML_DYNAMIC_POOL_SIZE // Size of dynamic memory block of memory_pool. // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) #endif #ifndef RAPIDXML_ALIGNMENT // Memory allocation alignment. // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. // All memory allocations for nodes, attributes and strings will be aligned to this value. // This must be a power of 2 and at least 1, otherwise memory_pool will not work. #define RAPIDXML_ALIGNMENT sizeof(void *) #endif namespace rapidxml { // Forward declarations template class xml_node; template class xml_attribute; template class xml_document; //! Enumeration listing all node types produced by the parser. //! Use xml_node::type() function to query node type. enum node_type { node_document, //!< A document node. Name and value are empty. node_element, //!< An element node. Name contains element name. Value contains text of first data node. node_data, //!< A data node. Name is empty. Value contains data text. node_cdata, //!< A CDATA node. Name is empty. Value contains data text. node_comment, //!< A comment node. Name is empty. Value contains comment text. node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. node_pi //!< A PI node. Name contains target. Value contains instructions. }; /////////////////////////////////////////////////////////////////////// // Parsing flags //! Parse flag instructing the parser to not create data nodes. //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. //! Can be combined with other flags by use of | operator. //!

//! See xml_document::parse() function. const int parse_no_data_nodes = 0x1; //! Parse flag instructing the parser to not use text of first data node as a value of parent element. //! Can be combined with other flags by use of | operator. //! Note that child data nodes of element node take precendence over its value when printing. //! That is, if element has one or more child data nodes and a value, the value will be ignored. //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. //!

//! See xml_document::parse() function. const int parse_no_element_values = 0x2; //! Parse flag instructing the parser to not place zero terminators after strings in the source text. //! By default zero terminators are placed, modifying source text. //! Can be combined with other flags by use of | operator. //!

//! See xml_document::parse() function. const int parse_no_string_terminators = 0x4; //! Parse flag instructing the parser to not translate entities in the source text. //! By default entities are translated, modifying source text. //! Can be combined with other flags by use of | operator. //!

//! See xml_document::parse() function. const int parse_no_entity_translation = 0x8; //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. //! By default, UTF-8 handling is enabled. //! Can be combined with other flags by use of | operator. //!

//! See xml_document::parse() function. const int parse_no_utf8 = 0x10; //! Parse flag instructing the parser to create XML declaration node. //! By default, declaration node is not created. //! Can be combined with other flags by use of | operator. //!

//! See xml_document::parse() function. const int parse_declaration_node = 0x20; //! Parse flag instructing the parser to create comments nodes. //! By default, comment nodes are not created. //! Can be combined with other flags by use of | operator. //!

//! See xml_document::parse() function. const int parse_comment_nodes = 0x40; //! Parse flag instructing the parser to create DOCTYPE node. //! By default, doctype node is not created. //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. //! Can be combined with other flags by use of | operator. //!

//! See xml_document::parse() function. const int parse_doctype_node = 0x80; //! Parse flag instructing the parser to create PI nodes. //! By default, PI nodes are not created. //! Can be combined with other flags by use of | operator. //!

//! See xml_document::parse() function. const int parse_pi_nodes = 0x100; //! Parse flag instructing the parser to validate closing tag names. //! If not set, name inside closing tag is irrelevant to the parser. //! By default, closing tags are not validated. //! Can be combined with other flags by use of | operator. //!

//! See xml_document::parse() function. const int parse_validate_closing_tags = 0x200; //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. //! By default, whitespace is not trimmed. //! This flag does not cause the parser to modify source text. //! Can be combined with other flags by use of | operator. //!

//! See xml_document::parse() function. const int parse_trim_whitespace = 0x400; //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. //! By default, whitespace is not normalized. //! If this flag is specified, source text will be modified. //! Can be combined with other flags by use of | operator. //!

//! See xml_document::parse() function. const int parse_normalize_whitespace = 0x800; // Compound flags //! Parse flags which represent default behaviour of the parser. //! This is always equal to 0, so that all other flags can be simply ored together. //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. //! This also means that meaning of each flag is a negation of the default setting. //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, //! and using the flag will disable it. //!

//! See xml_document::parse() function. const int parse_default = 0; //! A combination of parse flags that forbids any modifications of the source text. //! This also results in faster parsing. However, note that the following will occur: //!
    //!
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • //!
  • entities will not be translated
  • //!
  • whitespace will not be normalized
  • //!
//! See xml_document::parse() function. const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. //!

//! See xml_document::parse() function. const int parse_fastest = parse_non_destructive | parse_no_data_nodes; //! A combination of parse flags resulting in largest amount of data being extracted. //! This usually results in slowest parsing. //!

//! See xml_document::parse() function. const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; /////////////////////////////////////////////////////////////////////// // Internals //! \cond internal namespace internal { // Struct that contains lookup tables for the parser // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). template struct lookup_tables { static const unsigned char lookup_whitespace[256]; // Whitespace table static const unsigned char lookup_node_name[256]; // Node name table static const unsigned char lookup_text[256]; // Text table static const unsigned char lookup_text_pure_no_ws[256]; // Text table static const unsigned char lookup_text_pure_with_ws[256]; // Text table static const unsigned char lookup_attribute_name[256]; // Attribute name table static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes static const unsigned char lookup_digits[256]; // Digits static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters }; // Find length of the string template inline std::size_t measure(const Ch *p) { const Ch *tmp = p; while (*tmp) ++tmp; return tmp - p; } // Compare strings for equality template inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) { if (size1 != size2) return false; if (case_sensitive) { for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) if (*p1 != *p2) return false; } else { for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast(*p2)]) return false; } return true; } } //! \endcond /////////////////////////////////////////////////////////////////////// // Memory pool //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. //! In most cases, you will not need to use this class directly. //! However, if you need to create nodes manually or modify names/values of nodes, //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. //! Not only is this faster than allocating them by using new operator, //! but also their lifetime will be tied to the lifetime of document, //! possibly simplyfing memory management. //!

//! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. //! You can also call allocate_string() function to allocate strings. //! Such strings can then be used as names or values of nodes without worrying about their lifetime. //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, //! or when the pool is destroyed. //!

//! It is also possible to create a standalone memory_pool, and use it //! to allocate nodes, whose lifetime will not be tied to any document. //!

//! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. //! Until static memory is exhausted, no dynamic memory allocations are done. //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, //! by using global new[] and delete[] operators. //! This behaviour can be changed by setting custom allocation routines. //! Use set_allocator() function to set them. //!

//! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. //! This value defaults to the size of pointer on target architecture. //!

//! To obtain absolutely top performance from the parser, //! it is important that all nodes are allocated from a single, contiguous block of memory. //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT //! to obtain best wasted memory to performance compromise. //! To do it, define their values before rapidxml.hpp file is included. //! \param Ch Character type of created nodes. template class memory_pool { public: //! \cond internal typedef void *(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory typedef void (free_func)(void *); // Type of user-defined function used to free memory //! \endcond //! Constructs empty pool with default allocator functions. memory_pool() : m_alloc_func(0) , m_free_func(0) { init(); } //! Destroys pool and frees all the memory. //! This causes memory occupied by nodes allocated by the pool to be freed. //! Nodes allocated from the pool are no longer valid. ~memory_pool() { clear(); } //! Allocates a new node from the pool, and optionally assigns name and value to it. //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function //! will call rapidxml::parse_error_handler() function. //! \param type Type of node to create. //! \param name Name to assign to the node, or 0 to assign no name. //! \param value Value to assign to the node, or 0 to assign no value. //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. //! \return Pointer to allocated node. This pointer will never be NULL. xml_node *allocate_node(node_type type, const Ch *name = 0, const Ch *value = 0, std::size_t name_size = 0, std::size_t value_size = 0) { void *memory = allocate_aligned(sizeof(xml_node)); xml_node *node = new(memory) xml_node(type); if (name) { if (name_size > 0) node->name(name, name_size); else node->name(name); } if (value) { if (value_size > 0) node->value(value, value_size); else node->value(value); } return node; } //! Allocates a new attribute from the pool, and optionally assigns name and value to it. //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function //! will call rapidxml::parse_error_handler() function. //! \param name Name to assign to the attribute, or 0 to assign no name. //! \param value Value to assign to the attribute, or 0 to assign no value. //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. //! \return Pointer to allocated attribute. This pointer will never be NULL. xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, std::size_t name_size = 0, std::size_t value_size = 0) { void *memory = allocate_aligned(sizeof(xml_attribute)); xml_attribute *attribute = new(memory) xml_attribute; if (name) { if (name_size > 0) attribute->name(name, name_size); else attribute->name(name); } if (value) { if (value_size > 0) attribute->value(value, value_size); else attribute->value(value); } return attribute; } //! Allocates a char array of given size from the pool, and optionally copies a given string to it. //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function //! will call rapidxml::parse_error_handler() function. //! \param source String to initialize the allocated memory with, or 0 to not initialize it. //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. //! \return Pointer to allocated char array. This pointer will never be NULL. Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) { assert(source || size); // Either source or size (or both) must be specified if (size == 0) size = internal::measure(source) + 1; Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); if (source) for (std::size_t i = 0; i < size; ++i) result[i] = source[i]; return result; } //! Clones an xml_node and its hierarchy of child nodes and attributes. //! Nodes and attributes are allocated from this memory pool. //! Names and values are not cloned, they are shared between the clone and the source. //! Result node can be optionally specified as a second parameter, //! in which case its contents will be replaced with cloned source node. //! This is useful when you want to clone entire document. //! \param source Node to clone. //! \param result Node to put results in, or 0 to automatically allocate result node //! \return Pointer to cloned node. This pointer will never be NULL. xml_node *clone_node(const xml_node *source, xml_node *result = 0) { // Prepare result node if (result) { result->remove_all_attributes(); result->remove_all_nodes(); result->type(source->type()); } else result = allocate_node(source->type()); // Clone name and value result->name(source->name(), source->name_size()); result->value(source->value(), source->value_size()); // Clone child nodes and attributes for (xml_node *child = source->first_node(); child; child = child->next_sibling()) result->append_node(clone_node(child)); for (xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); return result; } //! Clears the pool. //! This causes memory occupied by nodes allocated by the pool to be freed. //! Any nodes or strings allocated from the pool will no longer be valid. void clear() { while (m_begin != m_static_memory) { char *previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; if (m_free_func) m_free_func(m_begin); else delete[] m_begin; m_begin = previous_begin; } init(); } //! Sets or resets the user-defined memory allocation functions for the pool. //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. //! Allocation function must not return invalid pointer on failure. It should either throw, //! stop the program, or use longjmp() function to pass control to other place of program. //! If it returns invalid pointer, results are undefined. //!

//! User defined allocation functions must have the following forms: //!
//!
void *allocate(std::size_t size); //!
void free(void *pointer); //!

//! \param af Allocation function, or 0 to restore default function //! \param ff Free function, or 0 to restore default function void set_allocator(alloc_func *af, free_func *ff) { assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet m_alloc_func = af; m_free_func = ff; } private: struct header { char *previous_begin; }; void init() { m_begin = m_static_memory; m_ptr = align(m_begin); m_end = m_static_memory + sizeof(m_static_memory); } char *align(char *ptr) { std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); return ptr + alignment; } char *allocate_raw(std::size_t size) { // Allocate void *memory; if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] { memory = m_alloc_func(size); assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp } else { memory = new char[size]; #ifdef RAPIDXML_NO_EXCEPTIONS if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc RAPIDXML_PARSE_ERROR("out of memory", 0); #endif } return static_cast(memory); } void *allocate_aligned(std::size_t size) { // Calculate aligned pointer char *result = align(m_ptr); // If not enough memory left in current pool, allocate a new pool if (result + size > m_end) { // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; if (pool_size < size) pool_size = size; // Allocate std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation char *raw_memory = allocate_raw(alloc_size); // Setup new pool in allocated memory char *pool = align(raw_memory); header *new_header = reinterpret_cast
(pool); new_header->previous_begin = m_begin; m_begin = raw_memory; m_ptr = pool + sizeof(header); m_end = raw_memory + alloc_size; // Calculate aligned pointer again using new pool result = align(m_ptr); } // Update pool and return aligned pointer m_ptr = result + size; return result; } char *m_begin; // Start of raw memory making up current pool char *m_ptr; // First free byte in current pool char *m_end; // One past last available byte in current pool char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory alloc_func *m_alloc_func; // Allocator function, or 0 if default is to be used free_func *m_free_func; // Free function, or 0 if default is to be used }; /////////////////////////////////////////////////////////////////////////// // XML base //! Base class for xml_node and xml_attribute implementing common functions: //! name(), name_size(), value(), value_size() and parent(). //! \param Ch Character type to use template class xml_base { public: /////////////////////////////////////////////////////////////////////////// // Construction & destruction // Construct a base with empty name, value and parent xml_base() : m_name(0) , m_value(0) , m_parent(0) { } /////////////////////////////////////////////////////////////////////////// // Node data access //! Gets name of the node. //! Interpretation of name depends on type of node. //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. //!

//! Use name_size() function to determine length of the name. //! \return Name of node, or empty string if node has no name. Ch *name() const { return m_name ? m_name : nullstr(); } //! Gets size of node name, not including terminator character. //! This function works correctly irrespective of whether name is or is not zero terminated. //! \return Size of node name, in characters. std::size_t name_size() const { return m_name ? m_name_size : 0; } //! Gets value of node. //! Interpretation of value depends on type of node. //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. //!

//! Use value_size() function to determine length of the value. //! \return Value of node, or empty string if node has no value. Ch *value() const { return m_value ? m_value : nullstr(); } //! Gets size of node value, not including terminator character. //! This function works correctly irrespective of whether value is or is not zero terminated. //! \return Size of node value, in characters. std::size_t value_size() const { return m_value ? m_value_size : 0; } /////////////////////////////////////////////////////////////////////////// // Node modification //! Sets name of node to a non zero-terminated string. //! See \ref ownership_of_strings. //!

//! Note that node does not own its name or value, it only stores a pointer to it. //! It will not delete or otherwise free the pointer on destruction. //! It is reponsibility of the user to properly manage lifetime of the string. //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - //! on destruction of the document the string will be automatically freed. //!

//! Size of name must be specified separately, because name does not have to be zero terminated. //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). //! \param name Name of node to set. Does not have to be zero terminated. //! \param size Size of name, in characters. This does not include zero terminator, if one is present. void name(const Ch *name, std::size_t size) { m_name = const_cast(name); m_name_size = size; } //! Sets name of node to a zero-terminated string. //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). //! \param name Name of node to set. Must be zero terminated. void name(const Ch *name) { this->name(name, internal::measure(name)); } //! Sets value of node to a non zero-terminated string. //! See \ref ownership_of_strings. //!

//! Note that node does not own its name or value, it only stores a pointer to it. //! It will not delete or otherwise free the pointer on destruction. //! It is reponsibility of the user to properly manage lifetime of the string. //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - //! on destruction of the document the string will be automatically freed. //!

//! Size of value must be specified separately, because it does not have to be zero terminated. //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). //!

//! If an element has a child node of type node_data, it will take precedence over element value when printing. //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. //! \param value value of node to set. Does not have to be zero terminated. //! \param size Size of value, in characters. This does not include zero terminator, if one is present. void value(const Ch *value, std::size_t size) { m_value = const_cast(value); m_value_size = size; } //! Sets value of node to a zero-terminated string. //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). //! \param value Vame of node to set. Must be zero terminated. void value(const Ch *value) { this->value(value, internal::measure(value)); } /////////////////////////////////////////////////////////////////////////// // Related nodes access //! Gets node parent. //! \return Pointer to parent node, or 0 if there is no parent. xml_node *parent() const { return m_parent; } protected: // Return empty string static Ch *nullstr() { static Ch zero = Ch('\0'); return &zero; } Ch *m_name; // Name of node, or 0 if no name Ch *m_value; // Value of node, or 0 if no value std::size_t m_name_size; // Length of node name, or undefined of no name std::size_t m_value_size; // Length of node value, or undefined if no value xml_node *m_parent; // Pointer to parent node, or 0 if none }; //! Class representing attribute node of XML document. //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. //! Thus, this text must persist in memory for the lifetime of attribute. //! \param Ch Character type to use. template class xml_attribute: public xml_base { friend class xml_node; public: /////////////////////////////////////////////////////////////////////////// // Construction & destruction //! Constructs an empty attribute with the specified type. //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. xml_attribute() { } /////////////////////////////////////////////////////////////////////////// // Related nodes access //! Gets document of which attribute is a child. //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. xml_document *document() const { if (xml_node *node = this->parent()) { while (node->parent()) node = node->parent(); return node->type() == node_document ? static_cast *>(node) : 0; } else return 0; } //! Gets previous attribute, optionally matching attribute name. //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters //! \return Pointer to found attribute, or 0 if not found. xml_attribute *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { if (name) { if (name_size == 0) name_size = internal::measure(name); for (xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) return attribute; return 0; } else return this->m_parent ? m_prev_attribute : 0; } //! Gets next attribute, optionally matching attribute name. //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters //! \return Pointer to found attribute, or 0 if not found. xml_attribute *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { if (name) { if (name_size == 0) name_size = internal::measure(name); for (xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) return attribute; return 0; } else return this->m_parent ? m_next_attribute : 0; } private: xml_attribute *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero xml_attribute *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero }; /////////////////////////////////////////////////////////////////////////// // XML node //! Class representing a node of XML document. //! Each node may have associated name and value strings, which are available through name() and value() functions. //! Interpretation of name and value depends on type of the node. //! Type of node can be determined by using type() function. //!

//! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. //! Thus, this text must persist in the memory for the lifetime of node. //! \param Ch Character type to use. template class xml_node: public xml_base { public: /////////////////////////////////////////////////////////////////////////// // Construction & destruction //! Constructs an empty node with the specified type. //! Consider using memory_pool of appropriate document to allocate nodes manually. //! \param type Type of node to construct. xml_node(node_type type) : m_type(type) , m_first_node(0) , m_first_attribute(0) { } /////////////////////////////////////////////////////////////////////////// // Node data access //! Gets type of node. //! \return Type of node. node_type type() const { return m_type; } /////////////////////////////////////////////////////////////////////////// // Related nodes access //! Gets document of which node is a child. //! \return Pointer to document that contains this node, or 0 if there is no parent document. xml_document *document() const { xml_node *node = const_cast *>(this); while (node->parent()) node = node->parent(); return node->type() == node_document ? static_cast *>(node) : 0; } //! Gets first child node, optionally matching node name. //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters //! \return Pointer to found child, or 0 if not found. xml_node *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { if (name) { if (name_size == 0) name_size = internal::measure(name); for (xml_node *child = m_first_node; child; child = child->next_sibling()) if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) return child; return 0; } else return m_first_node; } //! Gets last child node, optionally matching node name. //! Behaviour is undefined if node has no children. //! Use first_node() to test if node has children. //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters //! \return Pointer to found child, or 0 if not found. xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { assert(m_first_node); // Cannot query for last child if node has no children if (name) { if (name_size == 0) name_size = internal::measure(name); for (xml_node *child = m_last_node; child; child = child->previous_sibling()) if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) return child; return 0; } else return m_last_node; } //! Gets previous sibling node, optionally matching node name. //! Behaviour is undefined if node has no parent. //! Use parent() to test if node has a parent. //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters //! \return Pointer to found sibling, or 0 if not found. xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { assert(this->m_parent); // Cannot query for siblings if node has no parent if (name) { if (name_size == 0) name_size = internal::measure(name); for (xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) return sibling; return 0; } else return m_prev_sibling; } //! Gets next sibling node, optionally matching node name. //! Behaviour is undefined if node has no parent. //! Use parent() to test if node has a parent. //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters //! \return Pointer to found sibling, or 0 if not found. xml_node *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { assert(this->m_parent); // Cannot query for siblings if node has no parent if (name) { if (name_size == 0) name_size = internal::measure(name); for (xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) return sibling; return 0; } else return m_next_sibling; } //! Gets first attribute of node, optionally matching attribute name. //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters //! \return Pointer to found attribute, or 0 if not found. xml_attribute *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { if (name) { if (name_size == 0) name_size = internal::measure(name); for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) return attribute; return 0; } else return m_first_attribute; } //! Gets last attribute of node, optionally matching attribute name. //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters //! \return Pointer to found attribute, or 0 if not found. xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { if (name) { if (name_size == 0) name_size = internal::measure(name); for (xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) return attribute; return 0; } else return m_first_attribute ? m_last_attribute : 0; } /////////////////////////////////////////////////////////////////////////// // Node modification //! Sets type of node. //! \param type Type of node to set. void type(node_type type) { m_type = type; } /////////////////////////////////////////////////////////////////////////// // Node manipulation //! Prepends a new child node. //! The prepended child becomes the first child, and all existing children are moved one position back. //! \param child Node to prepend. void prepend_node(xml_node *child) { assert(child && !child->parent() && child->type() != node_document); if (first_node()) { child->m_next_sibling = m_first_node; m_first_node->m_prev_sibling = child; } else { child->m_next_sibling = 0; m_last_node = child; } m_first_node = child; child->m_parent = this; child->m_prev_sibling = 0; } //! Appends a new child node. //! The appended child becomes the last child. //! \param child Node to append. void append_node(xml_node *child) { assert(child && !child->parent() && child->type() != node_document); if (first_node()) { child->m_prev_sibling = m_last_node; m_last_node->m_next_sibling = child; } else { child->m_prev_sibling = 0; m_first_node = child; } m_last_node = child; child->m_parent = this; child->m_next_sibling = 0; } //! Inserts a new child node at specified place inside the node. //! All children after and including the specified node are moved one position back. //! \param where Place where to insert the child, or 0 to insert at the back. //! \param child Node to insert. void insert_node(xml_node *where, xml_node *child) { assert(!where || where->parent() == this); assert(child && !child->parent() && child->type() != node_document); if (where == m_first_node) prepend_node(child); else if (where == 0) append_node(child); else { child->m_prev_sibling = where->m_prev_sibling; child->m_next_sibling = where; where->m_prev_sibling->m_next_sibling = child; where->m_prev_sibling = child; child->m_parent = this; } } //! Removes first child node. //! If node has no children, behaviour is undefined. //! Use first_node() to test if node has children. void remove_first_node() { assert(first_node()); xml_node *child = m_first_node; m_first_node = child->m_next_sibling; if (child->m_next_sibling) child->m_next_sibling->m_prev_sibling = 0; else m_last_node = 0; child->m_parent = 0; } //! Removes last child of the node. //! If node has no children, behaviour is undefined. //! Use first_node() to test if node has children. void remove_last_node() { assert(first_node()); xml_node *child = m_last_node; if (child->m_prev_sibling) { m_last_node = child->m_prev_sibling; child->m_prev_sibling->m_next_sibling = 0; } else m_first_node = 0; child->m_parent = 0; } //! Removes specified child from the node // \param where Pointer to child to be removed. void remove_node(xml_node *where) { assert(where && where->parent() == this); assert(first_node()); if (where == m_first_node) remove_first_node(); else if (where == m_last_node) remove_last_node(); else { where->m_prev_sibling->m_next_sibling = where->m_next_sibling; where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; where->m_parent = 0; } } //! Removes all child nodes (but not attributes). void remove_all_nodes() { for (xml_node *node = first_node(); node; node = node->m_next_sibling) node->m_parent = 0; m_first_node = 0; } //! Prepends a new attribute to the node. //! \param attribute Attribute to prepend. void prepend_attribute(xml_attribute *attribute) { assert(attribute && !attribute->parent()); if (first_attribute()) { attribute->m_next_attribute = m_first_attribute; m_first_attribute->m_prev_attribute = attribute; } else { attribute->m_next_attribute = 0; m_last_attribute = attribute; } m_first_attribute = attribute; attribute->m_parent = this; attribute->m_prev_attribute = 0; } //! Appends a new attribute to the node. //! \param attribute Attribute to append. void append_attribute(xml_attribute *attribute) { assert(attribute && !attribute->parent()); if (first_attribute()) { attribute->m_prev_attribute = m_last_attribute; m_last_attribute->m_next_attribute = attribute; } else { attribute->m_prev_attribute = 0; m_first_attribute = attribute; } m_last_attribute = attribute; attribute->m_parent = this; attribute->m_next_attribute = 0; } //! Inserts a new attribute at specified place inside the node. //! All attributes after and including the specified attribute are moved one position back. //! \param where Place where to insert the attribute, or 0 to insert at the back. //! \param attribute Attribute to insert. void insert_attribute(xml_attribute *where, xml_attribute *attribute) { assert(!where || where->parent() == this); assert(attribute && !attribute->parent()); if (where == m_first_attribute) prepend_attribute(attribute); else if (where == 0) append_attribute(attribute); else { attribute->m_prev_attribute = where->m_prev_attribute; attribute->m_next_attribute = where; where->m_prev_attribute->m_next_attribute = attribute; where->m_prev_attribute = attribute; attribute->m_parent = this; } } //! Removes first attribute of the node. //! If node has no attributes, behaviour is undefined. //! Use first_attribute() to test if node has attributes. void remove_first_attribute() { assert(first_attribute()); xml_attribute *attribute = m_first_attribute; if (attribute->m_next_attribute) { attribute->m_next_attribute->m_prev_attribute = 0; } else m_last_attribute = 0; attribute->m_parent = 0; m_first_attribute = attribute->m_next_attribute; } //! Removes last attribute of the node. //! If node has no attributes, behaviour is undefined. //! Use first_attribute() to test if node has attributes. void remove_last_attribute() { assert(first_attribute()); xml_attribute *attribute = m_last_attribute; if (attribute->m_prev_attribute) { attribute->m_prev_attribute->m_next_attribute = 0; m_last_attribute = attribute->m_prev_attribute; } else m_first_attribute = 0; attribute->m_parent = 0; } //! Removes specified attribute from node. //! \param where Pointer to attribute to be removed. void remove_attribute(xml_attribute *where) { assert(first_attribute() && where->parent() == this); if (where == m_first_attribute) remove_first_attribute(); else if (where == m_last_attribute) remove_last_attribute(); else { where->m_prev_attribute->m_next_attribute = where->m_next_attribute; where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; where->m_parent = 0; } } //! Removes all attributes of node. void remove_all_attributes() { for (xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) attribute->m_parent = 0; m_first_attribute = 0; } private: /////////////////////////////////////////////////////////////////////////// // Restrictions // No copying xml_node(const xml_node &); void operator =(const xml_node &); /////////////////////////////////////////////////////////////////////////// // Data members // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. // This is required for maximum performance, as it allows the parser to omit initialization of // unneded/redundant values. // // The rules are as follows: // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage node_type m_type; // Type of node; always valid xml_node *m_first_node; // Pointer to first child node, or 0 if none; always valid xml_node *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero xml_attribute *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid xml_attribute *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero xml_node *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero xml_node *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero }; /////////////////////////////////////////////////////////////////////////// // XML document //! This class represents root of the DOM hierarchy. //! It is also an xml_node and a memory_pool through public inheritance. //! Use parse() function to build a DOM tree from a zero-terminated XML text string. //! parse() function allocates memory for nodes and attributes by using functions of xml_document, //! which are inherited from memory_pool. //! To access root node of the document, use the document itself, as if it was an xml_node. //! \param Ch Character type to use. template class xml_document: public xml_node, public memory_pool { public: //! Constructs empty XML document xml_document() : xml_node(node_document) { } //! Parses zero-terminated XML string according to given flags. //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. //! The string must persist for the lifetime of the document. //! In case of error, rapidxml::parse_error exception will be thrown. //!

//! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. //! Make sure that data is zero-terminated. //!

//! Document can be parsed into multiple times. //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. template void parse(Ch *text) { assert(text); // Remove current contents this->remove_all_nodes(); this->remove_all_attributes(); // Parse BOM, if any parse_bom(text); // Parse children while (1) { // Skip whitespace before node skip(text); if (*text == 0) break; // Parse and append new child if (*text == Ch('<')) { ++text; // Skip '<' if (xml_node *node = parse_node(text)) this->append_node(node); } else RAPIDXML_PARSE_ERROR("expected <", text); } } //! Clears the document by deleting all nodes and clearing the memory pool. //! All nodes owned by document pool are destroyed. void clear() { this->remove_all_nodes(); this->remove_all_attributes(); memory_pool::clear(); } private: /////////////////////////////////////////////////////////////////////// // Internal character utility functions // Detect whitespace character struct whitespace_pred { static unsigned char test(Ch ch) { return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; } }; // Detect node name character struct node_name_pred { static unsigned char test(Ch ch) { return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; } }; // Detect attribute name character struct attribute_name_pred { static unsigned char test(Ch ch) { return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; } }; // Detect text character (PCDATA) struct text_pred { static unsigned char test(Ch ch) { return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; } }; // Detect text character (PCDATA) that does not require processing struct text_pure_no_ws_pred { static unsigned char test(Ch ch) { return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; } }; // Detect text character (PCDATA) that does not require processing struct text_pure_with_ws_pred { static unsigned char test(Ch ch) { return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; } }; // Detect attribute value character template struct attribute_value_pred { static unsigned char test(Ch ch) { if (Quote == Ch('\'')) return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; if (Quote == Ch('\"')) return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; return 0; // Should never be executed, to avoid warnings on Comeau } }; // Detect attribute value character template struct attribute_value_pure_pred { static unsigned char test(Ch ch) { if (Quote == Ch('\'')) return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; if (Quote == Ch('\"')) return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; return 0; // Should never be executed, to avoid warnings on Comeau } }; // Insert coded character, using UTF8 or 8-bit ASCII template static void insert_coded_character(Ch *&text, unsigned long code) { if (Flags & parse_no_utf8) { // Insert 8-bit ASCII character // Todo: possibly verify that code is less than 256 and use replacement char otherwise? text[0] = static_cast(code); text += 1; } else { // Insert UTF8 sequence if (code < 0x80) // 1 byte sequence { text[0] = static_cast(code); text += 1; } else if (code < 0x800) // 2 byte sequence { text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; text[0] = static_cast(code | 0xC0); text += 2; } else if (code < 0x10000) // 3 byte sequence { text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; text[0] = static_cast(code | 0xE0); text += 3; } else if (code < 0x110000) // 4 byte sequence { text[3] = static_cast((code | 0x80) & 0xBF); code >>= 6; text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; text[0] = static_cast(code | 0xF0); text += 4; } else // Invalid, only codes up to 0x10FFFF are allowed in Unicode { RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); } } } // Skip characters until predicate evaluates to true template static void skip(Ch *&text) { Ch *tmp = text; while (StopPred::test(*tmp)) ++tmp; text = tmp; } // Skip characters until predicate evaluates to true while doing the following: // - replacing XML character entity references with proper characters (' & " < > &#...;) // - condensing whitespace sequences to single space character template static Ch *skip_and_expand_character_refs(Ch *&text) { // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip if (Flags & parse_no_entity_translation && !(Flags & parse_normalize_whitespace) && !(Flags & parse_trim_whitespace)) { skip(text); return text; } // Use simple skip until first modification is detected skip(text); // Use translation skip Ch *src = text; Ch *dest = src; while (StopPred::test(*src)) { // If entity translation is enabled if (!(Flags & parse_no_entity_translation)) { // Test if replacement is needed if (src[0] == Ch('&')) { switch (src[1]) { // & ' case Ch('a'): if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) { *dest = Ch('&'); ++dest; src += 5; continue; } if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) { *dest = Ch('\''); ++dest; src += 6; continue; } break; // " case Ch('q'): if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) { *dest = Ch('"'); ++dest; src += 6; continue; } break; // > case Ch('g'): if (src[2] == Ch('t') && src[3] == Ch(';')) { *dest = Ch('>'); ++dest; src += 4; continue; } break; // < case Ch('l'): if (src[2] == Ch('t') && src[3] == Ch(';')) { *dest = Ch('<'); ++dest; src += 4; continue; } break; // &#...; - assumes ASCII case Ch('#'): if (src[2] == Ch('x')) { unsigned long code = 0; src += 3; // Skip &#x while (1) { unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; if (digit == 0xFF) break; code = code * 16 + digit; ++src; } insert_coded_character(dest, code); // Put character in output } else { unsigned long code = 0; src += 2; // Skip &# while (1) { unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; if (digit == 0xFF) break; code = code * 10 + digit; ++src; } insert_coded_character(dest, code); // Put character in output } if (*src == Ch(';')) ++src; else RAPIDXML_PARSE_ERROR("expected ;", src); continue; // Something else default: // Ignore, just copy '&' verbatim break; } } } // If whitespace condensing is enabled if (Flags & parse_normalize_whitespace) { // Test if condensing is needed if (whitespace_pred::test(*src)) { *dest = Ch(' '); ++dest; // Put single space in dest ++src; // Skip first whitespace char // Skip remaining whitespace chars while (whitespace_pred::test(*src)) ++src; continue; } } // No replacement, only copy character *dest++ = *src++; } // Return new end text = src; return dest; } /////////////////////////////////////////////////////////////////////// // Internal parsing functions // Parse BOM, if any template void parse_bom(Ch *&text) { // UTF-8? if (static_cast(text[0]) == 0xEF && static_cast(text[1]) == 0xBB && static_cast(text[2]) == 0xBF) { text += 3; // Skup utf-8 bom } } // Parse XML declaration ( xml_node *parse_xml_declaration(Ch *&text) { // If parsing of declaration is disabled if (!(Flags & parse_declaration_node)) { // Skip until end of declaration while (text[0] != Ch('?') || text[1] != Ch('>')) { if (!text[0]) RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++text; } text += 2; // Skip '?>' return 0; } // Create declaration xml_node *declaration = this->allocate_node(node_declaration); // Skip whitespace before attributes or ?> skip(text); // Parse declaration attributes parse_node_attributes(text, declaration); // Skip ?> if (text[0] != Ch('?') || text[1] != Ch('>')) RAPIDXML_PARSE_ERROR("expected ?>", text); text += 2; return declaration; } // Parse XML comment (' return 0; // Do not produce comment node } // Remember value start Ch *value = text; // Skip until end of comment while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) { if (!text[0]) RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++text; } // Create comment node xml_node *comment = this->allocate_node(node_comment); comment->value(value, text - value); // Place zero terminator after comment value if (!(Flags & parse_no_string_terminators)) *text = Ch('\0'); text += 3; // Skip '-->' return comment; } // Parse DOCTYPE template xml_node *parse_doctype(Ch *&text) { // Remember value start Ch *value = text; // Skip to > while (*text != Ch('>')) { // Determine character type switch (*text) { // If '[' encountered, scan for matching ending ']' using naive algorithm with depth // This works for all W3C test files except for 2 most wicked case Ch('['): { ++text; // Skip '[' int depth = 1; while (depth > 0) { switch (*text) { case Ch('['): ++depth; break; case Ch(']'): --depth; break; case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); } ++text; } break; } // Error on end of text case Ch('\0'): RAPIDXML_PARSE_ERROR("unexpected end of data", text); // Other character, skip it default: ++text; } } // If DOCTYPE nodes enabled if (Flags & parse_doctype_node) { // Create a new doctype node xml_node *doctype = this->allocate_node(node_doctype); doctype->value(value, text - value); // Place zero terminator after value if (!(Flags & parse_no_string_terminators)) *text = Ch('\0'); text += 1; // skip '>' return doctype; } else { text += 1; // skip '>' return 0; } } // Parse PI template xml_node *parse_pi(Ch *&text) { // If creation of PI nodes is enabled if (Flags & parse_pi_nodes) { // Create pi node xml_node *pi = this->allocate_node(node_pi); // Extract PI target name Ch *name = text; skip(text); if (text == name) RAPIDXML_PARSE_ERROR("expected PI target", text); pi->name(name, text - name); // Skip whitespace between pi target and pi skip(text); // Remember start of pi Ch *value = text; // Skip to '?>' while (text[0] != Ch('?') || text[1] != Ch('>')) { if (*text == Ch('\0')) RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++text; } // Set pi value (verbatim, no entity expansion or whitespace normalization) pi->value(value, text - value); // Place zero terminator after name and value if (!(Flags & parse_no_string_terminators)) { pi->name()[pi->name_size()] = Ch('\0'); pi->value()[pi->value_size()] = Ch('\0'); } text += 2; // Skip '?>' return pi; } else { // Skip to '?>' while (text[0] != Ch('?') || text[1] != Ch('>')) { if (*text == Ch('\0')) RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++text; } text += 2; // Skip '?>' return 0; } } // Parse and append data // Return character that ends data. // This is necessary because this character might have been overwritten by a terminating 0 template Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) { // Backup to contents start if whitespace trimming is disabled if (!(Flags & parse_trim_whitespace)) text = contents_start; // Skip until end of data Ch *value = text, *end; if (Flags & parse_normalize_whitespace) end = skip_and_expand_character_refs(text); else end = skip_and_expand_character_refs(text); // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > if (Flags & parse_trim_whitespace) { if (Flags & parse_normalize_whitespace) { // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end if (*(end - 1) == Ch(' ')) --end; } else { // Backup until non-whitespace character is found while (whitespace_pred::test(*(end - 1))) --end; } } // If characters are still left between end and value (this test is only necessary if normalization is enabled) // Create new data node if (!(Flags & parse_no_data_nodes)) { xml_node *data = this->allocate_node(node_data); data->value(value, end - value); node->append_node(data); } // Add data to parent node if no data exists yet if (!(Flags & parse_no_element_values)) if (*node->value() == Ch('\0')) node->value(value, end - value); // Place zero terminator after value if (!(Flags & parse_no_string_terminators)) { Ch ch = *text; *end = Ch('\0'); return ch; // Return character that ends data; this is required because zero terminator overwritten it } // Return character that ends data return *text; } // Parse CDATA template xml_node *parse_cdata(Ch *&text) { // If CDATA is disabled if (Flags & parse_no_data_nodes) { // Skip until end of cdata while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) { if (!text[0]) RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++text; } text += 3; // Skip ]]> return 0; // Do not produce CDATA node } // Skip until end of cdata Ch *value = text; while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) { if (!text[0]) RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++text; } // Create new cdata node xml_node *cdata = this->allocate_node(node_cdata); cdata->value(value, text - value); // Place zero terminator after value if (!(Flags & parse_no_string_terminators)) *text = Ch('\0'); text += 3; // Skip ]]> return cdata; } // Parse element node template xml_node *parse_element(Ch *&text) { // Create element node xml_node *element = this->allocate_node(node_element); // Extract element name Ch *name = text; skip(text); if (text == name) RAPIDXML_PARSE_ERROR("expected element name", text); element->name(name, text - name); // Skip whitespace between element name and attributes or > skip(text); // Parse attributes, if any parse_node_attributes(text, element); // Determine ending type if (*text == Ch('>')) { ++text; parse_node_contents(text, element); } else if (*text == Ch('/')) { ++text; if (*text != Ch('>')) RAPIDXML_PARSE_ERROR("expected >", text); ++text; } else RAPIDXML_PARSE_ERROR("expected >", text); // Place zero terminator after name if (!(Flags & parse_no_string_terminators)) element->name()[element->name_size()] = Ch('\0'); // Return parsed element return element; } // Determine node type, and parse it template xml_node *parse_node(Ch *&text) { // Parse proper node type switch (text[0]) { // <... default: // Parse and append element node return parse_element(text); // (text); } else { // Parse PI return parse_pi(text); } // (text); } break; // (text); } break; // (text); } } // switch // Attempt to skip other, unrecognized node types starting with ')) { if (*text == 0) RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++text; } ++text; // Skip '>' return 0; // No node recognized } } // Parse contents of the node - children, data etc. template void parse_node_contents(Ch *&text, xml_node *node) { // For all children and text while (1) { // Skip whitespace between > and node contents Ch *contents_start = text; // Store start of node contents before whitespace is skipped skip(text); Ch next_char = *text; // After data nodes, instead of continuing the loop, control jumps here. // This is because zero termination inside parse_and_append_data() function // would wreak havoc with the above code. // Also, skipping whitespace after data nodes is unnecessary. after_data_node: // Determine what comes next: node closing, child node, data node, or 0? switch (next_char) { // Node closing or child node case Ch('<'): if (text[1] == Ch('/')) { // Node closing text += 2; // Skip '(text); if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) RAPIDXML_PARSE_ERROR("invalid closing tag name", text); } else { // No validation, just skip name skip(text); } // Skip remaining whitespace after node name skip(text); if (*text != Ch('>')) RAPIDXML_PARSE_ERROR("expected >", text); ++text; // Skip '>' return; // Node closed, finished parsing contents } else { // Child node ++text; // Skip '<' if (xml_node *child = parse_node(text)) node->append_node(child); } break; // End of data - error case Ch('\0'): RAPIDXML_PARSE_ERROR("unexpected end of data", text); // Data node default: next_char = parse_and_append_data(node, text, contents_start); goto after_data_node; // Bypass regular processing after data nodes } } } // Parse XML attributes of the node template void parse_node_attributes(Ch *&text, xml_node *node) { // For all attributes while (attribute_name_pred::test(*text)) { // Extract attribute name Ch *name = text; ++text; // Skip first character of attribute name skip(text); if (text == name) RAPIDXML_PARSE_ERROR("expected attribute name", name); // Create new attribute xml_attribute *attribute = this->allocate_attribute(); attribute->name(name, text - name); node->append_attribute(attribute); // Skip whitespace after attribute name skip(text); // Skip = if (*text != Ch('=')) RAPIDXML_PARSE_ERROR("expected =", text); ++text; // Add terminating zero after name if (!(Flags & parse_no_string_terminators)) attribute->name()[attribute->name_size()] = 0; // Skip whitespace after = skip(text); // Skip quote and remember if it was ' or " Ch quote = *text; if (quote != Ch('\'') && quote != Ch('"')) RAPIDXML_PARSE_ERROR("expected ' or \"", text); ++text; // Extract attribute value and expand char refs in it Ch *value = text, *end; const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes if (quote == Ch('\'')) end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); else end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); // Set attribute value attribute->value(value, end - value); // Make sure that end quote is present if (*text != quote) RAPIDXML_PARSE_ERROR("expected ' or \"", text); ++text; // Skip quote // Add terminating zero after value if (!(Flags & parse_no_string_terminators)) attribute->value()[attribute->value_size()] = 0; // Skip whitespace after attribute value skip(text); } } }; //! \cond internal namespace internal { // Whitespace (space \n \r \t) template const unsigned char lookup_tables::lookup_whitespace[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; // Node name (anything but space \n \r \t / > ? \0) template const unsigned char lookup_tables::lookup_node_name[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Text (i.e. PCDATA) (anything but < \0) template const unsigned char lookup_tables::lookup_text[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 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, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled // (anything but < \0 &) template const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled // (anything but < \0 & space \n \r \t) template const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Attribute name (anything but space \n \r \t / < > = ? ! \0) template const unsigned char lookup_tables::lookup_attribute_name[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Attribute data with single quote (anything but ' \0) template const unsigned char lookup_tables::lookup_attribute_data_1[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 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, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Attribute data with single quote that does not require processing (anything but ' \0 &) template const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Attribute data with double quote (anything but " \0) template const unsigned char lookup_tables::lookup_attribute_data_2[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Attribute data with double quote that does not require processing (anything but " \0 &) template const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Digits (dec and hex, 255 denotes end of numeric character reference) template const unsigned char lookup_tables::lookup_digits[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F }; // Upper case conversion template const unsigned char lookup_tables::lookup_upcase[256] = { // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F }; } //! \endcond } // Undefine internal macros #undef RAPIDXML_PARSE_ERROR // On MSVC, restore warnings state #ifdef _MSC_VER #pragma warning(pop) #endif #endif astra-toolbox-2.3.0/lib/include/rapidxml/rapidxml_print.hpp000066400000000000000000000367311475635207100241140ustar00rootroot00000000000000#ifndef RAPIDXML_PRINT_HPP_INCLUDED #define RAPIDXML_PRINT_HPP_INCLUDED // Copyright (C) 2006, 2009 Marcin Kalicinski // Version 1.13 // Revision $DateTime: 2009/05/13 01:46:17 $ //! \file rapidxml_print.hpp This file contains rapidxml printer implementation #include "rapidxml.hpp" // Only include streams if not disabled #ifndef RAPIDXML_NO_STREAMS #include #include #endif namespace rapidxml { /////////////////////////////////////////////////////////////////////// // Printing flags const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function. /////////////////////////////////////////////////////////////////////// // Internal //! \cond internal namespace internal { /////////////////////////////////////////////////////////////////////////// // Internal character operations // Copy characters from given range to given output iterator template inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out) { while (begin != end) *out++ = *begin++; return out; } // Copy characters from given range to given output iterator and expand // characters into references (< > ' " &) template inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out) { while (begin != end) { if (*begin == noexpand) { *out++ = *begin; // No expansion, copy character } else { switch (*begin) { case Ch('<'): *out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';'); break; case Ch('>'): *out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';'); break; case Ch('\''): *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';'); break; case Ch('"'): *out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';'); break; case Ch('&'): *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';'); break; default: *out++ = *begin; // No expansion, copy character } } ++begin; // Step to next character } return out; } // Fill given output iterator with repetitions of the same character template inline OutIt fill_chars(OutIt out, int n, Ch ch) { for (int i = 0; i < n; ++i) *out++ = ch; return out; } // Find character template inline bool find_char(const Ch *begin, const Ch *end) { while (begin != end) if (*begin++ == ch) return true; return false; } /////////////////////////////////////////////////////////////////////////// // Internal printing operations template inline OutIt print_node(OutIt out, const xml_node *node, int flags, int indent); // Print children of the node template inline OutIt print_children(OutIt out, const xml_node *node, int flags, int indent) { for (xml_node *child = node->first_node(); child; child = child->next_sibling()) out = print_node(out, child, flags, indent); return out; } // Print attributes of the node template inline OutIt print_attributes(OutIt out, const xml_node *node, int flags) { for (xml_attribute *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) { if (attribute->name() && attribute->value()) { // Print attribute name *out = Ch(' '), ++out; out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); *out = Ch('='), ++out; // Print attribute value using appropriate quote type if (find_char(attribute->value(), attribute->value() + attribute->value_size())) { *out = Ch('\''), ++out; out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); *out = Ch('\''), ++out; } else { *out = Ch('"'), ++out; out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); *out = Ch('"'), ++out; } } } return out; } // Print data node template inline OutIt print_data_node(OutIt out, const xml_node *node, int flags, int indent) { assert(node->type() == node_data); if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); return out; } // Print data node template inline OutIt print_cdata_node(OutIt out, const xml_node *node, int flags, int indent) { assert(node->type() == node_cdata); if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); *out = Ch('<'); ++out; *out = Ch('!'); ++out; *out = Ch('['); ++out; *out = Ch('C'); ++out; *out = Ch('D'); ++out; *out = Ch('A'); ++out; *out = Ch('T'); ++out; *out = Ch('A'); ++out; *out = Ch('['); ++out; out = copy_chars(node->value(), node->value() + node->value_size(), out); *out = Ch(']'); ++out; *out = Ch(']'); ++out; *out = Ch('>'); ++out; return out; } // Print element node template inline OutIt print_element_node(OutIt out, const xml_node *node, int flags, int indent) { assert(node->type() == node_element); // Print element name and attributes, if any if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); *out = Ch('<'), ++out; out = copy_chars(node->name(), node->name() + node->name_size(), out); out = print_attributes(out, node, flags); // If node is childless if (node->value_size() == 0 && !node->first_node()) { // Print childless node tag ending *out = Ch('/'), ++out; *out = Ch('>'), ++out; } else { // Print normal node tag ending *out = Ch('>'), ++out; // Test if node contains a single data node only (and no other nodes) xml_node *child = node->first_node(); if (!child) { // If node has no children, only print its value without indenting out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); } else if (child->next_sibling() == 0 && child->type() == node_data) { // If node has a sole data child, only print its value without indenting out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); } else { // Print all children with full indenting if (!(flags & print_no_indenting)) *out = Ch('\n'), ++out; out = print_children(out, node, flags, indent + 1); if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); } // Print node end *out = Ch('<'), ++out; *out = Ch('/'), ++out; out = copy_chars(node->name(), node->name() + node->name_size(), out); *out = Ch('>'), ++out; } return out; } // Print declaration node template inline OutIt print_declaration_node(OutIt out, const xml_node *node, int flags, int indent) { // Print declaration start if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); *out = Ch('<'), ++out; *out = Ch('?'), ++out; *out = Ch('x'), ++out; *out = Ch('m'), ++out; *out = Ch('l'), ++out; // Print attributes out = print_attributes(out, node, flags); // Print declaration end *out = Ch('?'), ++out; *out = Ch('>'), ++out; return out; } // Print comment node template inline OutIt print_comment_node(OutIt out, const xml_node *node, int flags, int indent) { assert(node->type() == node_comment); if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); *out = Ch('<'), ++out; *out = Ch('!'), ++out; *out = Ch('-'), ++out; *out = Ch('-'), ++out; out = copy_chars(node->value(), node->value() + node->value_size(), out); *out = Ch('-'), ++out; *out = Ch('-'), ++out; *out = Ch('>'), ++out; return out; } // Print doctype node template inline OutIt print_doctype_node(OutIt out, const xml_node *node, int flags, int indent) { assert(node->type() == node_doctype); if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); *out = Ch('<'), ++out; *out = Ch('!'), ++out; *out = Ch('D'), ++out; *out = Ch('O'), ++out; *out = Ch('C'), ++out; *out = Ch('T'), ++out; *out = Ch('Y'), ++out; *out = Ch('P'), ++out; *out = Ch('E'), ++out; *out = Ch(' '), ++out; out = copy_chars(node->value(), node->value() + node->value_size(), out); *out = Ch('>'), ++out; return out; } // Print pi node template inline OutIt print_pi_node(OutIt out, const xml_node *node, int flags, int indent) { assert(node->type() == node_pi); if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); *out = Ch('<'), ++out; *out = Ch('?'), ++out; out = copy_chars(node->name(), node->name() + node->name_size(), out); *out = Ch(' '), ++out; out = copy_chars(node->value(), node->value() + node->value_size(), out); *out = Ch('?'), ++out; *out = Ch('>'), ++out; return out; } // Print node template inline OutIt print_node(OutIt out, const xml_node *node, int flags, int indent) { // Print proper node type switch (node->type()) { // Document case node_document: out = print_children(out, node, flags, indent); break; // Element case node_element: out = print_element_node(out, node, flags, indent); break; // Data case node_data: out = print_data_node(out, node, flags, indent); break; // CDATA case node_cdata: out = print_cdata_node(out, node, flags, indent); break; // Declaration case node_declaration: out = print_declaration_node(out, node, flags, indent); break; // Comment case node_comment: out = print_comment_node(out, node, flags, indent); break; // Doctype case node_doctype: out = print_doctype_node(out, node, flags, indent); break; // Pi case node_pi: out = print_pi_node(out, node, flags, indent); break; // Unknown default: assert(0); break; } // If indenting not disabled, add line break after node if (!(flags & print_no_indenting)) *out = Ch('\n'), ++out; // Return modified iterator return out; } } //! \endcond /////////////////////////////////////////////////////////////////////////// // Printing //! Prints XML to given output iterator. //! \param out Output iterator to print to. //! \param node Node to be printed. Pass xml_document to print entire document. //! \param flags Flags controlling how XML is printed. //! \return Output iterator pointing to position immediately after last character of printed text. template inline OutIt print(OutIt out, const xml_node &node, int flags = 0) { return internal::print_node(out, &node, flags, 0); } #ifndef RAPIDXML_NO_STREAMS //! Prints XML to given output stream. //! \param out Output stream to print to. //! \param node Node to be printed. Pass xml_document to print entire document. //! \param flags Flags controlling how XML is printed. //! \return Output stream. template inline std::basic_ostream &print(std::basic_ostream &out, const xml_node &node, int flags = 0) { print(std::ostream_iterator(out), node, flags); return out; } //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. //! \param out Output stream to print to. //! \param node Node to be printed. //! \return Output stream. template inline std::basic_ostream &operator <<(std::basic_ostream &out, const xml_node &node) { return print(out, node); } #endif } #endif astra-toolbox-2.3.0/lib/licenses/000077500000000000000000000000001475635207100166775ustar00rootroot00000000000000astra-toolbox-2.3.0/lib/licenses/dlpack.txt000066400000000000000000000261211475635207100207000ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. 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. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor 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, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You 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 the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You 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 such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its 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. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2017 by Contributors 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. astra-toolbox-2.3.0/lib/licenses/rapidxml.txt000066400000000000000000000053001475635207100212560ustar00rootroot00000000000000Use of this software is granted under one of the following two licenses, to be chosen freely by the user. 1. Boost Software License - Version 1.0 - August 17th, 2003 =============================================================================== Copyright (c) 2006, 2007 Marcin Kalicinski 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. 2. The MIT License =============================================================================== Copyright (c) 2006, 2007 Marcin Kalicinski 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. astra-toolbox-2.3.0/matlab/000077500000000000000000000000001475635207100155645ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/algorithms/000077500000000000000000000000001475635207100177355ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/algorithms/DART/000077500000000000000000000000001475635207100204675ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/algorithms/DART/DARTalgorithm.m000066400000000000000000000203201475635207100233030ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef DARTalgorithm < matlab.mixin.Copyable % Algorithm class for Discrete Algebraic Reconstruction Technique (DART). %---------------------------------------------------------------------- properties (GetAccess=public, SetAccess=public) tomography = IterativeTomography(); % POLICY: Tomography object. segmentation = SegmentationDefault(); % POLICY: Segmentation object. smoothing = SmoothingDefault(); % POLICY: Smoothing object. masking = MaskingDefault(); % POLICY: Masking object. output = OutputDefault(); % POLICY: Output object. statistics = StatisticsDefault(); % POLICY: Statistics object. base = struct(); % DATA(set): base structure, should contain: 'sinogram', 'proj_geom', 'phantom' (optional). memory = 'no'; % SETTING: reduce memory usage? (disables some features) implementation = 'linear'; % SETTING: which type of projector is used ('linear', 'nonlinear') t = 5; % SETTING: # ARMiterations, each DART iteration. t0 = 100; % SETTING: # ARM iterations at DART initialization. end %---------------------------------------------------------------------- properties (GetAccess=public, SetAccess=private) V0 = []; % DATA(get): Initial reconstruction. V = []; % DATA(get): Reconstruction. S = []; % DATA(get): Segmentation. R = []; % DATA(get): Residual projection data. Mask = []; % DATA(get): Reconstruction Mask. stats = struct(); % Structure containing various statistics. iterationcount = 0; % Number of performed iterations. start_tic = 0; initialized = 0; % Is initialized? end %---------------------------------------------------------------------- properties (Access=private) adaptparam_name = {}; adaptparam_values = {}; adaptparam_iters = {}; end %---------------------------------------------------------------------- methods %------------------------------------------------------------------ function this = DARTalgorithm(varargin) % Constructor % >> D = DARTalgorithm(base); [base is a matlab struct that % should contain 'sinogram' and % 'proj_geom'] % >> D = DARTalgorithm('base_path'); [path to base struct file] % >> D = DARTalgorithm(sinogram, proj_geom) % narginchk(1, 2) if nargin == 1 && ischar(varargin{1}) this.base = load(varargin{1}); elseif nargin == 1 && isstruct(varargin{1}) this.base = varargin{1}; elseif nargin == 2 this.base = struct(); this.base.sinogram = varargin{1}; this.base.proj_geom = varargin{2}; else error('invalid arguments') end end %------------------------------------------------------------------ function D = deepcopy(this) % Create a deep copy of this object. % >> D2 = D.deepcopy(); D = copy(this); props = properties(this); for i = 1:length(props) if isa(this.(props{i}), 'handle') D.(props{i}) = copy(this.(props{i})); end end end %------------------------------------------------------------------ function this = initialize(this) % Initializes this object. % >> D.initialize(); % Initialize tomography part if ~this.tomography.initialized this.tomography.proj_geom = this.base.proj_geom; this.tomography.initialize(); end % Create an Initial Reconstruction if isfield(this.base, 'V0') this.V0 = this.base.V0; else this.output.pre_initial_iteration(this); this.V0 = this.tomography.reconstruct(this.base.sinogram, this.t0); this.output.post_initial_iteration(this); end this.V = this.V0; if strcmp(this.memory,'yes') this.base.V0 = []; this.V0 = []; end this.initialized = 1; end %------------------------------------------------------------------ % iterate function this = iterate(this, iters) % Perform several iterations of the DART algorithm. % >> D.iterate(iterations); if strcmp(this.implementation,'linear') this.iterate_linear(iters); elseif strcmp(this.implementation,'nonlinear') this.iterate_nonlinear(iters); end end %------------------------------------------------------------------ % iterate - linear projector implementation function this = iterate_linear(this, iters) this.start_tic = tic; for iteration = 1:iters this.iterationcount = this.iterationcount + 1; % initial output this.output.pre_iteration(this); % update adaptive parameters this.update_adaptiveparameter(this.iterationcount); % segmentation this.segmentation.estimate_grey_levels(this, this.V); this.S = this.segmentation.apply(this, this.V); % select update and fixed pixels this.Mask = this.masking.apply(this, this.S); this.V = (this.V .* this.Mask) + (this.S .* (1 - this.Mask)); F = this.V; F(this.Mask == 1) = 0; % compute residual projection difference this.R = this.base.sinogram - this.tomography.project(F); % ART update part this.V = this.tomography.reconstruct_mask(this.R, this.V, this.Mask, this.t); % blur this.V = this.smoothing.apply(this, this.V); %calculate statistics this.stats = this.statistics.apply(this); % output this.output.post_iteration(this); end end %------------------------------------------------------------------ % iterate - nonlinear projector implementation function this = iterate_nonlinear(this, iters) this.start_tic = tic; for iteration = 1:iters this.iterationcount = this.iterationcount + 1; % Output this.output.pre_iteration(this); % update adaptive parameters this.update_adaptiveparameter(this.iterationcount) % Segmentation this.segmentation.estimate_grey_levels(this, this.V); this.S = this.segmentation.apply(this, this.V); % Select Update and Fixed Pixels this.Mask = this.masking.apply(this, this.S); this.V = (this.V .* this.Mask) + (this.S .* (1 - this.Mask)); % ART update part this.V = this.tomography.reconstruct2_mask(this.base.sinogram, this.V, this.Mask, this.t); % blur this.V = this.smoothing.apply(this, this.V); % calculate statistics this.stats = this.statistics.apply(this); % output this.output.post_iteration(this); end end %------------------------------------------------------------------ % get data function data = getdata(this, string) if numel(this.(string)) == 1 data = astra_mex_data2d('get',this.(string)); else data = this.(string); end end %------------------------------------------------------------------ % add adaptive parameter function this = adaptiveparameter(this, name, values, iterations) this.adaptparam_name{end+1} = name; this.adaptparam_values{end+1} = values; this.adaptparam_iters{end+1} = iterations; end %------------------------------------------------------------------ % update adaptive parameter function this = update_adaptiveparameter(this, iteration) for i = 1:numel(this.adaptparam_name) for j = 1:numel(this.adaptparam_iters{i}) if iteration == this.adaptparam_iters{i}(j) new_value = this.adaptparam_values{i}(j); eval(['this.' this.adaptparam_name{i} ' = ' num2str(new_value) ';']); end end end end %------------------------------------------------------------------ function settings = getsettings(this) % Returns a structure containing all settings of this object. % >> settings = tomography.getsettings(); settings.tomography = this.tomography.getsettings(); settings.smoothing = this.smoothing.getsettings(); settings.masking = this.masking.getsettings(); settings.segmentation = this.segmentation.getsettings(); end %------------------------------------------------------------------ end % methods end % class astra-toolbox-2.3.0/matlab/algorithms/DART/IterativeTomography.m000066400000000000000000000356341475635207100246660ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef IterativeTomography < matlab.mixin.Copyable % Algorithm class for 2D Iterative Tomography. %---------------------------------------------------------------------- properties (SetAccess=public, GetAccess=public) superresolution = 1; % SETTING: Volume upsampling factor. proj_type = 'linear'; % SETTING: Projector type, only when gpu='no'. method = 'SIRT_CUDA'; % SETTING: Iterative method (see ASTRA toolbox documentation). gpu = 'yes'; % SETTING: Use gpu? {'yes', 'no'} gpu_core = 0; % SETTING: Which gpu core to use? Only when gpu='yes'. inner_circle = 'yes'; % SETTING: Do roi only? {'yes', 'no'} image_size = []; % SETTING: Overwrite default reconstruction size. Only if no vol_geom is specified. use_minc = 'no'; % SETTING: Use minimum constraint. {'no', 'yes'} end %---------------------------------------------------------------------- properties (SetAccess=public, GetAccess=public) proj_geom = []; % DATA: Projection geometry. vol_geom = []; % DATA: Volume geometry. end %---------------------------------------------------------------------- properties (SetAccess=private, GetAccess=public) initialized = 0; % Is this object initialized? end %---------------------------------------------------------------------- properties (SetAccess=protected, GetAccess=protected) cfg_base = struct(); % PROTECTED: base configuration structure for the reconstruction algorithm. proj_geom_sr = []; % PROTECTED: geometry of sinogram (with super-resolution) proj_id = []; % PROTECTED: astra id of projector (when gpu='no') proj_id_sr = []; % PROTECTED: astra id of super-resolution projector (when gpu='no') end %---------------------------------------------------------------------- methods (Access=public) %------------------------------------------------------------------ function this = IterativeTomography(varargin) % Constructor % >> tomography = IterativeTomography(proj_geom); % >> tomography = IterativeTomography(proj_geom, vol_geom); % Input: IterativeTomography(proj_geom) if nargin == 1 this.proj_geom = varargin{1}; % Input: IterativeTomography(proj_geom, vol_geom) elseif nargin == 2 this.proj_geom = varargin{1}; this.vol_geom = varargin{2}; end end %------------------------------------------------------------------ function delete(this) % Destructor % >> clear tomography; if strcmp(this.gpu,'no') && numel(this.proj_id) > 0 astra_mex_projector('delete', this.proj_id, this.proj_id_sr); end end %------------------------------------------------------------------ function settings = getsettings(this) % Returns a structure containing all settings of this object. % >> settings = tomography.getsettings(); settings.superresolution = this.superresolution; settings.proj_type = this.proj_type; settings.method = this.method; settings.gpu = this.gpu; settings.gpu_core = this.gpu_core; settings.inner_circle = this.inner_circle; settings.image_size = this.image_size; settings.use_minc = this.use_minc; end %------------------------------------------------------------------ function ok = initialize(this) % Initialize this object. Returns 1 if succesful. % >> tomography.initialize(); disp('sdfqnlmkqdsfmlkjdfqsjklm'); % create projection geometry with super-resolution if this.superresolution > 1 this.proj_geom_sr = astra_geom_superresolution(this.proj_geom, this.superresolution); else this.proj_geom_sr = this.proj_geom; end % if no volume geometry is specified by the user: create volume geometry if numel(this.vol_geom) == 0 if numel(this.image_size) < 2 this.image_size(1) = this.proj_geom.DetectorCount; this.image_size(2) = this.proj_geom.DetectorCount; end this.vol_geom = astra_create_vol_geom(this.image_size(1) * this.superresolution, this.image_size(2) * this.superresolution, ... -this.image_size(1)/2, this.image_size(1)/2, -this.image_size(2)/2, this.image_size(2)/2); else this.image_size(1) = this.vol_geom.GridRowCount; this.image_size(2) = this.vol_geom.GridColCount; end % create projector if strcmp(this.gpu,'no') this.proj_id = astra_create_projector(this.proj_type, this.proj_geom, this.vol_geom); this.proj_id_sr = astra_create_projector(this.proj_type, this.proj_geom_sr, this.vol_geom); end % create reconstruction configuration this.cfg_base = astra_struct(upper(this.method)); if strcmp(this.gpu,'no') this.cfg_base.ProjectorId = this.proj_id; this.cfg_base.ProjectionGeometry = this.proj_geom; this.cfg_base.ReconstructionGeometry = this.vol_geom; end this.cfg_base.option.DetectorSuperSampling = this.superresolution; if strcmp(this.gpu,'yes') this.cfg_base.option.GPUindex = this.gpu_core; end if this.use_minc this.cfg_base.option.MinConstraint = 0; end this.initialized = 1; ok = this.initialized; end %------------------------------------------------------------------ function projections = project(this, volume) % Compute forward projection. % >> projections = tomography.project(volume); if ~this.initialized this.initialize(); end % project projections = this.project_c(volume); end %------------------------------------------------------------------ function reconstruction = reconstruct(this, varargin) % Compute reconstruction. % Uses tomography.sinogram % Initial solution (if available) should be stored in tomography.V % >> reconstruction = tomography.reconstruct(projections, iterations); % >> reconstruction = tomography.reconstruct(projections, volume0, iterations); if ~this.initialized this.initialize(); end if numel(varargin) == 2 reconstruction = this.reconstruct_c(varargin{1}, [], [], varargin{2}); elseif numel(varargin) == 3 reconstruction = this.reconstruct_c(varargin{1}, varargin{2}, [], varargin{3}); else error('invalid parameter list') end if strcmp(this.inner_circle,'yes') reconstruction = this.selectROI(reconstruction); end end %------------------------------------------------------------------ function reconstruction = reconstruct_mask(this, varargin) % Compute reconstruction with mask. % Uses tomography.sinogram % Initial solution (if available) should be stored in tomography.V % >> reconstruction = tomography.reconstructMask(projections, mask, iterations); % >> reconstruction = tomography.reconstructMask(projections, volume0, mask, iterations); if ~this.initialized this.initialize(); end if numel(varargin) == 3 reconstruction = this.reconstruct_c(varargin{1}, [], varargin{2}, varargin{3}); elseif numel(varargin) == 4 reconstruction = this.reconstruct_c(varargin{1}, varargin{2}, varargin{3}, varargin{4}); else error('invalid parameter list') end if strcmp(this.inner_circle,'yes') reconstruction = this.selectROI(reconstruction); end end %------------------------------------------------------------------ end %---------------------------------------------------------------------- methods (Access = protected) %------------------------------------------------------------------ % Protected function: create FP function sinogram = project_c(this, volume) if this.initialized == 0 error('IterativeTomography not initialized'); end % data is stored in astra memory if numel(volume) == 1 if strcmp(this.gpu, 'yes') sinogram_tmp = astra_create_sino_cuda(volume, this.proj_geom_sr, this.vol_geom, this.gpu_core); else sinogram_tmp = astra_create_sino(volume, this.proj_id); end % sinogram downsampling if this.superresolution > 1 sinogram_data = astra_mex_data2d('get', sinogram_tmp); astra_mex_data2d('delete', sinogram_tmp); sinogram_data = downsample_sinogram(sinogram_data, this.superresolution); sinogram = astra_mex_data2d('create','sino', this.proj_geom, sinogram_data); else sinogram = sinogram_tmp; end % data is stored in matlab memory else % 2D and 3D slice by slice sinogram_tmp = zeros([astra_geom_size(this.proj_geom_sr), size(volume,3)]); sinogram_tmp2 = zeros([astra_geom_size(this.proj_geom), size(volume,3)]); for slice = 1:size(volume,3) if strcmp(this.gpu, 'yes') [tmp_id, sinogram_tmp2(:,:,slice)] = astra_create_sino_sampling(volume(:,:,slice), this.proj_geom, this.vol_geom, this.gpu_core, this.superresolution); astra_mex_data2d('delete', tmp_id); else [tmp_id, tmp] = astra_create_sino(volume(:,:,slice), this.proj_id_sr); sinogram_tmp2(:,:,slice) = downsample_sinogram(tmp, this.superresolution) * (this.superresolution^2); astra_mex_data2d('delete', tmp_id); end end % sinogram downsampling if strcmp(this.gpu, 'yes') %sinogram = downsample_sinogram(sinogram_tmp, this.superresolution); sinogram2 = sinogram_tmp2; if strcmp(this.proj_geom.type,'fanflat_vec') || strcmp(this.proj_geom.type,'fanflat') sinogram2 = sinogram2 / this.superresolution; elseif strcmp(this.proj_geom.type,'parallel') sinogram2 = sinogram2 / (this.superresolution * this.superresolution); end sinogram = sinogram2; else sinogram = sinogram_tmp2; end end end %------------------------------------------------------------------ % Protected function: reconstruct function V = reconstruct_c(this, sinogram, V0, mask, iterations) if this.initialized == 0 error('IterativeTomography not initialized'); end % data is stored in astra memory if numel(sinogram) == 1 V = this.reconstruct_c_astra(sinogram, V0, mask, iterations); % data is stored in matlab memory else V = this.reconstruct_c_matlab(sinogram, V0, mask, iterations); end end %------------------------------------------------------------------ % Protected function: reconstruct (data in matlab) function V = reconstruct_c_matlab(this, sinogram, V0, mask, iterations) if this.initialized == 0 error('IterativeTomography not initialized'); end % parse method method2 = upper(this.method); if strcmp(method2, 'SART') || strcmp(method2, 'SART_CUDA') iterations = iterations * size(sinogram,1); elseif strcmp(method2, 'ART') iterations = iterations * numel(sinogram); end % create data objects V = zeros(this.vol_geom.GridRowCount, this.vol_geom.GridColCount, size(sinogram,3)); reconstruction_id = astra_mex_data2d('create', '-vol', this.vol_geom); sinogram_id = astra_mex_data2d('create', '-sino', this.proj_geom); if numel(mask) > 0 mask_id = astra_mex_data2d('create', '-vol', this.vol_geom); end % algorithm configuration cfg = this.cfg_base; cfg.ProjectionDataId = sinogram_id; cfg.ReconstructionDataId = reconstruction_id; if numel(mask) > 0 cfg.option.ReconstructionMaskId = mask_id; end alg_id = astra_mex_algorithm('create', cfg); % loop slices for slice = 1:size(sinogram,3) % fetch slice of initial reconstruction if numel(V0) > 0 astra_mex_data2d('store', reconstruction_id, V0(:,:,slice)); else astra_mex_data2d('store', reconstruction_id, 0); end % fetch slice of sinogram astra_mex_data2d('store', sinogram_id, sinogram(:,:,slice)); % fecth slice of mask if numel(mask) > 0 astra_mex_data2d('store', mask_id, mask(:,:,slice)); end % iterate astra_mex_algorithm('iterate', alg_id, iterations); % fetch data V(:,:,slice) = astra_mex_data2d('get', reconstruction_id); end % correct attenuation factors for super-resolution if this.superresolution > 1 && strcmp(this.gpu,'yes') if strcmp(this.proj_geom.type,'fanflat_vec') || strcmp(this.proj_geom.type,'fanflat') if numel(mask) > 0 V(mask > 0) = V(mask > 0) ./ this.superresolution; else V = V ./ this.superresolution; end end end % garbage collection astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', sinogram_id, reconstruction_id); if numel(mask) > 0 astra_mex_data2d('delete', mask_id); end end %------------------------------------------------------------------ % Protected function: reconstruct (data in astra) function V = reconstruct_c_astra(this, sinogram, V0, mask, iterations) if this.initialized == 0 error('IterativeTomography not initialized'); end if numel(V0) > 1 || numel(mask) > 1 || numel(sinogram) > 1 error('Not all required data is stored in the astra memory'); end if numel(V0) == 0 V0 = astra_mex_data2d('create', '-vol', this.vol_geom, 0); end % parse method method2 = upper(this.method); if strcmp(method2, 'SART') || strcmp(method2, 'SART_CUDA') iterations = iterations * astra_geom_size(this.proj_geom, 1) this.cfg_base.option.ProjectionOrder = 'random'; elseif strcmp(method2, 'ART') s = astra_geom_size(this.proj_geom); iterations = iterations * s(1) * s(2); end % algorithm configuration cfg = this.cfg_base; cfg.ProjectionDataId = sinogram; cfg.ReconstructionDataId = V0; if numel(mask) > 0 cfg.option.ReconstructionMaskId = mask; end alg_id = astra_mex_algorithm('create', cfg); % iterate astra_mex_algorithm('iterate', alg_id, iterations); % fetch data V = V0; % correct attenuation factors for super-resolution if this.superresolution > 1 if strcmp(this.proj_geom.type,'fanflat_vec') || strcmp(this.proj_geom.type,'fanflat') if numel(mask) > 0 astra_data_op_masked('$1./s1', [V V], [this.superresolution this.superresolution], mask, this.gpu_core); else astra_data_op('$1./s1', [V V], [this.superresolution this.superresolution], this.gpu_core); end end end % garbage collection astra_mex_algorithm('delete', alg_id); end %------------------------------------------------------------------ function V_out = selectROI(~, V_in) if numel(V_in) == 1 cfg = astra_struct('RoiSelect_CUDA'); cfg.DataId = V_in; alg_id = astra_mex_algorithm('create',cfg); astra_mex_algorithm('run', alg_id); astra_mex_algorithm('delete', alg_id); V_out = V_in; else V_out = ROIselectfull(V_in, min([size(V_in,1), size(V_in,2)])); end end %------------------------------------------------------------------ end end astra-toolbox-2.3.0/matlab/algorithms/DART/IterativeTomography3D.m000066400000000000000000000340201475635207100250410ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef IterativeTomography3D < matlab.mixin.Copyable % Algorithm class for 3D Iterative Tomography. %---------------------------------------------------------------------- properties (SetAccess=public, GetAccess=public) superresolution = 1; % SETTING: Volume upsampling factor. proj_type = 'linear'; % SETTING: Projector type, only when gpu='no'. method = 'SIRT3D_CUDA'; % SETTING: Iterative method (see ASTRA toolbox documentation). gpu = 'yes'; % SETTING: Use gpu? {'yes', 'no'} gpu_core = 0; % SETTING: Which gpu core to use? Only when gpu='yes'. inner_circle = 'yes'; % SETTING: Do roi only? {'yes', 'no'} image_size = []; % SETTING: Overwrite default reconstruction size. Only if no vol_geom is specified. use_minc = 'no'; % SETTING: Use minimum constraint. {'no', 'yes'} maxc = +Inf; % SETTING: Maximum constraint. +Inf means off. end %---------------------------------------------------------------------- properties (SetAccess=public, GetAccess=public) sinogram = []; % DATA: Projection data. proj_geom = []; % DATA: Projection geometry. V = []; % DATA: Volume data. Also used to set initial estimate (optional). vol_geom = []; % DATA: Volume geometry. end %---------------------------------------------------------------------- properties (SetAccess=private, GetAccess=public) initialized = 0; % Is this object initialized? end %---------------------------------------------------------------------- properties (SetAccess=protected, GetAccess=protected) proj_geom_sr = []; % PROTECTED: geometry of sinogram (with super-resolution) proj_id = []; % PROTECTED: astra id of projector (when gpu='no') proj_id_sr = []; % PROTECTED: astra id of super-resolution projector (when gpu='no') cfg_base = struct(); % PROTECTED: base configuration structure for the reconstruction algorithm. end %---------------------------------------------------------------------- methods (Access=public) %------------------------------------------------------------------ function this = IterativeTomography3D(varargin) % Constructor % >> tomography = IterativeTomography3D(proj_geom); % >> tomography = IterativeTomography3D(proj_geom, vol_geom); % Input: IterativeTomography(proj_geom) if nargin == 1 this.proj_geom = varargin{1}; % Input: IterativeTomography(proj_geom, vol_geom) elseif nargin == 2 this.proj_geom = varargin{1}; this.vol_geom = varargin{2}; end end %------------------------------------------------------------------ function delete(this) % Destructor % >> clear tomography; if strcmp(this.gpu,'no') && numel(this.proj_id) > 0 astra_mex_projector('delete', this.proj_id, this.proj_id_sr); end end %------------------------------------------------------------------ function settings = getsettings(this) % Returns a structure containing all settings of this object. % >> settings = tomography.getsettings(); settings.superresolution = this.superresolution; settings.proj_type = this.proj_type; settings.method = this.method; settings.gpu = this.gpu; settings.gpu_core = this.gpu_core; settings.inner_circle = this.inner_circle; settings.image_size = this.image_size; settings.use_minc = this.use_minc; settings.maxc = this.maxc; end %------------------------------------------------------------------ function ok = initialize(this) % Initialize this object. Returns 1 if succesful. % >> tomography.initialize(); % % create projection geometry with super-resolution % this.proj_geom_sr = astra_geom_superresolution(this.proj_geom, this.superresolution); % if no volume geometry is specified by the user: create volume geometry if numel(this.vol_geom) == 0 if numel(this.image_size) < 2 this.image_size(1) = this.proj_geom.DetectorRowCount; this.image_size(2) = this.proj_geom.DetectorColCount; end this.vol_geom = astra_create_vol_geom(this.proj_geom.DetectorColCount, this.proj_geom.DetectorColCount, this.proj_geom.DetectorRowCount); else this.image_size(1) = this.vol_geom.GridRowCount; this.image_size(2) = this.vol_geom.GridColCount; end % create projector if strcmp(this.gpu, 'no') this.proj_id = astra_create_projector(this.proj_type, this.proj_geom, this.vol_geom); this.proj_id_sr = astra_create_projector(this.proj_type, this.proj_geom_sr, this.vol_geom); end % create reconstruction configuration this.cfg_base = astra_struct(upper(this.method)); if strcmp(this.gpu,'no') this.cfg_base.ProjectorId = this.proj_id; this.cfg_base.ProjectionGeometry = this.proj_geom; this.cfg_base.ReconstructionGeometry = this.vol_geom; this.cfg_base.option.ProjectionOrder = 'random'; end this.cfg_base.option.DetectorSuperSampling = this.superresolution; if strcmp(this.gpu,'yes') this.cfg_base.option.GPUindex = this.gpu_core; end this.cfg_base.option.UseMinConstraint = this.use_minc; if ~isinf(this.maxc) this.cfg_base.option.UseMaxConstraint = 'yes'; this.cfg_base.option.MaxConstraintValue = this.maxc; end this.initialized = 1; ok = this.initialized; end %------------------------------------------------------------------ function projections = project(this, volume) % Compute forward projection. % >> projections = tomography.project(volume); if ~this.initialized this.initialize(); end % project projections = this.project_c(volume); end %------------------------------------------------------------------ function reconstruction = reconstruct(this, varargin) % Compute reconstruction. % Uses tomography.sinogram % Initial solution (if available) should be stored in tomography.V % >> reconstruction = tomography.reconstruct(projections, iterations); % >> reconstruction = tomography.reconstruct(projections, volume0, iterations); if ~this.initialized this.initialize(); end if numel(varargin) == 2 reconstruction = this.reconstruct_c(varargin{1}, [], [], varargin{2}); elseif numel(varargin) == 3 reconstruction = this.reconstruct_c(varargin{1}, varargin{2}, [], varargin{3}); else error('invalid parameter list') end if strcmp(this.inner_circle,'yes') reconstruction = this.selectROI(reconstruction); end end %------------------------------------------------------------------ function reconstruction = reconstruct_mask(this, varargin) % Compute reconstruction with mask. % Uses tomography.sinogram % Initial solution (if available) should be stored in tomography.V % >> reconstruction = tomography.reconstructMask(projections, mask, iterations); % >> reconstruction = tomography.reconstructMask(projections, volume0, mask, iterations); if ~this.initialized this.initialize(); end if numel(varargin) == 3 reconstruction = this.reconstruct_c(varargin{1}, [], varargin{2}, varargin{3}); elseif numel(varargin) == 4 reconstruction = this.reconstruct_c(varargin{1}, varargin{2}, varargin{3}, varargin{4}); else error('invalid parameter list') end if strcmp(this.inner_circle,'yes') reconstruction = this.selectROI(reconstruction); end end %------------------------------------------------------------------ end %---------------------------------------------------------------------- methods (Access = protected) %------------------------------------------------------------------ % Protected function: create FP function sinogram = project_c(this, volume) if this.initialized == 0 error('IterativeTomography not initialized'); end % data is stored in astra memory if numel(volume) == 1 if strcmp(this.gpu, 'yes') sinogram_tmp = astra_create_sino_cuda(volume, this.proj_geom_sr, this.vol_geom, this.gpu_core); else sinogram_tmp = astra_create_sino(volume, this.proj_id); end % sinogram downsampling if this.superresolution > 1 sinogram_data = astra_mex_data2d('get', sinogram_tmp); astra_mex_data2d('delete', sinogram_tmp); sinogram_data = downsample_sinogram(sinogram_data, this.superresolution); sinogram = astra_mex_data2d('create', 'sino', this.proj_geom, sinogram_data); else sinogram = sinogram_tmp; end % data is stored in matlab memory else [tmp_id, sinogram] = astra_create_sino3d_cuda(volume, this.proj_geom, this.vol_geom); astra_mex_data3d('delete', tmp_id); end end %------------------------------------------------------------------ % Protected function: reconstruct function V = reconstruct_c(this, sinogram, V0, mask, iterations) if this.initialized == 0 error('IterativeTomography not initialized'); end % data is stored in astra memory if numel(sinogram) == 1 V = this.reconstruct_c_astra(sinogram, V0, mask, iterations); % data is stored in matlab memory else V = this.reconstruct_c_matlab(sinogram, V0, mask, iterations); end end %------------------------------------------------------------------ % Protected function: reconstruct (data in matlab) function V = reconstruct_c_matlab(this, sinogram, V0, mask, iterations) if this.initialized == 0 error('IterativeTomography not initialized'); end % parse method method2 = upper(this.method); if strcmp(method2, 'SART') || strcmp(method2, 'SART_CUDA') iterations = iterations * size(sinogram,1); elseif strcmp(method2, 'ART') iterations = iterations * numel(sinogram); end % create data objects % V = zeros(this.vol_geom.GridRowCount, this.vol_geom.GridColCount, size(sinogram,3)); reconstruction_id = astra_mex_data3d('create', '-vol', this.vol_geom); sinogram_id = astra_mex_data3d('create', '-proj3d', this.proj_geom); if numel(mask) > 0 mask_id = astra_mex_data3d('create', '-vol', this.vol_geom); end % algorithm configuration cfg = this.cfg_base; cfg.ProjectionDataId = sinogram_id; cfg.ReconstructionDataId = reconstruction_id; if numel(mask) > 0 cfg.option.ReconstructionMaskId = mask_id; end alg_id = astra_mex_algorithm('create', cfg); % % loop slices % for slice = 1:size(sinogram,3) % fetch slice of initial reconstruction if numel(V0) > 0 astra_mex_data3d('store', reconstruction_id, V0); else astra_mex_data3d('store', reconstruction_id, 0); end % fetch slice of sinogram astra_mex_data3d('store', sinogram_id, sinogram); % fecth slice of mask if numel(mask) > 0 astra_mex_data3d('store', mask_id, mask); end % iterate astra_mex_algorithm('iterate', alg_id, iterations); % fetch data V = astra_mex_data3d('get', reconstruction_id); % end % correct attenuation factors for super-resolution if this.superresolution > 1 && strcmp(this.gpu,'yes') if strcmp(this.proj_geom.type,'fanflat_vec') || strcmp(this.proj_geom.type,'fanflat') if numel(mask) > 0 V(mask > 0) = V(mask > 0) ./ this.superresolution; else V = V ./ this.superresolution; end end end % garbage collection astra_mex_algorithm('delete', alg_id); astra_mex_data3d('delete', sinogram_id, reconstruction_id); if numel(mask) > 0 astra_mex_data3d('delete', mask_id); end end %------------------------------------------------------------------ % Protected function: reconstruct (data in astra) function V = reconstruct_c_astra(this, sinogram, V0, mask, iterations) if this.initialized == 0 error('IterativeTomography not initialized'); end if numel(V0) > 1 || numel(mask) > 1 || numel(sinogram) > 1 error('Not all required data is stored in the astra memory'); end if numel(V0) == 0 V0 = astra_mex_data2d('create', '-vol', this.vol_geom, 0); end % parse method method2 = upper(this.method); if strcmp(method2, 'SART') || strcmp(method2, 'SART_CUDA') iterations = iterations * astra_geom_size(this.proj_geom, 1); elseif strcmp(method2, 'ART') s = astra_geom_size(this.proj_geom); iterations = iterations * s(1) * s(2); end % algorithm configuration cfg = this.cfg_base; cfg.ProjectionDataId = sinogram; cfg.ReconstructionDataId = V0; if numel(mask) > 0 cfg.option.ReconstructionMaskId = mask; end alg_id = astra_mex_algorithm('create', cfg); % iterate astra_mex_algorithm('iterate', alg_id, iterations); % fetch data V = V0; % correct attenuation factors for super-resolution if this.superresolution > 1 if strcmp(this.proj_geom.type,'fanflat_vec') || strcmp(this.proj_geom.type,'fanflat') if numel(mask) > 0 astra_data_op_masked('$1./s1', [V V], [this.superresolution this.superresolution], mask, this.gpu_core); else astra_data_op('$1./s1', [V V], [this.superresolution this.superresolution], this.gpu_core); end end end % garbage collection astra_mex_algorithm('delete', alg_id); end %------------------------------------------------------------------ function V_out = selectROI(~, V_in) if numel(V_in) == 1 cfg = astra_struct('RoiSelect_CUDA'); cfg.DataId = V_in; alg_id = astra_mex_algorithm('create',cfg); astra_mex_algorithm('run', alg_id); astra_mex_algorithm('delete', alg_id); V_out = V_in; else V_out = ROIselectfull(V_in, min([size(V_in,1), size(V_in,2)])); end end %------------------------------------------------------------------ end end astra-toolbox-2.3.0/matlab/algorithms/DART/Kernels.m000066400000000000000000000023731475635207100222550ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef Kernels %KERNELS Summary of this class goes here % Detailed explanation goes here properties end methods(Static) function K = BinaryPixelKernel(radius, conn) if nargin < 2 conn = 8; end % 2D, 4conn if conn == 4 K = [0 1 0; 1 1 1; 0 1 0]; for i = 2:radius K = conv2(K,K); end K = double(K >= 1); % 2D, 8conn elseif conn == 8 K = ones(2*radius+1, 2*radius+1); % 3D, 6conn elseif conn == 6 K = zeros(3,3,3); K(:,:,1) = [0 0 0; 0 1 0; 0 0 0]; K(:,:,2) = [0 1 0; 1 1 1; 0 1 0]; K(:,:,3) = [0 0 0; 0 1 0; 0 0 0]; for i = 2:radius K = convn(K,K); end K = double(K >= 1); % 2D, 27conn elseif conn == 26 K = ones(2*radius+1, 2*radius+1, 2*radius+1); else disp('Invalid conn') end end end end astra-toolbox-2.3.0/matlab/algorithms/DART/MaskingDefault.m000066400000000000000000000134001475635207100235410ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef MaskingDefault < matlab.mixin.Copyable % Default policy class for masking for DART. %---------------------------------------------------------------------- properties (Access=public) radius = 1; % SETTING: Radius of masking kernel. conn = 8; % SETTING: Connectivity window. For 2D: 4 or 8. For 3D: 6 or 26. edge_threshold = 1; % SETTING: Number of pixels in the window that should be different. random = 0.1; % SETTING: Percentage of random points. Between 0 and 1. gpu = 'yes'; % SETTING: Use gpu? {'yes', 'no'} gpu_core = 0; % SETTING: Which gpu core to use, only when gpu='yes'. end %---------------------------------------------------------------------- methods (Access=public) %------------------------------------------------------------------ function settings = getsettings(this) % Returns a structure containing all settings of this object. % >> settings = DART.masking.getsettings(); settings.radius = this.radius; settings.conn = this.conn; settings.edge_threshold = this.edge_threshold; settings.random = this.random; end %------------------------------------------------------------------ function Mask = apply(this, ~, S) % Applies masking. % >> Mask = DART.segmentation.apply(DART, S); % 2D, one slice if size(S,3) == 1 if strcmp(this.gpu,'yes') Mask = this.apply_2D_gpu(S); else Mask = this.apply_2D(S); end % 3D, slice by slice elseif this.conn == 4 || this.conn == 8 Mask = zeros(size(S)); for slice = 1:size(S,3) if strcmp(this.gpu,'yes') Mask(:,:,slice) = this.apply_2D_gpu(S(:,:,slice)); else Mask(:,:,slice) = this.apply_2D(S(:,:,slice)); end end % 3D, full else if strcmp(this.gpu,'yes') Mask = this.apply_3D_gpu(S); else Mask = this.apply_3D(S); end end end end %---------------------------------------------------------------------- methods (Access=protected) %------------------------------------------------------------------ function Mask = apply_2D_gpu(this, S) vol_geom = astra_create_vol_geom(size(S)); data_id = astra_mex_data2d('create', '-vol', vol_geom, S); mask_id = astra_mex_data2d('create', '-vol', vol_geom, 0); cfg = astra_struct('DARTMASK_CUDA'); cfg.SegmentationDataId = data_id; cfg.MaskDataId = mask_id; cfg.option.GPUindex = this.gpu_core; cfg.option.Connectivity = this.conn; cfg.option.Threshold = this.edge_threshold; cfg.option.Radius = this.radius; alg_id = astra_mex_algorithm('create',cfg); astra_mex_algorithm('iterate',alg_id,1); Mask = astra_mex_data2d('get', mask_id); astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', data_id, mask_id); % random RandomField = double(rand(size(S)) < this.random); % combine Mask = or(Mask, RandomField); end %------------------------------------------------------------------ function Mask = apply_2D(this, S) r = this.radius; w = 2 * r + 1; kernel = Kernels.BinaryPixelKernel(r, this.conn); % edges Xlarge = zeros(size(S,1)+w-1, size(S,2)+w-1); Xlarge(1+r:end-r, 1+r:end-r) = S; Edges = zeros(size(S)); for s = -r:r for t = -r:r if kernel(s+r+1, t+r+1) == 0 continue end Temp = abs(Xlarge(1+r:end-r, 1+r:end-r) - Xlarge(1+r+s:end-r+s, 1+r+t:end-r+t)); Edges(Temp > eps) = Edges(Temp > eps) + 1; end end Edges = Edges > this.edge_threshold; % random RandomField = double(rand(size(S)) < this.random); % combine Mask = or(Edges, RandomField); end %------------------------------------------------------------------ function Mask = apply_3D(this, S) r = this.radius; w = 2 * r + 1; kernel = Kernels.BinaryPixelKernel(r, this.conn); % edges Xlarge = zeros(size(S,1)+w-1, size(S,2)+w-1, size(S,3)+w-1); Xlarge(1+r:end-r, 1+r:end-r, 1+r:end-r) = S; Edges = zeros(size(S)); for s = -r:r for t = -r:r for u = -r:r if kernel(s+r+1, t+r+1, u+r+1) == 0 continue end Temp = abs(Xlarge(1+r:end-r, 1+r:end-r, 1+r:end-r) - Xlarge(1+r+s:end-r+s, 1+r+t:end-r+t, 1+r+u:end-r+u)); Edges(Temp > eps) = 1; end end end clear Xlarge; % random RandomField = double(rand(size(S)) < this.random); % combine Mask = or(Edges, RandomField); end %------------------------------------------------------------------ function Mask = apply_3D_gpu(this, S) vol_geom = astra_create_vol_geom(size(S,2), size(S,1), size(S,3)); data_id = astra_mex_data3d('create', '-vol', vol_geom, S); cfg = astra_struct('DARTMASK3D_CUDA'); cfg.SegmentationDataId = data_id; cfg.MaskDataId = data_id; cfg.option.GPUindex = this.gpu_core; cfg.option.Connectivity = this.conn; cfg.option.Threshold = this.edge_threshold; cfg.option.Radius = this.radius; alg_id = astra_mex_algorithm('create',cfg); astra_mex_algorithm('iterate',alg_id,1); Mask = astra_mex_data3d('get', data_id); astra_mex_algorithm('delete', alg_id); astra_mex_data3d('delete', data_id); % random RandomField = double(rand(size(S)) < this.random); % combine Mask = or(Mask, RandomField); end %------------------------------------------------------------------ end end astra-toolbox-2.3.0/matlab/algorithms/DART/MaskingGPU.m000066400000000000000000000054751475635207100226250ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef MaskingGPU < matlab.mixin.Copyable % Policy class for masking for DART with GPU accelerated code (deprecated). %---------------------------------------------------------------------- properties (Access=public) radius = 1; % SETTING: Radius of masking kernel. conn = 8; % SETTING: Connectivity window. For 2D: 4 or 8. For 3D: 6 or 26. edge_threshold = 1; % SETTING: Number of pixels in the window that should be different. gpu_core = 0; % SETTING: random = 0.1; % SETTING: Percentage of random points. Between 0 and 1. end %---------------------------------------------------------------------- methods (Access=public) %------------------------------------------------------------------ function settings = getsettings(this) % Returns a structure containing all settings of this object. % >> settings = DART.masking.getsettings(); settings.radius = this.radius; settings.conn = this.conn; settings.edge_threshold = this.edge_threshold; settings.random = this.random; end %------------------------------------------------------------------ function Mask = apply(this, ~, V_in) % Applies masking. % >> Mask = DART.segmentation.apply(DART, V_in); % 2D, one slice if size(V_in,3) == 1 Mask = this.apply_2D(V_in); % 3D, slice by slice elseif this.conn == 4 || this.conn == 8 Mask = zeros(size(V_in)); for slice = 1:size(V_in,3) Mask(:,:,slice) = this.apply_2D(V_in(:,:,slice)); end % 3D, full else error('Full 3D masking on GPU not implemented.') end end end %---------------------------------------------------------------------- methods (Access=protected) %------------------------------------------------------------------ function Mask = apply_2D(this, S) vol_geom = astra_create_vol_geom(size(S)); data_id = astra_mex_data2d('create', '-vol', vol_geom, S); mask_id = astra_mex_data2d('create', '-vol', vol_geom, 0); cfg = astra_struct('DARTMASK_CUDA'); cfg.SegmentationDataId = data_id; cfg.MaskDataId = mask_id; cfg.option.GPUindex = this.gpu_core; %cfg.option.Connectivity = this.conn; alg_id = astra_mex_algorithm('create',cfg); astra_mex_algorithm('iterate',alg_id,1); Mask = astra_mex_data2d('get', mask_id); astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', data_id, mask_id); end end end astra-toolbox-2.3.0/matlab/algorithms/DART/OutputDefault.m000066400000000000000000000123211475635207100234510ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef OutputDefault < matlab.mixin.Copyable % Default policy class for output for DART. properties (Access=public) directory = ''; % SETTING: Directory to save output. pre = ''; % SETTING: Prefix of output. save_images = 'no'; % SETTING: Save the images. 'no', 'yes' (='S') OR {'S', 'I', 'Mask', 'P'} save_results = 'no'; % SETTING: Save the results. 'yes', 'no' OR {'base', 'stats', 'settings', 'S', 'V', 'V0'} save_object = 'no'; % SETTING: Save the DART object. {'no','yes'} save_interval = 1; % SETTING: # DART iteration between saves. save_images_interval = []; % SETTING: Overwrite interval for save_images. save_results_interval = []; % SETTING: Overwrite interval for save_results. save_object_interval = []; % SETTING: Overwrite interval for save_object. slices = 1; % SETTING: In case of 3D, which slices to save? verbose = 'no'; % SETTING: Verbose? {'no','yes'} end methods (Access=public) %------------------------------------------------------------------ function pre_initial_iteration(this, ~) if strcmp(this.verbose,'yes') tic fprintf(1, 'initial iteration...'); end end %------------------------------------------------------------------ function post_initial_iteration(this, ~) if strcmp(this.verbose,'yes') t = toc; fprintf(1, 'done in %f s.\n', t); end end %------------------------------------------------------------------ function pre_iteration(this, DART) if strcmp(this.verbose,'yes') tic; fprintf(1, '%s dart iteration %d...', this.pre, DART.iterationcount); end end %------------------------------------------------------------------ function post_iteration(this, DART) % print output if strcmp(this.verbose,'yes') t = toc; s = DART.statistics.tostring(DART.stats); fprintf(1, 'done in %0.2fs %s.\n', t, s); end % save DART object if do_object(this, DART) save(sprintf('%s%sobject_%i.mat', this.directory, this.pre, DART.iterationcount), '-v7.3', 'DART'); end % save .mat if do_results(this, DART) base = DART.base; stats = DART.stats; S = DART.S; V = DART.V; V0 = DART.V0; settings = DART.getsettings(); if ~iscell(this.save_results) save(sprintf('%s%sresults_%i.mat', this.directory, this.pre, DART.iterationcount), '-v7.3', 'base', 'stats', 'S', 'V', 'V0', 'settings'); else string = []; for i = 1:numel(this.save_results) string = [string this.save_results{i} '|']; end save(sprintf('%s%sresults_%i.mat', this.directory, this.pre, DART.iterationcount), '-v7.3', '-regexp', string(1:end-1)); end end % save images if do_images(this, DART) if ~iscell(this.save_images) && strcmp(this.save_images, 'yes') output_image(this, DART, 'S') elseif iscell(this.save_images) for i = 1:numel(this.save_images) output_image(this, DART, this.save_images{i}); end end end end %------------------------------------------------------------------ end %---------------------------------------------------------------------- methods (Access=private) function output_image(this, DART, data) % 2D if numel(size(DART.S)) == 2 eval(['imwritesc(DART.' data ', sprintf(''%s%s' data '_%i.png'', this.directory, this.pre, DART.iterationcount))']); % 3D elseif numel(size(DART.S)) == 3 for slice = this.slices eval(['imwritesc(DART.' data '(:,:,slice), sprintf(''%s%s' data '_%i_slice%i.png'', this.directory, this.pre, DART.iterationcount, slice))']); end end end %------------------------------------------------------------------ function out = do_object(this, DART) if strcmp(this.save_object,'no') out = 0; return end if numel(this.save_object_interval) == 0 && mod(DART.iterationcount, this.save_interval) == 0 out = 1; elseif mod(DART.iterationcount, this.save_object_interval) == 0 out = 1; else out = 0; end end %------------------------------------------------------------------ function out = do_results(this, DART) if strcmp(this.save_results,'no') out = 0; return end if numel(this.save_results_interval) == 0 && mod(DART.iterationcount, this.save_interval) == 0 out = 1; elseif mod(DART.iterationcount, this.save_results_interval) == 0 out = 1; else out = 0; end end %------------------------------------------------------------------ function out = do_images(this, DART) if numel(this.save_images_interval) == 0 && mod(DART.iterationcount, this.save_interval) == 0 out = 1; elseif mod(DART.iterationcount, this.save_images_interval) == 0 out = 1; else out = 0; end end %------------------------------------------------------------------ end end astra-toolbox-2.3.0/matlab/algorithms/DART/SegmentationDefault.m000066400000000000000000000033051475635207100246100ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef SegmentationDefault < matlab.mixin.Copyable % Default policy class for segmentation for DART. %---------------------------------------------------------------------- properties (Access=public) rho = []; % SETTING: Grey levels. tau = []; % SETTING: Threshold values. end %---------------------------------------------------------------------- methods (Access=public) %------------------------------------------------------------------ function settings = getsettings(this) % Returns a structure containing all settings of this object. % >> settings = DART.segmentation.getsettings(); settings.rho = this.rho; settings.tau = this.tau; end %------------------------------------------------------------------ function this = estimate_grey_levels(this, ~, ~) % Estimates grey levels % >> DART.segmentation.estimate_grey_levels(); end %------------------------------------------------------------------ function V_out = apply(this, ~, V_in) % Applies segmentation. % >> V_out = DART.segmentation.apply(DART, V_in); V_out = ones(size(V_in)) * this.rho(1); for n = 2:length(this.rho) V_out(this.tau(n-1) < V_in) = this.rho(n); end end %------------------------------------------------------------------ end end astra-toolbox-2.3.0/matlab/algorithms/DART/SmoothingDefault.m000066400000000000000000000120351475635207100241220ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef SmoothingDefault < matlab.mixin.Copyable % Default policy class for smoothing for DART. %---------------------------------------------------------------------- properties (Access=public) radius = 1; % SETTING: Radius of smoothing kernel. b = 0.1; % SETTING: Intensity of smoothing. Between 0 and 1. full3d = 'yes'; % SETTING: smooth in 3D? {'yes','no'} gpu = 'yes'; % SETTING: Use gpu? {'yes', 'no'} gpu_core = 0; % SETTING: Which gpu core to use, only when gpu='yes'. end %---------------------------------------------------------------------- methods (Access=public) %------------------------------------------------------------------ function settings = getsettings(this) % Returns a structure containing all settings of this object. % >> settings = DART.smoothing.getsettings(); settings.radius = this.radius; settings.b = this.b; settings.full3d = this.full3d; end %------------------------------------------------------------------ function V_out = apply(this, ~, V_in) % Applies smoothing. % >> V_out = DART.smoothing.apply(DART, V_in); % 2D, one slice if size(V_in,3) == 1 if strcmp(this.gpu,'yes') V_out = this.apply_2D_gpu(V_in); else V_out = this.apply_2D(V_in); end % 3D, slice by slice elseif ~strcmp(this.full3d,'yes') V_out = zeros(size(V_in)); for slice = 1:size(V_in,3) if strcmp(this.gpu,'yes') V_out(:,:,slice) = this.apply_2D_gpu(V_in(:,:,slice)); else V_out(:,:,slice) = this.apply_2D(V_in(:,:,slice)); end end % 3D, full else if strcmp(this.gpu,'yes') V_out = this.apply_3D_gpu(V_in); else V_out = this.apply_3D(V_in); end end end end %---------------------------------------------------------------------- methods (Access=protected) %------------------------------------------------------------------ function V_out = apply_2D(this, V_in) r = this.radius; w = 2 * r + 1; % Set Kernel K = ones(w) * this.b / (w.^2-1); % edges K(r+1,r+1) = 1 - this.b; % center % output window V_out = zeros(size(V_in,1) + w-1, size(V_in,2) + w - 1); % blur convolution for s = -r:r for t = -r:r V_out(1+r+s:end-r+s, 1+r+t:end-r+t) = V_out(1+r+s:end-r+s, 1+r+t:end-r+t) + K(r+1+s, r+1+t) * V_in; end end % shrink output window V_out = V_out(1+r:end-r, 1+r:end-r); end %------------------------------------------------------------------ function V_out = apply_2D_gpu(this, V_in) vol_geom = astra_create_vol_geom(size(V_in)); in_id = astra_mex_data2d('create', '-vol', vol_geom, V_in); out_id = astra_mex_data2d('create', '-vol', vol_geom, 0); cfg = astra_struct('DARTSMOOTHING_CUDA'); cfg.InDataId = in_id; cfg.OutDataId = out_id; cfg.option.Intensity = this.b; cfg.option.Radius = this.radius; cfg.option.GPUindex = this.gpu_core; alg_id = astra_mex_algorithm('create',cfg); astra_mex_algorithm('iterate',alg_id,1); V_out = astra_mex_data2d('get', out_id); astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', in_id, out_id); end %------------------------------------------------------------------ function I_out = apply_3D(this, I_in) r = this.radius; w = 2 * r + 1; % Set Kernel K = ones(w,w,w) * this.b / (w.^3-1); % edges K(r+1,r+1,r+1) = 1 - this.b; % center % output window I_out = zeros(size(I_in,1)+w-1, size(I_in,2)+w-1, size(I_in,3)+w-1); % blur convolution for s = -r:r for t = -r:r for u = -r:r I_out(1+r+s:end-r+s, 1+r+t:end-r+t, 1+r+u:end-r+u) = I_out(1+r+s:end-r+s, 1+r+t:end-r+t, 1+r+u:end-r+u) + K(r+1+s, r+1+t, r+1+u) * I_in; end end end % shrink output window I_out = I_out(1+r:end-r, 1+r:end-r, 1+r:end-r); end %------------------------------------------------------------------ function V_out = apply_3D_gpu(this, V_in) vol_geom = astra_create_vol_geom(size(V_in,2),size(V_in,1),size(V_in,3)); data_id = astra_mex_data3d('create', '-vol', vol_geom, V_in); cfg = astra_struct('DARTSMOOTHING3D_CUDA'); cfg.InDataId = data_id; cfg.OutDataId = data_id; cfg.option.Intensity = this.b; cfg.option.Radius = this.radius; cfg.option.GPUindex = this.gpu_core; alg_id = astra_mex_algorithm('create',cfg); astra_mex_algorithm('iterate', alg_id, 1); V_out = astra_mex_data3d('get', data_id); astra_mex_algorithm('delete', alg_id); astra_mex_data3d('delete', data_id); end %------------------------------------------------------------------ end end astra-toolbox-2.3.0/matlab/algorithms/DART/SmoothingGPU.m000066400000000000000000000065141475635207100231760ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef SmoothingGPU < matlab.mixin.Copyable % Default policy class for smoothing for DART. %---------------------------------------------------------------------- properties (Access=public) radius = 1; % SETTING: Radius of smoothing kernel. b = 0.1; % SETTING: Intensity of smoothing. Between 0 and 1. full3d = 'yes'; % SETTING: smooth in 3D? {'yes','no'} gpu_core = 0; % SETTING: end %---------------------------------------------------------------------- methods (Access=public) %------------------------------------------------------------------ function settings = getsettings(this) % Returns a structure containing all settings of this object. % >> settings = DART.smoothing.getsettings(); settings.radius = this.radius; settings.b = this.b; settings.full3d = this.full3d; end %------------------------------------------------------------------ function V_out = apply(this, ~, V_in) % Applies smoothing. % >> V_out = DART.smoothing.apply(DART, V_in); % 2D, one slice if size(V_in,3) == 1 V_out = this.apply_2D(V_in); % 3D, slice by slice elseif ~strcmp(this.full3d,'yes') V_out = zeros(size(V_in)); for slice = 1:size(V_in,3) V_out(:,:,slice) = this.apply_2D(V_in(:,:,slice)); end % 3D, full else V_out = this.apply_3D(V_in); end end end %---------------------------------------------------------------------- methods (Access=protected) %------------------------------------------------------------------ function V_out = apply_2D(this, V_in) vol_geom = astra_create_vol_geom(size(V_in)); in_id = astra_mex_data2d('create', '-vol', vol_geom, V_in); out_id = astra_mex_data2d('create', '-vol', vol_geom, 0); cfg = astra_struct('DARTSMOOTHING_CUDA'); cfg.InDataId = in_id; cfg.OutDataId = out_id; cfg.Intensity = this.b; cfg.option.GPUindex = this.gpu_core; alg_id = astra_mex_algorithm('create',cfg); astra_mex_algorithm('iterate',alg_id,1); V_out = astra_mex_data2d('get', out_id); astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', in_id, out_id); end %------------------------------------------------------------------ function I_out = apply_3D(this, I_in) r = this.radius; w = 2 * r + 1; % Set Kernel K = ones(w,w,w) * this.b / (w.^3-1); % edges K(r+1,r+1,r+1) = 1 - this.b; % center % output window I_out = zeros(size(I_in,1)+w-1, size(I_in,2)+w-1, size(I_in,3)+w-1); % blur convolution for s = -r:r for t = -r:r for u = -r:r I_out(1+r+s:end-r+s, 1+r+t:end-r+t, 1+r+u:end-r+u) = I_out(1+r+s:end-r+s, 1+r+t:end-r+t, 1+r+u:end-r+u) + K(r+1+s, r+1+t, r+1+u) * I_in; end end end % shrink output window I_out = I_out(1+r:end-r, 1+r:end-r, 1+r:end-r); end %------------------------------------------------------------------ end end astra-toolbox-2.3.0/matlab/algorithms/DART/StatisticsDefault.m000066400000000000000000000041341475635207100243060ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef StatisticsDefault < matlab.mixin.Copyable % Default policy class for statistics for DART. properties (Access=public) pixel_error = 'no'; % SETTING: Store pixel error? {'yes','no'} proj_diff = 'no'; % SETTING: Store projection difference? {'yes','no'} timing = 'no'; % SETTING: Store timings? {'yes','no'} end methods (Access=public) %------------------------------------------------------------------ function stats = apply(this, DART) % Applies statistics. % >> stats = DART.statistics.apply(DART); stats = DART.stats; % timing if strcmp(this.timing, 'yes') stats.timing(DART.iterationcount) = toc(DART.start_tic); end % pixel error if strcmp(this.pixel_error, 'yes') && isfield(DART.base,'phantom') [stats.rnmp, stats.nmp] = compute_rnmp(DART.base.phantom, DART.S); stats.rnmp_hist(DART.iterationcount) = stats.rnmp; stats.nmp_hist(DART.iterationcount) = stats.nmp; end % projection difference if strcmp(this.proj_diff, 'yes') new_sino = DART.tomography.project(DART.S); stats.proj_diff = sum((new_sino(:) - DART.base.sinogram(:)) .^2 ) ./ (sum(DART.base.sinogram(:)) ); stats.proj_diff_hist(DART.iterationcount) = stats.proj_diff; end end %------------------------------------------------------------------ function s = tostring(~, stats) % To string. % >> stats = DART.statistics.apply(stats); s = ''; if isfield(stats, 'nmp') s = sprintf('%s [%d]', s, stats.nmp); end if isfield(stats, 'proj_diff') s = sprintf('%s {%0.2d}', s, stats.proj_diff); end end %------------------------------------------------------------------ end end astra-toolbox-2.3.0/matlab/algorithms/DART/examples/000077500000000000000000000000001475635207100223055ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/algorithms/DART/examples/cylinders.png000066400000000000000000000075361475635207100250220ustar00rootroot00000000000000‰PNG  IHDRÑ‹&sRGB®ÎéIDATxÚíÛ–Û8 Ý:þÿ_î}È&'“ñØE‚ Xõ²YmA@¼˜"X­e®³Ü…«¸VË}æ¸Nv©@ìÃïÄ…ÒK5¢z'®’_¹-tÖ[q‘ûÅQ&þ Ÿ2 Tr³‹ÉåxÀÆù¶WÌQÈm&œU`Ò™&ö ­åg½‡Æ_T€îñh¡1F”+eûX‹ý°þ›çBóÕÅr2ajÉNÂsÅôO¥}ù·@ÕΪ[UéÔñpü§;öU»¿ZW` üÔÁRF«„âÊ¿2Z%×ü+£UZi&àX:þÿþÕ9¬ ®û¯ŒV Ôþ5Y%ÿ2¶ €eŠD Y® !K*@>_ëQ–„mÎQ!µµ²ñ €z@€.ÀSATJ ì*£5*m ØT´T€Œ¥Ö­ ‚ ⇨+`hË®¬±b³è™æª7F©à4_²Q/"qPÛ›½OðŠ À9æÚ*:ÊÅ?©³³*à9ö†5Å?~±q„È2ã˜Pyõ®œ)kÖpÍiãÇãa¯(⿎Ž2ñW©Ë„ù3ï<€JX•~rë¨RJ«r`3pÔqµÊÆdV)§©Jå¤;®rrù=GÇ¿FSžc<0ÂüçÐø'õóò»<¹ßMÄïùc¿ø—¡‹Žqñg(·ÂØàH&HVÀAø÷VÀ‘FŠEÉÞàÄïð,~“¡"|ÎsYÛ€‡+ÑDifèF]¼|Œ‹?®º4ë6Ê!“l+—€cdüyN/Bž\`iŽåÒi§¡àò@ãé‰{°¢@&¥]›WØ]Ñå–^ €nG*OSPì)Ò½‚ñ4@'6éF ¸µLH¨°³¼wb› @P6€‰jVžy’µ› ÀSD`­&êtà„{œ¤øë¿o•{ߊg¦ðg‘€_¼Pµš: x½³Øì„én•¶¬úÛkºÔ8zêÎï¬ÊÜ<‡zWïÇí=´ŽîoÕ£×n.±‰Ðî.Ýwu»]¾yß\ÌÝ3.î`Õsšž|k”öÙ—î;È…+€z¦‚Æ%›&ZõuÆÃ•*€=ãßÁ_¶Ó5à‹ó ‘6þdÑ€^ÏÄœGÙòÿ'›4߬TL™r”J4 þÅJÀ‘6þ}¼«wÿÛf–Kùèˆ×±CðWÌÕiûíR-ųpúíi«ŸY‰~/G«·&%²gêäð‚Û>$*À„âïþ]gßÏPéÍ•€Ne¬˜ÛO°BãœÈ¤ÜÀŸÒNP…ýZáfh ôNƆx¹:Ðg@éÄ‘R±u0M$)Ï+ídTÌÌÔò†9¥.w©žîê¬Í€WÉ‚‡òHð´ pñ&•Ò×J«G\ì¨ rj@ûÅŽÈk ¤»ØQÌé¥p@<ŽÐøSÊtWUÆ×½¬êvö®C+@(yïpìH—Ö_LÐÂÉæêþijP²½\ñ~Ep@x’/![‘ ß}íßÿѼìtê§Ñ‚:“~·÷[G»ös À \퉚d»øI®öªiYKI~ñÄøo}jؼ³cý³!Ñáìà\xƒ+æ€3¹Ú3£á-à™éí\±ð†˜tÏ¿ÊN:‡§|†aGú+o^2Î õ $©Iã?È,ùþ„%àIt þŸÿ1`ŒtûkÒŸÝ£haø1ÞÓº¾»WØ6Z¤ <ú ÂÓ×üºËç)ÓäÉž}®QtÓϧ/Ðõy禥ë>ÇÄÆ™v[úçßîþ ž¢´Æ®çš†úY‚:eï 4©Ì­@-…B—mêpÿýŽñ‡Ï¹ýîËÈC¾oÖ,u“¨úXtÖ2µJÌcs3\ç Àk—J½Ü ~éóSÙzcZöŸ“€:@AS }ŒÀÙÐd Lµ¿A¹¹„çšæÙôî-·Ìò Õ(±Zsm¤Ôþ&[¡ àŽ¹6ÌÑŠœæ'Ù¼ МkIóF½djÑ8²øKƒ.öÃè3:uÓ6‰w írH«úÞ™œuŒ¾½à\;הܙ]Ð2ê_¼t<¥YÉn'ÓDGž0*gêhÀ÷;O¶tÀ ¡æW§qØ‘5.ªñoëY$ažc @¡3V3—JM« ¾Òaåy€%–BS–:  ëÆ+Ês!€7ýY÷sYºL'g€éØu†(î.Sð:X<êè‰>Àl9$Õà“èN œ²ãÙñn^ÜŠËÍèY]Ã_  Пu·RÇ\s’*áÑ—ÔâøºŠ#iÂ;×weòÒ1ø^ÂsÍ}¯U~øÐ{ ”Y;?°UÏ ø¼äÞ À­¯3 &$zm‡Ê)Ûaë# ™©ëó¯¶ìíI7·hhϵ±%àó×{Áx^&b&ðÄ܉GiíÆæí¾uG«&æäРZ{ùý1'K¦~Vb€tÙ™ój­›´ñ˜ºCõ‚ =×üìé׆Ùý®•¼Ãw||Û¦¬Z[7–c(ÎV|¯`]¹i5ùGA™vù¨µYur³`ÝC® МkVL¦ùú45ÿû|wð‚7äšõ˜êèvË|ËS ¹å!M@ìÑ,SZ]5J#Œ×2`HªÍìg9*– òó|OûRVk 8(Å!‡µ÷l´Zê¯U¾¹9¯WýhF•tï®ùAÊ·fòØæNy׬V@Ò\ Jš›¥ê«©jõ8r©]ê#u6«Oúj+Üð’yZ©¾fÜ8ZgòaQ?|¢d#p$rµz%˨ƒúT¯Œm4Ç[9€-‡šáh»êé(í<€~°]—õ¶>=’†±}Ä~æÌ&¬kœ¨b Ó™A]>úãHBS»¢Äx­èa£+ @‘>RÎp||ã.´×íº,uvx¢³ƒG\I)b{ÈQh£H½zÉÔÿ鸱<ìÊG£V€¸TüËTÑÖÝÇ8òÏ8å^ætŽ®ÿ wCÓ" ÿ«È²47žút³‹¥ò&ÀcÓLCcšþN^ÚÆN—d.–ý‰š€–Þ¶æè²TøóÐË.ý°>€®þ=&þp %Í—5çÖ˳åÖü¢~‚h´+Æ?g0ùZNZ/ š¤J*€§™ 0ÃÕš)í®ƒd;g®Cu¹SÐ8¸æjÿ¸ÿ·úÊÕàb ˆ( öãç-à5y`ê}*€æ8îOäC~Ë1à’_o¬¹òå·Ü½w«ÍÄbûêZ®GvJ=À./€«w+ûÚg>ñ·UR¨Ž@§}J­/ ³‡I^ª`Ü]àÖõØ]‚ýê?~b×9Ág| æ.ât@ÙÙkEP燯<6þ1ºSzov¿Ï“ØSnÅ×ÿ˜ª?çàŸ›Qzç7ný]!µôî´WMŒ¸NxÙÑKö¿œv’lDÙ: AuÍ7?ê3_ä)Úü¿­|ÔÀ{8Þ³ÿ¼àQ2k´ï—KœoB©½ðö± Ï[µßó8ÒZý½¾àCS_wî´£“@2¨B>®%\¼lÞw€ú+@Q®v)-(aññ M¸/MV@NO+ÜF=RV€èûNRÖúay¬<Ô}ZR¦[ `GW+Þ,í)aUÉ  ¼V ¶n½Q‰=qTZÑm?j¹šN@¨œMB{ *À?ð³{q([" ›IHUJµ–ÙœJûŽ’y»ÿC­ÚQÚ(EÈïl£ú“‚¹¦MìNß`ö - §L¿Ì2èÒôùSø/šõ”àVµ^Ü¥+ÀeøóKnr¶»¥߸e.&Zôþt׎“ÛAëÊ4Ç?š-€ Ð}3tðýsv/€ ÈPh5Ã7 …²kÙ¡C§a`‚Ö$¦ÎPvHT&|ð 3@X’g_-ú›Üþ-_µÕ8ÈcYêô¬uƒAþÌ÷9½}Õéo‰?pvG|/q_Nż€úŠò÷÷ù£T'@ü¸ptË‹Oèå÷¼€:ËRo¯á¥ôsp¯•þêx±Óý­uø#e8=ƒ®#¨¹GwsÜͽºAMï¤&8=üýû5T"ºöÝ2C§J ž>ÁË7Z&m#€þù8O--' &&;‹–O±JÙÝ’ºhäg²ÌøEÛ¡…0ÒÓžœË>8¸ð“AóV–/t.dšy€‹ãås®vÆT³VŠÿ‚@ÙͲ_”(*ÀÕä‰Ó‡ºÇÉët‚+€FÉŶE—ämÜàê1á÷ÉN–TÇ3WÔu2Ù¼Tf.J“×_íñGóÜï6|ÖI¬r“‹=5€Áàc‡ËJ(]ë¬P¼\Ïà™Í ÿ½4‹ûüeM ÎJ`…~á3ŸIñ£çÏÁ†åcŒîû‡S6ÿ¢PFOkÛø'®äÿÞ þUG§~{!ü»gm>íýBŸQ$þ}€àóˆÿ*@å¼xñm/Ž4ÎVJù•ßøfÒý¥üÕlÒ"ƒ¹?³¯OFݽ ûSÃV«Í%°^Û,°uüû*l@°óàP¬;¦C€ € @€X#@€Ð@€ ™G;(À`kô-O<]c£FkA£z`óN!}†€,ƒ @0« x^ø¨4°s¦ X¹Ø[-`k;€ûüé;}ÍJÝ<IEND®B`‚astra-toolbox-2.3.0/matlab/algorithms/DART/examples/example1.m000066400000000000000000000037701475635207100242060ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- addpath('../'); %% Example 1: 2D parallel beam, cuda % configuration proj_count = 20; dart_iterations = 20; filename = 'cylinders.png'; outdir = './'; prefix = 'example1'; rho = [0, 255]; tau = 128; gpu_core = 0; % load phantom I = imreadgs(filename); % create projection and volume geometries det_count = size(I, 1); angles = linspace2(0, pi, proj_count); proj_geom = astra_create_proj_geom('parallel', 1, det_count, angles); vol_geom = astra_create_vol_geom(det_count, det_count); % create sinogram [sinogram_id, sinogram] = astra_create_sino_cuda(I, proj_geom, vol_geom); astra_mex_data2d('delete', sinogram_id); % DART D = DARTalgorithm(sinogram, proj_geom); D.t0 = 100; D.t = 10; D.tomography.method = 'SIRT'; D.tomography.gpu_core = gpu_core; D.tomography.use_minc = 'yes'; D.tomography.gpu = 'no'; D.segmentation = SegmentationPDM(); D.segmentation.rho = rho*1.8; D.segmentation.tau = tau*1.5; D.segmentation.interval = 5; D.smoothing.b = 0.1; D.smoothing.gpu_core = gpu_core; D.smoothing.gpu = 'no'; D.masking.random = 0.1; D.masking.gpu_core = gpu_core; D.masking.gpu = 'no'; D.output.directory = outdir; D.output.pre = [prefix '_']; D.output.save_images = 'no'; D.output.save_results = {'stats', 'settings', 'S', 'V'}; D.output.save_interval = dart_iterations; D.output.verbose = 'yes'; D.statistics.proj_diff = 'no'; D.initialize(); D.iterate(dart_iterations); % save the reconstruction and the segmentation to file imwritesc(D.S, [outdir '/' prefix '_S.png']); imwritesc(D.V, [outdir '/' prefix '_V.png']); astra-toolbox-2.3.0/matlab/algorithms/DART/examples/example2.m000066400000000000000000000037241475635207100242060ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- addpath('../'); %% Example 2: 3D parallel beam, cuda % configuration proj_count = 20; dart_iterations = 20; outdir = './'; prefix = 'example2'; rho = [0, 0.5, 1]; tau = [0.25, 0.75]; gpu_core = 0; % load phantom load('phantom3d'); % create projection and volume geometries det_count = size(I, 1); slice_count = size(I,3); angles = linspace2(0, pi, proj_count); proj_geom = astra_create_proj_geom('parallel3d', 1, 1, slice_count, det_count, angles); vol_geom = astra_create_vol_geom(size(I)); % create sinogram [sinogram_id, sinogram] = astra_create_sino3d_cuda(I, proj_geom, vol_geom); astra_mex_data3d('delete', sinogram_id); % DART D = DARTalgorithm(sinogram, proj_geom); D.t0 = 100; D.t = 10; D.tomography = IterativeTomography3D(); D.tomography.method = 'SIRT3D_CUDA'; D.tomography.gpu_core = gpu_core; D.tomography.use_minc = 'yes'; D.segmentation.rho = rho; D.segmentation.tau = tau; D.smoothing.b = 0.1; D.smoothing.full3d = 'yes'; D.smoothing.gpu_core = gpu_core; D.masking.random = 0.1; D.masking.conn = 4; D.masking.gpu_core = gpu_core; D.output.directory = outdir; D.output.pre = [prefix '_']; D.output.save_images = 'no'; D.output.save_results = {'stats', 'settings', 'S', 'V'}; D.output.save_interval = dart_iterations; D.output.verbose = 'yes'; D.statistics.proj_diff = 'no'; D.initialize(); D.iterate(dart_iterations); % save the central slice of the reconstruction and the segmentation to file imwritesc(D.S(:,:,64), [outdir '/' prefix '_S_slice_64.png']); imwritesc(D.V(:,:,64), [outdir '/' prefix '_V_slice_64.png']); astra-toolbox-2.3.0/matlab/algorithms/DART/examples/example3.m000066400000000000000000000040061475635207100242010ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- addpath('../'); %% Example 3: 3D cone beam, cuda % configuration proj_count = 20; dart_iterations = 20; outdir = './'; prefix = 'example3'; rho = [0, 0.5, 1]; tau = [0.25, 0.75]; gpu_core = 0; % load phantom load('phantom3d'); % create projection and volume geometries det_count = size(I, 1); slice_count = size(I,3); angles = linspace2(0, pi, proj_count); proj_geom = astra_create_proj_geom('cone', 1, 1, slice_count, det_count, angles, 500, 0); vol_geom = astra_create_vol_geom(size(I)); % create sinogram [sinogram_id, sinogram] = astra_create_sino3d_cuda(I, proj_geom, vol_geom); astra_mex_data3d('delete', sinogram_id); % DART D = DARTalgorithm(sinogram, proj_geom); D.t0 = 100; D.t = 10; D.tomography = IterativeTomography3D(); D.tomography.method = 'SIRT3D_CUDA'; D.tomography.gpu_core = gpu_core; D.tomography.use_minc = 'yes'; D.segmentation.rho = rho; D.segmentation.tau = tau; D.smoothing.b = 0.1; D.smoothing.full3d = 'yes'; D.smoothing.gpu_core = gpu_core; D.masking.random = 0.1; D.masking.conn = 4; D.masking.gpu_core = gpu_core; D.output.directory = outdir; D.output.pre = [prefix '_']; D.output.save_images = 'no'; D.output.save_results = {'stats', 'settings', 'S', 'V'}; D.output.save_interval = dart_iterations; D.output.verbose = 'yes'; D.statistics.proj_diff = 'no'; D.initialize(); D.iterate(dart_iterations); % save the central slice of the reconstruction and the segmentation to file imwritesc(D.S(:,:,64), [outdir '/' prefix '_S_slice_64.png']); imwritesc(D.V(:,:,64), [outdir '/' prefix '_V_slice_64.png']); astra-toolbox-2.3.0/matlab/algorithms/DART/examples/phantom3d.mat000066400000000000000000007317361475635207100247260ustar00rootroot00000000000000MATLAB 7.3 MAT-file, Platform: GLNXA64, Created on: Fri Jun 28 11:28:22 2013 HDF5 schema 1.00 . IM‰HDF  ÿÿÿÿÿÿÿÿÞ³ÿÿÿÿÿÿÿÿ`ˆ¨ˆ¨TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0HEAPXÈIH €€€ ?@4 4ÿ deflate x€@6WÍQ 0 MATLAB_classdoubleSNOD TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3Ïýa9ñÈr¢ÙÓ@+Ü‹o@d6{€@x^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íØ1ƒ0EAnÆýo“#PÑ  –¥ÕßIC Þy†"ÛæC€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€Ü üvBÎÎÿŽ :ÍøÔûú}'›äYßv÷þÏÚ úgõækwÏÿ¨píóô¯Ýgõêô_-\óþ³Ý½ÿkv]•þ£RÙçÍîƒlüéôÏo|7¡þ½ûŸÓÝô2ôÏè8;ÅÛ}0û{®¯-ð´j¯ÞêV øÿg•¬û @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ 'p2l‰Mx^íÚA Â0†ÑÞÌû߯#¸n„Jhÿ”N湡Æä}Sݸm @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ 0.ð~}¯ÝŸÇßéÊJ¿^W:“½ Œövÿ¯1Eg{ë_»ª»ßÿZsþµzîvÖ}ïþ¯1Oú×è”Þåìîîÿt±ìzúg=«­¦µb™ýÞÕÝ÷¦WzýÓ¢µÖÓ¿V¯ônõO‹Ö\ï®9¨©³þ®õ_¿ñ¿êß»ÿ~úÙs@ùÙú?»Ï]»›5wíßç\Ðÿšß*ïNÏÁ*.ÝΑšƒnn«ž÷ì<¬êÑý\£óÐÝ©Ûùýÿ¿[qç%@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€˜#ðèY¬x^íØ1’Â0 PnÆýo³GØ‚¡Ù6v&²У¡ˆ‘¥÷•†ÇÇ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ø_àçùz~ôÍñ;Žrþôü;¦ï7ÅÙ¼ÿþ®ŸÜ='¾*oùß+ÿªÜßuï¥Ñ§ÛêÜ培K«r—¿ü3zvµú½÷þgí™ü³òXÕͮܽÿ«¾æÚª=ÉPè×EUž³uûÉgL<›SÕù ~]Tå9[·Ÿ|ÆÄ³9UÏÐè×EUž³uûÉgL<›SÕù ~]Tå9[·Ÿ|ÆÄ³9UÏÐè×EUž³uûÉgL<›SÕù ¾]Tå:Z·¯|Æä£9UËPÐEU¾GuÉgåTõ\s›* @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€è,ð bT%Ox^íѱm1 ÀïÌýwãN º{Rð5Ÿ|p"Á}½ü @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€® |~|¿]ý_ßèe¢ÀªßÕ÷ÄLnú[`ÕçÝײַ!p·×«ïŸ‘þ¼+¯öW}wžlvâjŸwç³5ιîno]ïÏÎLÚÕã»{2UιêÝÞºæÎ‘ÎJÚÕ_uO–Êükª}uÏÏÏJØÝ_u_–ÎÜkª=횟+ž•lWÕ½YJs¯©ö´k~®xF²]½uíÍPš{EWO»öÌ•ÏH¶«·®½Js¯èêiמ¹òÉvõÖµ7Ciî]=íÚ3W>#Ù®Þºöf(ͽ¢«§]{æÊg$ÛÕ[×Þ ¥¹Wtõ´kÏ\ù¬d»ú«îÍRš{Mµ§]ósų’í꯺7Kiþ5Õ¾ºçç‹g%ì/Kçœkª½uÍŸ#ž™´«Çw÷dªœsÕ»½uÍ#´«Ï»{²UλînÕ÷ç ?#qµ×«óÏÐpåÕ>ï¾#û,»ý®Þ?+½k ¬ú]}':S`ÕûÏ÷™é¥"@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ÿ%ð–Šðãx^íÔAN1ÀüŒÿÿ†'p@¹D‚x×=³»¸ »ÇS-òxø!@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @ +ðýõ›÷üM—v•Àk¯£_õ^sçFû}wnînw ¼ëñìç]ï7ç˜ÀÙ>Þ;ö*§«Žö7{¾zùc³=ž½?ö:§ªÎö–ºWµ—ÜÿRýÍæè©W`¶¯ôýÞí÷–î-•·o#½›§úJçô*ì7-ÝW:o¿Fz7N÷•ÎëÕØgZº§ª¼}éÝ´ª¯tn¯ÊúÓÒýTç­ßHï†Õ}¥ó{uÖŸ–î§:oýFz7¬î+ß«³þ´t?Õyë7Ò³auOUù=:ëO©ê§:wýfz6¬î©*¿Ggý)UýTç®ßLï†Õ}¥ó{uÖŸ–î§:oýFz7¬î+ß«³þ´t?Õyë7Ò»au_éü^}¦¥{ªÊÛ§‘ÞM«úJçöªì7-ÝW:o¿Fz7N÷•ÎëÕØwZº·TÞ¾ônžê+Ó«`Zº¿Ù<\#0Û[êþ5Û›úHõx6G÷8Ûßì½{lïW}¿§Àìÿõèý{nïU¯£}=Gú3Žöü×ùÏÜÞ«Sß$÷xýØk{Û @€ @€ @€ @€ @€ @€ @€ @€ @€ @€:~$ó®Rx^íÔAR#A EAߌû߆#° Øvu}©l+ÙÌ‚n©•/†ÛÍ @€ @€ @€ @€ @€ @€ @€ @€ @€ øüøžó׿™-¦œ¸×÷ÞïO}·½×îõ\ýýµ¯ðV—ÀjÏÕç»î°ç1Õ~»Ï?öUžªØíxõýê»Ìÿ_àj·Ô{úô ¤º¥æô^o[ª[jŽ"=©^é9=×Ïݒ7·LíåéNUójæN¯ê•ž;·PÍåé>ÕójæN­î•ž?·Töòt—®yY…¹Óºz¥÷Ì-–¹<Ý£{^Faî”î^é}sËe.O÷èž—Q˜;¥»WzßÜr{—§;œš·§0÷íS½Ò{çÜ»<ÝáÔ¼=…¹oŸê•Þ;·àÞåé§æí)Ì}ûT¯ôÞ¹÷.Ow85oOaîÛ§z¥÷Î-˜¹<Ý£{^Faî”î^é}sËe.O÷èž—Q˜;¥»WzßÜrÙËÓ]ºæeæNëê•Þ3·XÍåé>ÕójæN­î•ž?·TíåéNUójæN¯ê•ž;·PÏåé^éy= ¶¤»¥æ)Ó+ê–šÓ{½m©n©9ŠœHu¼:çìõ¶ÿ\í·ûžÏ%°ÛsõýçºÞ×üXí¹ú<ñ×Xí{ïù׺Þצÿ>}oÿÿß»¯ë @€ @€ @€ @€ @€ @€ @€ @€ @€x/ÑUu9x^íÚÁmÛPPw–þ»H )!‡Ä‡p¾IͬeìóE€É?Ë}#é`øíÍ @€ @€ @€ @€ @€ @€ @€ @€ @€Y_?þä}ôš&mZàÔïéúôóšwOàÔãÝë÷žÆ©¶ÀÝ>¯žkï!ÿšÀÕþž½ÿÚÓ¹;-ðlÏžOï#ïsÏö–:ÿ¹§uWJ Õ[*'µ—œÿ ¤úJçè­+î+×Ý~ozº§VÞÞ†:›·zjåvö¦¶zjåîm*»y«ŸvnVa_Z»Ÿvþ¾Æ²·ûiçg5ö¤µ{™ÊßÓXvÓ©~Ús²*{ÒÚ½Låïi,³éT/Ss2*{R¦z™š³§¹Ì¦S½LÍɨìI™ê%=ççßÿ~|ÝÓÜs›¦û˜Êû¨÷÷ß?§²çôT_©9§Þõí½›êe*Gÿ×ú=Ý=Õ[jŽþO^»žêe*Gÿ×ú=Ý=Õ[jŽþO^»žêe*Gÿ×ú=Ý=Õ[zÎé}pÚÛõÒýLåùûOæ<Õ×ÔœŒÊž”©^¦æìi.³éT/Ss2*ûR¦úiÏÙ×\fãv/Sù})Sý´çìk.»q»Ÿv~Vc_Z»Ÿvþ¾Æ:·{jåw4ö¦¶zjåîmª³y«§VnGAj«¯t®¦ºé¾ÒyÝí¥¿ ¤{KåihV Õ[*gv{Ó^íû@#_+úßÍùÚíM¸ÛãÝsxM»}^=÷šÛ{ª©ïÒß[àêçýñþï½½§? œÞ§ó® @€ @€ @€ @€ @€ @€ @€ @€ @€¤~Ý;2Fx^íÚArÕ0EÑìŒýï‚%d “_"»_÷·­Ã$E!µ¤sþ @€ @€ @€ @€ @€ @€ @€ @€ @€3Ÿ¿þžóõsæT§t ¼ö<ú÷®{™›8Úuu}ö–¦¥VûU×¥îkNM ÚñìþÚ­í® œí–ÚW½¿ýÇRÝRsŽÝÞ곩^é9gßcßš@ºWzÞÚ+¬:*îÔ5ï軬ÿ¿@W§®¹zfºútÏͼޔîN]ó•« tu™š[{ý¾»§útŸ³oÁÚË»»Lͯ)ì·{ªËÔ9û¬½xªËÔ95}vOõHŸóûßÿ÷ùîç>k/Mwéš÷Sïׯ©ì³»«WuîÑÞúûf«}ºöW»í?¦±ßê®~Õ¹úÏ|‹ÕNéý©î~ÿ×¾Ÿt¿ê<ý׺¥VU{¥ö§»ûý_ûBRýªsô_ë•ZUí•Þ¯ªìÚœt¿ê<ý׺¥VU{¥÷ëŸ*»6'ݯ:OÿµnéUÕn©ýú§Ë®ÍKõ«ÎÑ­WzUµ[zú;H{=m^º_užþ³_XµW×þÔw0«y¿ÓºúUçê?û-U{uí¯~³Š÷=­«_zîÑïá¾Efožî45ï§ïaVñþ§Mu›:çþEf_0ÕeêœY½çœ6Õ§ûœç™}Iw—©ù³jÏ;mªS×9Ï+òžuõéžû­çÚÝ©kþóJ¼÷E]ºæ¾Wë¹§wõJÏ}nk¼,Ý+=ïJÏ¿Eº[jÞóå¯õÂT·Ôœkéìw›Tdzsö¿æ‹Ïö«î»¦†[U»®î'}ÕžG×ÝãõnùÀÑÞ¯ëÉî% ÿ^½½– @€ @€ @€ @€ @€ @€ @€ @€+ üV£èDx^íÙ½yÐ0†Q6cÿ-( Á‘ô]ýX‡&E¬ké¼r¾}ó @€ @€ @€ @€ @€ @€ @€ @€Æ~~ÿ=£õçø›M˜)ÐÚ÷éù™{÷®¯ §·âþÝ=ͧzÿþÂ{œ :ÇÑú{è½O—£9Uí{Ù=OR•k´îžjïÛu4·¬uï+ù'ËÊùU÷P:ïYsqžÜY'¾š“³4œ– @€ @€ @€ @€ @€ @€ @€tøÖ‡8x^íÙÍqÜ0 Pw–þ»H )!ÇÏ8‚@"¡çË–?Àû ÝïLJ? @€ @€ @€ @€ @€ @€ @€ @`®ÀŸ_ŸçÝ}[…ÓV ÜÍ5º~U½Îˆæ7ºn¬J»g Œæ˜Ý?«~çÜÈæ5{ß½ª­Î ÌÎmÖyÙ~ì‹ ÌÊiÕ9±.¬º+°*¯ÙçÞíËúÿ ÌÎgõyòœ#°:§UçÏéþ½§¬Ê¥êÜ÷&7ÖyU>«ïSxßîÕyTŸÿ¾sWç’½ï÷¿ß¢¯9÷ìÊæ°z_4ß«uïI2×éê£ç_å˜}?§ÒW4—Õë²¹F÷õO2×áê\¯Îæ7º.§Ów×U.«ßÍóîþ¾Iæ:[ïOçßÍmÖúœR¿]oËýk~ú%™ë¨:ÿYÏïè99­>»Þš»çÿs†åßçYÎtR•ÿèçôªý³N{äß)Íx/U¹ݳêù=7.ÖkeUþ£ù¬Þß+Õx7ò[u\)ÿŽ©Æ{’ܪãJùwL5Þ“üãVWÊ¿cªñžä·ê¸RþS÷$ÿ¸UÇ•òï˜j¼'ùÇ­:®¬Êßï?{OOÕ¬þ'{þÞ鬯Nþëw¾¡*ÿ]¿vΦ¢6ùW(ïÇ[ç`ÿdj*¬Î—ïƒÝsnyÛœ“LM¥OåÿÔçAêy·¼eÎK¦¦â§ó¯ú<¨Ñ<÷–îspn25•ï’ÿ÷:²ÿïý¾¯Fñü[vƒÑ¹8?™ÚN™ƒhµz}n‹úî¾®O"Ït²{¾Wõ=£ÖïÖ+ç]ßï—ijíšóOu=«Õ÷öSæ o{t¶ûì¡Ô¿Š]ç ¿üžî2{꼯ª§æá}Ògt\5gh¨rÕ<í!Ýë‚ @€ @€ @€ @€ @€ @€ @€' ü"×Hx^í›[nÃ0 {³Þÿ=BPi>j °¢Ý•Mqú1×â -ç~|ð@€ @€ @€ @€ @€ @€ @€ \Kàûóqþç㵫á쳎gŸÏžŸº5f½ŽÖ­é‚³Œõæ:nt]—!àò8›“éŠÔWf=¥ê0•%òæÊÍvß7Ýå'Ó×P¦ó´/w~†B¿T·—UyýLy;^å)u/>i)«sûótºÚû|_ßÿ?=TöOq{p罎>ßߜ֡ۓ+oÔïÙq}«]ž\9gg_ß× Ö™ËÛlάÏwë4JûUÏúrÕ½ëO=~?ƒZG.ïæ¨gë5ZûT¿ëËuü¬7WÝ>µN\>Gs\þÔZýêQ_®ãT_îúúµ\^ÏrÜÞ\y½ºÕg¾Ü¯»|¹sêÔVîöû*Ïí˧Q¬[ÿºî”•ãýA¯ëïøÇÿ/Ô¸ïÏéþ<\s‘Þ§Sù•öpe­.ÏU¿ç5? ÓŠµÌŸÿœûþqžRût*·â5¬¬™ëŸëŸëŸïRû@jŸNå*{iÅÚ”÷ªï*:TÖŒÿÞ÷ÿçì0ÊUT¿ÿõ*¬ò÷߇†;Ô®šƒÔûw5w‡J«üßuPØíTÛuvr¨ô²Úÿ]ö…ÙŽµÝæ`G‡JOWù¿j?PXí\Ûevv¨ôvµÿU¿'(Œ:ÔÞmÜ÷‰=ÞuÔyp°é”q÷98[ßñûÂNžq®òº“IǬ*ž_­³£³DÏUç Á¢sfµ9èì*Ù{•9H2 Û÷ÿ…©yÂÑZ)³¹k»çlG³Þ\u¹—×Ñœ{uÏjVï¯I`ôú>;®f÷¬z–Àqfs¨ƒ @€ @€ @€ @€ @€ @€ @pø–‰{%x^íš]NA ¹÷¿EŽ# hE LÆmvÓvñ²ëŸvU{w¼½ñ@€ @€ @€ @€ @€ @€ @€ PGà÷û«×ÓkÝ‰è¤ ðäsõ}Å™¨¡'°êѯ?9=¼þ¢yž³’'õ¦ÊOB •/uËÙ‰ñPûR×óOF濨ýd×â†@¶§¬úšéçVÉòRUw®¹ØäU~²ûÄ(ÌËÎöQ]žAßÄÕ^Vûýúü}ÿê«Æœ¬UÙñ«~Ÿâç˜\›4Û£µþ“¿èûkTæD[ýdÅE½ZóçµMšåó©®Õ—:ÎF¥Ô“Ÿ¬÷Õ>Wëõ7k›0Ëï]ÝUOYñ6:}£¦z¿îS_³¶Éªügío´®R¿¨éÞ§ï?þûí´e"¼¿(MÝÿlÿÑïãê|ËÎtŠÁÿÌýÇûWïÓ>ÿñÿ?²îAõ÷¶ª_§ïöÿÍ‚wö?cÿU{¸«N÷ýÏÚû«î.oª¾øðÞ•‡]uðÿÎwÀ»×Ö¼]{«êÛÙ}äsÿ=n†Õ£7Nµ‡»êô°|?…׫5o—7U_üóü×ùX÷اÚÃ]u:»çùïïÿyÜݯîþ¯ù¼ûý”·koU}ñÏ÷ÿ„;ð´ÇÑ÷UûX]g‚{žîŸðûü?ýï€Süg?žzð¯ÙüŸq“¢ÏyÖüêç8o¿3¬éOiõóz©ÊÓ“=£bÔ«5¿Ê£·Ï¶òNiõóúÉÎË#{Få¨×ÕülŸ«õϰ”ÊUÑøUOYñùdÏèõéÍÏòj­{†ºSz=Fó¬¾ÔqudÏêõÍW{žþ÷ÿÕÛõ§ÊϾ«\¦Å«<ªê¨ïÃ4ŸÞyUþ²êxï…—ÇÔ¼,»êNõ{—/uß(‡éùjÕõ¦ûSÍ_íMÕO5?u^T^ªêà-—@•GoŸÜé©~ðúÉÎÃÐÙ^­õ÷LO×מּÔq˜ø™ÔžïêýÌé9Õõ½€tOÖ{Òsz¦‚ @€ @€ @€ @€ @€ @€ œJà˜œ"x^íšËm1 ÝYúïÂ%¸„ž`CŠ?‰ÔäâƒDJœ!w7N¾¾ø@€ @€ @€ @€ @€ @€ @€@ Ÿ?¯s´?knÅ)^ZŸ«û¼÷">–Àª?ïþØÛ“m•€×Ÿ7~õ¾ì÷ðúŠŽ÷UC´D ÚWt>éþ¬ÛD{ÊÊg«Ž¨'Yž²òbÒG ËKU^_õ÷FWùÉ>ç^ƒ¶Ê³}Tç·Q¸/ªÚ‹õ¼ïßßÿjÞgr­b«‡ì8­_iß{vgûÓæ—üy×ï1ºV©ÖOÖ>¯Wmü•ù»³|Jyµ¾¢÷Í7ª«Pò“µís5ŸŽÎÜ]Y^Ÿò®úÉÞ?׬®²*ÿÙ­ùu”æíºÝû»_æ™ÕU„§i»ðþ2zëügû·¾‡wÅM›ï§z²½¿óïòh=ÿ/Þþ°ò߇ŸÿÝþ¼çO÷ïk)ÞËw<þ™ÿÉ= ͯu}÷ÜF?Ù}Äw]—ßã[ûÿ¶ç¿•÷iqSý[ŸëRÜiþ¼÷ÁÿÚü{yŸüOìé9n]?m~½÷™èžïþÏ¿ïIýžÿ{Àú|—â¤yê¶>Ñ=ÏžÿÒ[׻ͷt_æŸ÷ÿİη'ÍS·õ‰îyÿóþ÷µ4ÏÖõnsþtß©óÿ©Oñ¿öý×õÿù2ÿ6ÏÒûAš¯Ó×™__œîWºßtÿ|üÿ;ÿ¾ùïþ=€ÿÿ]ûàÿÙïü÷è$é{>j]úî:e½‡µø[Fy–òœâùÖ¿ÿ?uŽä-jÿñ³™1ʳ”çÔ>ˆdÙ1—ä-zý´>èè,ãÎÑžµùv÷CËÎ9µÞ¢÷íêƒÎ®2ïíw5_U?d2ìœ{ÕWÖþì>èì¨âîY^½y£ú¢‚á„3¼¾ªâWûb‚›Êª«zª @€ @€ @€ @€ @€ @€ 0À_j¾tx^í›AR+1 s3À"?U@1±,ëÙ#©Ù°K¶ºe…I?€ @€ @€ @€ @€ @€ @€  'ðùñÜcö·þdìA`Ö«u}ÄÙÈGÀê-j]ÜÉÉä!åÑ›Çsfbü¼žTqþJˆ´Py‹Êk©5vQ^vå±WÆÊwvùŠÞ«k¢}ìηV}ßèÝžTûõ58W¹Š¿:ï¿ÿß ^ýž£ÐoµÚÏjþ‘ßÑó~Fç*^õ?òè}>G£Ïê(oÞ<^Ÿ³q}ŒÚ*õúZ›õµÞF¥þªUÞø(Þ<õ;¯ÐëÍçõ¤ŠÃÿ“€×§5Nåo5oWÿVo«ëVý¨ãñ¯¹ÿjoQù»ù_½Ï£ø(/»òà?öþïòµOÿ£{»ú<ÊÇî<ø_»ÿ»}EïWÝÿê½ÅGûØÿ¾û¿Û“j?üã¿bŒæ¶÷¹êžÊ[ѽòûÜSžTûânþ«<œÊ‹›ÿS~ÔûVóïý\Å©=œÊî¥ÝcïóS÷S½o%÷Š¿ûÕüOçÇÿûùÚzüã¿Rx?߯âÔ÷ïtþJîùüߤÜ+ðÞ ¯¿Ì}»í§gü=ìs¯TùÏþ^˜Ûêüé郟s`ž`îü÷ö¯~/Ìöyû6ûO¯žYúÀO°Fd÷>¨aÑ_Å.ÿw~rµ"w÷Á]ú¡–ÅõjºõÁ:±šNõÁî¹PÓ^\U§û@Ýq¤jfº‹Õÿ§Õ´_ÕÝûÀÛñ¤zdÌÚ¿ÏÝÖ®Êì} #Ó+sÖ>èeI_m¶>Ðé½ÃÝû¡·ýÕß­ö`ÇïN÷6îE`w?Ü«zNsE@Õ¯AÀÛ5ª§ @€ @€ @€ @€ @€ @€ T#ð„FIx^íšIRÄ0 ó3þÿ žÀ8„©¢(2¶dÉÖÒ\8DRìnÉ–ëâ € @€ @€ @€ @€ @€ @ð%ðõq××~÷]ÕW h½Îæ­®|³¾¬ãlVO)kÚzÒu¯# õã§Û Y#ÞÞ¬êöÁs+/»êÈvGô]¾¬ßƒQk§êévß7ë”'¯÷ö5)Û¹«ºŸ?¿”~—QèmåǪŽÔï(¾ŸÑ¹[ùZ­3ò·ú|ŽFŸ¨U_«ù«>¥ù}̾ßéª7m¾Ô—u<þoZÚï5§ëVrïùwœÓž¼Þÿ÷ç¿÷(uñÿ =0{Kã¢Ì©×:*¸÷¸÷½xG«‹ÿÿÏÿhž¼ÖƒüWèé½>Š÷š·hu+¸çþ¿.m_áŸó¿BŒÎsésíÈ=ÕòÕ{ŸÙú@N°F}PÃãê.º÷Á*¿*ù»ú ÚýPÅßê>vûҫܪåŸêƒSýPÍŸÕ~N÷Á®~°âUµN”>ðꇪެ÷­þ®gôwþ§çÖœª×‹ÞÒõU÷åµ?)ç¨ñ^|ºÕêw´®nž¼÷;âí¹7®õ£y~ZOW?»÷µvsà}7(ý€NõCŒÝ³Š'Þ}ùÜVû#÷îY= @€ @€ @€ @€ @€ @¨Là£æ{x^íš]rÛ0 s³Þÿ=Bé¸~ˆgTáç#€›—ÌÄî´¬øë‹@€ @€ @€ @€ @€ @€ õþüz]#ú{}…\!C êÕš—©\«/uœn¬ä! ö]ÏS3±qQ?«óâ;"óV{S­E-•—]ëhwÞj»<­ºÎyÆ4;^åc÷º笲Ûêz¿ÿ='úü}޹ÜNUÔë\yµþ=Gen¶ÚSv=«OoÜ\ƒ¹e}Eó½þ²ñ9Jó²£Þ²yYÑüyc;ÊúóæG}©ób´ædy½eãÕþ²ëÍ1éÛIÖ£5?ëgu¾Úœh«¿hÜjoªõçµí$êÓš§ò²kµþQVѸ]¾Ô×éoÖ¶ƒ¨×»<µÝëÙèõºó—}}·/õõúšµUžõ{•¯öðÔz6Šý¢ðþrv×WýÌÚ*Æÿ™þñnóþ>lÓÔ'Jíÿîüìþz³¶Jñæü«½¿×ë>ßwõÛ¦ª~”Úÿ·)¯×7k«ÿ¾sÚýþÏô÷˜÷)óüÿ% êƒ)÷uÖ}Øî®êF©¼Ÿòyï³/êšµU†ÎÅùo=/§ÅÙ¦¬n”jþ§yµî§®Y[eøçüçü¿ÿžÇÕy`›²ºQÌ?óÏü3ÿœ±s îÉn«Låç?6ÞÕ¢ð›{þÿóâöÙ?ÖÏÍSâªÍ³·æÿìù÷ }ëï¼UÇ?þÏNûPuž½u©çÿ”>ðr®¯îƒ)÷ùSŸÿö#þ}÷ÕçÙ[ŸÚÿô÷/ß.ñôíèâÓ['þÏö¿ê¹Ð´÷ï\u‹_uLéƒn>£õ®êƒîŸ£<»å­òßýèæ1[/}ðó¾0˳k>}ÐÕœ¦îÕþ»¼/hhö]eWT퇾洕ŸÚZŠýWÛÝOŸ ý­ÙÁ)}°†^ÿUŸòuÝUÏ™ú›Ú³ƒjý zߨCoÎUªöÁ]]§|ÿgW§Ýñîòú.^S¯ÓÅóUS½<µ¯nýð§é×íÒÓ=TÙ_Õ~¨Âç´:ªôÃiÜ«î÷©~¨Êƒº^V÷œ{ÈöGïÝS= @€ @€ @€ @€ @€ @`:o²0iÔx^íšArÃ0û³þÿ}BŸÐCê™ÆM,@€ Ú^z°„¬]@±“þ @€ @€ @€ @€ @€ @È!ðýùXÇú?ç.YÅJÀêU:Ïz_Ìó% õå=ÎwD“ðöh'½_ÆÍ°ú‰ž7·+f¿#íÍ+>}xùÈŽã³û}£dûò^o_ss;÷ö°*Þ…}f¯òc]÷ë÷½Ðèÿ>m;µòž7ò*½n£ÒV´?i|©Gë¸þ&u;”z‰gõh§£Ówt”ÏQ\«7¯y}Êv6òã}ÝË›W¥¾£¼ý¾‹çåË;N_³×;ÛÝû‘G»ùöî]ŸÑñvñ÷‡és>áÿAÀšÑõ¿»«Wé¼h?Ññño«ÿh/Yñ»ú—Ö¯v\–—¬uð/«ÿ,Ùëtó¯­géøl/Yëáÿºþ³<¬Z§‹ikÇ­ò’µ.þ_×ÿÕëàÿ•s@ÛÏGãW×cöú•Ýϼ·­ö=mT^àÿ¹ÿGq¾k\ü¿þ^쮾¼ï«ªÿÑ9®½î͵J<üSÿs@[ߣñUêÕû>+º÷üÜïͳZ<üÓÿ+æÀ¨ŸK¯W«Wïû­èžþÿÿwœÖ¼À?ý¿bHûûhœµnºÌ«èžþOÿÕµôz—:¶îƒúçü¯˜Òú³ÖM—yÝ{žÿG~tñ©Ýþéÿs`Ô×µ×µuÓe|E÷ïYë™ß}=÷;üóû¯Ê9àUÿ»~¬ìžç€ù÷€Õý÷O°=ÇàÿùüßíÀÿkÿ»äAÿœ{÷ÿ(ÿÝû@·úʃ.ïûÎûÀÿõù~®è–]ýGõnçþuõß­t÷O¸~.Àÿ\ýW?vñݪæÁnþ³ò J>ìêŸ<ØÝ¼Ï¹¯ýÞñnïÈ‚5yp—óÿÏ´õì=>»?àÿ5o¯ÖxÑù€ÿkVoÑó¼òÿ2Ñ>½ãKóC¶{F xûZ³sVyóZwn÷ÌÎ~äåýˆƒÁÞž¢âÅ잨U>/`j ¨zÖÆ]³{V½KÀĽ hëY;þÞ»çîF´¾ÏãGñ¹@€ @€ @€ @€ @€ @« ü6&ù´x^íœAr*1 ¹Yî‹!GÈ‚P)¨ñHϲ%õßdÁX¶»%Ù@òo7þA€ @€ @€ @€ @€ @€@Ÿ¯û\ÖŸq+e& «×Ñq–51FO`Ô—ú9ýNˆ8B@íÑod­<ã'`õ3{œgD8"0Û›*>ö4T>¢ãhvß7J´/õ|}ÍÙv®æ¿:žB¿Q«=Îÿý÷yÐèÏ~&¯íx”{Ôs£^GŸ»F£ÏÓQ>ßÍ3êÏû\£c;]åÝëÑ:~ŒJý§¢½[}©ÇÕ7ûy‡]½?ò¨«ÿ(ïêzUÇëæïwã]ë¶u}Ύץþñþ\÷]êïÇÞñçbÍÙ}9*~Õþoõz6.ÊKÔ<øëQ>¢ç©æÿ¬~­¯G{‰šÿŸë?Êêyªø·ÖõêïßVy¯vÿÇÿç÷yïò,{ýãÝæ½JýãÿžÏqù³ú^5ÖþOÝûê>{ÿÇ?þéûÿßã[Ïîýßʭʸlþéûš¾ŸõüÇ?þ9÷ýç~÷ú¯r~{÷Ñõü÷r«2ÿÚó4[^àÿ™r@uÿÏV§³Ö›É½âÞßýûž×<Â?ý?SÐÿµùšÉ=ý_÷¹ŸÿhëhÖýlv\ê¿wtõÏû€læïëUÝÿðŸÓÿcÕª<˜}¾î?kà_soéî¿û9€MíÞçùû¯Ï÷Ǭþ¼ëÎZÿê{`×sÿÏ}Á[OÙÆg÷OðÝ_ð|/ÈVÇÖõVñO°õü}®l­¯ÝÇUóO¸Öð?VÿUßVõ?«TËü_«ÿ×ïv?ßÏÖWÝÿì>½tñOß ñïëÿÙÿÿÐnþ£ú@–s¡«ÿè<Ø5ºûïžø& ú½Â«qÎÞ§ÍzÿÇ®úS??Ëwö¿ÿÎWµWojùGY½¶æ{{òŠŸßlß ZþfŸ{y‰ŠÛG/ï¨Y¯­yQ~¼×ÉköýÎ[þfŸ{ûˆŽÿV>D{‰Z¯šÿ–ÇÙçQ>¢×Áÿûúö½^ÿ³u}5/ÚêõðÿºþWùˆ^7»êþfp6oðoã7Ë}—yYýS÷š¼Å¿†ã.õ<ºÓýòª6>›ú¾¶_ê¿ZÏžÿÚzšõ°jþñŸ)T÷ÿªzÛmÝ,îñîÓ§ðïÃu·:¿ÚþñŸ!èÿ>yšÁýïñEd¹—£öIýûÔU”?ë:øÇ†àþ÷ÉÓ î÷þ=¬ý²Ú|üûÔU–<Á?þ3ä÷¿OžfpÏý?ÿÿÝ­{(‹ÿû>éÚ>€-ÏV½íöÿøÏ”ôm¾fr¯|ä÷ læ÷KÐô¬Y òzÀ¿¦Žv{¯ïÝOVÿü É[üßœzd÷¯î§åþëÿ¹3‚ïcï¹mÿß½Úõ~PÍ?ïg׿—ÿª} jý“}} ºòà}àÿõ÷¿Ñ÷ÈÞß[wwŠï>õýà4ÿQy%NõOœnþñü£÷½uü.ïdÁÙy€ÿ׬õmÕðÿž€Õ£j¾W>࿀ʣWœÙüè;=£¢¿/xåÉs\ÌÚDyòZÇvzfgï ô!àU¯ê¸>§'ê3µ7Uï™ý}®±˜Y=dÇÍú]Céœ,ÙGùG¾¢_?ÇܵŒ¼d½íÓ›ïµý¯Îòú.¯×OvüþçvÐÝû½¯æèíU¶ÿì¹Ê¿¿Ißð~ãÕmþñþ轋¼¿ö~º¼öŽÿoŸDÝw©äñÝ=éG{}ZãU|EסoÔW¡Õ§5.š·Z>]Ýh«Okœš§¬ztú*³zµÆeñVË룬mõiSó“]žQ_EV¯£¸lΪù}´u¢G>½¯«úÉ®KǨ¯¯ß]¾ÍöýœßG½>:Êû=ÏjÞjëÕõUå_ÍCU=>úõÑø·}®oí§z£¾ ðßÓ?Þc½ïöýþñ?ó}ísßXß»ÄùÞ}뢯ΟÞ}Öõ­ŒÿžçÿUï|Îó¹o|S¸>ÿ9s¿Ëý?þñÏ}ÿïÿkxïïFñëOtߊÌ?óÏü3ÿWÏÑ9Øõußi¼>úªwžÿxþûO ë|ö½~¢}+2ÿÜÿqÿ—w~ù¦q}4óß{þïwµFïƒ]__?Ñs+â?瘳±þª«þy|Ý?ëMέˆæŸç€øç€¹i¬»Šs ö¨39·2þñÏû@ÜûÀÜÖ]5ÿ<Ô9ŒX9ªº~î³Ëßÿ½ë•(ÿÝψY¬ÌÕ]ÏJwkGùïzD8PÈAÌ=*¸‹¨ÿ½ýß{ˆ>ðõAÄì)åˆöú}’»ÈZèÛ9É\)W–ÿÓÎ%gµÐŸÏ æŠ9éƒ×} è*³&úà±2Y+禔íä×–íÿ9¿ê÷ ù¤µWèÞÚvÖU·ºTž#×Þc¥ª>¨ê‡=¬ÔUYÝÙ}QGv¯•Uú ú¾r/ õÕªöÁl]õD÷¬`–·Úu{Ò׫ZÍ«µ=’gTdå_wmý]T{~·¾>¹3+Té‡3éî·«ª~ØTÏŠ³ú£'Íswíí“sI°3@€ @€ @€ @€ @€À‰þë“’fx^í›Av#! s³Üÿ9BŽ…ã—ØÏn胄j6Y4­†*Iжçダ @€ @€ @€ @€ Ä"ðýy›Oëo¬Y3›wZG¯C<Q£÷ÅZ}ÙŒúò¾¯ñ½+õöæo/•sŸîåGç\kW¦ö¤Š¿–ÒyOSyY÷<#Ú­ò²ê9ZZçD_å£÷9_¿ŸóŒþ=ÇïJz=¨ÇúmÝçK-4µÇVü–/ïëùù¬ åEuÝÛgo<zy£¨¼¾‹ÛëG=>¯¹¹™¯ò®ö7Žb¾»ñ~svÏ›|Çf¬ö>[‡»î£™ï.•ÿ]Þ¼ž›ÏdߌñþØïŸó¦fžÑx¿ö~úþïíß«ßF‹“§¢m3Å»­îO«¼÷yÇÿW–ÏéTû†­«ÆEÝÕý)õïå_U_ÑãÆ­ìë™yy¿Ç‰îI5¿êþU\³ÄÍæŸºŸÛï³þçå?K}ªçYµþÕ\³ÄÏ⟺÷íûÙÞÿðÿ«Ïë¬ù‘¥/¯šgôþoõÚ·Šg¶çà_ÓW³äþñ9Z}½u=Kîšgd÷œ÷þ~§­ÊüÓÿ#æ@«¯[¯«ê攸Ý{ôýêßëZóÿôÿˆ9`íï­qÖ:¨:.¢{ú¿þÜýûŸV][¯W­k뺩öÿˆ9`­ïÖ8kTÑ=û?û«®­×«ÖµuÝQëÿ>/«çêÿËê;Ûïñ¯=ŸRÿZ¾£u¹ê¾Óýó=Àu~ãŸúœ³û?õŸ»þ½Þȃ×y¹öÿÏ> Ù§ð¯áºêü>ûœ,þÙ4yŠ ×Ùº\uUÿœ³™œ/çAŸ¾•5 ¼üWïYýs¬]ÿÞþ«öìõOÌõüß<Ÿ'V½í~Î)þéc}à4ÿäA_àÿuÿ¯ò{ÂSý«úÀiï §û'®÷ü÷õÿÓö…*þÕ} ëûc5ÿäÁã~PÕÿê<ˆzn¬îWDÉü?ðþ^¹7Þêσñÿš@¯7õxU^àßF@íw4þl^ØVϨÝç„Ñüh݇Ù9-¾Ñ¯Ï­ž»³÷ jD¯ûûü4«'ê3¨ù€©=¢äÞÕóÔwVç&rPåEŽÕ3Ë^Ö|éËx@€ @€ @€ @€ @ØMàù”¿x^íœkNì0 FÙûßÅ]K@h¨D‡ËäáG>'‡?µIsl'Soo|A€ @€ @€ @€ @€@ïX¯ïu"'ÒŸÞž=Îþ U-³gÇi­þ¼hf½y;øš{ùòžg ýŸêí)j¾ýMä¬0ÊOô¼9tö}J´Ÿèù÷5³²hÙóÇPÚoÖl/½Ïû÷ý>göû~¦|VÔË?ë¾Y¿­q>´ö™%Ëç_Ïiùò¾¾9ÛJVy÷ö9:ŸZýÑÙÞGýDß_ßàÜ ²¼Gû³Î?G¯î(¼?Ü]yS×äXäÑÞ­u¸jüÅzwãý^ïÏyVÏèXÄQþWÕ«÷sÇhÖ¹ï¯ë~×ýï}ÞñÿàÔÊï~«6_Žþ:Ò–ÇÑëjž¢â©îÔkëþ(Ϊóâÿ¾¨zŠŠ«ªÿV^â«>ïéþÕýDÇWÍÿh]·îæ«>ÿ©þÕ½dŇÿ±÷%Y^²žSÅ«÷^ÏâZå9ø§þ•s ·®[÷U©Çì8•Ý÷¼Ÿoy¿®gs­ò<üÓÿ•s ·¾Uþž¾JÝ«ÿþ×ê¾ß××Tkÿ}þ¬ýfWÿV.§ŒÇN©æþñ¯˜Öý_µÞÔâRtïñÞG³j<ø§ÿ+倵ïó¹,Ÿ•Ü{ô}üãÿ‹€ê~«õ?V/jþ¬ñ¨ù¿â±ž¬\Nê_1¨ÿœ¼Ttïñ9à”þm]'þsêÌê)j<þñ¯˜ÖýŸ÷@}y­èÞcÿÇmÿ¼èóg=¨Ö¿—úÀë<ÂNYë4j<þñ¯žœã~ŸYÁ=þñÏy0fŸªRÿøÇ¿ç>ÀçÂj•—÷Ã>ý jxù?½Tõï}85ðÿ €ÿÚ™À~0w¨mýwôäÁXàÿÞÿOû?B»ù:îz>ØÕ?yз࿯ÿïº/ìî?ºTßNñŸ•Õòá4ÿäÁý\pªÿì`ôsJÿÖÙžfûõïopl…³~²ÚE½eÅÑéåo´Ÿ,³ýô5[Ù¨¯ÕøYOUíb”úE­zŒ¶¯ò–Õo?³ïWõ6—åEÕÏ-þg}FÛ©|eÓÝÔßl\¶uøõ¯öT5^Wÿ£>£ñUvõÛÍÔãhÜ.?Õãvñ?ê3_Íwÿø}þïö¢ÿtÿÑ}Sqwÿ.>Ôó8Õt?ŠSóvï4ÿŸ|ŽþÞ͇z>·úWsvÿœÿ'äÀh]ÿ)Þuîš× îg¾ŸÇ¬®¹ûgßÇ<ÎÖü×òõ¢j×Ý¿Šã©ã¸ú§îkêþ5œ]ëCWÿ®¼Ýæ…ö¿SpîkóÑÉ=ßóèßCwóïv¾ºÏÿÚzë–øÇ¿S¬ÞÿÜö—û|œÜgÜÿÜy»ÍÿÔ§ þkóÑÉ=õŸçö?û¥¸Ý¯ÜçCý×î7·|À?þr€ó_›NîWÎýï¼q«¯îóéæŸ<«øã徟Gççæÿ{>Ü4y‰ çÑ}©ŠïêŸ{@,¯ñã¤Úêq\ýsÐäewÿœïóÿš}¦®ëÑñÜýgÔ×yŽöÿ)9ñ~€:ðœï'¹Çþ߇ÝêŸ:pšùçù®¾ÀÿÙþy.ȹ·žžYuàÖzpºÿì:p[àÿAàÖÿÚÅUè^ºù'Æî…]ý“±<ÀÿûóÿÓóEô=›k\wÿÕuàôûÁ-þɃ×çÁmþUypJ]¸Õ¿:\óávÿ»òÀ%ðÿLàÓ}¿ú÷êçü¿'Pí;ÚU^à?F êiWÜl~ÄVOÔî{BU^av@•U¿k«§õÿTÞ²ÆÁ` ,?ÕýÔ¬ž^"Pís´Lí%0ê+;~ïê}wÀÀ™²êÀ™«gÖ€ @€ @€ @€ @¸À_ÈÑ^Öø*/Uëô1êËÔêÓWå£zUýh«Ok\µêõôÚ2´ú´ÆU{Xµž®~”Õë(n•‡Uëê›}Ÿáȧ÷óUV­ÛÕ¿×ë(~ÿÕëžî5ÿÕëwó?:ÇÖÏWsWYÿ7*>ªóèâßz®GqÕ|Õ×Ã?ç_¹FçÙú¹ú9\•Ÿ²ûßܬ~Gq«øª¯»»uþ«óSõ?:ÏÖÏWóU_WÿêÜUòÃ?÷¥°öõQœÊùRÏCÉ}Ä}_·Z~ø§ÿ+ÕÀ¨¯>W;_êù(¹§ÿ×ÿJÅÿè\[?W?ojùíâ_k—|ðÏýO¡¬ýýU\—ó¦–§‚{î}õ÷¾{âŸþ¯Pôÿ5u¨àžþOÿçüsþgú€Ú½ºK>ôÿ5çN¥>ð…àù¿¦ÜÏ<÷ëF¥¯vÉCÅÿ=ú@mÀ-oµ¾€ü+Õý¿¶•Üs¬ÿXÍ?÷À³Ï?þñÏs î9 Úÿé5}`wÿ÷÷ µ÷n•|ð_sÎT|?æ¡î?ê9@x^çøçüw©Þ âß :¹Ç?þ¹Ä>¯ºÿhÿ§ß »ú§bú@wÿÑu úžž•þoNý{„»øî§Ü vóOøîøÞÿOù&võŸÕv»'ìîŸ:xÿ<8Å?uð¼Nó_U]ÞNõ_]ªõpºÿUu RøÿO`ößÍŽÏúž÷Õ¼øO`ÖgÔø¬ºÀ¿@”Çìy¼ubÛ=Q²=VÍÙ9Už²Ö™Û=£»÷ æÈ:·QóæîžÙÕû†4Dgï<»'‹W¼>½ñ߃€×û=~ݳ @€ @€ @€ @€ p"¸zðx^í™Áq1 •™óÏÂ!8ו¤‡U–€ä¶?~, `»‡¼;ûí?€ @€ @€ @€ @ó üùõ>ãwÏ&üÉßÊïê9tgXùÊ~>ëíŸ3M¶Çh½çïyÓ¨—ª}=TîíZå-«Ï½&jß,ËGuZJ÷t«ö¤êw훨ø«êþþøÝ¿ú[Kíüê*?Ѻ+ŸÞççÒ¼AÔOÖ>¯Çèz ½óªfyóÖ‰zËÚwž©Ü‰½¾v×gy˪“Kóœj»­û³<©êœc,gR«·è:•'Uݪó«D}Z÷©ü¨ëÎ7·7¡Õ_tÚºþݹ»£>WûÔ>ªëÏ5¸7ÙÊ£÷yµ—ª~{”çíöz]­¯òÐÕgžÁØD+Þç]>ªûÆhÏÙåõºZ_Í¿»ß“±IV>­Ï»=tõQïßeõj]×Å¿»o¿ÉØV¯«uÝü»ûÇè÷íZù´>ïæ>¥ŸI_g«×Õº)ܧÌá³Ð·zåÕú| ÷)sôµu¶z]­›Â{Ú6 }«V^­Ï§qŸ2OŸY[g«ßïÖMáe®z³¶Ž»þ§ð>‡ÍFý*ü¿3Wç§ÞìÏw½sïûrs›õy¹­>þ}çÿšÄpï÷äPcÓ_u×ÿmç²ê}ü¦4;ðÏùˆæ ê¼ÜÖGsšýU£Þù½·woøMivàÏcô^ÒØôWú¾7ûüŽ;¢Þ¹÷sî …SOMüçxŒÞgWеøÇ?¿ûôÿÏ÷Ýý 8ÓžšœÎ?çŸó½¢ß{Øç¹¥uk£Þùý—ó¹¡3ë«Íçx/>KºÕQÿÜøàˆå@w¢}•9ÿ1»¹÷YÒ­Æ?þùw€úÏ1݉öUÞ=ÿ|ŒÝ>KúÕ»9Øý<|Ú~½Q_üÇÎq4·>;úÕ»þùðåGo4Öa7Ñóð´}1;ú]ø÷ãhnõ&cvýó9`ËOÌNÝ®ÝDÏÅSöÕ™ŒuÚõÏ=ðó=³R· ÿ¶{ µR_ørPo¨¦#9°å ÆF}—lÿ·~?¨7SÛQ•ƒ[òPk£¯9øÿçAŸ‘žÎäàßôXèïªÎÁ)Ÿý&z'¨ÊÁÔ<ôÒŸÓ½:Sò0ÇÀ¬IºòP‹YÔçMÓƒ¯ý³ÿ¿iñ™MËÁjkNfÒž?ÕŠÿ)Ïç“>cÂS|ó ºçLyZÎ!{æ¤Óóp&Õó§ž’‹óIÞõÕ¹¸‹Þ½o£ÊŽÄx³Un @€ @€ @€ @€ÀÉþÆyíjx^íëm1 ÝYúï"%¤„ Ø3²·’Hê#©É9‰”f¨Ç®møë‹€ @€ @€ @€ @:¿~\¹­_u3 ó'V¯£ý± %0ê)ªvöçdòg{޽3µzÙÕ/•¾ÙvùòÎÓ×Hì̼=¨âÅRê]åÉš÷çßçÅï_ûò‘•{Tÿ;Ÿ³ÿïK«O´(o£qg=®¶ïcÌg&£~¼Ú­zóêçC­n/£q¼¼yÅ©kÎ6òQ_Öv^ž¢âØ(Öëmõ9Ú?Ê—wÜz×F<êmµ·—]ñÖhÖéµês´ß.OQyꘜ騿ÙvQTqç¨Öi=ëõ©½ÊOtÞ:FÇFúäqöóhþêøcTó·šõúÔ^íeWþüf?ðÉãì绸gɃÿ‹@»ÇQÕÿ캾k¿›w¶|Õü{yÅÉæc÷xNõ¿›sÖ|Uü{­û¬TãÂ?÷¿Ì5Àº­ÏÌîÿŒÍê_µ¯VɛտÕ;÷û±}£«ÿ*ëO=Nü­µ§¨üÙü[÷ý(N]ãâŸõŸ©Xÿ{ë1‹{¼ïõþ:Ïð¯ážå>üg¨öMªÝã]ã=Ëùü[¾Ï“åUuìÿÚõ§®üã_YœÿÚúSºçÜ×ÿÞþµëóÿâ?{¨¹uÉÏúgý+k`vÝós}¾õªt¿²ïãÿÿÖM—sX5ªëŸ}ÀgÀ¿GÕúµæÅ?þ•5°zÿgÿ÷©[¥{Ëýÿ=ü¿ê}ÀÇçì}@½þñ¯ñžåç¿ðîºïgÙÿÙ4ûþ5ÜgïiQíñÿl5À}`ß} £{üãßú>ˆ÷ƒcçZÖõÏóÀ˜?ë½°»öÏu”Ý¿×>@ü¿ð¿gŸµîÓQý«øgˆ©ÓSýsT3ÿ>^ž}öƒÚU0ÿ{cü ÷ºÁÿEàÔó ºï{áiuÐÅ?u°vèæŸ:˜«ü¿ŸÿOÏQïaTq»úھׇʛWÞîþ©ƒÏçÁ)þwÕAµç‡ÓüS½ÞÿXë÷é¾çý¹×¹íÇʯ[oßOñ¼<®ÆéæÏk>OÞ¢?_õ9ÛÏ‹W×8ÑžWãÏz¾kßÕ[Ô¼V}©ûáß·"Ô>½òûR97š—ÝqÎ5;óÝWóÅR úî÷M³u€!-Y_Þíµ³'ûoÏwñ0P“€W}Ôœ=£† @€ @€ @€ @à"ðÂ=Ox^íœAr#! Es³¹ÿ-æ9Â,z\»b7 }‰—MÞûÜ•Ê×?€ @€ @€ @€ høþsÍgö·Ö*˜Í+YŸ³í!®A`Ö›W{ÕŸ3 /o^ãœC>g¥^ž¢ÆÉ¡Ò÷©Qž¢ÆíkbÏÊ¢¼ìw¥>OÙåe×sú˜‰]É.³Ïùûÿ{¿õw,µº£Ïzˆjoõ:Ú¯®¡˜™Gy¼wÔ—w»ŠõF½óãý¹·GëxõLùÌØÛçÝxV?Ñý|hÖåΓ×çÑ޼Ưcnm¦^^ßãåc÷8kTõ{ãýrô.Wúm3Œò¾{F?ÏFW¿—·ÿhYã뛜›!Þ?×ûלÍÑÕm÷9ïè›ÞmÞñq{ä'ëüÍ~îØ.Ókåµï³ùg?_Ïìçá}­ÞW¿ÿáÿLÿx÷õ^íþ·ê?ûœU}¾úù¿êýôûý]îºû¿[ÿ韫úgßÇœ÷Uîÿ«þOß×£ëWÛÿ«Þ9ïçêF7ÿ£¹§šùç÷ñÖ:€×šûßê›z?ç[õþ‡ÿ5Öº§r àÿLÿxÏñ®òþÿøÿùw8³y°ž{ôÓ8ùg}¿¶ÇãZýÈNÕ?Þ×¼gŸÿVï|ß÷ñŽ_ŽUëQVýgÿkäÿ²êþñŸ‘ê¿Fî2ܯ¼ïáþï›üûòÌ:Ç­ÏÅ?þ32Àù¯‘» ÷œÿïÿ“µŽ[ûá_cZý­öÃ?þ32Àù¯‘» ÷?ŸiÍÁjÝ£¶ùëùVÿ¼ò©Ù)À¿Gk=Ã.«7¯~øÇv¸ä½RpüsÌ9‡Töÿcä`oð¿—·×½Ýkœnþy/4—g5ÿœsþVë@WÿÔ±©ú§Œùëºÿ½üS>çH}ÿ{å`uŸtíŠêÀïu Š¯:@žs€ÿ=÷,Õó£šê€o^«ú'>9ÀÿEàÔ{AuÿÞuà´tñOlçA7ÿä`.ø>ÿïþþDõ{œu^]ýGÕnÿ´»rðù<8Åÿ®TûþpšÿÝ9PÏéþ_×}wïóþÜz_óî‡ÿgÞžGÇóö::þ'0ê-ºÝ¨Gk;üˆölßê=ûÿ¿Q×keõ¤ÚOp­©zW-Úú³å®ÒNŸhíªx~7ÚtëÍ^-õöœqV.zÒ쳪è\ô!uöJ¬99›«‡ @€ @€ @€ ÐÀ?'~¾?x^íœkn\! F³³î]B—PEéHít&€ñçœþ‰ªÃ=Ç6܉”þA€ @€ @€ @€@_¿~|íýñ³ï“ܹóg»ÿ¿“bݧÞõ¹:¿.‰³w¶êI5þlÊùO§òæ7ŸÐY;ðòç,úñOåIµN<±3VTùˆŽ{† ýSD{Y]ïçŸ÷úÕŸzr=WX寿êuv|O;º]«=ŽâÏzó§#Ù+òÈ‹÷ç^þvãô²ä¿[o¯ïâízRÍ÷'Ú#¢Ú»Ê—wܶöw‰ï/†Ïù³O¶v¼¿öþȃÚööw§òï݇³âí®ÁÛ{–õº5íÙw…÷ïûý©ç?Þ×¼ŸrþãÝæ½»¼ïyÇ¿?õýLß~ÓÊ™éU÷j®]âçX´¯Šß¾e7;ï¾Þ»œÿx×x¿Å—s8kŸ±]|~5ê^[÷Õë×V=u[w¾"cFîzÌïæ!k¿1VçWÙõŸÅ±ëºófbFâ?æÜ¯vþã=Ö;þsxW;'bºúxê?'Çf´#ðžã½JÿÇ?þ? Xó ÚyÚm?Úî>ŽŽ÷;ëßêï÷|óe\¡šø÷õh=w4vÇQñ˽ϚçÌ{oãJÕŒ°Ö?}û†Æî8*þ}=ZëblJ3ÿøçüÿÿï1XëØ:OSÝï£Zëž÷~M¿À¿†«µ£çáÿ‘9@ÿ¯•o‘îÿ^ËšÑýñôõð_«£ó ÿøÏÈú¼ËpoùÞ‡÷M¾tóOøæþ}yFßßv×Ã?þ³r€{À}¿ÿyÎ5ë{÷Ÿ¾•Yû;õüؽÿÜ>?»þësøÔój>ã?‡ûª'Õxüã¿JpŒ¿ÏTrüïÞy/\;ϪÕ?ïkþvï…§ú§ÌåQUÿô9§Ö¿—úÀ÷yT½þÉmèâŸ<Ðäþ5\wÏå¨ùÝüÓ|óõvÿ·ß»ú÷î·æAwÿäÁÞypŠòÀ–§ù'ÖòàTÿª<8힀ÿ/Öß;G½§«Ö9Ý¿ºtï·ø~Nk½ÏÎSÕ«wÜ[ýGõ…êýávÿÑyP-ðÿ/Ùþ®çÝßGñðÿš€Ê¯5îÈ£õsüϰz‹š‡ÿ9^£¢¼ª×ñâqkµuü[½©ž[íË;¾Šq÷¾Wööü.žrDù­“óô¬úŽÀÈ—÷ç˜èEÿ½|UÛísþTÛû @€ @€ @€,~…$Òx^í›Ar\! }³Üÿ9BŽr§jâÊOBítKúØ|ð@€ @€ @€ @yüùõØ‹õ{ž“°“WVŸ«ó ~–Àª/õø³§¿guµ7U¼{ ÄžTåÇ;N,•¾«y{òŠß׈ïɼ|DÇõ¥Ô'z´Õz¿¿~>|÷½!Ÿ“¨<¨âŒ|®>÷¡V?ªÊ×jœU»ãë›Òœ`Õ“jü®¿Ýùzu£¨<ÎÆÙõ¥ž_×ÜÞÎg}íŽSûRÇÛ£Xoö®ÏÑ|µïxõ Úv<òf}îíÇ;¾fYV¯£yÞ^¢â×1¹¶Ó‘?ëó(/Që¬QÍ?Úêõݼ(§ÖÉotn‡xpZÍ£9ºyGáÝæý™'yÍÎíLåµnºŒŸ£œoÞ÷ê¾jýã]㽚•÷gœ.ý{÷ù:ûÿw¤ò¿Ë«Ûüìþñ®í÷ßó·»ÿnõª>OVÿÔ½oÝg¿ÿíúW×I×xÙê×;÷ûµ¾ÑÍ×:õ:WÿÔýZݪò¡‹ÛâàÿLÝeɳÓþwû~ŽU÷êÿdXë¿j½eÛ÷)÷Vïü|¯íWø×òÌVߣýàÿ'rÀÚÿGùÌóµ|>áþsMü¯yòÊëhÿVïÜû|òÿ>\½êUÿøÌú®|‹tϽoýÿóÔýþôç?­õïÍáÖøÔ®~‡øÇdÐÿså[¤{îÜÿ¨êßÒ¢ïE·¬W¥ÿóûŸ¾®Uúþñ™Öûýß'O#Ý¿®e̓*}µÊ>ñïSWøÿ9³¬õÏ{@›¯Õêÿøÿ$P¥¿fßç©ú®k}dçZeUýóмð¯áX¥ÞOþë]¾Yßô½ü=]ÿ»÷üãÿõïÉUûð©}g©úÀ^[ó§›Þky”Í?}`ÍŸµîŸóºú§ÌåQVÿô9]ë_åŸ>ðse¯òÀ·ÜæŸ~ðo>UñOðéÕü“Ú<¨êŸ<Ðäþn½T÷¯î·åAÿäí}ÐÍ¿Wtí ]ý“sý »ò ÇïUyºû9ÃÑüÝ¿ÇDÏWq­gäQõ<ÚçêzÕ¼©÷«ò<gÕ÷x5Ϫñfý©ÇyûůêË{ßjϳñF¾ÔϽ9V?ë-jþÏdT”_õ:£|9C³þªjO§âÕ7‘ã§üí®›ƒ^¿]ìz‰šß|ÎEù\]''­{vµêK=þÒµNªöü.^-*ìö;Ý<( @€ @€ @€ ÐÀ_½ÿŒvx^í›ËU\1ÉÌùgá‚g< 00’úöWå žZRÕm=1à·7þA€ @€ @€ @½üùõXïw_{íæÞÕ¾òxúý{‰ÖÚù©?ë¸Zæ¯ÆêK=~>ñܪ}©ëåÒ™7»Úw½ybwäíÇ»~,­þ³yûˆ®ß߈ï¢}ìÎ÷ûßÏ÷§_}éõ­¾ëÁëùS¯«ãúòY¹—ÇWuW}©Ÿó¡Ø§ê+/êï«ýYëõ1¥]©Úëwõ¬~¼Çk©Ö¯æíÝÛ—º~}cšâýÁñs~4tëVñò®îìzuÍÙV†÷¯û}zÿã}Íû3¶.«3Zí=ë<Žž·ŽÁ³•à}¯ß§ÿ*ÿÑ}We¾³®Ë…w[ßw}ÿã]ã½›•÷g*çoö:òOòµ¨ügó®6ÿý¼§ð®=ï»Ýÿ­þ«õ[µõäuöÏ3[½óž_;7¦ú¯ÖgU×SÍ¿µï«r®º.ü¯“UýY×UÅ?}Ÿ“Cüçp·ö­j|¶ú>7øÏå¯êãÓ:øÇF8÷kä.ÃýûœøÇ¿%§ï;Æ}Ì]·þÇŸö܈öϹ¯õgíü×òaõ¹;ÿøÌÀéù¿›kž_Ëu¤{îûÿÿÿÛìœâ­O²=yÍüGdàô½Ïßñùæ3½å½ü¿ðzÿÝ^—þ÷í¯êùÂ?þ#2Àý¯fÎ"Üsÿ«{Á;Œº7DùÎsúˆâqÛ<ø§ÿ#3@ÿ×Ê[¤{Ë=ð¶s9j¿]üó9°Ï¹®QýkÿøÎ÷€:Ÿe¸·øç =¯ð¯åi}GÇ?þ³2À{ ÿéÿø?ý<˜{ æ½•ÝÿÖß ’[ðoã}_WÏWÅ?ç@NñŸÃ]ÝǧõªùçˆÍãTÿÜ ×rTÕ?çÀš¿Ósÿ9nºΟsTÝ¿ê _çà6ÿäàcºøçð¹tóO´9èêŸhr€ÿ[ïÝý«ÏÛr0Å¿W¦çašr°w/˜êŸ¬å`ºït?ÜâŸôþüWSë߮޷þ~Î{¼šk·z«UÏyûÜ­ßÍ—÷zUžwëìzS=ïͳký]^Ï«<W§«Ÿ¨u{yUÕµæ#Šã”yTÞªÔ™â%kU<ž®#‹ÛÔyO=d›ê¡Ê¾²¼®Î[…Ó-ëXõõÜ-Ü«ï3Ê÷çyªsa}^ù€ïl¯r3{÷ì€ @€ @€ ÜDà/~òx^íœKrÝ0 }³Üÿ9BŽJ9oãròD ÈÎÆ ‹$Ô=€ä_>>ø@€ @€ @€ xøõ㳞Ý^wC5/»>W×A¼—Àª¯ìë{ïþžÓ³½eíwífù©ÞGKåÜÓª=Uí®‘Ú;«ò¡Þ·–Ò9»«½ìž÷óï׃O?žc¨æNv=d¯{êsõºjswÍö¶ºßª¿èõsMåV¾ê)z}Ô[Öú\Šóv‹z|º>ËWö>óŒÅ*~ê+z]¶§ªýb4笎ú|·¾ÊOõ¾s îUúÎ[ôóÕ~ª÷ߣê¿*êõ_ë«}¨÷÷7¹V!Þ?y=ÍÑ]ß«ñ¾æý•_£k•eùÚ7§\·FÙïj¼ïõýôþÇ{ÌûTÿxÏñŽÿ\ŽSßüžèßWDß×äÕÝ?Þk¼O™ÿQÿS粪n×þz­WqœzΩþ§úP×íæ?Ú÷j~ÓÏÃíû•{>\üÓ÷=9Äw—¹Ð퟾ïÍþ{ùwÏ©þ»¹r~—æ¾ÇÜÁ¿‡‡®y2ͧSÏUûgî{Íü{ùPÏ™)þÕ\n9ÿô¿2»Ïÿ[úQ}Ÿ*÷»Þù=ŽÚù„ÿZ¾ê~^=ÏÝÿêýpýZžñ¿Æë´|áÿŠ ì¾ÿÖon÷£pÿç ü{Îü{zQÍ üã_‘æ¿gÎîyþ?ÿÿ˜Ts_ý÷Ÿô?ý¿3ÔýpÛyÌϾTåÐÝ??ÿ«Í'þkùªúx÷•ÿ×9¼zå ÿ^>vûxwþñ¯ÌÀîüç=°&§J÷;_ÿÍËîœcÝ÷ùQûç=°¦wó=Í?ÏÜüà?—çnv­ëòÏsÀ#wSýóÈÉþs8vÍïè¹Ýþ£Ïæ@,¿øñ‹ö_÷zÿÌž⿇{wß«ÿo5güœ@“ËU/ªë£þy/|–•ÏÝs¢9p™³®uìzQ­‹úgü¨äÄk´Ä»Ì{wÿx×yïêï6Þo÷¿{?šþ\S|= «u?ÝçéüÖäsŸÀ»í~ÿš\»ëÞµþO×Ãmϯ ä<¡õ>í=­W.sì®{Õú÷â5­îÚDìx÷=﫟ÿø¿Ó?Þc½W{ÿƒÿ;ýã=Ç{•õÿ;ýã=×{öúÇ?þ? œæ`Úû—ìùľÝ9÷ýšl^ÓúÇ}8+W]ügñ™Þ/þYÿ8½çqîÇä2½äžü˜~þfϯúúÏæ3½üÇì³Us„üGd@zÿ¯ºn¦ŒËÛ½Ô;¿³/á?†sÕýÿø÷Ìûí|yºç½_ý÷Wø¯½>½ï øÇ¿g8ÿkçËÓý¿µ¥9ðÞÿn¯ÿÚëÓ;ŸøÇDØÿkæ,½æ=€÷þw{ýêþù=ï¾_¾Õ÷—(ÿÏ~¸ÔÊþkùˆÞ/ºøçà“Süûp^ÇÒþ¢ýs¨•·nþ9lóƒ[žÒ}8«]–í9À>`“[üÛpÌZ¿Ú~³ý³äæÿ¹üµëWÛ¾Šöœâ?‡»vÝZµ¯æŸ} 6Sýóùp/GUý³ìùÓžÓý³üœ£êþ­örð}ºø'>çÁ­þÙº™ÿ¼Ò¿#|×N{êÚ¾w ôÿOþöÿ3ÚÝ¿õ½à¶saŠr »NóOÎr€ÿÓûd×ûÞ븧úשßÝç»çàÿ^çB÷Ï·ùÊA—Ï·úÎAÕ<Üî?+Uò€ÿï ìÞÿ¼ž‹ºWâÿg^~µu­òÿ3ZoÕÚŸÍž§£Þ+D壶¢¼Yõc;{ªuÛ0KÀjÝZÕ‰=½½#`åó´Fz8õºû|Ù3J)U¤ui@€ @€ @€@E~­šux^í›ANd1 D¹ÙÜÿsŽ0BÐÒ¨ECb»ÛylX8N½²ý·x{ãPPPPPPPPPPPP žï>s²þ®w#2òðÜõjŸU`—Wôú³·¿çôhnQñî!{Ó(>ê8¹ªÌ=MÍI.íÍT<²ãjUš=›Ëîy¿ÞwÏ!{“]ýUëwyî®U­4Çßâîr‹ZߟXÌ ~ãý÷(~Þ81êõ‹ÍóUîðÑ/kN«Î©WÙ?gU÷*=»ÅíÂîš~U?Ü5Ü»Ì/ÿný8;ߪõïåþØŸ­g·ó¦òïÆáT¾Õø{ëþ”Ž]Ï…¿öùªº/ªð§îÏø°;ÿêõU=¿Óü©û3u_åýßÊ¿z]uÉïTý[¹ó^Û/à«g—º?Ýÿ­õßMßêùf׿•;}_Ó§ºð¯^G]󃿦®ºø!‹?}¿¦ÏªóïRG]ó„ͺÌòüáŸáëüϪƒ[ÏQ³·rç}?§/Uåk=fßþ9u–Íuõ<øÃ_éëü_õ/ë|þU²ÿˆ µ¿á_›üïæø( òýÿnÁþJðü_Û_Jö¼ÿéævÔóük×gçWq६óŸïs|©dÿl«ÔýïöøðÏ©³ª>ƒ?ü3<@ÿ¯é³ öžÏxÔú&‹ÿãú€–çîsükñØåç]ß…?s@ãÓlþÌ GkèÆŸ>ëøÇêi­ÃSûNñgÔð]WþÌÿœæOˆáhÝùÓ|þ©ÂßÛðÍð·éfí·ÕöUãOÈõãTþ̃5UåOXãç'ÓùÓ~öQuþQ}|ïƒ.üñftãb}p;ÿÛçBWþÑ}àVtç|ó` |`óÁ4þ*LSùヵ~0¿ÚÝûÂ-üñÁŒÏÿ¢üjý?´Ý}ÞïgÔû£ôìg—«w½šëjüîÜ¢ó÷rµî_å½.Z¿)ñ¬£÷Eó~Ž7…—úÑ\£ãY}¢Ömjüh~§âMå“}¯Sü¼çfëtËy^.YûoáqúžYY;…U(€(€(€(€(€(€(€(€(€(€(€=ø~Ãéx^íœQN$1 D¹÷¿GØ#¬Ì­éÄ.»’<~šNìÔ+;éîooü                      ì¡À¿÷¯u<~ﱪsVqåýûåÖXi”çèø5TÙ'ËQ>êë÷QÖs%j~Ñù=U[7«(êñë*í‘y5¯ìx*®“E¶þÕó}|ßÿ=~¯£|O¦Õ|^Å»ò‹þÝ£ªÔWÔŸG¹ÞïO¢&C5Ïßæ¿ËIu]º¾Qª¸«øEçõ%£ÍLÍ=Ê¥j¼VeŸÙáýÅâ´ó?ÜŸsßýþOŽª/WÅñéÐ9™Àýïzßµÿgs¯ª¿î89U×7 ÜÇê}·úÏâß]‡]ñû*7_õü÷î«ñ‡{.÷Óøwí¯îqc»°~t´îÝõïÎOOp.B”ûc|·¾îñçèèGEù»ëî’ŸžäX¸kÎy¿ùmŒŽþjøŸÉîµÜÝîÿfù»ì£«æ¡ïèG˜åÎù>§_¬ÊÕzsË»‹?uŸS¿Q?­Æ?º^Æÿô]5êÞ£î»Îÿð‡ÿ§£> ok|³Jÿ‡ÿÚüGëýz=üÏäw ÷êóßlýÃþŸ àÔç?ê^Ã-«àïÍ'‹s×÷?¨oQÿÞ|¨ÿ³ùÀþÊûúÿÙþ‚?üàÜ¿†¯ìgÞïò}οÀ¿Gwõ¹þîüð‡¿Êìþï­”ìáî¼÷êß›ÏÝsÜìuð‡¿Òôo)ÙsþãüGýŸ]ÿþ‚<} îÿð÷äÎ÷ÿ½¹ÌÞÏŽs¯Þ j}ZÅŸ}@Ëq´î«û?üáyÀ> ñÏ*ýþ{ðgÐp\eÿò§äú§ºÿ_ãñ\0—çhX•?} Ç7Ýü£ûÁ¨ß¹þ§oVçOˆõþÑ>€æ|ÿ9ÝvÙGÜøÓjý¸+öƒ{>råO¸Ç/º¹óÇZœÂŸýà¹VáŸÕðçóŸQξ7àÿŠïÁŸ~s.­;·ë³úÀ©û‚ÏÙ|ðÁ\?˜ÕÛu\¶vï ®£yáƒ{ý ª³ûx•vé îü²òÃ{<ÿÁ÷úúÝ÷Yz®:º/¸ï«rËλÊnϳuÜe¾.?T÷‹]x©ÖÑíu¿Pé¶ë¼n~Íçz.Ü•SÕºFõw»¾J§Sâ¸ñ}•Ï)\ºÖùJÿîÏ»t95n7ïküS9¸®»Ú®:×s²ýÎg)@ÿ?‹7«E@@@@@@@@8Qÿ…Û5˜x^íœ[N,1 DÙûßK¸K@hÔÓmÇ•”Ó‡>&qœ:e'ÄÛ_(€(€(€(€(€(€(€(€(€(€(€Þ ü{äýî½+²‹òŒŽGáµ DyU_»ûû¬^Í­*Þ}ÌÝiuœ¹ªì»šš“*þ¾D´;Sñ˜W«Ò>Ñgs‰®÷ñÿç¿è÷}Õî$ª¿j|”gt|­jý£©8žÅr«ßŸØØθT^Å­*Θz}gWs=ŠWÅI§/Á\æjî*Nª¸9ûÍRqWq™·ÉXÆpèu䧘š}FWsŸU³×éCôZ¦p]ïýuMUÿQpqúÀŸìë ážãÞ÷Ùç­ÛzÝêîcõÞõü‡{-÷nýþ÷äw w÷ú‡»–ûîüÝîÙ®ù¸ÝÿGëÞUg×¼à?§ÏÂ_ûŽçª¯{^.õŸíûîúºç·š–ûsž»¾îùuåï®k—üVñ§î=îÝøw©«.yÎæOÝ{Ôýª÷¿,ÿ.õÔ-Ï.õßM×.ùÎ⟭{~ÎÓžîü»ÔQ×<ᯭ/w_ÀþJdÏ}÷ºÙ%?%û¯Øð÷î/ð÷æ£î3ð‡¿Òôo©ØÃÝ›»úýþðÏÜÿÕ÷âÿö%ý¿Gª| ø+<ÀùßÃW ö™sŸßó®ñ ü×è®:Ï£qá…8ÿ{øJÁžóÿøÿmFû³z<ü{Ô©Êð‡¿Âœÿ=|¥`ÿ3&>ðöü½ù¨Î}õ¢þ½ýEý{ó¡þïͧ;Îoÿªû?üáÏ{°ï{°{ýówÚþ1‹?瀖cöžO.YžÑy]øsh|:›?瀆c´îg½ÿù+û.L¨õϪú§ÔrìVÿ£üé5þY]ÿ£>Èúžy.äyp¨©ç¨¯½\€¢üFÇÃMÝr«šïÆô>À½0æç]ùãƒk>på_ÕðÁk¸óÇ×ê8{€¿Vß,—Yóºð§h|Ú?>¨õAWþÕ>¸ë=±;|0Övár>Ø?>ˆù`Wþ*ìvOØ?>Øãý¯Ê§£gp6Ö»MÕ:Uºv‹sƱêó*Nª8ݸUç[ÅùjÇlÜj=»Ç»Ê±z\–ßè¼î¼TùWóÍÆå{6_¥ßnq³üfÍ;ã|ôùnœfígWõ:³ôÚ}5'Uüݹ¬ÚŸŠWuÜUúÜmÝjnUñîÆÁm¿U³qÜô Ÿß d¹^‡Þ{(p•÷ßq{ìž]                    |+ð éŠìx^íœQn1 Ds³Þÿ=BP©ÄÈÚZŠC ¥—Ÿ|d%Qóf(ÙIüñÁ                      €—~}Ö3ûÝkWT3Ëóîx_£À]Nªç×ìþœUUܲæ=‡DÍN³¸TÍS£Ê¾«TqR­³/ÍÎTVÍ«QiŸYWqy·îïÿ¯ÿf¿ïC*w'ïôWÿ|–ëèø\ÕúΦæy5ÿ('Õs}‰åT^Å]ÅovÞûÌr:ïg¿ô!7W©šûlWŸSÕ4Ü?]ùËŸ`¬B÷U9U­S×wÜ_ç}×ó?›»*onóú&y¬2¸ßËû.ù‡û÷‡ÆRæóÜs¸ŸÎßí^UO²_W’•ûU:»®ëÎî¹ý¾Ûýo–¿kî\êrÍ?ܵ¹w½ÿÍrŒwÉ—{nùŸåï®·[}.üá^Óï]ïð?“?Ü×pw¹ÿEù»£]ëYuþG¹s¿ÏíÝøwÍ™kÝÕüÉ}n~g}Õ…ÿì>ÿ³ïàï•ÇjŸVñ§ï{úÌuN[þž¹¬ò¡š?}ßÛ_®ü«üú:ð÷Χڟð‡¿ÒÑó_í{æWR.-\jû‘Êä¾–c47ðïÁ)Ê÷Ý8øÃ_áú_)Øÿ›þðøàÝyÅÏs}EþsõìæOøÃ_áÎÿ¾R°œûü]÷¿Àî.÷øÃ_áÎÿ¾R°çü¿þ¼e—¾¯þÿOòOþ#}À-»×Cÿï‘S•Uüórxû þÞ|T¹WßÿÈ_‘ÿœT}À•?¿¨ñ¥š?ç@ Çh€¿7Ÿ(×Ñqîü9´þ¬âÏ9 å8š÷ççàïÉ%Êóî¸.ü94>­æÏ9 áx7÷Uïÿ]ù+ú{ú@®VåŸ>˱[þgùÓrü³:ÿø ‡c×üÃþ_{÷ÂZ?¸ôÿ¬>À½àžÜøgù zž6Ε?>¸—ã¨owçÏyðÚGîü³ú>øÙ]øãÍyp*úA7òßë}Ÿày|ôþÔ}\oÄ?gîÊ?ÝyÞ­¿;ÿì{ÁiçÂ.üU>ØÝ»ñÇ÷^'ìÊ_íƒ]úÂîüñÁïÿeù4ûuc÷×Yºvçt?tå–]w•ÜÞwÊÖq—ùVù¡ú^¹ /Õ>Vû@}¿Pé¶ë¼®~ˆÖµ+§ª}EuwW¥Ó)ë¸p­ã.«ö9ÊaÕs«t9}ÝU¼Ÿ×=ƒÛþ«}á¶êS Ë'c«ñ                    @þHÑŽx^íœ]nÜ0 „s³Þÿ=BPé"APÇú’CëËË>Ø¢¨ù†”ÖÆæí?@@@@@@@@@@~ üùõ‘óÝg¿•™ñÇÕëgªé·êU~»ãü”xvF»¼Ô㟭vÝêÔœ¢âÕ)ô¬™£øDÇ}…¼ÕDsÉŠŸ§XxìÎóûß÷¾ÑÏÞTâ²ßå ?Êsö¾8{FVs7ËMuOJº¬Gù¨îSqSÅÑ)Ù+’Šç]§¨8½¨íg{Çk÷z§¨¸ûŠöˆ°Ëõj|—¬¸=è­g ÷í®ü´®¬÷H5÷¬z̞Ǜâ|vpÿ¹Þ¿ûk^aÏpŸãþò'Íñ¬Tܳû®Ë|ãJ{Ý ÷µzïÞÿá®áÞ­ÿÃ]˽ ¸Çp?…¿Ë9Ë5¯SÝg6Ô}lÝ»Ö?Üs¸?•¿kŸuÍË¥ÿïÖ½«¾îyÁ?·ßºù¡š?u_ë¿®üÝê¨k>Uü©ûÚº¯>ÿ¯òïZg®yg×ÿ*÷×8W»æÕ…W}Ýó†¿Ç>\å“,þ«}¿J—Sæ…?õŸáêßÓgÑìáîÉ=ëû?üáÿ®À¬N9U¯“þï]ŸÑþˆâ?[ï<ß«ñ!ükt®ëÑøð‡„èÿ=|Á~å¼Ïþ_ãþ£û÷i}¢æOß×ò‰ö;ü{ñRûþðWz€þßËOJöœû¯ÿÏšºo«âÁ¿W½ª¸G½ÿ¥ÿ÷òõß‹õ6/øÃÿ]•Ôýÿoö ZqæêÃ…?ï温|ÿÝUüvãÀþ˜Ýÿéÿ5>Œ`Ïs`Ýù|·¿ßâ¿ú=€>ÛàŸ«÷]=f_‡?ü#=À9ÐÛ_‘ì¿ÆÆž>€¿'—¬süáŸåž ø=Èdø¯žy.³Oe×?Ïc8®ž»ñ§hýSÅŸ> åØ­þwùÓ4þ©®| áصþáÅ{öƒ5¹ôUÀs>pãæø­îûQ¿ÿTû‰ç…±~PóRÇÛåÏ~ð³Ô¼¢âი>Å+*.>Ðú ŠSt\| ñA4§èøø`ÏÑ|²â«|pÚy1‹OÖ<ø`®dqÉžŒù ›Kö|jø?¨âQ5o”ºö…*ÕóFû ‹ª9TÏŸåW?Tëï6¶ª}ᦿK>U>¸šw÷=ÿÕx½ÝópóƒÊ'æ×ÅwyºêÛ-¯;]¯wÓ¹K¾®¼¿çÕEϧäéæ‹§èÚ}U¾è®Û)ùGùãýN[ç¨_NÓ…õ¢                À üá2Ùx^íœQrÛ0 Ds³Þÿ=Bɤžiœ¸A,¸ ^~ü!÷-@Êòäí?@@@@@@@@@@_þüúÌ-ú黲{gå9;îÞ*ï_ý,¯ìû÷+p ²¹eÅ»‡úu«ÌâR§N™3gªâ¤šçL*ºU©8슫SêŒÈ»¸Œæýý÷¹oõó Jù«鯾¾Êõêø|åzFTó|ÿ*'Õ}=iåe]Å]Åo5nž’½"©¹¯r©ß‹Z<[xj÷쫸¢=FÂýgîô 8Ÿ¥Š{U_®šg^Yïpÿ½ŸÚÿ³¹WÕßîy¼«yœÜçêý”ú‡û÷®ç?¸çp¿;ÿÝû®ËüãÖ㎬ºwÑÝ%º¯³€{n¿ïrþƒ»–»ûþ¿Êߥ¿ºçáÖÿá^S÷nõ¿Êý1Þ½ÞÜòs©ÿUþnºvÉg7¸×ö{·ó?üïÉî{¹ï>ÿEùwÙW»äY½ÿG¹s¾×ô‹.ü»ÔS·<ᯩ«.>¨âíû]tìš'ü©ÿ Pÿž>S³‡»'÷ªçøÃÿCYt=OuË[ÕÿgyóýΞ>ÿ=º»ô øÃ_áú_)ØsÞûþ\ú½ú÷Ô}ºW=ÿÃþôúÿlpÝOÍËåÜÇ÷>5û…Š÷sÜÙº‡¿–÷Ç<ð×òœÝàïÅc–_ôþjîÔ¿—ÏàïÅ#ZÇ«ãª}Àþïå;ø{ñX­çèø*Pÿ=ü¦òü{ðçýO/NѾ?—ݨÿ^¾Êæý`äS®k|åŸ÷¾£ºÿÝG\ª®Ãþ pìá+ûcâoÀß›úø«=ðŸ}ÀÓgìáïû{pø{Ö¥zßW½ÿù‰}ÀËo#^Ù×áÎ>çìú¾>àÑ®òʾ/ÊŸ÷„¹¾Éæ:äòœ}n˜å•}?üïÍÿá'|°ÇÙõ·ÊŸsAÌ?Q^ªq«>˜Ýÿî~¿Šãj\|«çY?¯rR_åÏ~pÍ?*~YqñÁ5޳u¿ëýOÔø@ãƒ(]ãðA®vqŒÎ›ÅŸóA”€Ç8|Óˆ+ï5Ä|àEq=›lœ~NXWÜ3>¸Ö<éåe¥òÁ)}!OiïHøàç~àMM—~ÐiÛ)²ÚîûD'VÊ\«|ðx5?õ_Aÿû®~ˆæµGÅsfêî2î+qáz5ÕÎÏâ*êûÎWÞ{…Õ¼ŸçóVç¾ÙUùâ¾ Ÿ±òUŸœ¡«@@@@@@@@@øªÀ;5W?{x^í›Ñqã0 DÓÙõßÅ•p%d2Žg.šØ)`± _~üa÷- Zv>>øC@@@@@@@@@ðQàߟG-Q¯>;£’H®WýêZ®rQ]§Ýý~ÙTgóìG$gdzúW¯ËQcý¨ÕÜ¢ò¯O*f‡Qz«ãüýþ¼ðê5Fu£¨y½ÊwÆqöýuÉÍí¬š÷,ÇÙus*­·JÍ}–WôºõH^Û‘Šw4¯èx×ÔZçªlîÑ|²ã­CöýNàþÐçè§ÕùgqÏîKUüUùGsWñPçY?ÜŸó«?ÿû÷§º÷wõÜuÉו?Üçú½ûùî1ܻ͸ÇrïÂî9ÜwáïrÎr­Ãõüw·ï]õv«Ëÿ]îÏõn:»Ö³W]ërá·ï]õu¯«š?ÜsÏ÷gþƒ­þg|²ß¯âOß{øþ²ûÜåû_úÞËoêþŸå_Õ«ç…¿W?ªý¦âOß{ú þž\Ts ›ÿlßó_ãKWþ*ÿïžþš>sõüáŸáÙû¾kŸ¬ZWû¯˜ðï1Wà߃SÖüq៵?â¾÷w4æ~¯yÿ^¼¢çü÷äÍýùïå§,ίâÂ߃¿š;ý¿7÷YþÑç™]ãUõû1ïèüß•Wô¾áï1£¹^ÿ=ù»pçþ_ã?ø×è~u.«®sñç¿?¿FwUŸåéÊŸßùÆú¶Ú£óþ±üŸs¢ÊðÏáy6ÿ»ÿïìþX7ç·¬ù@ÿÏñ¨òq´à¿7žÂÿKæ@DÏÿÙþçs`_à_£{Õyï˜7‹?s ‡¯à߃SÖ¼€?ü3=0û9€ó Æ—™ìÿ=냬¹G\ùGøkúyÔ×ZàƒQ>Ù×Ãß³/³¹WýN`ö>Ày0ǧêþ¿û\Äúþ±zªævTž*þÌßÁ߃CT?Æ©æÏ¨õŸ |PãƒÕøóù`ÌGnü™cüFï÷êßÜõÏ‹rýp—Oöú»ü¹¼÷O6¿¨øø gDñQÅÁ±>Pq‹Îƒb|ÍEÜóšWV¾(ìv^ÌâQŒÍƒ*NÙyñÁ5ds¨ŽíƒÕîÕ|Tù³|ÐÝ*ý]òàƒŸ÷.ê:²}Ðe.¨uwͧòƒ›/\yTÕ¥öÁ1ßÝïsG×WéÜ%oµ²çEÕuºø`´Ž³yP­k×ü£\¯ïª¿[Ý®|ÏêrÓqµzÎô¯~5½Ý÷SÍû˜ß]¯ÝêSûc7}»ï7ÚÝõ ~@@@@@@@@@ßøåÿ5cx^íœÛm\1 DÝYúï"%¤„Àpˆ ¯)J| WÇ?þ DRsF¼w×€ßÞøA@@@@@@@@˜©ÀŸ_}[¿gžî¾®-Ž»ñû”Ô<ñ.¿Ó}šj¼nW§¼¢÷¿®Ò½'‹æ”•¯W¥×©žÅ';ïë¨=I6—ªüµªÍ­VÅc·ÎïŸ÷¼¿çÉí|—Cô>/Oïú\çeæ·šÏË-jýgë^[ñj]n©§Îÿ]ç„?ßÿdzÀšëÏâ]÷ᶺ™ìßsÃ_{¾À_›Oö<ÊâϽŸá+øÏà”5àÿ xç–¿Éû³¿£Ù{¹?ÖégÁ¿G÷n¿Gsäãþkù)‹ó³¼ðïå_Íûk=/ÿî9ø*õ»¹ïÎÿWÑ¿ëS¹óÞ󜀌Ž]÷w·® ÷ݹÏý?ó-üÏôÛ½w*ûà'5îÌÿZ¿Vo•yÿµ5x¿÷áýïÌÇð?ÓOõ^¯öÿ»ù?|2Ý«~gÝ÷~‡?s@ɼöú±Û ðïåßý9þZüŸ½7eÍ øÏàŸýùÁëÞï{|ÃèÑ]Åïð‡†¼óŸ¿ôø0ƒýÿ9ñA×Õç üµù¬rÜ]øg{à=?ÏMŸU°‡¿îÿ—WçÏç‚ܹQÅÿQ‡ç@.Oï{àþÌßTógäpôÞûì¿ÿX¾Ú}0býcqÊŽïú`×ïìûìŸl¾Vþ]þ̘9`ñ©ŠãƒžÞùVÅתÿ»ùŸ~.ày°çë^vÅ™{<§Îÿ¯>;åÏФ¹ßU´^ý=a_iíø`mhS<ï.˯2Ξ•?|ž ³èÅu›íƒ)ó!NÑ™™ª| ê‡™Ôòº®öC·/ò”œ¹Ë_ëîþ]oußlJuÝ«øáY«¼»ÿ?\±ÜJê~Xí/W¥{²¯ê­¶îBµ'Uãü¬ŸZU¨¦æ ˆh(Ðå ÓÓ…¥@–?¬ºÄg*°ê—™§£k@@@@@@@@[¿®Z8x^íœKn1 D}³Üÿ9BŽö,2€-²øÕzÞx1"Å®W¤ÔcÀü                  œ¡ÀŸ_ŸuzŸñt÷Ué娮¿OÙYO¬rËŠ›¥Æs«Éâ•繊ïy²l>Õùö¨ôœ]«ùT牞'©æÑ¿GµswéæaÝï÷×û^ô÷¹dj+·r¨Zåj¯UñœìUWy­œªÖC¨¦ÒŸ¬Ï«øEóÖ¨:7kÏUž(—®ø¹¤r+[ñŠ~ÞÅ+{Ÿ\•çe‹rý.>›Ã®|óˆåT÷OW¾ÊQ{N–lî+ýNÿ|¹X%p·õû»_cªïÎâ~z«õï'¨Uw­ßOï¸çpù@ë¾þ(¸çr?…?Ük¸ßÂ_½Ý×?Ém;Fûþ~Ñç´Ñè[÷Úy?ýþÿ;ùý—û”ûÜ÷p‡ÿ^Ý£÷¶¬ø¾›Ýÿ;Ñ÷3üwÿ,ß“gyú~Fßï:ÿUþôkoº¦€Êýÿ;ùý†{÷üWûþgó‡{-¿hTŸÿð‡ÿ?¼>ˆúšx›ïªúßË›{¾W¶¯á¿G÷lŽj>øÃ?ÓÌý³ü”É^¹çqîïõ ü÷ꯞÛÖ¸l¾«|Ìÿ½~Zñ©þÜËßêcÖýì«j®Öüðïí+—êu^îÜûb>©æéÍÿOëùæåÒµþðWÞÿ­¾¿}]W«ûÐÿµý¯r銃?ü™ÿëÿ·§žc]}¬îCÿÓÿô?ýïê<¼-NË]q^î|ÿç;/º8ªûÀßÇS_*Ÿê8øÃŸû_ÝýoúÿmzÍïPç qÕ]Ëÿžs`ê<ðòç= ×/Z׿EÁ?—gôœË#kËÿYüWþ±Qµ¯‚ÿÝüÕ÷î{|cïlßJæÀž«yßõÞÿ»ùsÀ_ù>˜{@¯o|§º5ç@/Ï)çÿ»SðÁLø;Z‹€?ü¹ôý}ØzhݬG1fͤ©òç½ Æ7Åx”êë\cÍ/q’Z•?sÀÆÕê^^>ÈåiåþZ—GRËÿ»ùGÿNÀyóÖµuQ̃ÏÓæÖ÷ÄÌÍ7uËÌÐxžÞÿ̃îSîÿ«)œ ?ûi¥ÿ”ÏñAÍ\˜Â×Z>ÈõU÷iëðAަqõÖƒb>ðê=u}–n»/Nå©Ö…|ó@Õyz\¶ž:¦sŒÖ‡žñþ?Õ§Ï…¨®§ÅW̓÷¼Þïáw­?_v½·û![ÏSóuù`Úœ8•WuÝ»üÐ}Ÿ¨Öñôü»}ðÝþY÷…Óùt×?Õj]Ýú=m?U÷)qOã±ûy¦pµÖ±[¯[ö·òè^w‹þSŸ³›÷û~Su¹½®._Ü®óSž_õËSžŸç@@@@@@@@xWà/µ6Cx^íœKr[1 }³Üÿ9BŽJ9ZDeç ²³ñB$È7=RVäþ¡                 ôTà×Ï}íþìùt÷îj—§uþ½J×>¹•“j|­ 篮âU÷|¹OÅ%«N®:笖ÅG½Î9D´O¢æ ªÿóïû„ï~jU›_]ÅÅZ÷‰£÷õù„bŸÀÊ%z¼—£w^¬zs«Es|ªçå=o.±½?ñ‰z=šWt½=çÍŽâú]h>êzóúv¬â®æ£®ïS³ÿ,ïW]5—¬úýIÚv¨âžÅ#{›º}GÃý“Õ?}‰®í,š»U¿éã×Tî7 î¾~÷k?²ÿßÜc¸¿|p+ÿé¹µÿ)ü£ú>J·Sêtç÷ؼŸrþÃ]˽ûù¿Ëÿ”|V?G·ü‡{Nßwë¸çr‡Þê<·Ö¯Îú¾Ö‡Uüá^˽:ÿá'¸÷à^Õÿ^þÖ{ ã×|–}þÃK–³øÃ½÷ìü‡ÿüáÞ“{VÿÃþ°ú ëþsû:ªûŸ•÷iß«˜â+ø÷ÎgµàÿHû³üÉÞsÏãܯõ ükõWïÑ\WëYó_õü·Õ]å£ÿÜÿ³ù$›§u=øÛx>Ýû¦ü¾—Oà¯åŸõ}-kßÃ?‡û”<°æ€5ÿ¯ß¼ýªšÿ»óÀÊŸ÷¿¨úû©.ü5èí/×Õyð‡¿ç>Àyã›Õ>ÞGäð´öÍ.×Õùð¿›?¿€¿çÀçZ߬æwÔ8ï9€4>ˆâj­ƒ4<»ÞÿÞýÿ»ùïÞ9bücÍmÕxò †ç”üç<¨áÝýÿ‘’¹¾Påùn]|ãƒ]Nªù»ü¹®ùGÅ/ª.>Xãh½÷uÿ>Qôý<øÚGQ}šU‡<ˆÍƒ,nQëDñ'¢ˆÔÔÁ19PC/nU|°çƒ8µ•¢}pËùPK-~u|`˃x=*ª|pZ.ô ¥ß…ÊÞß»t™§W¾× *LÍ…^tòv£öÁ?ä)Þ{¥,?tóEo*ù»ËöÁûzÙ÷‚|…g­Xíu^Ì¢Q·Û.>°îã)Oê½²•C×ñ³)ôÛ}WÎßí«Ÿ‚gí¨»ÎR{ÎÓtñÅÅîØi¶/îPõ¼§ŒòÉyÊðD(€(€(€(€(€(€(€(€Ÿ üº¥8Ax^í›Kr1 D}³Üÿ>BŽrÙZh*c`ãCÎóÆåýJQ>>øA@@@@@@@8O¿¾Ïôú}Þ Ï<Ñ•›êï3ÕÚïT*žÖ<û)µ÷Ž­|¢×ï­fßÝGsSåï«à^;SñÈγ—Ê}v›Í)ª^E{ï$JUÞÏŸ÷uÖß½U¯ÛŠËj+Oëú:…{V^åå·rS­ïI!oW^^Þ87Už<¥{Uòò³Æ©8EåéE%n7VnÞõQœ¢òÆ)Þ#³—ãl\—¬¼=(éw1ËϺ.‹KV½òµ­âh}½ŠGv]šLV®£õÙúW׫¡¶^uÄÑúz5‡ªúë$r3X¹Þ­¯Ò»[Ý\zþjpÿÖNí?‘œH¸Çpù(‡¢¿Ê*u¿œ–ÏO&6î±}ßµÿW¹¿âOëÓ¨óÄv±=û*ÿ(NÍk'÷œyõq Íù¬p¯áÞåùïåê<Î>×|§jWz¹s¿ÓÎ -Õùl^þÙýqz½ybš•^îô½¶ï«žÿ^þ§÷aÕù4]=ÎâåNßÇô}vÿ{ùWõÅSêŽ;wm…—;}Û÷Yýïåÿ”þ«>çZw£áŸÓÇ^ úVÀ½7÷èùø)`õwŽçó›oºßGYysÏ÷qSùþµú«8zóÀÿLþj®³ù¬óßë[âÞ};Ë'j•;Ï}Í܉âiÍ  ÏÑ\³rÉZøó~_ÿÿó¢?§SÍkÿ毫ÈääÌüÏ¡·^þZþëDr3Àþ–ûÏ÷ßý’Û½þjÖ¾çsŸ¹9á'’ ÿ9žÖy—KÑ_ þð·<÷™ÿs~ñwdM¤uXçáÓÖ×PôW…ÿ\_ÏúØO¢&þð·ÜfûàéëjºÙ^•þ×öÿ.ÿî÷rŠ•?ïl~±wdnüm<½Ïµ\ªóÕàËýù¯ñË|‡æ¬´Îï$îÝ?9tÇU¬ü™š9pí‡1©˜ðá5ïÔ.€ÿ³ùóyü=ï¸ÔøF=ÿéÿŽÞûA|°‡à¿'oâºòç>ãËhþ<r8Žúüîõîü™±þÉâψåؽÿWù3bü“Ýÿ«>ðúœ¸ÿûg7þÌí¨âÏÐrôη]ù34þ©æ¿:ðÁšºðÇkwÿWÿy¿?róêñ´¸nýÏÈ]ùボΟûáï>êÎ_5ðA¯Ïÿ¼¾ã~¨}.x9TÇ©|ðô¹PÍqµ>>X›«úw‰Ç>tá§Ú>°ù@¥{·ìA)o—Ù~¨öEž²{UªòAö÷ö¢R·Û.~¸Û‡÷{+uŠî]¹»f÷·7…>»ŸÕ»Ûº> žµ“nœïös–êýOÓÍý{Æ«|ñ u÷?e”?öW†X¸úÈËZ@@@@@@@ÝøžËC—x^í›KRå0 EÙYd^BL(Öÿ& bIÎ=Wr’‚·7~PPPPPPPPö*ðïÏÇÞO¿÷Þá;?ñó^¿CŹwéå矫̳væå”ÿ,•çÜM¯è¼sÛ¹“hÕùvªÞ·ëj>Ùõú”ÜQ9[oþ¿ŸïyÖß;(ÔïÒËÅoå©«WvfE//k¼–Wôú™4êve妋敯Né•´Ü¬ë£ødç™A%VŽÒ¸lNYùó•ï­ å§]—Å£:o/¼êZžÒõÕ|²ëåèÉ,å(]—­wþJñU¥<¥ëº¹TÕ'Q›QÊó´®JïiujiÅU;ñ”^ŸÆ£z?qDj2I¹žÖUë<µ^ 5•Oéõ©ºöå'“›AÊõ´®KßéuséÙ³ŸxJ¯O׿{vB¹‘R¾¯Öu뺥~.E}v¸hVå=¡œ¸×rÿòWM}VøßÉî=Ü»ûî½Üá?Cÿªç¼Wuô'µ/‚¾Ÿå;M}´•wŸ<µ¾ž -ÂÊý+î©úwß—¦>ÊÊ¿[Ÿ§×ד´EÀÖ¹_õü÷™Üá?›KÕ¹c›æç(ú~‡¿Î$m+àÿw´>¨š{Ô±õõ)JË›÷ûÞ9q⩽ÿ^žÚ¹¦å{ZÿüOœ²®kùkýÊúŸý•ÅSšWËsß7'¤\ªÖÁßÇS:תxjëÀ?—¿–Gõzøçð¯æh­§å/w·¯³ò¨ŽƒlÿWóóÖƒÿüµÜyïûÝ'Þ>¬Ž‡ÿ}ÿå3øÃÿ]­n®ŸòÿÞóBËóŸóÿ]úÿgxû±:žþçüçü›gÕýë­GÿÓÿôÿ½ýoýÀóßÝïð‡?ïçsÃû|VÏs`ìs`ÕÿgFùþ9ü·øþ¹ü§ûþ5üŸæÞb|uŽ{ó0bxzûÂËÑÿü¥þ±r~ÿ»ù[¿ów!=¾‰îø÷p”Îûïëà¿‹—•s×ßò<0Û_YýÏ90›{õw$æÀL?d÷?s`&÷-ýÏ{a®ªúŸ9ËÑú^°…?s Ç?Õü½sÄúþ±zZçpW\æÀ ßuóǽ>€¯þ]s¿úý_ê3ëwBž m>–r©^‡l<µó¤š«´ž—?ó@æ)®uø@ÆQÛ÷SÏÿW>Ã9>èêkk]|ë+‡î8|ãƒnŽÞúøÀç¯þSâ£|pÛ{Ã~QûÀºy¥û´<Ñ>xê\˜Æ-z?øà÷y­÷Ô|Y>Ø>¦òÊÚW¶¶ù!Kç-yo÷ÃNÙû¬òÁ÷:ÖïöQqÙºnÍßå‡êóc+Ÿª}wûàTß;ªt|Jmן¥ë>¶ñþ¾ß.Ýž^w‹/žÎaÊýMõÃ}nÝG·/nÕ}Ë}gûc‹ìS§€Ô7º¬¬F@@@@@@Ø¥À©cDx^íœ]r!„}³Üÿ9‚Ú²U)«¼Óó_^ü†þzVVòñÁ@@@@@@@@> |þùªÕú³ÏϪÔÊqöù³T­·ÛYn^ãê)²gE^¼¼çÙSí¼]yóQÏ—§Ô+«ù¨ç߃BÜ.Ô<¢çS®çJÑ<Þ­÷÷û=ÏëgO*ºªßé¯þ{/®£óè”ì5³šëóü£|ÔÏõ¢äWí©¼Ÿýä§h™ÔÜÕýê=jëUª¸{óˆžo]ÙÚ3Àý‹Ï¯jÓ³Wï×¼w=ÿánãþð½Ãjðæ}þf¯W‹æx5pŸë÷îùwî]óß‹vîVYà_ã–å5ú¾¶¿à_›:à…Èý¾R°¿æ„?ü->PŸsÌÿ»½ûŸ¾ïÑ÷ªÏÿá_ƒ¿w_ÎgåO.ûøe”ê9+w~Ÿ³Æ]Åqv^ø¯ñÍÁY>êqð×òWó[ßÊÔï§?·Ê%j<ü}û?Š›×:ð?“¿•;÷þ×>ñêǨyàfß?üþQýê½üá)`õÁéïuÕþÖl.X¹sÿû=/fõÏòŸü¿ÿÿUGϹì>ž]Ÿþ§ÿéúßš£¹xÊs³ù›=ÎÊû?÷ÿKSúÚºÏì~¶®OÿûÜÿTßǵòœ}Þêk_œöü,‡¬qð?;àïË¿Ûy`åÏ{€Í/Y¹>º.ümêsú¿¶¿ÔýÏ9ÿKr ¦¢úŸ€ÿJð^ ñOtÿ“޳ïÝø“¾þÉ⿚øÀÇð÷Ñq6³Çeó'rýW…?>ÈñÁnü¹Ø|T¿Wàƒ1T寸­Þ«óÇZtá4>8•?÷ƒnäÖ;ûûä»q«çh×ñ½]0ÿ½|м6N9örìîƒÝø?ïÇûž°›vçïýÞøì§®÷¾¨ïWó—*ºæB5>Qõ¨}Ð%'¢ô®¾Î©~¨Î%º¾hdçD´¾ÝÖËöƒÚÝxd×[Í«õdëÙ}ýUý³Çw׿ZýÙ<­ëWÓo×z¬\¢žßUï.ûŠâ|·NN«3ʧéºë~gý²«ì PPPPPPPàRàP;?x^íœÉu[1 EÝYúï"%¤„G +’‰™ x½ñâÞ}¨ÁþøàPPPPPPPP ¯~}íÍû»ï ïÜ™—§6þN•÷ŸZË)kü~%fï ‹[Ô¼³Õ¯?]—ªyêš±bŸìufÐÈ?E6‡¬ùÿ{ðîw¾rg¯ÅE;õùÙtâw¯å5ÞÊϯàY3Fñ“Îãå­¸ÝJyyÇEóŠž/NÑ3fòò\ÅGóÉžï jö]®xyŸgóÉžß®lïH/×wñÙ<ªçïMQ¿;¸i&õ‘^ážÑÜ¥ú>®'Mù®à®Ë÷g¿Ê•î5î>îô¢ºÞM÷ÓëvÔþ׊÷÷˜|?­þÃ=‡û)õßË?ªNN§GuÿpÏÍû®ù÷îÝø{¹?â§Öé¬su©ÿ^þYúLŸw7¸×Öûn¯ÿ¬ü§çeÕùvå¿•;}>¶^Tó‡{,?o8…¿÷œÄ¿ö]ò¾WÞW¿þ·ò'os}Ó=ÿá6ò>—Ÿ7?²óþwò‡{oîÙ÷?øÃÿS©¼}Œx›ß¢û¿”÷ó8øÙøyuƒÿݽܤñÑ|Wóió_zÆýìÓ—ìçZî|ŽSw²¹Jç‡ ÏU“ò¨ÿ\þÕ<µëiù¯|Îs-½ãá›ÿ{iÊW×rçÞ'ó‰œÀÞ‘ð—ñ”ö³½4õ«Ã?†¿^ùð‡ÿ§ZHëá-ãzd³~p÷å¿^ñ^ð‡¿¦þßRÏ¥çì•ÍúÝÿä?ù/ÿ›ÝþþVŸñß#ÈòŸü'ÿ¥u@z/ºeœ·þrçûž?÷‰Ý­ëÃß×ÿ³ÿÃÊUøkî|þ?«ÿ1ùúûZÜr¿·žSÚ»ŒƒÿÝu@ËŸ{€Î/]òüÝ>à¯ã9­/À¿†÷{¢ÖÖ< î»ßºô-î¹u£ÚðÏå]÷²ü¡õAô¹˜OæÃ.üé2^Ѿ†ÿÝ£9ZçËâÿ˜WÛ¨µ~„­ÞÖ<ÍŠËæOèí/ø÷æ“•÷Õß;²Þ¸äú³*ÿé¹­uâþÔÿTó÷Ö|ëƒ]ü½>°Ö;âz}nȽ06ŸµþÞÿÞ:@?ðùg |`óAþÔ?m½ïþý1|Pëƒnùÿ¼~˜ÎŸ{ÁÏþéÎ?ªàƒ×>8…?>Èé§ñöÁíuáTþø ¦œÎø|0…–¦÷‡iüñ®LåŸíƒ)ua:ÿè÷WïGzß¯Ž¿U]8¥>ÜÊ¿Ú]ýp;ÿêþð®T×ýêïÿŸê³U¿¯zžåS¹Tﻊsô:+ßTë8e½hN»æ›Â£Ë9vq´®ÛE·©û°r©Š›ª{÷sUñ]­Ó]§Ûö·âýü6}§œ7ÊSôà(€(€(€(€(€(€(€¯ø ‹œ>ûx^íœQR,! EÝ™ûß…Kp –¥S¥S¶MBnà¼?†@¸ç&Ð=ú^^ø‡(€(€(€(€(€(€(€( §ÀûëWNQ?õvxvFQ\Gç9[íúÝr©W¯ÀY+Vqô®s¼Ýzõ_—§ÈÞ3¯æµþÞ”âv¥wöž£ýÌË¥*ÎÊtß§«â7»ücë–GU¼•;çþÿ>©âµüϬû‡àÃ?ª«ç?ü-ß÷œ~ŸWýûoß°Ö?ü5ßßÁ?¦[ýíÕ]%ŽúŸó Goð‡?÷¿ëÿ_õê<ðÖ›Zõï«5ŽÞ|àïãŸõûW^ŽÞ8øÃŸóß~þSÿsuc}ÎVïí»*qôÿ9«pôæaåÏ÷ÿûÅ«ÿê8øÏÕ÷{ücøwõüÏæÿ8¬>P½«äµú\·®ÿØ>Ðí<°òç9Àæk=V‡¿§÷œ©æj]Ïê¯ÄYÉÔŒ·òçˆí5”¯W,Ϭ>—åø÷àŸý|zø€>ЃS·sÀû^û`­³êþµ½ýþ=8yùÞÅeó§hûK?÷\ÿTñ§är¼ëó*‡ä}/@ÈñOuýÓr8v©ÿYþôXÿ¬ª|˱[ý?ûŽ{Á?¬®úÀîÙßÿy}E¨õƒ—Sv>¨ñA6Gïü³üyNó—OU>ãØýþç'|ãƒ;ÝÕ>DZ>Pã;š>ˆñÁ¨Þªã¢|pê}Q•«5/|àëVÕÇã›Ôyzó‹öÁ®çƒWß.qY>ØÅ]8Îæ‰þ>fuíŸí‡.ý¡+¿¨¼«| ê‡(w™§Ú«}± ·è}¬òÁóºÞïuFã¢uÛu>?Üå1Ê]õ÷ºøçŽC—ϻ譞gÞÏyªëÚ=?u_t×·[þj~è¦ßîùVûcw=wÛ_´?vÓ‡ý              üTà5§3…x^íœÉq1 D•™òÏÂ!8Õ/{ú%v A¶/:1ýš˜¥$|ð T€ P*@¨ T€ P*p¶?Ͼ¾Ó®îá•ýó4¶^O6Wm½­zmí[Ë¥+n«Žè}wñ‹îƒ®ã–þ¢¦ò·è‹Öç¯ì}ÑtEí'[÷¬zþ¿Çy¢ê=ÝWŸh/WmÞ´Î(ûG9y󵜪âPôŸêÃËÍšWÅ/ZwJ÷©}­Ü¼ñQ.]ùSº÷õrÔæuñÊÞ§›C÷~Z~Ö¸lSõºytíg婟âTµo®}´µqUº£ÔíâR½–§6…OuÕ\ªëkyJqÕ:£Ö¯æSU_â©]GåÒÕWŸªºZ®R\—¾èûTqÊ®+ñÔ®£óèî/›Sv=-W)®[×-ûeóʪ'ñÔ®oá0Õg¯¬:Z®RÜ”žÛöÍâ–UGâ*­oÓºß,nÑ:Wi}ZÇ­ûG¹Eó%®ÒúVÝQúŽò‹æK|¥u·öåçÍ—¸Jë[õFëÛË/š'ñýmM¿íýD9Zó½ÜŸ¼íz£õoå÷òGÓí”~¢<µùäþO)4ßhùEãÈÿNþäŽÉý™CÑs-å“ÿüÉ›{õù'ò) õÚsñ-ýH÷o뺖÷{Ü-z£]§•¯OþXs_╽nåv¶ö“ÍÑZÏÊßócsÂʧ:žüc<µs§š£·>ù×ð÷òèγò×úýÖ¸n~Þý¬Üyß×Í /î<ò×ñÔαn~ÑýÈ?‡”ÃT¾•¿öÜ7Å/º/ùÇÎTÿé|ò'ÿ—ZÜ6×¥ë>¿ÑýµÜùÞ÷}NDuGÉ'ßüGáíƒüïäoåÎùÖü'ß¹¯þ½Ëè<·æ[} =ß²nÕ5žü}s•§µ/ò'~ÿñÿ]§õ¼¡ÅóüûÎÿ)ÏäOþœÿþù¿}XÏ?¿ý¹^㫪ù`åÏ÷Á¾Ò¹!ÿÝ%.]ëUüŸºœØþ"l>Õs š?ç¶¿È›Ï)çŸsÓg]ç?ÊŸï‡5þéæõAõ<¼­þ6þœ¹s`ŠtÐ9> ÿ·Þ7¦ùsÌú…?}0ãƒÓøó¹Àæ#4þYs€>Ðù•?} ã}îDçOÔú` ú ÆÛøÓ¹>ØÊŸ>ÈñÁvþÙ>¸í½áþôoœÆ¿Ê§Î…SùÓºyp:ÿjlŸ ·ðïòÁ6?ÜÆÿýz½ŸbÍ‹~§­Ê¿÷\x÷MWm]òÿYëù®Š×rôÆ‘¿N*¾Ñº^îèÿŒŽÊ\T”Jþœ‚gíŒÂÓÚÇYp®ÆÊa*G±;:™âüÛ¾w¨Ž•S¾ÀW†¾¨òÕ½SÇOw^=¯š P*@¨ T€ P*p“_3Ex^íšËq#1 DÙæŸÅ†°!l¹d¬²<ħpø|ñA@ök‚œ‘>>øC@@@@@@¸‡ÿþ<Öñîÿ=VyÿU\qô~~åöX¡—_4nuöŸe”“*~eg®@Å+;ïLõö™U6ê|û(=c¦Õ|Ôõf¨:wjý½ùÿ~=×EÿÏU¾wf^.YqQ®«ñ½*Ï©žÅÍšg•“jÜ=3±òòŽWñ‹æíQ½¯ª—Ÿ5.Ê¥*¾Dme+?ëø*^Ùuj)ÔW³r\ŸÍ¡+_=‘šŠ«WÇuñQ×­¡¡¯²ÊÑ:N­w~=m+Ï«ñÝ<ªëkéè²_q´~^­û”z:BšÌV®Wã§p蚇†’.ëÏÕÏ»ôžVWG*7ó*×wã¦é>e>¹”ò³Áý¡©Ê/ùÄr2Â]Ëýé§ZyY¢ÜŸñªýr·¼yär2Eùßz=9ÔâYà^Óï_ý'Ë÷îSÎ/u_<%l÷ú£½Ü¹ßåö ?A_$ÜsùEû”¢?ÊË?ºNâöŸ¤-ÒË~¯í6ŠþÑ^þìÛ½ù{¹³ïµÜ«žÿ¼üÙ÷ðW~ï…¿ügùJ$û¾fÿF}¼ÂÒ3þgò‡ûÜU÷?øïÁßÓÓWbàßË…‘rŒ•ôþrz¼’¥%·•;ïw|}¤r,ü}Îâ#þ¹û¿ƒ¡§¦•;çþï>ñ0èŒξïd©måúyþný±ðíÿNvµáÿOV}@ÿÏØusr¬rçÞÿ½OÌ!蛉•;üáÿ©ý߷ߦE±ÿ¹÷Yî}ôÿi;86ö¿oÿÇTŸ ÕïîªøsþûŸgª÷«ªžµðü§"Ñ“þ¾s ‡V~UøûøŸz¤ÿŸýþ?÷‹üÎ\“ÑÚÿy¾zÿÜX³kó«XûçÀï÷Æ|BÚŒð=ìþ»P+î6¿hwo<;üm<½ç_œ”6ƒÕ^ˆÓrôf·òçÈí^nYqðÏå©êsY¼_óÀþêïðÁ> ìÁi·sàé+úÀl©ö?ügsWŸÿQþ<ÖøG½ÿ£>P{ä­"ÿ¨ã½Ð´} Ö~°_5>Ø…?}àü£÷|ëƒêýÏûâ\~ÑsqWþôuóç<ÈáèíSøãƒÜ?ç‚ÍGÓøÓlü¼}¿êýÔ_¼7Ôú!ʧ*h|PÅ/«NÔܾû(‹KUž,þø Š˜¦>È94tê²âƒ˜êHi+eûà”óAK¥/{¶¢ÏÙSãûÕTÎöÁÝúB …þ**ìî‡~2µ3Pû`7?Ôª?¯Úé~˜G¤gFU>x­Ó}/ìQ{~Õ.?TŸóIôΰÛïêgõ^u÷«>ÕÞyíG`ÖŒ½ºO‰›¥æ}f3…ïÕ<î£øì•\qèú|¶jçÌþç°ö¬TåÏ\ˆÙG+ßì³fŠ(€(€(€(€(€(àSà?ˆA™x^í›Kr1 D}³Üÿ9BŽR9ZDeiˆOƒçy£…H€ì×9cûë‹@@@@@@@™ üùõ½îÕÏ™»¼ÏªW9zÇÝGÉž;õrËš×S•óV•Å+;ÎyJïÝQ6u¼½jÍϮ棎?Ÿ@íÔ<ªãת7/[5Õ|¿ÿ=çE?çÑ®xUÕ¸(Oë|­šs¢«x¾‹kå¤?‡PîJïÊûÕG¹ªö¦æ®ªSUÜþÄrV¨â®âR7GݾQàþÍæŸú’ó­ ÞŸyŸzþgs¯ê¿»óøª¬Ï,¸Ûêý”ú‡{ŒûÓ}*ym%pÏá>÷Ýçm·ükU·T”7Ý»¬g?ÙÏ+€{n¿Ÿrÿƒ»–{×óî5Ü»ñrÎïr®NYG—ó?ÊŠÞÝÖ¹›?Ükû}—ûÜ÷rß}þ{ùwëŸÓ×SÝÿ½Ü¹ßiúÅþÓë¬ëú«øS÷šúúª;ÿèþ˜¿÷u·š~¡®ø×pôÖ‹Š?Ü{sW?ÿÃþ¬>ðö1æùü–Ýÿ­¼y¯ãã–åwøïÕ?‹ã»8Ù|¯âYë_½ÿ»Ä¿â¢þÞʾë;jžÖøðñ¼êSVÕã­ü¯öË÷ÕcùàŸ[ÿ1u³­Ü9÷÷þ>&ÛðÏ©ûl.UñàË{^îuÿû¥ªNUy¬õø?¸»TõX×Z÷Üûï]÷ð‡?}¿ª;ëóÐÿ}Ï}z25àËs?çM]Ve¡þ©êßþ£ª>Õy¨êŸú§þ­}àîï}Õÿw£îûÏøVîÜÿÏzÿçõõ_U¡5y¬}þgõøûž¸Ät;­Ôtëü,Öúçø³ïóÉÔD„n«¡–ŸÅêƒÓúwö~ò i#Â?·L»Zùs°ùE[½ñèð·ñŒžqbšVDu`¾†£7ª•?瀶ox9zçÁ_Ë3»ßy9_ÍÃ3|pÅÑû=üáÿPÀêƒìþF¼Ï>ôÖ÷ê<+}c•£wükyZû—«u>èé+Gïx/έo¼<½ó¼>°ö5ƯùÆËÑ;ÏËŸ>°ÆÓê{/Çè<¯¬ûcüÞç¿w>ñò§äöhGçãƒ\žÖ~å—5ìñA¿hœ(Ο¢Ü²çG}`íwŸÍ/+>ðÕ³ÕÏY¼TqðÖ*nÙqñÆÙœTñ¢ü¹þì/UÜ,࡚¸ø ç<¨¡¥Ë‚b>Б©Œ|>¨¥¤Ï–íƒÓï z"{3à‡Ï}a/ºìøàŒç¿¨cT>˜zNDõœ:_íƒ×øÖ÷òUã§òË^÷]ý­ãôxÕ>ØÝ'¦óR¯·ÞåÏ:Ôú¿«¼ë:Oõ~¼ºw™W­×éùºp]]Çé<ºìo•Gõ¸.úÜuÕ¼_óÝU÷)ûVûcЬsM«_Ö¢2 PPPPPP`®¥DWx^íœKr1 D}3ßÿ9BŽàJÙZDeiâ×à×#iËî¶zö­MmÑp÷qŸÚÿ£¸ŸÒ¿½ûЩè÷™À=¦Þ§õ¸çpŸÒÿ½ü½ýñôùªýî¹u¯Zÿp¯á®ÆîµÜáߣ·Ú}¢ûü§î{}ØÅî½Ü»ûÿ.µþ9=Ÿêúßåþ˜7]oµü§ðWÓí”|ªøïÖý):«îþ÷°.dó§îµým>Ù}!‹?u?ÃWðŸÁ)«¨ðÏÚqßû;š?}V?ÿ,^«ý,šëjPá•G´N?¢tW‹ƒÖú·¬|ðÃï~ÈÒ[5n–¦žªœ²óÊöÁ?dë<%~•Ô|1…OUžÕ>x^o÷ßqwçUé:un?dûc*—®¼ÕüàͧKÇSÖõêß=ÿ*ûèæi]_E·Óó°r©ºîêû«âüju}îž_¶?î®ï)ûßõÉ)ûg(€(€(€(€(€(€¯øÃ?[x^íQnÜ0 Ds³Þÿ=BŽPÉ"ØE¼&G¤DJ/?²"MÍR¶´|¡             ôRàóÏW½Þï½vyNµ^Žêús­µS•Wt\-Uö«&šWt¾ý_»£h>ÙùÖªÕÿêÙ|²ó÷'0wÙ<²òÿý~>xý>W½~WËâ¡æ½â¨þ¼‘ÜŠU.Qq*G5.WÍ>Ù£øY󨼢ãúŠ­ÔÊit]4¯è|±ªÖÏ6Êó.>šOv¾úÄÆ*¼ã5úy6ŸìücêÖåzŸÍcvþºµÊàþ¥›ÕGšÊõ¢¢¹[õë¾®I_Ep÷õû.ïÿ¢¸wïßÑú}ݶ~5ÜÇú½kÿÃ=–ûÃë;ú}pÏá^?Üs¹ïÎô¾è”øjó´ïOáµÏ*üá>gÞW½ÿWùGõÁ©yV÷¿Êýw*·¨}¯â÷5ó¾ÊüWùGùž¨ò ÿµü­ç‹zÎßÅÁ¿ÿìyzøà®ŸÕÏá6ÿ‡oðAm¨ýmƒÿÙü™ðW~?Ìûá9¾±Îñ¨uœs¸®~på•?ó Ç7Q}íÍ£úÀêkÖÙüâåµ^åϰqµú?ЧšÄò´rÏ~ÿëõƒêï~Yÿì7/§¬õ*΃±ù‘ÅSÍ‹Æxzç›Ê);ÌñA6G5ÿ(ΛT>³âð£wîW»ÿ¿ó>ÈñÁîÕ>DZ>¨Æ×Z>ˆñUïªë¢|pêýbU®Þºð6¼:W_íƒÝçBužj}øÀ6T}»Äeù`—¹Ð…cTøáy.DéÚ-O¶ºÌ‡nܲêå‡j¾ÈÒ³kÞÙ>x½žú_ëÊiVÝ«ýíY:îrj~°Ös5vá²jVý«®[¥Û®×­Êùª®]9TÙWu?TÑé´:ªøâ4Ý»ìw–?ºèAïPý‚®(€(€(€(€(€(€»+ð:5?[x^íœKr1 D}³Üÿ9BŽR9ZHeeˆ_౩Ýguwk¿œ¶¾ѵJàë÷wÿ®©¾Üs¹O™ÿp¯áÞ?Ük¹ŸÎÿ´{ZÕóì?Ù_+ˆö}•N§ÆíÂîšyßíþ÷=Ü»œÿð¿'¸ïå¾»ÿ½üO½‡íz.õýî=ú^Ýÿ^îÏ}»úãô¼ªþ÷ò?]ÿÝÏWÍß˾ל]ùï䇿¦Ïºú©Š¿wîwÕéÔºàOÿgz€¾Ÿå§LöXðïÉ?›ó§xVþ§ž«êçRñÍâÎëûØœØÍû=¿µïáoãßw”¿z>NÍ×û³>kÿO塪ûTîÌýÿÏý)ܽ}ÿŸùOãî寚ŸSòLåÛýý“៣ã”~WîªÊ_Öû>çþ«Ï«¸¨âÂß7·T|ªóÀþ¬>˜vNg×[Ý—ªøp§ÿ-ýŸÝGÓâ©úR•‡þ·õ¿Š‹*üáÏü_ÿ›ª¾Tå¡ÿéúŸþ_ÓîëUõªæsužUîïëªt·š‹*>ümçÿ)ïû=ýø[î¼ÿ{Öû¿Þ90圮®SuNW籞ÕºN‹_ͧ:>ü}÷€SîƒVþÜ~öKuŸVҬÿßÏ«*NÕq­>˜vN«ë­æ•þ¹s`ÚýÀÊŸ{€Í/Ùýšþ6žÑó$›_V<«¢:°?‹\N+ÎÍÜÈ¡{þžYso>˜áÝë]ð¿7ÿ§CðAo\wrlüáÿPôôA¬»×wÃþÌõ¿Èz}wg½ƒsV2zͪö(ø ‡ìärvxùóûâ\ßäÐôGÁ¹<¯ÎûnŸ7‚ÿ½ùGOÈyór×ìdÄxN›ÿŸ\„4>¨éâxÔ(Î…5ÿÄIÕFÀk­sÿ.Ÿ/~÷W§S÷Õvo~tæAî<È'¤‰ˆr| ¡U—%Ëw½/Ö‘ÑFƾy ¥TŸ Ø|POdO†lœz>ì¡£ËZåƒSü #Ñ#~x=zPÑWQíƒ)óA¯|¯Œ*tõC/û«Qûa·/ö+Þ»‚]~P½oÑ[ý>ÕuñµŽ«÷­ú(<«+‡®ëg©Þ¿Ú®œ?ÕÕ_ÑÙv÷ÃluçVßÅs<»r•?ÎVñü§‹úä|…xB@@@@@¸»jæ:Ÿx^í›Mr[1 ƒs³Üÿ=Bñ¤^ÄÓ˜âJè& I$…ä{±›ý“R@ H) ¤€R@ H) f(ð÷ó»ÎèÏ·¼¯Ê(Oï¹û”帱—S×~5έ¢‹[UÜs•ßs³*.¨8{TšŸŧ;Ï|˜ts¨ŽÿçßïÖOŒzs³Ts‰Æ³8F×ç’é©<Ê'{.Ê/{®GÅ9Q³Ü¼ç³¼ªÏÏ!U[©—[t5¯êxµªòG‹r\=Wͧ;?±\…«Ü¢ûºùtÇÏ©Ë{:ÊÓ:×ÍŸ—`¬2‹ŸwÍ/¦2ß)/Wk?šÃ®||$}YW×wé¿;¯OmžÝ«\­}»õߟ‡èZ%ÏÕõݺ³ä_S}ÿ®U®Ö>ÝYêØOö}ÏÕu½Ùê`å¿ÊÕÚǦ7[=§ògÓ™µ6þV?[ë¬:³ÖÅÂßâj­³êË^×nþWk]_öú¦òg×uJ}»ø[}m­OÑ—½ÎiüÙõœVš¿Õ׿­OÓuJ½(þQîÏsSôœV';ÿizN«·›¿úþ[aV_°ògÕ봺ğ»?»ýÖÅ?:÷»ï«ø?ý.þêÿJ¨ïgù©’ý#–øsñ¯ækÅóò×ó¸Æ/—îu/w}®—ãÞÍÓ_üs<­9èåÞïåoÝWëh‚¹|â_Ûÿ9¸Ó^îzî¿÷ Ž\M&ñ¯éûø(^þz®c>E9AüsýâÔ•Gücü»x âz¹ë½ïî¹/þ¨ÎÄäQÿß9÷Ÿîòò×{?¦/QYÄß×ÿ(.¨<â/þV}pûüGõ%*Ï*w½÷£ˆ`óˆ¿æ¿æÿúßõ`»³?›ú_ý¯þWÿ¯ÎÛßÿŸ÷ïŸÌ˜ «Ü_÷Ýî þ,âï{þ«ÿcz:/ú;“Á;Nå齆NñÏ͵~B½Ä_ü=Ÿèû€ÿû¥·Kû¢{û_üßÏ‹>R½‘½>ð¾/ݶ¿—V}tñϽüæïzR=½üõðù¥‡Z]Tñ÷ñÌ>ÏêÈÕFòú «ƒÎ×òËFóò×s wndyzÏ‹/Ïêyç建_>˜áƒUžÞ}â7ÿ§_änxûÚ»_üÅÿ¡€|Àéo?G÷‹¿øk¬ÿñêßóX¾_ÐàšÑyž='pø Ë1z>Ê_Ÿ×ú&ʯê\Ô¨çãéyª8FãDùkÔÌ(·êsòA OJæ˜'`}åÕu^>Àø ‹_6n–¿ÞÖü“åÔ}^>Xãè}îOû{dù ÇÝý[_>¨õA5T<ù Æ(^]y䃜º¸ ãVùà¶ßМºóɾyÐÍcWüjœ:vñAåíòÁ)~@q`É#?ü|>°pA×Ñíƒ)ó­;[>”XýÀÆcw=h?ìöÅn½ÙóïòÃkÞè÷;Ö9výYêcñÁj÷ißÿ±øàYÇ*ö}lºN¯‡÷k}Óõf¯ŸÝìúZ‹/NÕwú½Pþ˜®Óíõg}r»~º¿R@ H) ¤€Rà|¾D5£x^íšÍq[1 „ÝYúï"%¸„ŒÇÖ!ÉøäæâIo¿%åãƒÿ¨ T€ P*@¨ T€ P |þù®ÓûwÇSÞS¥—£÷Ü=Êb=©—Wö9,UΫ&›Wv¼óŸy¢l.]ñfÔÚŸµ‹Oužý$zž šCvü¿?Ÿ ¤¿=êíË’Í#Oâè]ßG¦¶â('ïy/¿è¹Z5ñ£{yyÏEyeŸÇ'TS¡—Ÿõ\6¯ìx5êâEµr³îÏæÒTnEVŽÖý]œªòäªÍÊQ»¿ŠÃT\b9•h9j÷MqéÊ›£ú|-Oí¾.ý§óÌ“‹U å)í›æ0•?¦þÜi‰§v}Jw”¼s}™µ\¥}(úO×á£ÐJâ©]ŸÖ-?I[F-Wišî(õØhôí–xj×QtF­£¨.“–«´Uo´ºtTúvI\¥u4}Ñëé#û{&‰«´Ž®3j}Óü%®Ò:ª®[êÚÊ‹¾èuNñ—úZZG×uK}Ýü%®Òú]·Ô¹…ÿ=·ÕÙÅ_êki}›®[êEç¿EÇ­uVó—úZZߪ떺QùoÑo{Uü¥¾~·¾]Ïmõ“ÿ·Û¸eÕKþäŸéÎý]~Êdÿ‹ü±øgó•âYùg½Çn#q©^·rì¿›÷ù«yZã“íÜ·òèÞoåïõýmçº9ZóY¹sîëæ„•ÃÔ~ò×ñÔέ)ŽÞ¼äŸÃß«ÿô9+mܲoš_4?ùÇú?ªÿôyò÷ñŸæÍoåÎ{ÿÿ>‰ê?}žüïìû‡ïÈŸü¿Ðúà–û¼ôœÓs;+¿–;ßûYŠcÅ!ÛüÇ¢¯†üÉŸïýÿwVö?ûŸýÏþ×ÎésÑéëXÓ;^–;?ÿŵFŒ@þ¶÷ÿc¾!²ôÔdåÏ9àQ÷ ù³ÿ-÷öÿY¿ÿz<ýž¯}>ÜÉn«ÌúÐêsË>›Úx»Éßw8åó€•?ï¯ý‚×ÙºŠÈ?ÖÿÏï9ê8»È?—ÿÖ÷‚Õ·Üï¢Ï‰Óé¿WbåÏ{€mn û€ümÈõA7¿¬|ôA޲xLÅ¡b>˜â–7Ë·}~Ìæ0>°Íƒi^Uù³}pê\¨Ò%n•Nñ §ê:èƒ×ï…jÝQãWûaË|@åÓUW—PýÐ¥ó–<Ý~˜öÅ.ÝuNùà]^ïï{Ò¹n]·æCóƒTÄ}Ë│~‘ôß²Žªï¶º¶ð~®s›ÎÛêE÷Å6=O©Å§èyÊstûâÝn}ލ_nÕÏM¨ T€ P*@¨¸G 9HSx^íœAr\1D}³Üÿ9BŽrMf‘Š¢é?o¼ø ~ šñØþøà PPPPPPP`¶¿~¼êÛý>û”Ï«n—§wÿóžqb/'ÕújÜ[…Š[VÜ{•¯=Yê8µ*Ý“­š“*ß=D´'Q鯊ûóÏûƒÕw­jçFWq‰Æ]qŒ>?—¦ò(ŸÝ}Q~»û4*žu—›wÿ.¯ìýçÊ­ÔË-º>›Wv¼\UçF‹ò³îËæRo.±œÊ¬ü¢ëª8©òä¨ïâR•wOí9»½\Wë«ôïÎ3‡`¬’Gëón]ùcª÷ï²r]­ëÒ}JÞ~’¾ V<­Ï§èß]‡Oý¾ÕV®«uÝzOËßGÔ–yÅÓú|šîSê±Q¨_eåºZ7Eç©uÔ“ýÆOëó©zO«ë6þÓô^ÏþÖ¾þnÝt§Ö×Íî/]þ8•—^·åíâOß÷öýÛÇÕüá>ƒûiüo›»SÎSÕÿôý¬¾¯îÿ(ÿ)}rkêþrï»U÷)çšÊŠ>·×¡âíûÛõžv>øÏ|]VåøÃ?ÓÌý³ü”Éþ3ügðÏæjçå_uÏÝšÇÊE½ÎË÷÷±9¡æÿOë\Šr©Úçåo=÷Ó×Uñ‹æñrgîÛæD”Gõ>øÛxZçX5¿Ý|ðÏá¿Ë¡k¿—¿µž²®‹[V^øÇú?Kÿ®8^î¼îûÛ']ܲòÂÿ™}ÿöüáÿ©€×Oy]÷Ý9³æow¸ûú¿›Wv~øÃß3ÿ™ûÙØþ§ÿéûßm÷vk~vúŸþ§ÿéëàõ_þ îŒhåÎÏý;)érßûßsÿ3t½ØÙÛÿðï ¤Ë ßü¯þ:ò¯Èðñºžþ>þ{}s›ÔsZŸ{ ÇÏjNªøðÏáê½ÿ\þOñÁm÷¸ê<ª¹—9 ™§ÌøkùßêÕÜ|JÜì9¾9P3VþÞåÝÿüWþPß'øà Dû|µþð|Nh[¬ËñתwŸ3r8©ü¾Ëwµ?ʟߪñÍŠ_Öó¨T¾'nY[œ(æ€vØèå­Šú€~Õø ¬-R”?sàþo—à Ou­nUÔÞs²þk¿éÈÚ"GùsäÌ%ý*|ÃÓ;çôd}ðA­|tô«wùs/øü£'Ë€|½s_ýùŒú¿»ðÖYœÔqðÆjnÙññA®²ùTÅÃ9>¨â¥Êƒö| âR7ËO{ÿXÍIøæšGWülÜ:ºøTåUùà?Tqè΃¾¾º¹tåWûá”ùÐ¥ÿ”¼U>˜ê‡)¦ÔQí‡n_LÑ}Z]>ø.oôó½Õ¾iºO­gšVõ¬¸Ÿòù/~x)°â}>UßÓêŠêß½ï4O«·›ï*ÿizÞRïŠKÕó[ô¼åUÜßynÑíiçÈòÉÓtã¼(€(€(€(€(€(ð<~4E?›x^íœAr\1D}³Üÿ9BŽrMf‘Š¢é?o¼ø ~ šñØþøà PPPPPPP`¶¿~¼êÛý>û”Ï«n—§wÿóžqb/'ÕújÜ[…Š[VÜ{•¯=Yê8µ*Ý“­š“*ß=D´'Q鯊ûóÏûƒÕw­jçFWq‰Æ]qŒ>?—¦ò(ŸÝ}Q~»û4*žu—›wÿ.¯ìýçÊ­ÔË-º>›Wv¼\UçF‹ò³îËæRo.±œÊ¬ü¢ëª8©òä¨ïâR•wOí9»½\Wë«ôïÎ3‡`¬’Gëón]ùcª÷ï²r]­ëÒ}JÞ~’¾ V<­Ï§èß]‡Oý¾ÕV®«uÝzOËßGÔ–yÅÓú|šîSê±Q¨_eåºZ7Eç©uÔ“ýÆOëó©zO«ë6þÓô^ÏþÖ¾þnÝt§Ö×Íî/]þ8•—^·åíâOß÷öýÛÇÕüá>ƒûiüo›»SÎSÕÿôý¬¾¯îÿ(ÿ)}rkêþrï»U÷)çšÊŠ>·×¡âíûÛõžv>øÏ|]VåøÃ?ÓÌý³ü”Éþ3ügðÏæjçå_uÏÝšÇÊE½ÎË÷÷±9¡æÿOë\Šr©Úçåo=÷Ó×Uñ‹æñrgîÛæD”Gõ>øÛxZçX5¿Ý|ðÏá¿Ë¡k¿—¿µž²®‹[V^øÇú?Kÿ®8^î¼îûÛ']ܲòÂÿ™}ÿöüáÿ©€×Oy]÷Ý9³æow¸ûú¿›Wv~øÃß3ÿ™ûÙØþ§ÿéûßm÷vk~vúŸþ§ÿéëàõ_þ îŒhåÎÏý;)érßûßsÿ3t½ØÙÛÿðï ¤Ë ßü¯þ:ò¯Èðñºžþ>þ{}s›ÔsZŸ{ ÇÏjNªøðÏáê½ÿ\þOñÁm÷¸ê<ª¹—9 ™§ÌøkùßêÕÜ|JÜì9¾9P3VþÞåÝÿüWþPß'øà Dû|µþð|Nh[¬ËñתwŸ3r8©ü¾Ëwµ?ʟߪñÍŠ_Öó¨T¾'nY[œ(æ€vØèå­Šú€~Õø ¬-R”?sàþo—à Ou­nUÔÞs²þk¿éÈÚ"GùsäÌ%ý*|ÃÓ;çôd}ðA­|tô«wùs/øü£'Ë€|½s_ýùŒú¿»ðÖYœÔqðÆjnÙññA®²ùTÅÃ9>¨â¥Êƒö| âR7ËO{ÿXÍIøæšGWülÜ:ºøTåUùà?Tqè΃¾¾º¹tåWûá”ùÐ¥ÿ”¼U>˜ê‡)¦ÔQí‡n_LÑ}Z]>ø.oôó½Õ¾iºO­gšVõ¬¸Ÿòù/~x)°â}>UßÓêŠêß½ï4O«·›ï*ÿizÞRïŠKÕó[ô¼åUÜßynÑíiçÈòÉÓtã¼(€(€(€(€(€(ð<~4E?›TREE9Ïý¢Ùa9ùÍI:ZÒV;£Öe<ùÚŠ=^ß•>èã‰?}è‰@í•A ŠB¤eC.VD“IEéaF2 ‚G“$‰H)5Iž-NJÓ1K!6_L::†M™>+NC<OJGhP†KñQîORßSôSáWÛTÕ[ãU°_ÏV“cÖWbgÔX8kàY oÔZìrô[ÀvÝ\´zÒ]‘~Ð^c‚P_3†Ä`ƒŠŸaGާbæ‘­c•Ôd:™exf› ”g¤¡h§§viH«ij¾®†k'²}l­µ\m*¹n†¼7o•¿pÌÂûqßÅÈrTREE9ÿÿÿÿÿÿÿÿñ3¸3ë33Q3„3·3ê›Ê¸ ‚R ”{ æ!‰ a$È ê&û²)­,7À/÷2\6}b9†ß<ie@vÎC¡DG”åJxyNñQÔ~U­RY§ÿ\Ÿ¦`ÄEdP hÐ!YlÒ")pÝ#ûsô$ØwÔ%Ì{à& Ô'€ƒÖ(T‡Ï)*‹ã*ùŽÛ+Ü’ô,·–-«šñ.­žh/ž¢<0§+1B«†2m¯_3ó³4R¸N5k¼56¹À‰7îÄ‚8wÉa9x^íšÍq[1 „ÝYúï"%¸„ŒÇÖ!ÉøäæâIo¿%åãƒÿ¨ T€ P*@¨ T€ P |þù®ÓûwÇSÞS¥—£÷Ü=Êb=©—Wö9,UΫ&›Wv¼óŸy¢l.]ñfÔÚŸµ‹Oužý$zž šCvü¿?Ÿ ¤¿=êíË’Í#Oâè]ßG¦¶â('ïy/¿è¹Z5ñ£{yyÏEyeŸÇ'TS¡—Ÿõ\6¯ìx5êâEµr³îÏæÒTnEVŽÖý]œªòäªÍÊQ»¿ŠÃT\b9•h9j÷MqéÊ›£ú|-Oí¾.ý§óÌ“‹U å)í›æ0•?¦þÜi‰§v}Jw”¼s}™µ\¥}(úO×á£ÐJâ©]ŸÖ-?I[F-Wišî(õØhôí–xj×QtF­£¨.“–«´Uo´ºtTúvI\¥u4}Ñëé#û{&‰«´Ž®3j}Óü%®Ò:ª®[êÚÊ‹¾èuNñ—úZZG×uK}Ýü%®Òú]·Ô¹…ÿ=·ÕÙÅ_êki}›®[êEç¿EÇ­uVó—úZZߪ떺QùoÑo{Uü¥¾~·¾]Ïmõ“ÿ·Û¸eÕKþäŸéÎý]~Êdÿ‹ü±øgó•âYùg½Çn#q©^·rì¿›÷ù«yZã“íÜ·òèÞoåïõýmçº9ZóY¹sîëæ„•ÃÔ~ò×ñÔέ)ŽÞ¼äŸÃß«ÿô9+mܲoš_4?ùÇú?ªÿôyò÷ñŸæÍoåÎ{ÿÿ>‰ê?}žüïìû‡ïÈŸü¿Ðúà–û¼ôœÓs;+¿–;ßûYŠcÅ!ÛüÇ¢¯†üÉŸïýÿwVö?ûŸýÏþ×ÎésÑéëXÓ;^–;?ÿŵFŒ@þ¶÷ÿc¾!²ôÔdåÏ9àQ÷ ù³ÿ-÷öÿY¿ÿz<ýž¯}>ÜÉn«ÌúÐêsË>›Úx»Éßw8åó€•?ï¯ý‚×ÙºŠÈ?ÖÿÏï9ê8»È?—ÿÖ÷‚Õ·Üï¢Ï‰Óé¿WbåÏ{€mn û€ümÈõA7¿¬|ôA޲xLÅ¡b>˜â–7Ë·}~Ìæ0>°Íƒi^Uù³}pê\¨Ò%n•Nñ §ê:èƒ×ï…jÝQãWûaË|@åÓUW—PýÐ¥ó–<Ý~˜öÅ.ÝuNùà]^ïï{Ò¹n]·æCóƒTÄ}Ë│~‘ôß²Žªï¶º¶ð~®s›ÎÛêE÷Å6=O©Å§èyÊstûâÝn}ލ_nÕÏM¨ T€ P*@¨¸G 9HSx^í›Mr[1 ƒs³Üÿ=Bñ¤^ÄÓ˜âJè& I$…ä{±›ý“R@ H) ¤€R@ H) f(ð÷ó»ÎèÏ·¼¯Ê(Oï¹û”帱—S×~5έ¢‹[UÜs•ßs³*.¨8{TšŸŧ;Ï|˜ts¨ŽÿçßïÖOŒzs³Ts‰Æ³8F×ç’é©<Ê'{.Ê/{®GÅ9Q³Ü¼ç³¼ªÏÏ!U[©—[t5¯êxµªòG‹r\=Wͧ;?±\…«Ü¢ûºùtÇÏ©Ë{:ÊÓ:×ÍŸ—`¬2‹ŸwÍ/¦2ß)/Wk?šÃ®||$}YW×wé¿;¯OmžÝ«\­}»õߟ‡èZ%ÏÕõݺ³ä_S}ÿ®U®Ö>ÝYêØOö}ÏÕu½Ùê`å¿ÊÕÚǦ7[=§ògÓ™µ6þV?[ë¬:³ÖÅÂßâj­³êË^×nþWk]_öú¦òg×uJ}»ø[}m­OÑ—½ÎiüÙõœVš¿Õ׿­OÓuJ½(þQîÏsSôœV';ÿizN«·›¿úþ[aV_°ògÕ봺ğ»?»ýÖÅ?:÷»ï«ø?ý.þêÿJ¨ïgù©’ý#–øsñ¯ækÅóò×ó¸Æ/—îu/w}®—ãÞÍÓ_üs<­9èåÞïåoÝWëh‚¹|â_Ûÿ9¸Ó^îzî¿÷ Ž\M&ñ¯éûø(^þz®c>E9AüsýâÔ•Gücü»x âz¹ë½ïî¹/þ¨ÎÄäQÿß9÷Ÿîòò×{?¦/QYÄß×ÿ(.¨<â/þV}pûüGõ%*Ï*w½÷£ˆ`óˆ¿æ¿æÿúßõ`»³?›ú_ý¯þWÿ¯ÎÛßÿŸ÷ïŸÌ˜ «Ü_÷Ýî þ,âï{þ«ÿcz:/ú;“Á;Nå齆NñÏ͵~B½Ä_ü=Ÿèû€ÿû¥·Kû¢{û_üßÏ‹>R½‘½>ð¾/ݶ¿—V}tñϽüæïzR=½üõðù¥‡Z]Tñ÷ñÌ>ÏêÈÕFòú «ƒÎ×òËFóò×s wndyzÏ‹/Ïêyç建_>˜áƒUžÞ}â7ÿ§_änxûÚ»_üÅÿ¡€|Àéo?G÷‹¿øk¬ÿñêßóX¾_ÐàšÑyž='pø Ë1z>Ê_Ÿ×ú&ʯê\Ô¨çãéyª8FãDùkÔÌ(·êsòA OJæ˜'`}åÕu^>Àø ‹_6n–¿ÞÖü“åÔ}^>Xãè}îOû{dù ÇÝý[_>¨õA5T<ù Æ(^]y䃜º¸ ãVùà¶ßМºóɾyÐÍcWüjœ:vñAåíòÁ)~@q`É#?ü|>°pA×Ñíƒ)ó­;[>”XýÀÆcw=h?ìöÅn½ÙóïòÃkÞè÷;Ö9výYêcñÁj÷ißÿ±øàYÇ*ö}lºN¯‡÷k}Óõf¯ŸÝìúZ‹/NÕwú½Pþ˜®Óíõg}r»~º¿R@ H) ¤€Rà|¾D5£x^íœKr1 D}³Üÿ9BŽR9ZHeeˆ_౩Ýguwk¿œ¶¾ѵJàë÷wÿ®©¾Üs¹O™ÿp¯áÞ?Ük¹ŸÎÿ´{ZÕóì?Ù_+ˆö}•N§ÆíÂîšyßíþ÷=Ü»œÿð¿'¸ïå¾»ÿ½üO½‡íz.õýî=ú^Ýÿ^îÏ}»úãô¼ªþ÷ò?]ÿÝÏWÍß˾ל]ùï䇿¦Ïºú©Š¿wîwÕéÔºàOÿgz€¾Ÿå§LöXðïÉ?›ó§xVþ§ž«êçRñÍâÎëûØœØÍû=¿µïáoãßw”¿z>NÍ×û³>kÿO塪ûTîÌýÿÏý)ܽ}ÿŸùOãî寚ŸSòLåÛýý“៣ã”~WîªÊ_Öû>çþ«Ï«¸¨âÂß7·T|ªóÀþ¬>˜vNg×[Ý—ªøp§ÿ-ýŸÝGÓâ©úR•‡þ·õ¿Š‹*üáÏü_ÿ›ª¾Tå¡ÿéúŸþ_ÓîëUõªæsužUîïëªt·š‹*>ümçÿ)ïû=ýø[î¼ÿ{Öû¿Þ90圮®SuNW籞ÕºN‹_ͧ:>ü}÷€SîƒVþÜ~öKuŸVҬÿßÏ«*NÕq­>˜vN«ë­æ•þ¹s`ÚýÀÊŸ{€Í/Ùýšþ6žÑó$›_V<«¢:°?‹\N+ÎÍÜÈ¡{þžYso>˜áÝë]ð¿7ÿ§CðAo\wrlüáÿPôôA¬»×wÃþÌõ¿Èz}wg½ƒsV2zͪö(ø ‡ìärvxùóûâ\ßäÐôGÁ¹<¯ÎûnŸ7‚ÿ½ùGOÈyór×ìdÄxN›ÿŸ\„4>¨éâxÔ(Î…5ÿÄIÕFÀk­sÿ.Ÿ/~÷W§S÷Õvo~tæAî<È'¤‰ˆr| ¡U—%Ëw½/Ö‘ÑFƾy ¥TŸ Ø|POdO†lœz>ì¡£ËZåƒSü #Ñ#~x=zPÑWQíƒ)óA¯|¯Œ*tõC/û«Qûa·/ö+Þ»‚]~P½oÑ[ý>ÕuñµŽ«÷­ú(<«+‡®ëg©Þ¿Ú®œ?ÕÕ_ÑÙv÷ÃluçVßÅs<»r•?ÎVñü§‹úä|…xB@@@@@¸»jæ:Ÿx^íQnÜ0 Ds³Þÿ=BŽPÉ"ØE¼&G¤DJ/?²"MÍR¶´|¡             ôRàóÏW½Þï½vyNµ^Žêús­µS•Wt\-Uö«&šWt¾ý_»£h>ÙùÖªÕÿêÙ|²ó÷'0wÙ<²òÿý~>xý>W½~WËâ¡æ½â¨þ¼‘ÜŠU.Qq*G5.WÍ>Ù£øY󨼢ãúŠ­ÔÊit]4¯è|±ªÖÏ6Êó.>šOv¾úÄÆ*¼ã5úy6ŸìücêÖåzŸÍcvþºµÊàþ¥›ÕGšÊõ¢¢¹[õë¾®I_Ep÷õû.ïÿ¢¸wïßÑú}ݶ~5ÜÇú½kÿÃ=–ûÃë;ú}pÏá^?Üs¹ïÎô¾è”øjó´ïOáµÏ*üá>gÞW½ÿWùGõÁ©yV÷¿Êýw*·¨}¯â÷5ó¾ÊüWùGùž¨ò ÿµü­ç‹zÎßÅÁ¿ÿìyzøà®ŸÕÏá6ÿ‡oðAm¨ýmƒÿÙü™ðW~?Ìûá9¾±Îñ¨uœs¸®~på•?ó Ç7Q}íÍ£úÀêkÖÙüâåµ^åϰqµú?ЧšÄò´rÏ~ÿëõƒêï~Yÿì7/§¬õ*΃±ù‘ÅSÍ‹Æxzç›Ê);ÌñA6G5ÿ(ΛT>³âð£wîW»ÿ¿ó>ÈñÁîÕ>DZ>¨Æ×Z>ˆñUïªë¢|pêýbU®Þºð6¼:W_íƒÝçBužj}øÀ6T}»Äeù`—¹Ð…cTøáy.DéÚ-O¶ºÌ‡nܲêå‡j¾ÈÒ³kÞÙ>x½žú_ëÊiVÝ«ýíY:îrj~°Ös5vá²jVý«®[¥Û®×­Êùª®]9TÙWu?TÑé´:ªøâ4Ý»ìw–?ºèAïPý‚®(€(€(€(€(€(€»+ð:5?[x^íœKr1 D}3ßÿ9BŽàJÙZDeiâ×à×#iËî¶zö­MmÑp÷qŸÚÿ£¸ŸÒ¿½ûЩè÷™À=¦Þ§õ¸çpŸÒÿ½ü½ýñôùªýî¹u¯Zÿp¯á®ÆîµÜáߣ·Ú}¢ûü§î{}ØÅî½Ü»ûÿ.µþ9=Ÿêúßåþ˜7]oµü§ðWÓí”|ªøïÖý):«îþ÷°.dó§îµým>Ù}!‹?u?ÃWðŸÁ)«¨ðÏÚqßû;š?}V?ÿ,^«ý,šëjPá•G´N?¢tW‹ƒÖú·¬|ðÃï~ÈÒ[5n–¦žªœ²óÊöÁ?dë<%~•Ô|1…OUžÕ>x^o÷ßqwçUé:un?dûc*—®¼ÕüàͧKÇSÖõêß=ÿ*ûèæi]_E·Óó°r©ºîêû«âüju}îž_¶?î®ï)ûßõÉ)ûg(€(€(€(€(€(€¯øÃ?[x^í›Kr1 D}³Üÿ9BŽR9ZDeiˆOƒçy£…H€ì×9cûë‹@@@@@@@™ üùõ½îÕÏ™»¼ÏªW9zÇÝGÉž;õrËš×S•óV•Å+;ÎyJïÝQ6u¼½jÍϮ棎?Ÿ@íÔ<ªãת7/[5Õ|¿ÿ=çE?çÑ®xUÕ¸(Oë|­šs¢«x¾‹kå¤?‡PîJïÊûÕG¹ªö¦æ®ªSUÜþÄrV¨â®âR7GݾQàþÍæŸú’ó­ ÞŸyŸzþgs¯ê¿»óøª¬Ï,¸Ûêý”ú‡{ŒûÓ}*ym%pÏá>÷Ýçm·ükU·T”7Ý»¬g?ÙÏ+€{n¿Ÿrÿƒ»–{×óî5Ü»ñrÎïr®NYG—ó?ÊŠÞÝÖ¹›?Ükû}—ûÜ÷rß}þ{ùwëŸÓ×SÝÿ½Ü¹ßiúÅþÓë¬ëú«øS÷šúúª;ÿèþ˜¿÷u·š~¡®ø×pôÖ‹Š?Ü{sW?ÿÃþ¬>ðö1æùü–Ýÿ­¼y¯ãã–åwøïÕ?‹ã»8Ù|¯âYë_½ÿ»Ä¿â¢þÞʾë;jžÖøðñ¼êSVÕã­ü¯öË÷ÕcùàŸ[ÿ1u³­Ü9÷÷þ>&ÛðÏ©ûl.UñàË{^îuÿû¥ªNUy¬õø?¸»TõX×Z÷Üûï]÷ð‡?}¿ª;ëóÐÿ}Ï}z25àËs?çM]Ve¡þ©êßþ£ª>Õy¨êŸú§þ­}àîï}Õÿw£îûÏøVîÜÿÏzÿçõõ_U¡5y¬}þgõøûž¸Ät;­Ôtëü,Öúçø³ïóÉÔD„n«¡–ŸÅêƒÓúwö~ò i#Â?·L»Zùs°ùE[½ñèð·ñŒžqbšVDu`¾†£7ª•?瀶ox9zçÁ_Ë3»ßy9_ÍÃ3|pÅÑû=üáÿPÀêƒìþF¼Ï>ôÖ÷ê<+}c•£wükyZû—«u>èé+Gïx/έo¼<½ó¼>°ö5ƯùÆËÑ;ÏËŸ>°ÆÓê{/Çè<¯¬ûcüÞç¿w>ñò§äöhGçãƒ\žÖ~å—5ìñA¿hœ(Ο¢Ü²çG}`íwŸÍ/+>ðÕ³ÕÏY¼TqðÖ*nÙqñÆÙœTñ¢ü¹þì/UÜ,࡚¸ø ç<¨¡¥Ë‚b>Б©Œ|>¨¥¤Ï–íƒÓï z"{3à‡Ï}a/ºìøàŒç¿¨cT>˜zNDõœ:_íƒ×øÖ÷òUã§òË^÷]ý­ãôxÕ>ØÝ'¦óR¯·ÞåÏ:Ôú¿«¼ë:Oõ~¼ºw™W­×éùºp]]Çé<ºìo•Gõ¸.úÜuÕ¼_óÝU÷)ûVûcЬsM«_Ö¢2 PPPPPP`®¥DWx^íšËq#1 DÙæŸÅ†°!l¹d¬²<ħpø|ñA@ök‚œ‘>>øC@@@@@@¸‡ÿþ<Öñîÿ=VyÿU\qô~~åöX¡—_4nuöŸe”“*~eg®@Å+;ïLõö™U6ê|û(=c¦Õ|Ôõf¨:wjý½ùÿ~=×EÿÏU¾wf^.YqQ®«ñ½*Ï©žÅÍšg•“jÜ=3±òòŽWñ‹æíQ½¯ª—Ÿ5.Ê¥*¾Dme+?ëø*^Ùuj)ÔW³r\ŸÍ¡+_=‘šŠ«WÇuñQ×­¡¡¯²ÊÑ:N­w~=m+Ï«ñÝ<ªëkéè²_q´~^­û”z:BšÌV®Wã§p蚇†’.ëÏÕÏ»ôžVWG*7ó*×wã¦é>e>¹”ò³Áý¡©Ê/ùÄr2Â]Ëýé§ZyY¢ÜŸñªýr·¼yär2Eùßz=9ÔâYà^Óï_ý'Ë÷îSÎ/u_<%l÷ú£½Ü¹ßåö ?A_$ÜsùEû”¢?ÊË?ºNâöŸ¤-ÒË~¯í6ŠþÑ^þìÛ½ù{¹³ïµÜ«žÿ¼üÙ÷ðW~ï…¿ügùJ$û¾fÿF}¼ÂÒ3þgò‡ûÜU÷?øïÁßÓÓWbàßË…‘rŒ•ôþrz¼’¥%·•;ïw|}¤r,ü}Îâ#þ¹û¿ƒ¡§¦•;çþï>ñ0èŒξïd©måúyþný±ðíÿNvµáÿOV}@ÿÏØusr¬rçÞÿ½OÌ!蛉•;üáÿ©ý߷ߦE±ÿ¹÷Yî}ôÿi;86ö¿oÿÇTŸ ÕïîªøsþûŸgª÷«ªžµðü§"Ñ“þ¾s ‡V~UøûøŸz¤ÿŸýþ?÷‹üÎ\“ÑÚÿy¾zÿÜX³kó«XûçÀï÷Æ|BÚŒð=ìþ»P+î6¿hwo<;üm<½ç_œ”6ƒÕ^ˆÓrôf·òçÈí^nYqðÏå©êsY¼_óÀþêïðÁ> ìÁi·sàé+úÀl©ö?ügsWŸÿQþ<ÖøG½ÿ£>P{ä­"ÿ¨ã½Ð´} Ö~°_5>Ø…?}àü£÷|ëƒêýÏûâ\~ÑsqWþôuóç<ÈáèíSøãƒÜ?ç‚ÍGÓøÓlü¼}¿êýÔ_¼7Ôú!ʧ*h|PÅ/«NÔܾû(‹KUž,þø Š˜¦>È94tê²âƒ˜êHi+eûà”óAK¥/{¶¢ÏÙSãûÕTÎöÁÝúB …þ**ìî‡~2µ3Pû`7?Ôª?¯Úé~˜G¤gFU>x­Ó}/ìQ{~Õ.?TŸóIôΰÛïêgõ^u÷«>ÕÞyíG`ÖŒ½ºO‰›¥æ}f3…ïÕ<î£øì•\qèú|¶jçÌþç°ö¬TåÏ\ˆÙG+ßì³fŠ(€(€(€(€(€(àSà?ˆA™x^íœÉq1 D•™òÏÂ!8Õ/{ú%v A¶/:1ýš˜¥$|ð T€ P*@¨ T€ P*p¶?Ͼ¾Ó®îá•ýó4¶^O6Wm½­zmí[Ë¥+n«Žè}wñ‹îƒ®ã–þ¢¦ò·è‹Öç¯ì}ÑtEí'[÷¬zþ¿Çy¢ê=ÝWŸh/WmÞ´Î(ûG9y󵜪âPôŸêÃËÍšWÅ/ZwJ÷©}­Ü¼ñQ.]ùSº÷õrÔæuñÊÞ§›C÷~Z~Ö¸lSõºytíg婟âTµo®}´µqUº£ÔíâR½–§6…OuÕ\ªëkyJqÕ:£Ö¯æSU_â©]GåÒÕWŸªºZ®R\—¾èûTqÊ®+ñÔ®£óèî/›Sv=-W)®[×-ûeóʪ'ñÔ®oá0Õg¯¬:Z®RÜ”žÛöÍâ–UGâ*­oÓºß,nÑ:Wi}ZÇ­ûG¹Eó%®ÒúVÝQúŽò‹æK|¥u·öåçÍ—¸Jë[õFëÛË/š'ñýmM¿íýD9Zó½ÜŸ¼íz£õoå÷òGÓí”~¢<µùäþO)4ßhùEãÈÿNþäŽÉý™CÑs-å“ÿüÉ›{õù'ò) õÚsñ-ýH÷o뺖÷{Ü-z£]§•¯OþXs_╽nåv¶ö“ÍÑZÏÊßócsÂʧ:žüc<µs§š£·>ù×ð÷òèγò×úýÖ¸n~Þý¬Üyß×Í /î<ò×ñÔαn~ÑýÈ?‡”ÃT¾•¿öÜ7Å/º/ùÇÎTÿé|ò'ÿ—ZÜ6×¥ë>¿ÑýµÜùÞ÷}NDuGÉ'ßüGáíƒüïäoåÎùÖü'ß¹¯þ½Ëè<·æ[} =ß²nÕ5žü}s•§µ/ò'~ÿñÿ]§õ¼¡ÅóüûÎÿ)ÏäOþœÿþù¿}XÏ?¿ý¹^㫪ù`åÏ÷Á¾Ò¹!ÿÝ%.]ëUüŸºœØþ"l>Õs š?ç¶¿È›Ï)çŸsÓg]ç?ÊŸï‡5þéæõAõ<¼­þ6þœ¹s`ŠtÐ9> ÿ·Þ7¦ùsÌú…?}0ãƒÓøó¹Àæ#4þYs€>Ðù•?} ã}îDçOÔú` ú ÆÛøÓ¹>ØÊŸ>ÈñÁvþÙ>¸í½áþôoœÆ¿Ê§Î…SùÓºyp:ÿjlŸ ·ðïòÁ6?ÜÆÿýz½ŸbÍ‹~§­Ê¿÷\x÷MWm]òÿYëù®Š×rôÆ‘¿N*¾Ñº^îèÿŒŽÊ\T”Jþœ‚gíŒÂÓÚÇYp®ÆÊa*G±;:™âüÛ¾w¨Ž•S¾ÀW†¾¨òÕ½SÇOw^=¯š P*@¨ T€ P*p“_3Ex^íœQR,! EÝ™ûß…Kp –¥S¥S¶MBnà¼?†@¸ç&Ð=ú^^ø‡(€(€(€(€(€(€(€( §ÀûëWNQ?õvxvFQ\Gç9[íúÝr©W¯ÀY+Vqô®s¼Ýzõ_—§ÈÞ3¯æµþÞ”âv¥wöž£ýÌË¥*ÎÊtß§«â7»ücë–GU¼•;çþÿ>©âµüϬû‡àÃ?ª«ç?ü-ß÷œ~ŸWýûoß°Ö?ü5ßßÁ?¦[ýíÕ]%ŽúŸó Goð‡?÷¿ëÿ_õê<ðÖ›Zõï«5ŽÞ|àïãŸõûW^ŽÞ8øÃŸóß~þSÿsuc}ÎVïí»*qôÿ9«pôæaåÏ÷ÿûÅ«ÿê8øÏÕ÷{ücøwõüÏæÿ8¬>P½«äµú\·®ÿØ>Ðí<°òç9Àæk=V‡¿§÷œ©æj]Ïê¯ÄYÉÔŒ·òçˆí5”¯W,Ϭ>—åø÷àŸý|zø€>ЃS·sÀû^û`­³êþµ½ýþ=8yùÞÅeó§hûK?÷\ÿTñ§är¼ëó*‡ä}/@ÈñOuýÓr8v©ÿYþôXÿ¬ª|˱[ý?ûŽ{Á?¬®úÀîÙßÿy}E¨õƒ—Sv>¨ñA6Gïü³üyNó—OU>ãØýþç'|ãƒ;ÝÕ>DZ>Pã;š>ˆñÁ¨Þªã¢|pê}Q•«5/|àëVÕÇã›Ôyzó‹öÁ®çƒWß.qY>ØÅ]8Îæ‰þ>fuíŸí‡.ý¡+¿¨¼«| ê‡(w™§Ú«}± ·è}¬òÁóºÞïuFã¢uÛu>?Üå1Ê]õ÷ºøçŽC—ϻ譞gÞÏyªëÚ=?u_t×·[þj~è¦ßîùVûcw=wÛ_´?vÓ‡ý              üTà5§3…x^íœÉu[1 EÝYúï"%¤„G +’‰™ x½ñâÞ}¨ÁþøàPPPPPPPP ¯~}íÍû»ï ïÜ™—§6þN•÷ŸZË)kü~%fï ‹[Ô¼³Õ¯?]—ªyêš±bŸìufÐÈ?E6‡¬ùÿ{ðîw¾rg¯ÅE;õùÙtâw¯å5ÞÊϯàY3Fñ“Îãå­¸ÝJyyÇEóŠž/NÑ3fòò\ÅGóÉžï jö]®xyŸgóÉžß®lïH/×wñÙ<ªçïMQ¿;¸i&õ‘^ážÑÜ¥ú>®'Mù®à®Ë÷g¿Ê•î5î>îô¢ºÞM÷ÓëvÔþ׊÷÷˜|?­þÃ=‡û)õßË?ªNN§GuÿpÏÍû®ù÷îÝø{¹?â§Öé¬su©ÿ^þYúLŸw7¸×Öûn¯ÿ¬ü§çeÕùvå¿•;}>¶^Tó‡{,?o8…¿÷œÄ¿ö]ò¾WÞW¿þ·ò'os}Ó=ÿá6ò>—Ÿ7?²óþwò‡{oîÙ÷?øÃÿS©¼}Œx›ß¢û¿”÷ó8øÙøyuƒÿݽܤñÑ|Wóió_zÆýìÓ—ìçZî|ŽSw²¹Jç‡ ÏU“ò¨ÿ\þÕ<µëiù¯|Îs-½ãá›ÿ{iÊW×rçÞ'ó‰œÀÞ‘ð—ñ”ö³½4õ«Ã?†¿^ùð‡ÿ§ZHëá-ãzd³~p÷å¿^ñ^ð‡¿¦þßRÏ¥çì•ÍúÝÿä?ù/ÿ›ÝþþVŸñß#ÈòŸü'ÿ¥u@z/ºeœ·þrçûž?÷‰Ý­ëÃß×ÿ³ÿÃÊUøkî|þ?«ÿ1ùúûZÜr¿·žSÚ»ŒƒÿÝu@ËŸ{€Î/]òüÝ>à¯ã9­/À¿†÷{¢ÖÖ< î»ßºô-î¹u£ÚðÏå]÷²ü¡õAô¹˜OæÃ.üé2^Ѿ†ÿÝ£9ZçËâÿ˜WÛ¨µ~„­ÞÖ<ÍŠËæOèí/ø÷æ“•÷Õß;²Þ¸äú³*ÿé¹­uâþÔÿTó÷Ö|ëƒ]ü½>°Ö;âz}nȽ06ŸµþÞÿÞ:@?ðùg |`óAþÔ?m½ïþý1|Pëƒnùÿ¼~˜ÎŸ{ÁÏþéÎ?ªàƒ×>8…?>Èé§ñöÁíuáTþø ¦œÎø|0…–¦÷‡iüñ®LåŸíƒ)ua:ÿè÷WïGzß¯Ž¿U]8¥>ÜÊ¿Ú]ýp;ÿêþð®T×ýêïÿŸê³U¿¯zžåS¹Tﻊsô:+ßTë8e½hN»æ›Â£Ë9vq´®ÛE·©û°r©Š›ª{÷sUñ]­Ó]§Ûö·âýü6}§œ7ÊSôà(€(€(€(€(€(€(€¯ø ‹œ>ûx^íœ]r!„}³Üÿ9‚Ú²U)«¼Óó_^ü†þzVVòñÁ@@@@@@@@> |þùªÕú³ÏϪÔÊqöù³T­·ÛYn^ãê)²gE^¼¼çÙSí¼]yóQÏ—§Ô+«ù¨ç߃BÜ.Ô<¢çS®çJÑ<Þ­÷÷û=ÏëgO*ºªßé¯þ{/®£óè”ì5³šëóü£|ÔÏõ¢äWí©¼Ÿýä§h™ÔÜÕýê=jëUª¸{óˆžo]ÙÚ3Àý‹Ï¯jÓ³Wï×¼w=ÿánãþð½Ãjðæ}þf¯W‹æx5pŸë÷îùwî]óß‹vîVYà_ã–å5ú¾¶¿à_›:à…Èý¾R°¿æ„?ü->PŸsÌÿ»½ûŸ¾ïÑ÷ªÏÿá_ƒ¿w_ÎgåO.ûøe”ê9+w~Ÿ³Æ]Åqv^ø¯ñÍÁY>êqð×òWó[ßÊÔï§?·Ê%j<ü}û?Š›×:ð?“¿•;÷þ×>ñêǨyàfß?üþQýê½üá)`õÁéïuÕþÖl.X¹sÿû=/fõÏòŸü¿ÿÿUGϹì>ž]Ÿþ§ÿéúßš£¹xÊs³ù›=ÎÊû?÷ÿKSúÚºÏì~¶®OÿûÜÿTßǵòœ}Þêk_œöü,‡¬qð?;àïË¿Ûy`åÏ{€Í/Y¹>º.ümêsú¿¶¿ÔýÏ9ÿKr ¦¢úŸ€ÿJð^ ñOtÿ“޳ïÝø“¾þÉ⿚øÀÇð÷Ñq6³Çeó'rýW…?>ÈñÁnü¹Ø|T¿Wàƒ1T寸­Þ«óÇZtá4>8•?÷ƒnäÖ;ûûä»q«çh×ñ½]0ÿ½|м6N9örìîƒÝø?ïÇûž°›vçïýÞøì§®÷¾¨ïWó—*ºæB5>Qõ¨}Ð%'¢ô®¾Î©~¨Î%º¾hdçD´¾ÝÖËöƒÚÝxd×[Í«õdëÙ}ýUý³Çw׿ZýÙ<­ëWÓo×z¬\¢žßUï.ûŠâ|·NN«3ʧéºë~gý²«ì PPPPPPPàRàP;?x^í›KRå0 EÙYd^BL(Öÿ& bIÎ=Wr’‚·7~PPPPPPPPö*ðïÏÇÞO¿÷Þá;?ñó^¿CŹwéå矫̳væå”ÿ,•çÜM¯è¼sÛ¹“hÕùvªÞ·ëj>Ùõú”ÜQ9[oþ¿ŸïyÖß;(ÔïÒËÅoå©«WvfE//k¼–Wôú™4êve妋敯Né•´Ü¬ë£ødç™A%VŽÒ¸lNYùó•ï­ å§]—Å£:o/¼êZžÒõÕ|²ëåèÉ,å(]—­wþJñU¥<¥ëº¹TÕ'Q›QÊó´®JïiujiÅU;ñ”^ŸÆ£z?qDj2I¹žÖUë<µ^ 5•Oéõ©ºöå'“›AÊõ´®KßéuséÙ³ŸxJ¯O׿{vB¹‘R¾¯Öu뺥~.E}v¸hVå=¡œ¸×rÿòWM}VøßÉî=Ü»ûî½Üá?Cÿªç¼Wuô'µ/‚¾Ÿå;M}´•wŸ<µ¾ž -ÂÊý+î©úwß—¦>ÊÊ¿[Ÿ§×ד´EÀÖ¹_õü÷™Üá?›KÕ¹c›æç(ú~‡¿Î$m+àÿw´>¨š{Ô±õõ)JË›÷ûÞ9q⩽ÿ^žÚ¹¦å{ZÿüOœ²®kùkýÊúŸý•ÅSšWËsß7'¤\ªÖÁßÇS:תxjëÀ?—¿–Gõzøçð¯æh­§å/w·¯³ò¨ŽƒlÿWóóÖƒÿüµÜyïûÝ'Þ>¬Ž‡ÿ}ÿå3øÃÿ]­n®ŸòÿÞóBËóŸóÿ]úÿgxû±:žþçüçü›gÕýë­GÿÓÿôÿ½ýoýÀóßÝïð‡?ïçsÃû|VÏs`ìs`ÕÿgFùþ9ü·øþ¹ü§ûþ5üŸæÞb|uŽ{ó0bxzûÂËÑÿü¥þ±r~ÿ»ù[¿ów!=¾‰îø÷p”Îûïëà¿‹—•s×ßò<0Û_YýÏ90›{õw$æÀL?d÷?s`&÷-ýÏ{a®ªúŸ9ËÑú^°…?s Ç?Õü½sÄúþ±zZçpW\æÀ ßuóǽ>€¯þ]s¿úý_ê3ëwBž m>–r©^‡l<µó¤š«´ž—?ó@æ)®uø@ÆQÛ÷SÏÿW>Ã9>èêkk]|ë+‡î8|ãƒnŽÞúøÀç¯þSâ£|pÛ{Ã~QûÀºy¥û´<Ñ>xê\˜Æ-z?øà÷y­÷Ô|Y>Ø>¦òÊÚW¶¶ù!Kç-yo÷ÃNÙû¬òÁ÷:ÖïöQqÙºnÍßå‡êóc+Ÿª}wûàTß;ªt|Jmן¥ë>¶ñþ¾ß.Ýž^w‹/žÎaÊýMõÃ}nÝG·/nÕ}Ë}gûc‹ìS§€Ô7º¬¬F@@@@@@Ø¥À©cDx^í›Kr1 D}³Üÿ>BŽrÙZh*c`ãCÎóÆåýJQ>>øA@@@@@@@8O¿¾Ïôú}Þ Ï<Ñ•›êï3ÕÚïT*žÖ<û)µ÷Ž­|¢×ï­fßÝGsSåï«à^;SñÈγ—Ê}v›Í)ª^E{ï$JUÞÏŸ÷uÖß½U¯ÛŠËj+Oëú:…{V^åå·rS­ïI!oW^^Þ87Už<¥{Uòò³Æ©8EåéE%n7VnÞõQœ¢òÆ)Þ#³—ãl\—¬¼=(éw1ËϺ.‹KV½òµ­âh}½ŠGv]šLV®£õÙúW׫¡¶^uÄÑúz5‡ªúë$r3X¹Þ­¯Ò»[Ý\zþjpÿÖNí?‘œH¸Çpù(‡¢¿Ê*u¿œ–ÏO&6î±}ßµÿW¹¿âOëÓ¨óÄv±=û*ÿ(NÍk'÷œyõq Íù¬p¯áÞåùïåê<Î>×|§jWz¹s¿ÓÎ -Õùl^þÙýqz½ybš•^îô½¶ï«žÿ^þ§÷aÕù4]=ÎâåNßÇô}vÿ{ùWõÅSêŽ;wm…—;}Û÷Yýïåÿ”þ«>çZw£áŸÓÇ^ úVÀ½7÷èùø)`õwŽçó›oºßGYysÏ÷qSùþµú«8zóÀÿLþj®³ù¬óßë[âÞ};Ë'j•;Ï}Í܉âiÍ  ÏÑ\³rÉZøó~_ÿÿó¢?§SÍkÿ毫ÈääÌüÏ¡·^þZþëDr3Àþ–ûÏ÷ßý’Û½þjÖ¾çsŸ¹9á'’ ÿ9žÖy—KÑ_ þð·<÷™ÿs~ñwdM¤uXçáÓÖ×PôW…ÿ\_ÏúØO¢&þð·ÜfûàéëjºÙ^•þ×öÿ.ÿî÷rŠ•?ïl~±wdnüm<½Ïµ\ªóÕàËýù¯ñË|‡æ¬´Îï$îÝ?9tÇU¬ü™š9pí‡1©˜ðá5ïÔ.€ÿ³ùóyü=ï¸ÔøF=ÿéÿŽÞûA|°‡à¿'oâºòç>ãËhþ<r8Žúüîõîü™±þÉâψåؽÿWù3bü“Ýÿ«>ðúœ¸ÿûg7þÌí¨âÏÐrôη]ù34þ©æ¿:ðÁšºðÇkwÿWÿy¿?róêñ´¸nýÏÈ]ùボΟûáï>êÎ_5ðA¯Ïÿ¼¾ã~¨}.x9TÇ©|ðô¹PÍqµ>>X›«úw‰Ç>tá§Ú>°ù@¥{·ìA)o—Ù~¨öEž²{UªòAö÷ö¢R·Û.~¸Û‡÷{+uŠî]¹»f÷·7…>»ŸÕ»Ûº> žµ“nœïös–êýOÓÍý{Æ«|ñ u÷?e”?öW†X¸úÈËZ@@@@@@@ÝøžËC—x^íœKr[1 }³Üÿ9BŽJ9ZDeç ²³ñB$È7=RVäþ¡                 ôTà×Ï}íþìùt÷îj—§uþ½J×>¹•“j|­ 篮âU÷|¹OÅ%«N®:笖ÅG½Î9D´O¢æ ªÿóïû„ï~jU›_]ÅÅZ÷‰£÷õù„bŸÀÊ%z¼—£w^¬zs«Es|ªçå=o.±½?ñ‰z=šWt½=çÍŽâú]h>êzóúv¬â®æ£®ïS³ÿ,ïW]5—¬úýIÚv¨âžÅ#{›º}GÃý“Õ?}‰®í,š»U¿éã×Tî7 î¾~÷k?²ÿßÜc¸¿|p+ÿé¹µÿ)ü£ú>J·Sêtç÷ؼŸrþÃ]˽ûù¿Ëÿ”|V?G·ü‡{Nßwë¸çr‡Þê<·Ö¯Îú¾Ö‡Uüá^˽:ÿá'¸÷à^Õÿ^þÖ{ ã×|–}þÃK–³øÃ½÷ìü‡ÿüáÞ“{VÿÃþ°ú ëþsû:ªûŸ•÷iß«˜â+ø÷ÎgµàÿHû³üÉÞsÏãܯõ ükõWïÑ\WëYó_õü·Õ]å£ÿÜÿ³ù$›§u=øÛx>Ýû¦ü¾—Oà¯åŸõ}-kßÃ?‡û”<°æ€5ÿ¯ß¼ýªšÿ»óÀÊŸ÷¿¨úû©.ü5èí/×Õyð‡¿ç>Àyã›Õ>ÞGäð´öÍ.×Õùð¿›?¿€¿çÀçZ߬æwÔ8ï9€4>ˆâj­ƒ4<»ÞÿÞýÿ»ùïÞ9bücÍmÕxò †ç”üç<¨áÝýÿ‘’¹¾Påùn]|ãƒ]Nªù»ü¹®ùGÅ/ª.>Xãh½÷uÿ>Qôý<øÚGQ}šU‡<ˆÍƒ,nQëDñ'¢ˆÔÔÁ19PC/nU|°çƒ8µ•¢}pËùPK-~u|`˃x=*ª|pZ.ô ¥ß…ÊÞß»t™§W¾× *LÍ…^tòv£öÁ?ä)Þ{¥,?tóEo*ù»ËöÁûzÙ÷‚|…g­Xíu^Ì¢Q·Û.>°îã)Oê½²•C×ñ³)ôÛ}WÎßí«Ÿ‚gí¨»ÎR{ÎÓtñÅÅîØi¶/îPõ¼§ŒòÉyÊðD(€(€(€(€(€(€(€(€Ÿ üº¥8Ax^íœKn1 D}³Üÿ9BŽö,2€-²øÕzÞx1"Å®W¤ÔcÀü                  œ¡ÀŸ_ŸuzŸñt÷Ué娮¿OÙYO¬rËŠ›¥Æs«Éâ•繊ïy²l>Õùö¨ôœ]«ùT牞'©æÑ¿GµswéæaÝï÷×û^ô÷¹dj+·r¨Zåj¯UñœìUWy­œªÖC¨¦ÒŸ¬Ï«øEóÖ¨:7kÏUž(—®ø¹¤r+[ñŠ~ÞÅ+{Ÿ\•çe‹rý.>›Ã®|óˆåT÷OW¾ÊQ{N–lî+ýNÿ|¹X%p·õû»_cªïÎâ~z«õï'¨Uw­ßOï¸çpù@ë¾þ(¸çr?…?Ük¸ßÂ_½Ý×?Ém;Fûþ~Ñç´Ñè[÷Úy?ýþÿ;ùý—û”ûÜ÷p‡ÿ^Ý£÷¶¬ø¾›Ýÿ;Ñ÷3üwÿ,ß“gyú~Fßï:ÿUþôkoº¦€Êýÿ;ùý†{÷üWûþgó‡{-¿hTŸÿð‡ÿ?¼>ˆúšx›ïªúßË›{¾W¶¯á¿G÷lŽj>øÃ?ÓÌý³ü”É^¹çqîïõ ü÷ꯞÛÖ¸l¾«|Ìÿ½~Zñ©þÜËßêcÖýì«j®Öüðïí+—êu^îÜûb>©æéÍÿOëùæåÒµþðWÞÿ­¾¿}]W«ûÐÿµý¯r銃?ü™ÿëÿ·§žc]}¬îCÿÓÿô?ýïê<¼-NË]q^î|ÿç;/º8ªûÀßÇS_*Ÿê8øÃŸû_ÝýoúÿmzÍïPç qÕ]Ëÿžs`ê<ðòç= ×/Z׿EÁ?—gôœË#kËÿYüWþ±Qµ¯‚ÿÝüÕ÷î{|cïlßJæÀž«yßõÞÿ»ùsÀ_ù>˜{@¯o|§º5ç@/Ï)çÿ»SðÁLø;Z‹€?ü¹ôý}ØzhݬG1fͤ©òç½ Æ7Åx”êë\cÍ/q’Z•?sÀÆÕê^^>ÈåiåþZ—GRËÿ»ùGÿNÀyóÖµuQ̃ÏÓæÖ÷ÄÌÍ7uËÌÐxžÞÿ̃îSîÿ«)œ ?ûi¥ÿ”ÏñAÍ\˜Â×Z>ÈõU÷iëðAަqõÖƒb>ðê=u}–n»/Nå©Ö…|ó@Õyz\¶ž:¦sŒÖ‡žñþ?Õ§Ï…¨®§ÅW̓÷¼Þïáw­?_v½·û![ÏSóuù`Úœ8•WuÝ»üÐ}Ÿ¨Öñôü»}ðÝþY÷…Óùt×?Õj]Ýú=m?U÷)qOã±ûy¦pµÖ±[¯[ö·òè^w‹þSŸ³›÷û~Su¹½®._Ü®óSž_õËSžŸç@@@@@@@@xWà/µ6Cx^íœÛm\1 DÝYúï"%¤„Àpˆ ¯)J| WÇ?þ DRsF¼w×€ßÞøA@@@@@@@@˜©ÀŸ_}[¿gžî¾®-Ž»ñû”Ô<ñ.¿Ó}šj¼nW§¼¢÷¿®Ò½'‹æ”•¯W¥×©žÅ';ïë¨=I6—ªüµªÍ­VÅc·ÎïŸ÷¼¿çÉí|—Cô>/Oïú\çeæ·šÏË-jýgë^[ñj]n©§Îÿ]ç„?ßÿdzÀšëÏâ]÷ᶺ™ìßsÃ_{¾À_›Oö<ÊâϽŸá+øÏà”5àÿ xç–¿Éû³¿£Ù{¹?ÖégÁ¿G÷n¿Gsäãþkù)‹ó³¼ðïå_Íûk=/ÿî9ø*õ»¹ïÎÿWÑ¿ëS¹óÞ󜀌Ž]÷w·® ÷ݹÏý?ó-üÏôÛ½w*ûà'5îÌÿZ¿Vo•yÿµ5x¿÷áýïÌÇð?ÓOõ^¯öÿ»ù?|2Ý«~gÝ÷~‡?s@ɼöú±Û ðïåßý9þZüŸ½7eÍ øÏàŸýùÁëÞï{|ÃèÑ]Åïð‡†¼óŸ¿ôø0ƒýÿ9ñA×Õç üµù¬rÜ]øg{à=?ÏMŸU°‡¿îÿ—WçÏç‚ܹQÅÿQ‡ç@.Oï{àþÌßTógäpôÞûì¿ÿX¾Ú}0býcqÊŽïú`×ïìûìŸl¾Vþ]þ̘9`ñ©ŠãƒžÞùVÅתÿ»ùŸ~.ày°çë^vÅ™{<§Îÿ¯>;åÏФ¹ßU´^ý=a_iíø`mhS<ï.˯2Ξ•?|ž ³èÅu›íƒ)ó!NÑ™™ª| ê‡™Ôòº®öC·/ò”œ¹Ë_ëîþ]oußlJuÝ«øáY«¼»ÿ?\±ÜJê~Xí/W¥{²¯ê­¶îBµ'Uãü¬ŸZU¨¦æ ˆh(Ðå ÓÓ…¥@–?¬ºÄg*°ê—™§£k@@@@@@@@[¿®Z8x^í›Ñqã0 DÓÙõßÅ•p%d2Žg.šØ)`± _~üa÷- Zv>>øC@@@@@@@@@ðQàߟG-Q¯>;£’H®WýêZ®rQ]§Ýý~ÙTgóìG$gdzúW¯ËQcý¨ÕÜ¢ò¯O*f‡Qz«ãüýþ¼ðê5Fu£¨y½ÊwÆqöýuÉÍí¬š÷,ÇÙus*­·JÍ}–WôºõH^Û‘Šw4¯èx×ÔZçªlîÑ|²ã­CöýNàþÐçè§ÕùgqÏîKUüUùGsWñPçY?ÜŸó«?ÿû÷§º÷wõÜuÉו?Üçú½ûùî1ܻ͸ÇrïÂî9ÜwáïrÎr­Ãõüw·ï]õv«Ëÿ]îÏõn:»Ö³W]ërá·ï]õu¯«š?ÜsÏ÷gþƒ­þg|²ß¯âOß{øþ²ûÜåû_úÞËoêþŸå_Õ«ç…¿W?ªý¦âOß{ú þž\Ts ›ÿlßó_ãKWþ*ÿïžþš>sõüáŸáÙû¾kŸ¬ZWû¯˜ðï1Wà߃SÖüq៵?â¾÷w4æ~¯yÿ^¼¢çü÷äÍýùïå§,ίâÂ߃¿š;ý¿7÷YþÑç™]ãUõû1ïèüß•Wô¾áï1£¹^ÿ=ù»pçþ_ã?ø×è~u.«®sñç¿?¿FwUŸåéÊŸßùÆú¶Ú£óþ±üŸs¢ÊðÏáy6ÿ»ÿïìþX7ç·¬ù@ÿÏñ¨òq´à¿7žÂÿKæ@DÏÿÙþçs`_à_£{Õyï˜7‹?s ‡¯à߃SÖ¼€?ü3=0û9€ó Æ—™ìÿ=냬¹G\ùGøkúyÔ×ZàƒQ>Ù×Ãß³/³¹WýN`ö>Ày0ǧêþ¿û\Äúþ±zªævTž*þÌßÁ߃CT?Æ©æÏ¨õŸ |PãƒÕøóù`ÌGnü™cüFï÷êßÜõÏ‹rýp—Oöú»ü¹¼÷O6¿¨øø gDñQÅÁ±>Pq‹Îƒb|ÍEÜóšWV¾(ìv^ÌâQŒÍƒ*NÙyñÁ5ds¨ŽíƒÕîÕ|Tù³|ÐÝ*ý]òàƒŸ÷.ê:²}Ðe.¨uwͧòƒ›/\yTÕ¥öÁ1ßÝïsG×WéÜ%oµ²çEÕuºø`´Ž³yP­k×ü£\¯ïª¿[Ý®|ÏêrÓqµzÎô¯~5½Ý÷SÍû˜ß]¯ÝêSûc7}»ï7ÚÝõ ~@@@@@@@@@ßøåÿ5cx^íœQrÛ0 Ds³Þÿ=Bɤžiœ¸A,¸ ^~ü!÷-@Êòäí?@@@@@@@@@@_þüúÌ-ú黲{gå9;îÞ*ï_ý,¯ìû÷+p ²¹eÅ»‡úu«ÌâR§N™3gªâ¤šçL*ºU©8슫SêŒÈ»¸Œæýý÷¹oõó Jù«鯾¾Êõêø|åzFTó|ÿ*'Õ}=iåe]Å]Åo5nž’½"©¹¯r©ß‹Z<[xj÷쫸¢=FÂýgîô 8Ÿ¥Š{U_®šg^Yïpÿ½ŸÚÿ³¹WÕßîy¼«yœÜçêý”ú‡û÷®ç?¸çp¿;ÿÝû®ËüãÖ㎬ºwÑÝ%º¯³€{n¿ïrþƒ»–»ûþ¿Êߥ¿ºçáÖÿá^S÷nõ¿Êý1Þ½ÞÜòs©ÿUþnºvÉg7¸×ö{·ó?üïÉî{¹ï>ÿEùwÙW»äY½ÿG¹s¾×ô‹.ü»ÔS·<ᯩ«.>¨âíû]tìš'ü©ÿ Pÿž>S³‡»'÷ªçøÃÿCYt=OuË[ÕÿgyóýΞ>ÿ=º»ô øÃ_áú_)ØsÞûþ\ú½ú÷Ô}ºW=ÿÃþôúÿlpÝOÍËåÜÇ÷>5û…Š÷sÜÙº‡¿–÷Ç<ð×òœÝàïÅc–_ôþjîÔ¿—ÏàïÅ#ZÇ«ãª}Àþïå;ø{ñX­çèø*Pÿ=ü¦òü{ðçýO/NѾ?—ݨÿ^¾Êæý`äS®k|åŸ÷¾£ºÿÝG\ª®Ãþ pìá+ûcâoÀß›úø«=ðŸ}ÀÓgìáïû{pø{Ö¥zßW½ÿù‰}ÀËo#^Ù×áÎ>çìú¾>àÑ®òʾ/ÊŸ÷„¹¾Éæ:äòœ}n˜å•}?üïÍÿá'|°ÇÙõ·ÊŸsAÌ?Q^ªq«>˜Ýÿî~¿Šãj\|«çY?¯rR_åÏ~pÍ?*~YqñÁ5޳u¿ëýOÔø@ãƒ(]ãðA®vqŒÎ›ÅŸóA”€Ç8|Óˆ+ï5Ä|àEq=›lœ~NXWÜ3>¸Ö<éåe¥òÁ)}!OiïHøàç~àMM—~ÐiÛ)²ÚîûD'VÊ\«|ðx5?õ_Aÿû®~ˆæµGÅsfêî2î+qáz5ÕÎÏâ*êûÎWÞ{…Õ¼ŸçóVç¾ÙUùâ¾ Ÿ±òUŸœ¡«@@@@@@@@@øªÀ;5W?{x^íœ]nÜ0 „s³Þÿ=BPé"APÇú’CëËË>Ø¢¨ù†”ÖÆæí?@@@@@@@@@@~ üùõ‘óÝg¿•™ñÇÕëgªé·êU~»ãü”xvF»¼Ô㟭vÝêÔœ¢âÕ)ô¬™£øDÇ}…¼ÕDsÉŠŸ§XxìÎóûß÷¾ÑÏÞTâ²ßå ?Êsö¾8{FVs7ËMuOJº¬Gù¨îSqSÅÑ)Ù+’Šç]§¨8½¨íg{Çk÷z§¨¸ûŠöˆ°Ëõj|—¬¸=è­g ÷í®ü´®¬÷H5÷¬z̞Ǜâ|vpÿ¹Þ¿ûk^aÏpŸãþò'Íñ¬Tܳû®Ë|ãJ{Ý ÷µzïÞÿá®áÞ­ÿÃ]˽ ¸Çp?…¿Ë9Ë5¯SÝg6Ô}lÝ»Ö?Üs¸?•¿kŸuÍË¥ÿïÖ½«¾îyÁ?·ßºù¡š?u_ë¿®üÝê¨k>Uü©ûÚº¯>ÿ¯òïZg®yg×ÿ*÷×8W»æÕ…W}Ýó†¿Ç>\å“,þ«}¿J—Sæ…?õŸáêßÓgÑìáîÉ=ëû?üáÿ®À¬N9U¯“þï]ŸÑþˆâ?[ï<ß«ñ!ükt®ëÑøð‡„èÿ=|Á~å¼Ïþ_ãþ£û÷i}¢æOß×ò‰ö;ü{ñRûþðWz€þßËOJöœû¯ÿÏšºo«âÁ¿W½ª¸G½ÿ¥ÿ÷òõß‹õ6/øÃÿ]•Ôýÿoö ZqæêÃ…?ï温|ÿÝUüvãÀþ˜Ýÿéÿ5>Œ`Ïs`Ýù|·¿ßâ¿ú=€>ÛàŸ«÷]=f_‡?ü#=À9ÐÛ_‘ì¿ÆÆž>€¿'—¬süáŸåž ø=Èdø¯žy.³Oe×?Ïc8®ž»ñ§hýSÅŸ> åØ­þwùÓ4þ©®| áصþáÅ{öƒ5¹ôUÀs>pãæø­îûQ¿ÿTû‰ç…±~PóRÇÛåÏ~ð³Ô¼¢âი>Å+*.>Ðú ŠSt\| ñA4§èøø`ÏÑ|²â«|pÚy1‹OÖ<ø`®dqÉžŒù ›Kö|jø?¨âQ5o”ºö…*ÕóFû ‹ª9TÏŸåW?Tëï6¶ª}ᦿK>U>¸šw÷=ÿÕx½ÝópóƒÊ'æ×ÅwyºêÛ-¯;]¯wÓ¹K¾®¼¿çÕEϧäéæ‹§èÚ}U¾è®Û)ùGùãýN[ç¨_NÓ…õ¢                À üá2Ùx^íœQn1 Ds³Þÿ=BP©ÄÈÚZŠC ¥—Ÿ|d%Qóf(ÙIüñÁ                      €—~}Ö3ûÝkWT3Ëóîx_£À]Nªç×ìþœUUܲæ=‡DÍN³¸TÍS£Ê¾«TqR­³/ÍÎTVÍ«QiŸYWqy·îïÿ¯ÿf¿ïC*w'ïôWÿ|–ëèø\ÕúΦæy5ÿ('Õs}‰åT^Å]ÅovÞûÌr:ïg¿ô!7W©šûlWŸSÕ4Ü?]ùËŸ`¬B÷U9U­S×wÜ_ç}×ó?›»*onóú&y¬2¸ßËû.ù‡û÷‡ÆRæóÜs¸ŸÎßí^UO²_W’•ûU:»®ëÎî¹ý¾Ûýo–¿kî\êrÍ?ܵ¹w½ÿÍrŒwÉ—{nùŸåï®·[}.üá^Óï]ïð?“?Ü×pw¹ÿEù»£]ëYuþG¹s¿ÏíÝøwÍ™kÝÕüÉ}n~g}Õ…ÿì>ÿ³ïàï•ÇjŸVñ§ï{úÌuN[þž¹¬ò¡š?}ßÛ_®ü«üú:ð÷Χڟð‡¿ÒÑó_í{æWR.-\jû‘Êä¾–c47ðïÁ)Ê÷Ý8øÃ_áú_)Øÿ›þðøàÝyÅÏs}EþsõìæOøÃ_áÎÿ¾R°œûü]÷¿Àî.÷øÃ_áÎÿ¾R°çü¿þ¼e—¾¯þÿOòOþ#}À-»×Cÿï‘S•Uüórxû þÞ|T¹WßÿÈ_‘ÿœT}À•?¿¨ñ¥š?ç@ Çh€¿7Ÿ(×Ñqîü9´þ¬âÏ9 å8š÷ççàïÉ%Êóî¸.ü94>­æÏ9 áx7÷Uïÿ]ù+ú{ú@®VåŸ>˱[þgùÓrü³:ÿø ‡c×üÃþ_{÷ÂZ?¸ôÿ¬>À½àžÜøgù zž6Ε?>¸—ã¨owçÏyðÚGîü³ú>øÙ]øãÍyp*úA7òßë}Ÿày|ôþÔ}\oÄ?gîÊ?ÝyÞ­¿;ÿì{ÁiçÂ.üU>ØÝ»ñÇ÷^'ìÊ_íƒ]úÂîüñÁïÿeù4ûuc÷×Yºvçt?tå–]w•ÜÞwÊÖq—ùVù¡ú^¹ /Õ>Vû@}¿Pé¶ë¼®~ˆÖµ+§ª}EuwW¥Ó)ë¸p­ã.«ö9ÊaÕs«t9}ÝU¼Ÿ×=ƒÛþ«}á¶êS Ë'c«ñ                    @þHÑŽx^íœ[N,1 DÙûßK¸K@hÔÓmÇ•”Ó‡>&qœ:e'ÄÛ_(€(€(€(€(€(€(€(€(€(€(€Þ ü{äýî½+²‹òŒŽGáµ DyU_»ûû¬^Í­*Þ}ÌÝiuœ¹ªì»šš“*þ¾D´;Sñ˜W«Ò>Ñgs‰®÷ñÿç¿è÷}Õî$ª¿j|”gt|­jý£©8žÅr«ßŸØØθT^Å­*Θz}gWs=ŠWÅI§/Á\æjî*Nª¸9ûÍRqWq™·ÉXÆpèu䧘š}FWsŸU³×éCôZ¦p]ïýuMUÿQpqúÀŸìë ážãÞ÷Ùç­ÛzÝêîcõÞõü‡{-÷nýþ÷äw w÷ú‡»–ûîüÝîÙ®ù¸ÝÿGëÞUg×¼à?§ÏÂ_ûŽçª¯{^.õŸíûîúºç·š–ûsž»¾îùuåï®k—üVñ§î=îÝøw©«.yÎæOÝ{Ôýª÷¿,ÿ.õÔ-Ï.õßM×.ùÎ⟭{~ÎÓžîü»ÔQ×<ᯭ/w_ÀþJdÏ}÷ºÙ%?%û¯Øð÷î/ð÷æ£î3ð‡¿Òôo©ØÃÝ›»úýþðÏÜÿÕ÷âÿö%ý¿Gª| ø+<ÀùßÃW ö™sŸßó®ñ ü×è®:Ï£qá…8ÿ{øJÁžóÿøÿmFû³z<ü{Ô©Êð‡¿Âœÿ=|¥`ÿ3&>ðöü½ù¨Î}õ¢þ½ýEý{ó¡þïͧ;Îoÿªû?üáÏ{°ï{°{ýówÚþ1‹?瀖cöžO.YžÑy]øsh|:›?瀆c´îg½ÿù+û.L¨õϪú§ÔrìVÿ£üé5þY]ÿ£>Èúžy.äyp¨©ç¨¯½\€¢üFÇÃMÝr«šïÆô>À½0æç]ùãƒk>på_ÕðÁk¸óÇ×ê8{€¿Vß,—Yóºð§h|Ú?>¨õAWþÕ>¸ë=±;|0Övár>Ø?>ˆù`Wþ*ìvOØ?>Øãý¯Ê§£gp6Ö»MÕ:Uºv‹sƱêó*Nª8ݸUç[ÅùjÇlÜj=»Ç»Ê±z\–ßè¼î¼TùWóÍÆå{6_¥ßnq³üfÍ;ã|ôùnœfígWõ:³ôÚ}5'Uüݹ¬ÚŸŠWuÜUúÜmÝjnUñîÆÁm¿U³qÜô Ÿß d¹^‡Þ{(p•÷ßq{ìž]                    |+ð éŠìx^íœQN$1 D¹÷¿GØ#¬Ì­éÄ.»’<~šNìÔ+;éîooü                      ì¡À¿÷¯u<~ﱪsVqåýûåÖXi”çèø5TÙ'ËQ>êë÷QÖs%j~Ñù=U[7«(êñë*í‘y5¯ìx*®“E¶þÕó}|ßÿ=~¯£|O¦Õ|^Å»ò‹þÝ£ªÔWÔŸG¹ÞïO¢&C5Ïßæ¿ËIu]º¾Qª¸«øEçõ%£ÍLÍ=Ê¥j¼VeŸÙáýÅâ´ó?ÜŸsßýþOŽª/WÅñéÐ9™Àýïzßµÿgs¯ª¿î89U×7 ÜÇê}·úÏâß]‡]ñû*7_õü÷î«ñ‡{.÷Óøwí¯îqc»°~t´îÝõïÎOOp.B”ûc|·¾îñçèèGEù»ëî’ŸžäX¸kÎy¿ùmŒŽþjøŸÉîµÜÝîÿfù»ì£«æ¡ïèG˜åÎù>§_¬ÊÕzsË»‹?uŸS¿Q?­Æ?º^Æÿô]5êÞ£î»Îÿð‡ÿ§£> ok|³Jÿ‡ÿÚüGëýz=üÏäw ÷êóßlýÃþŸ àÔç?ê^Ã-«àïÍ'‹s×÷?¨oQÿÞ|¨ÿ³ùÀþÊûúÿÙþ‚?üàÜ¿†¯ìgÞïò}οÀ¿Gwõ¹þîüð‡¿Êìþï­”ìáî¼÷êß›ÏÝsÜìuð‡¿Òôo)ÙsþãüGýŸ]ÿþ‚<} îÿð÷äÎ÷ÿ½¹ÌÞÏŽs¯Þ j}ZÅŸ}@Ëq´î«û?üáyÀ> ñÏ*ýþ{ðgÐp\eÿò§äú§ºÿ_ãñ\0—çhX•?} Ç7Ýü£ûÁ¨ß¹þ§oVçOˆõþÑ>€æ|ÿ9ÝvÙGÜøÓjý¸+öƒ{>råO¸Ç/º¹óÇZœÂŸýà¹VáŸÕðçóŸQξ7àÿŠïÁŸ~s.­;·ë³úÀ©û‚ÏÙ|ðÁ\?˜ÕÛu\¶vï ®£yáƒ{ý ª³ûx•vé îü²òÃ{<ÿÁ÷úúÝ÷Yz®:º/¸ï«rËλÊnϳuÜe¾.?T÷‹]x©ÖÑíu¿Pé¶ë¼n~Íçz.Ü•SÕºFõw»¾J§Sâ¸ñ}•Ï)\ºÖùJÿîÏ»t95n7ïküS9¸®»Ú®:×s²ýÎg)@ÿ?‹7«E@@@@@@@@8Qÿ…Û5˜x^í›ANd1 D¹ÙÜÿsŽ0BÐÒ¨ECb»ÛylX8N½²ý·x{ãPPPPPPPPPPPP žï>s²þ®w#2òðÜõjŸU`—Wôú³·¿çôhnQñî!{Ó(>ê8¹ªÌ=MÍI.íÍT<²ãjUš=›Ëîy¿ÞwÏ!{“]ýUëwyî®U­4Çßâîr‹ZߟXÌ ~ãý÷(~Þ81êõ‹ÍóUîðÑ/kN«Î©WÙ?gU÷*=»ÅíÂîš~U?Ü5Ü»Ì/ÿný8;ߪõïåþØŸ­g·ó¦òïÆáT¾Õø{ëþ”Ž]Ï…¿öùªº/ªð§îÏø°;ÿêõU=¿Óü©û3u_åýßÊ¿z]uÉïTý[¹ó^Û/à«g—º?Ýÿ­õßMßêùf׿•;}_Ó§ºð¯^G]󃿦®ºø!‹?}¿¦ÏªóïRG]ó„ͺÌòüáŸáëüϪƒ[ÏQ³·rç}?§/Uåk=fßþ9u–Íuõ<øÃ_éëü_õ/ë|þU²ÿˆ µ¿á_›üïæø( òýÿnÁþJðü_Û_Jö¼ÿéævÔóük×gçWq६óŸïs|©dÿl«ÔýïöøðÏ©³ª>ƒ?ü3<@ÿ¯é³ öžÏxÔú&‹ÿãú€–çîsükñØåç]ß…?s@ãÓlþÌ GkèÆŸ>ëøÇêi­ÃSûNñgÔð]WþÌÿœæOˆáhÝùÓ|þ©ÂßÛðÍð·éfí·ÕöUãOÈõãTþ̃5UåOXãç'ÓùÓ~öQuþQ}|ïƒ.üñftãb}p;ÿÛçBWþÑ}àVtç|ó` |`óÁ4þ*LSùヵ~0¿ÚÝûÂ-üñÁŒÏÿ¢üjý?´Ý}ÞïgÔû£ôìg—«w½šëjüîÜ¢ó÷rµî_å½.Z¿)ñ¬£÷Eó~Ž7…—úÑ\£ãY}¢Ömjüh~§âMå“}¯Sü¼çfëtËy^.YûoáqúžYY;…U(€(€(€(€(€(€(€(€(€(€(€=ø~Ãéx^íœ[N\1ÙYö¿‹,!KˆÐ0R2b°Ýïn?|pݶ«ŽsA||ð@€ @€ @€ ÐÀŸ_1¿ûÞoFwŽxåQúó;iÖ›µÔŸ¶]=³G¤õeÝ~6íüÙYû²®—OhÖ¬ýx×›E?~6Þ~¼ëÇëÝ£·èú½mø>ÚÇn¿¿>×k¿ûìÙî¯ç´^wÛ÷´c?j/«º»ž¼ž³'Ù«âÊõϽäÄk´Ä»Ì{wÿx×yïêï6Þo÷¿{?šþ\S|= «u?ÝçéüÖäsŸÀ»í~ÿš\»ëÞµþO×Ãmϯ ä<¡õ>í=­W.sì®{Õú÷â5­îÚDìx÷=﫟ÿø¿Ó?Þc½W{ÿƒÿ;ýã=Ç{•õÿ;ýã=×{öúÇ?þ? œæ`Úû—ìùľÝ9÷ýšl^ÓúÇ}8+W]ügñ™Þ/þYÿ8½çqîÇä2½äžü˜~þfϯúúÏæ3½üÇì³Us„üGd@zÿ¯ºn¦ŒËÛ½Ô;¿³/á?†sÕýÿø÷Ìûí|yºç½_ý÷Wø¯½>½ï øÇ¿g8ÿkçËÓý¿µ¥9ðÞÿn¯ÿÚëÓ;ŸøÇDØÿkæ,½æ=€÷þw{ýêþù=ï¾_¾Õ÷—(ÿÏ~¸ÔÊþkùˆÞ/ºøçà“Süûp^ÇÒþ¢ýs¨•·nþ9lóƒ[žÒ}8«]–í9À>`“[üÛpÌZ¿Ú~³ý³äæÿ¹üµëWÛ¾Šöœâ?‡»vÝZµ¯æŸ} 6Sýóùp/GUý³ìùÓžÓý³üœ£êþ­örð}ºø'>çÁ­þÙº™ÿ¼Ò¿#|×N{êÚ¾w ôÿOþöÿ3ÚÝ¿õ½à¶saŠr »NóOÎr€ÿÓûd×ûÞ븧úשßÝç»çàÿ^çB÷Ï·ùÊA—Ï·úÎAÕ<Üî?+Uò€ÿï ìÞÿ¼ž‹ºWâÿg^~µu­òÿ3ZoÕÚŸÍž§£Þ+D壶¢¼Yõc;{ªuÛ0KÀjÝZÕ‰=½½#`åó´Fz8õºû|Ù3J)U¤ui@€ @€ @€@E~­šux^íœKrÝ0 }³Üÿ9BŽJ9oãròD ÈÎÆ ‹$Ô=€ä_>>ø@€ @€ @€ xøõ㳞Ý^wC5/»>W×A¼—Àª¯ìë{ïþžÓ³½eíwífù©ÞGKåÜÓª=Uí®‘Ú;«ò¡Þ·–Ò9»«½ìž÷óï׃O?žc¨æNv=d¯{êsõºjswÍö¶ºßª¿èõsMåV¾ê)z}Ô[Öú\Šóv‹z|º>ËWö>óŒÅ*~ê+z]¶§ªýb4笎ú|·¾ÊOõ¾s îUúÎ[ôóÕ~ª÷ߣê¿*êõ_ë«}¨÷÷7¹V!Þ?y=ÍÑ]ß«ñ¾æý•_£k•eùÚ7§\·FÙïj¼ïõýôþÇ{ÌûTÿxÏñŽÿ\ŽSßüžèßWDß×äÕÝ?Þk¼O™ÿQÿS粪n×þz­WqœzΩþ§úP×íæ?Ú÷j~ÓÏÃíû•{>\üÓ÷=9Äw—¹Ð퟾ïÍþ{ùwÏ©þ»¹r~—æ¾ÇÜÁ¿‡‡®y2ͧSÏUûgî{Íü{ùPÏ™)þÕ\n9ÿô¿2»Ïÿ[úQ}Ÿ*÷»Þù=ŽÚù„ÿZ¾ê~^=ÏÝÿêýpýZžñ¿Æë´|áÿŠ ì¾ÿÖon÷£pÿç ü{Îü{zQÍ üã_‘æ¿gÎîyþ?ÿÿ˜Ts_ý÷Ÿô?ý¿3ÔýpÛyÌϾTåÐÝ??ÿ«Í'þkùªúx÷•ÿ×9¼zå ÿ^>vûxwþñ¯ÌÀîüç=°&§J÷;_ÿÍËîœcÝ÷ùQûç=°¦wó=Í?ÏÜüà?—çnv­ëòÏsÀ#wSýóÈÉþs8vÍïè¹Ýþ£Ïæ@,¿øñ‹ö_÷zÿÌž⿇{wß«ÿo5güœ@“ËU/ªë£þy/|–•ÏÝs¢9p™³®uìzQ­‹úgü¨|÷½!Ÿ“¨<¨âŒ|®>÷¡V?ªÊ×jœU»ãë›Òœ`Õ“jü®¿Ýùzu£¨<ÎÆÙõ¥ž_×ÜÞÎg}íŽSûRÇÛ£Xoö®ÏÑ|µïxõ Úv<òf}îíÇ;¾fYV¯£yÞ^¢â×1¹¶Ó‘?ëó(/Që¬QÍ?Úêõݼ(§ÖÉotn‡xpZÍ£9ºyGáÝæý™'yÍÎíLåµnºŒŸ£œoÞ÷ê¾jýã]㽚•÷gœ.ý{÷ù:ûÿw¤ò¿Ë«Ûüìþñ®í÷ßó·»ÿnõª>OVÿÔ½oÝg¿ÿíúW×I×xÙê×;÷ûµ¾ÑÍ×:õ:WÿÔýZݪò¡‹ÛâàÿLÝeɳÓþwû~ŽU÷êÿdXë¿j½eÛ÷)÷Vïü|¯íWø×òÌVߣýàÿ'rÀÚÿGùÌóµ|>áþsMü¯yòÊëhÿVïÜû|òÿ>\½êUÿøÌú®|‹tϽoýÿóÔýþôç?­õïÍáÖøÔ®~‡øÇdÐÿså[¤{îÜÿ¨êßÒ¢ïE·¬W¥ÿóûŸ¾®Uúþñ™Öûýß'O#Ý¿®e̓*}µÊ>ñïSWøÿ9³¬õÏ{@›¯Õêÿøÿ$P¥¿fßç©ú®k}dçZeUýóмð¯áX¥ÞOþë]¾Yßô½ü=]ÿ»÷üãÿõïÉUûð©}g©úÀ^[ó§›Þky”Í?}`ÍŸµîŸóºú§ÌåQVÿô9]ë_åŸ>ðse¯òÀ·ÜæŸ~ðo>UñOðéÕü“Ú<¨êŸ<Ðäþn½T÷¯î·åAÿäí}ÐÍ¿Wtí ]ý“sý »ò ÇïUyºû9ÃÑüÝ¿ÇDÏWq­gäQõ<ÚçêzÕ¼©÷«ò<gÕ÷x5Ϫñfý©ÇyûůêË{ßjϳñF¾ÔϽ9V?ë-jþÏdT”_õ:£|9C³þªjO§âÕ7‘ã§üí®›ƒ^¿]ìz‰šß|ÎEù\]''­{vµêK=þÒµNªöü.^-*ìö;Ý<( @€ @€ @€ ÐÀ_½ÿŒvx^íœkn\! F³³î]B—PEéHít&€ñçœþ‰ªÃ=Ç6܉”þA€ @€ @€ @€@_¿~|íýñ³ï“ܹóg»ÿ¿“bݧÞõ¹:¿.‰³w¶êI5þlÊùO§òæ7ŸÐY;ðòç,úñOåIµN<±3VTùˆŽ{† ýSD{Y]ïçŸ÷úÕŸzr=WX寿êuv|O;º]«=ŽâÏzó§#Ù+òÈ‹÷ç^þvãô²ä¿[o¯ïâízRÍ÷'Ú#¢Ú»Ê—wܶöw‰ï/†Ïù³O¶v¼¿öþȃÚööw§òï݇³âí®ÁÛ{–õº5íÙw…÷ïûý©ç?Þ×¼ŸrþãÝæ½»¼ïyÇ¿?õýLß~ÓÊ™éU÷j®]âçX´¯Šß¾e7;ï¾Þ»œÿx×x¿Å—s8kŸ±]|~5ê^[÷Õë×V=u[w¾"cFîzÌïæ!k¿1VçWÙõŸÅ±ëºófbFâ?æÜ¯vþã=Ö;þsxW;'bºúxê?'Çf´#ðžã½JÿÇ?þ? Xó ÚyÚm?Úî>ŽŽ÷;ëßêï÷|óe\¡šø÷õh=w4vÇQñ˽ϚçÌ{oãJÕŒ°Ö?}û†Æî8*þ}=ZëblJ3ÿøçüÿÿï1XëØ:OSÝï£Zëž÷~M¿À¿†«µ£çáÿ‘9@ÿ¯•o‘îÿ^ËšÑýñôõð_«£ó ÿøÏÈú¼ËpoùÞ‡÷M¾tóOøæþ}yFßßv×Ã?þ³r€{À}¿ÿyÎ5ë{÷Ÿ¾•Yû;õüؽÿÜ>?»þësøÔój>ã?‡ûª'Õxüã¿JpŒ¿ÏTrüïÞy/\;ϪÕ?ïkþvï…§ú§ÌåQUÿô9§Ö¿—úÀ÷yT½þÉmèâŸ<Ðäþ5\wÏå¨ùÝüÓ|óõvÿ·ß»ú÷î·æAwÿäÁÞypŠòÀ–§ù'ÖòàTÿª<8힀ÿ/Öß;G½§«Ö9Ý¿ºtï·ø~Nk½ÏÎSÕ«wÜ[ýGõ…êýávÿÑyP-ðÿ/Ùþ®çÝßGñðÿš€Ê¯5îÈ£õsüϰz‹š‡ÿ9^£¢¼ª×ñâqkµuü[½©ž[íË;¾Šq÷¾Wööü.žrDù­“óô¬úŽÀÈ—÷ç˜èEÿ½|UÛísþTÛû @€ @€ @€,~…$Òx^íœAr#! Es³¹ÿ-æ9Â,z\»b7 }‰—MÞûÜ•Ê×?€ @€ @€ @€ høþsÍgö·Ö*˜Í+YŸ³í!®A`Ö›W{ÕŸ3 /o^ãœC>g¥^ž¢ÆÉ¡Ò÷©Qž¢ÆíkbÏÊ¢¼ìw¥>OÙåe×sú˜‰]É.³Ïùûÿ{¿õw,µº£Ïzˆjoõ:Ú¯®¡˜™Gy¼wÔ—w»ŠõF½óãý¹·GëxõLùÌØÛçÝxV?Ñý|hÖåΓ×çÑ޼Ưcnm¦^^ßãåc÷8kTõ{ãýrô.Wúm3Œò¾{F?ÏFW¿—·ÿhYã뛜›!Þ?×ûלÍÑÕm÷9ïè›ÞmÞñq{ä'ëüÍ~îØ.Ókåµï³ùg?_Ïìçá}­ÞW¿ÿáÿLÿx÷õ^íþ·ê?ûœU}¾úù¿êýôûý]îºû¿[ÿ韫úgßÇœ÷Uîÿ«þOß×£ëWÛÿ«Þ9ïçêF7ÿ£¹§šùç÷ñÖ:€×šûßê›z?ç[õþ‡ÿ5Öº§r àÿLÿxÏñ®òþÿøÿùw8³y°ž{ôÓ8ùg}¿¶ÇãZýÈNÕ?Þ×¼gŸÿVï|ß÷ñŽ_ŽUëQVýgÿkäÿ²êþñŸ‘ê¿Fî2ܯ¼ïáþï›üûòÌ:Ç­ÏÅ?þ32Àù¯‘» ÷œÿïÿ“µŽ[ûá_cZý­öÃ?þ32Àù¯‘» ÷?ŸiÍÁjÝ£¶ùëùVÿ¼ò©Ù)À¿Gk=Ã.«7¯~øÇv¸ä½RpüsÌ9‡Töÿcä`oð¿—·×½Ýkœnþy/4—g5ÿœsþVë@WÿÔ±©ú§Œùëºÿ½üS>çH}ÿ{å`uŸtíŠêÀïu Š¯:@žs€ÿ=÷,Õó£šê€o^«ú'>9ÀÿEàÔ{AuÿÞuà´tñOlçA7ÿä`.ø>ÿïþþDõ{œu^]ýGÕnÿ´»rðù<8Åÿ®TûþpšÿÝ9PÏéþ_×}wïóþÜz_óî‡ÿgÞžGÇóö::þ'0ê-ºÝ¨Gk;üˆölßê=ûÿ¿Q×keõ¤ÚOp­©zW-Úú³å®ÒNŸhíªx~7ÚtëÍ^-õöœqV.zÒ쳪è\ô!uöJ¬99›«‡ @€ @€ @€ ÐÀ?'~¾?x^íëm1 ÝYúï"%¤„ Ø3²·’Hê#©É9‰”f¨Ç®møë‹€ @€ @€ @€ @:¿~\¹­_u3 ó'V¯£ý± %0ê)ªvöçdòg{޽3µzÙÕ/•¾ÙvùòÎÓ×Hì̼=¨âÅRê]åÉš÷çßçÅï_ûò‘•{Tÿ;Ÿ³ÿïK«O´(o£qg=®¶ïcÌg&£~¼Ú­zóêçC­n/£q¼¼yÅ©kÎ6òQ_Öv^ž¢âØ(Öëmõ9Ú?Ê—wÜz×F<êmµ·—]ñÖhÖéµês´ß.OQyꘜ騿ÙvQTqç¨Öi=ëõ©½ÊOtÞ:FÇFúäqöóhþêøcTó·šõúÔ^íeWþüf?ðÉãì绸gɃÿ‹@»ÇQÕÿ캾k¿›w¶|Õü{yÅÉæc÷xNõ¿›sÖ|Uü{­û¬TãÂ?÷¿Ì5Àº­ÏÌîÿŒÍê_µ¯VɛտÕ;÷û±}£«ÿ*ëO=Nü­µ§¨üÙü[÷ý(N]ãâŸõŸ©Xÿ{ë1‹{¼ïõþ:Ïð¯ážå>üg¨öMªÝã]ã=Ëùü[¾Ï“åUuìÿÚõ§®üã_YœÿÚúSºçÜ×ÿÞþµëóÿâ?{¨¹uÉÏúgý+k`vÝós}¾õªt¿²ïãÿÿÖM—sX5ªëŸ}ÀgÀ¿GÕúµæÅ?þ•5°zÿgÿ÷©[¥{Ëýÿ=ü¿ê}ÀÇçì}@½þñ¯ñžåç¿ðîºïgÙÿÙ4ûþ5ÜgïiQíñÿl5À}`ß} £{üãßú>ˆ÷ƒcçZÖõÏóÀ˜?ë½°»öÏu”Ý¿×>@ü¿ð¿gŸµîÓQý«øgˆ©ÓSýsT3ÿ>^ž}öƒÚU0ÿ{cü ÷ºÁÿEàÔó ºï{áiuÐÅ?u°vèæŸ:˜«ü¿ŸÿOÏQïaTq»úھׇʛWÞîþ©ƒÏçÁ)þwÕAµç‡ÓüS½ÞÿXë÷é¾çý¹×¹íÇʯ[oßOñ¼<®ÆéæÏk>OÞ¢?_õ9ÛÏ‹W×8ÑžWãÏz¾kßÕ[Ô¼V}©ûáß·"Ô>½òûR97š—ÝqÎ5;óÝWóÅR úî÷M³u€!-Y_Þíµ³'ûoÏwñ0P“€W}Ôœ=£† @€ @€ @€ @à"ðÂ=Ox^í™Áq1 •™óÏÂ!8ו¤‡U–€ä¶?~, `»‡¼;ûí?€ @€ @€ @€ @ó üùõ>ãwÏ&üÉßÊïê9tgXùÊ~>ëíŸ3M¶Çh½çïyÓ¨—ª}=TîíZå-«Ï½&jß,ËGuZJ÷t«ö¤êw훨ø«êþþøÝ¿ú[Kíüê*?Ѻ+ŸÞççÒ¼AÔOÖ>¯Çèz ½óªfyóÖ‰zËÚwž©Ü‰½¾v×gy˪“Kóœj»­û³<©êœc,gR«·è:•'Uݪó«D}Z÷©ü¨ëÎ7·7¡Õ_tÚºþݹ»£>WûÔ>ªëÏ5¸7ÙÊ£÷yµ—ª~{”çíöz]­¯òÐÕgžÁØD+Þç]>ªûÆhÏÙåõºZ_Í¿»ß“±IV>­Ï»=tõQïßeõj]×Å¿»o¿ÉØV¯«uÝü»ûÇè÷íZù´>ïæ>¥ŸI_g«×Õº)ܧÌá³Ð·zåÕú| ÷)sôµu¶z]­›Â{Ú6 }«V^­Ï§qŸ2OŸY[g«ßïÖMáe®z³¶Ž»þ§ð>‡ÍFý*ü¿3Wç§ÞìÏw½sïûrs›õy¹­>þ}çÿšÄpï÷äPcÓ_u×ÿmç²ê}ü¦4;ðÏùˆæ ê¼ÜÖGsšýU£Þù½·woøMivàÏcô^ÒØôWú¾7ûüŽ;¢Þ¹÷sî …SOMüçxŒÞgWеøÇ?¿ûôÿÏ÷Ýý 8ÓžšœÎ?çŸó½¢ß{Øç¹¥uk£Þùý—ó¹¡3ë«Íçx/>KºÕQÿÜøàˆå@w¢}•9ÿ1»¹÷YÒ­Æ?þùw€úÏ1݉öUÞ=ÿ|ŒÝ>KúÕ»9Øý<|Ú~½Q_üÇÎq4·>;úÕ»þùðåGo4Öa7Ñóð´}1;ú]ø÷ãhnõ&cvýó9`ËOÌNÝ®ÝDÏÅSöÕ™ŒuÚõÏ=ðó=³R· ÿ¶{ µR_ørPo¨¦#9°å ÆF}—lÿ·~?¨7SÛQ•ƒ[òPk£¯9øÿçAŸ‘žÎäàßôXèïªÎÁ)Ÿý&z'¨ÊÁÔ<ôÒŸÓ½:Sò0ÇÀ¬IºòP‹YÔçMÓƒ¯ý³ÿ¿iñ™MËÁjkNfÒž?ÕŠÿ)Ïç“>cÂS|ó ºçLyZÎ!{æ¤Óóp&Õó§ž’‹óIÞõÕ¹¸‹Þ½o£ÊŽÄx³Un @€ @€ @€ @€ÀÉþÆyíjx^íœAv#! s³Üÿ9BŽ—çx?ÛH$¾ f“…ˆ*¡¦™ùøà @€ @€ @€ @€  Aàûó–‡÷§FödñŠ€×§7ò¼Þ¢â5vNQÞ¢æ9‡üšFyÊšg •}WÍò”5ï¾&jv–å¥jÞJû­Rå'{ýÌäì(ÛCÔü_ßXæÐÚgÖ(/³óX}zãö1³“YO³ã½þfãc¨õŸeÖÛÕñ³þfÇ÷77·ƒ«Þ®Ž›õ=~Ž^ßÑWýyÇEûŠž¯¯Ák™{ýyã£ýdÏwb¿Q^ÞølOYó÷3éËØëÑŸå¥j^Í>Ñ^Öø*/Uëô1êËÔêÓWå£zUýh«Ok\µêõôÚ2´ú´ÆU{Xµž®~”Õë(n•‡Uëê›}Ÿáȧ÷óUV­ÛÕ¿×ë(~ÿÕëžî5ÿÕëwó?:ÇÖÏWsWYÿ7*>ªóèâßz®GqÕ|Õ×Ã?ç_¹FçÙú¹ú9\•Ÿ²ûßܬ~Gq«øª¯»»uþ«óSõ?:ÏÖÏWóU_WÿêÜUòÃ?÷¥°öõQœÊùRÏCÉ}Ä}_·Z~ø§ÿ+ÕÀ¨¯>W;_êù(¹§ÿ×ÿJÅÿè\[?W?ojùíâ_k—|ðÏýO¡¬ýýU\—ó¦–§‚{î}õ÷¾{âŸþ¯Pôÿ5u¨àžþOÿçüsþgú€Ú½ºK>ôÿ5çN¥>ð…àù¿¦ÜÏ<÷ëF¥¯vÉCÅÿ=ú@mÀ-oµ¾€ü+Õý¿¶•Üs¬ÿXÍ?÷À³Ï?þñÏs î9 Úÿé5}`wÿ÷÷ µ÷n•|ð_sÎT|?æ¡î?ê9@x^çøçüw©Þ âß :¹Ç?þ¹Ä>¯ºÿhÿ§ß »ú§bú@wÿÑu úžž•þoNý{„»øî§Ü vóOøîøÞÿOù&võŸÕv»'ìîŸ:xÿ<8Å?uð¼Nó_U]ÞNõ_]ªõpºÿUu RøÿO`ößÍŽÏúž÷Õ¼øO`ÖgÔø¬ºÀ¿@”Çìy¼ubÛ=Q²=VÍÙ9Už²Ö™Û=£»÷ æÈ:·QóæîžÙÕû†4Dgï<»'‹W¼>½ñ߃€×û=~ݳ @€ @€ @€ @€ p"¸zðx^íœËu[1 ÝYúï"%¤„,/”#K \^‚ã3 ȧçã¯/~ @€ @€ @€ @€ @@Oàϯǘ«Ÿú™3b„Àª×hûÈ\ˆ©#õTW·2z~E Êãl¿Xª!0ëCÝ®fõ÷öªö·:Þ½¦rV¾Êwû ÷ô²ÛWöø÷˜[[i6÷ìþ~ÿû>`ôsJÿÖÙžfûõïopl…³~²ÚE½eÅÑéåo´Ÿ,³ýô5[Ù¨¯ÕøYOUíb”úE­zŒ¶¯ò–Õo?³ïWõ6—åEÕÏ-þg}FÛ©|eÓÝÔßl\¶uøõ¯öT5^Wÿ£>£ñUvõÛÍÔãhÜ.?Õãvñ?ê3_Íwÿø}þïö¢ÿtÿÑ}Sqwÿ.>Ôó8Õt?ŠSóvï4ÿŸ|ŽþÞ͇z>·úWsvÿœÿ'äÀh]ÿ)Þuîš× îg¾ŸÇ¬®¹ûgßÇ<ÎÖü×òõ¢j×Ý¿Šã©ã¸ú§îkêþ5œ]ëCWÿ®¼Ýæ…ö¿SpîkóÑÉ=ßóèßCwóïv¾ºÏÿÚzë–øÇ¿S¬ÞÿÜö—û|œÜgÜÿÜy»ÍÿÔ§ þkóÑÉ=õŸçö?û¥¸Ý¯ÜçCý×î7·|À?þr€ó_›NîWÎýï¼q«¯îóéæŸ<«øã徟Gççæÿ{>Ü4y‰ çÑ}©ŠïêŸ{@,¯ñã¤Úêq\ýsÐäewÿœïóÿš}¦®ëÑñÜýgÔ×yŽöÿ)9ñ~€:ðœï'¹Çþ߇ÝêŸ:pšùçù®¾ÀÿÙþy.ȹ·žžYuàÖzpºÿì:p[àÿAàÖÿÚÅUè^ºù'Æî…]ý“±<ÀÿûóÿÓóEô=›k\wÿÕuàôûÁ-þɃ×çÁmþUypJ]¸Õ¿:\óávÿ»òÀ%ðÿLàÓ}¿ú÷êçü¿'Pí;ÚU^à?F êiWÜl~ÄVOÔî{BU^av@•U¿k«§õÿTÞ²ÆÁ` ,?ÕýÔ¬ž^"Pís´Lí%0ê+;~ïê}wÀÀ™²êÀ™«gÖ€ @€ @€ @€ @¸À_È'‡?µIsl'Soo|A€ @€ @€ @€ @€@ïX¯ïu"'ÒŸÞž=Îþ U-³gÇi­þ¼hf½y;øš{ùòžg ýŸêí)j¾ýMä¬0ÊOô¼9tö}J´Ÿèù÷5³²hÙóÇPÚoÖl/½Ïû÷ý>göû~¦|VÔË?ë¾Y¿­q>´ö™%Ëç_Ïiùò¾¾9ÛJVy÷ö9:ŸZýÑÙÞGýDß_ßàÜ ²¼Gû³Î?G¯î(¼?Ü]yS×äXäÑÞ­u¸jüÅzwãý^ïÏyVÏèXÄQþWÕ«÷sÇhÖ¹ï¯ë~×ýï}ÞñÿàÔÊï~«6_Žþ:Ò–ÇÑëjž¢â©îÔkëþ(Ϊóâÿ¾¨zŠŠ«ªÿV^â«>ïéþÕýDÇWÍÿh]·îæ«>ÿ©þÕ½dŇÿ±÷%Y^²žSÅ«÷^ÏâZå9ø§þ•s ·®[÷U©Çì8•Ý÷¼Ÿoy¿®gs­ò<üÓÿ•s ·¾Uþž¾JÝ«ÿþ×ê¾ß××Tkÿ}þ¬ýfWÿV.§ŒÇN©æþñ¯˜Öý_µÞÔâRtïñÞG³j<ø§ÿ+倵ïó¹,Ÿ•Ü{ô}üãÿ‹€ê~«õ?V/jþ¬ñ¨ù¿â±ž¬\Nê_1¨ÿœ¼Ttïñ9à”þm]'þsêÌê)j<þñ¯˜ÖýŸ÷@}y­èÞcÿÇmÿ¼èóg=¨Ö¿—úÀë<ÂNYë4j<þñ¯žœã~ŸYÁ=þñÏy0fŸªRÿøÇ¿ç>ÀçÂj•—÷Ã>ý jxù?½Tõï}85ðÿ €ÿÚ™À~0w¨mýwôäÁXàÿÞÿOû?B»ù:îz>ØÕ?yз࿯ÿïº/ìî?ºTßNñŸ•Õòá4ÿäÁý\pªÿ쯹¹™¯ò®ö7Žb¾»ñ~svÏ›|Çf¬ö>[‡»î£™ï.•ÿ]Þ¼ž›ÏdߌñþØïŸó¦fžÑx¿ö~úþïíß«ßF‹“§¢m3Å»­îO«¼÷yÇÿW–ÏéTû†­«ÆEÝÕý)õïå_U_ÑãÆ­ìë™yy¿Ç‰îI5¿êþU\³ÄÍæŸºŸÛï³þçå?K}ªçYµþÕ\³ÄÏ⟺÷íûÙÞÿðÿ«Ïë¬ù‘¥/¯šgôþoõÚ·Šg¶çà_ÓW³äþñ9Z}½u=Kîšgd÷œ÷þ~§­ÊüÓÿ#æ@«¯[¯«ê攸Ý{ôýêßëZóÿôÿˆ9`íï­qÖ:¨:.¢{ú¿þÜýûŸV][¯W­k뺩öÿˆ9`­ïÖ8kTÑ=û?û«®­×«ÖµuÝQëÿ>/«çêÿËê;Ûïñ¯=ŸRÿZ¾£u¹ê¾Óýó=Àu~ãŸúœ³û?õŸ»þ½Þȃ×y¹öÿÏ> Ù§ð¯áºêü>ûœ,þÙ4yŠ ×Ùº\uUÿœ³™œ/çAŸ¾•5 ¼üWïYýs¬]ÿÞþ«öìõOÌõüß<Ÿ'V½í~Î)þéc}à4ÿäA_àÿuÿ¯ò{ÂSý«úÀiï §û'®÷ü÷õÿÓö…*þÕ} ëûc5ÿäÁã~PÕÿê<ˆzn¬îWDÉü?ðþ^¹7Þêσñÿš@¯7õxU^àßF@íw4þl^ØVϨÝç„Ñüh݇Ù9-¾Ñ¯Ï­ž»³÷ jD¯ûûü4«'ê3¨ù€©=¢äÞÕóÔwVç&rPåEŽÕ3Ë^Ö|éËx@€ @€ @€ @€ @ØMàù”¿x^í›Qr"1 s³½ÿ-ö9ÂÖ¡RP€%d=[Ÿ| ‘ånÉÌ@òõÅ @€ @€ @€ @€ @€@=ï?·¬¿ë+¦ «Ooœembò x½EÅçïŒ^ˆòw5vÖ¸ê)ëú5»ï·J–¯è¼ýÌäì8Ú˪|94ÎϺÊOö:益Ýa¶Õùc霛mµëz>ï™ý}®±˜Y=dÇÍú]Céœ,ÙGùG¾¢_?ÇܵŒ¼d½íÓ›ïµý¯Îòú.¯×OvüþçvÐÝû½¯æèíU¶ÿì¹Ê¿¿Ißð~ãÕmþñþ轋¼¿ö~º¼öŽÿoŸDÝw©äñÝ=éG{}ZãU|EסoÔW¡Õ§5.š·Z>]Ýh«Okœš§¬ztú*³zµÆeñVË룬mõiSó“]žQ_EV¯£¸lΪù}´u¢G>½¯«úÉ®KǨ¯¯ß]¾ÍöýœßG½>:Êû=ÏjÞjëÕõUå_ÍCU=>úõÑø·}®oí§z£¾ ðßÓ?Þc½ïöýþñ?ó}ísßXß»ÄùÞ}뢯ΟÞ}Öõ­ŒÿžçÿUï|Îó¹o|S¸>ÿ9s¿Ëý?þñÏ}ÿïÿkxïïFñëOtߊÌ?óÏü3ÿWÏÑ9Øõußi¼>úªwžÿxþûO ë|ö½~¢}+2ÿÜÿqÿ—w~ù¦q}4óß{þïwµFïƒ]__?Ñs+â?瘳±þª«þy|Ý?ëMέˆæŸç€øç€¹i¬»Šs ö¨39·2þñÏû@ÜûÀÜÖ]5ÿ<Ô9ŒX9ªº~î³Ëßÿ½ë•(ÿÝψY¬ÌÕ]ÏJwkGùïzD8PÈAÌ=*¸‹¨ÿ½ýß{ˆ>ðõAÄì)åˆöú}’»ÈZèÛ9É\)W–ÿÓÎ%gµÐŸÏ æŠ9éƒ×} è*³&úà±2Y+禔íä×–íÿ9¿ê÷ ù¤µWèÞÚvÖU·ºTž#×Þc¥ª>¨ê‡=¬ÔUYÝÙ}QGv¯•Uú ú¾r/ õÕªöÁl]õD÷¬`–·Úu{Ò׫ZÍ«µ=’gTdå_wmý]T{~·¾>¹3+Té‡3éî·«ª~ØTÏŠ³ú£'Íswíí“sI°3@€ @€ @€ @€ @€À‰þë“’fx^íšKR1 ¹÷¿Gà²HÈÄ=É–Ül²[¶»%y’âãƒ?@€ @€ @€ @€ @€ @` ïÏÛº£Ÿkv˪½F}ŽŽïÝã| ŒzS÷=ѯ¨üYã`(†€Õ“×ü˜ÓŸ·Š—/uÜóÌøœXí%*žs¢FyòZçSš“zyXWC¥~”U~z×ýúûýgô³¾9Û {ù{õÚ;ÞF§îloŸ­ø½þ¬ãêœ;YË‹ú¹ÕŸuþ¥z³Ô^[ñ¬ÞTóë™;QË“ê¹Ê—:έ:£U^[qÔ¾Ôñêí;IË—õ¹Úw¼>jùGY½¶æ{{òŠŸßlß ZþfŸ{y‰ŠÛG/ï¨Y¯­yQ~¼×ÉköýÎ[þfŸ{ûˆŽÿV>D{‰Z¯šÿ–ÇÙçQ>¢×Áÿûúö½^ÿ³u}5/ÚêõðÿºþWùˆ^7»êþfp6oðoã7Ë}—yYýS÷š¼Å¿†ã.õ<ºÓýòª6>›ú¾¶_ê¿ZÏžÿÚzšõ°jþñŸ)T÷ÿªzÛmÝ,îñîÓ§ðïÃu·:¿ÚþñŸ!èÿ>yšÁýïñEd¹—£öIýûÔU”?ë:øÇ†àþ÷ÉÓ î÷þ=¬ý²Ú|üûÔU–<Á?þ3ä÷¿OžfpÏý?ÿÿÝ­{(‹ÿû>éÚ>€-ÏV½íöÿøÏ”ôm¾fr¯|ä÷ læ÷KÐô¬Y òzÀ¿¦Žv{¯ïÝOVÿü É[üßœzd÷¯î§åþëÿ¹3‚ïcï¹mÿß½Úõ~PÍ?ïg׿—ÿª} jý“}} ºòà}àÿõ÷¿Ñ÷ÈÞß[wwŠï>õýà4ÿQy%NõOœnþñü£÷½uü.ïdÁÙy€ÿ׬õmÕðÿž€Õ£j¾W>࿀ʣWœÙüè;=£¢¿/xåÉs\ÌÚDyòZÇvzfgï ô!àU¯ê¸>§'ê3µ7Uö4T>¢ãhvß7J´/õ|}ÍÙv®æ¿:žB¿Q«=Îÿý÷yÐèÏ~&¯íx”{Ôs£^GŸ»F£ÏÓQ>ßÍ3êÏû\£c;]åÝëÑ:~ŒJý§¢½[}©ÇÕ7ûy‡]½?ò¨«ÿ(ïêzUÇëæïwã]ë¶u}Ύץþñþ\÷]êïÇÞñçbÍÙ}9*~Õþoõz6.ÊKÔ<øëQ>¢ç©æÿ¬~­¯G{‰šÿŸë?Êêyªø·ÖõêïßVy¯vÿÇÿç÷yïò,{ýãÝæ½JýãÿžÏqù³ú^5ÖþOÝûê>{ÿÇ?þéûÿßã[Ïîýßʭʸlþéûš¾ŸõüÇ?þ9÷ýç~÷ú¯r~{÷Ñõü÷r«2ÿÚó4[^àÿ™r@uÿÏV§³Ö›É½âÞßýûž×<Â?ý?SÐÿµùšÉ=ý_÷¹ŸÿhëhÖýlv\ê¿wtõÏû€læïëUÝÿðŸÓÿcÕª<˜}¾î?kà_soéî¿û9€MíÞçùû¯Ï÷Ǭþ¼ëÎZÿê{`×sÿÏ}Á[OÙÆg÷OðÝ_ð|/ÈVÇÖõVñO°õü}®l­¯ÝÇUóO¸Öð?VÿUßVõ?«TËü_«ÿ×ïv?ßÏÖWÝÿì>½tñOß ñïëÿÙÿÿÐnþ£ú@–s¡«ÿè<Ø5ºûïžø& ú½Â«qÎÞ§ÍzÿÇ®úS??Ëwö¿ÿÎWµWo}xùÈŽã³û}£dûò^o_ss;÷ö°*Þ…}f¯òc]÷ë÷½Ðèÿ>m;µòž7ò*½n£ÒV´?i|©Gë¸þ&u;”z‰gõh§£Ówt”ÏQ\«7¯y}Êv6òã}ÝË›W¥¾£¼ý¾‹çåË;N_³×;ÛÝû‘G»ùöî]ŸÑñvñ÷‡és>áÿAÀšÑõ¿»«Wé¼h?Ññño«ÿh/Yñ»ú—Ö¯v\–—¬uð/«ÿ,Ùëtó¯­géøl/Yëáÿºþ³<¬Z§‹ikÇ­ò’µ.þ_×ÿÕëàÿ•s@ÛÏGãW×cöú•Ýϼ·­ö=mT^àÿ¹ÿGq¾k\ü¿þ^쮾¼ï«ªÿÑ9®½î͵J<üSÿs@[ߣñUêÕû>+º÷üÜïͳZ<üÓÿ+æÀ¨ŸK¯W«Wïû­èžþÿÿwœÖ¼À?ý¿bHûûhœµnºÌ«èžþOÿÕµôz—:¶îƒúçü¯˜Òú³ÖM—yÝ{žÿG~tñ©Ýþéÿs`Ô×µ×µuÓe|E÷ïYë™ß}=÷;üóû¯Ê9àUÿ»~¬ìžç€ù÷€Õý÷O°=ÇàÿùüßíÀÿkÿ»äAÿœ{÷ÿ(ÿÝû@·úʃ.ïûÎûÀÿõù~®è–]ýGõnçþuõß­t÷O¸~.Àÿ\ýW?vñݪæÁnþ³ò J>ìêŸ<ØÝ¼Ï¹¯ýÞñnïÈ‚5yp—óÿÏ´õì=>»?àÿ5o¯ÖxÑù€ÿkVoÑó¼òÿ2Ñ>½ãKóC¶{F xûZ³sVyóZwn÷ÌÎ~äåýˆƒÁÞž¢âÅ잨U>/`j ¨zÖÆ]³{V½KÀĽ hëY;þÞ»çîF´¾ÏãGñ¹@€ @€ @€ @€ @€ @« ü6&ù´x^íš]rÛ0 s³Þÿ=Bé¸~ˆgTáç#€›—ÌÄî´¬øë‹@€ @€ @€ @€ @€ @€ õþüz]#ú{}…\!C êÕš—©\«/uœn¬ä! ö]ÏS3±qQ?«óâ;"óV{S­E-•—]ëhwÞj»<­ºÎyÆ4;^åc÷º笲Ûêz¿ÿ='úü}޹ÜNUÔë\yµþ=Gen¶ÚSv=«OoÜ\ƒ¹e}Eó½þ²ñ9Jó²£Þ²yYÑüyc;ÊúóæG}©ób´ædy½eãÕþ²ëÍ1éÛIÖ£5?ëgu¾Úœh«¿hÜjoªõçµí$êÓš§ò²kµþQVѸ]¾Ô×éoÖ¶ƒ¨×»<µÝëÙèõºó—}}·/õõúšµUžõ{•¯öðÔz6Šý¢ðþrv×WýÌÚ*Æÿ™þñnóþ>lÓÔ'Jíÿîüìþz³¶Jñæü«½¿×ë>ßwõÛ¦ª~”Úÿ·)¯×7k«ÿ¾sÚýþÏô÷˜÷)óüÿ% êƒ)÷uÖ}Øî®êF©¼Ÿòyï³/êšµU†ÎÅùo=/§ÅÙ¦¬n”jþ§yµî§®Y[eøçüçü¿ÿžÇÕy`›²ºQÌ?óÏü3ÿœ±s îÉn«Låç?6ÞÕ¢ð›{þÿóâöÙ?ÖÏÍSâªÍ³·æÿìù÷ }ëï¼UÇ?þÏNûPuž½u©çÿ”>ðr®¯îƒ)÷ùSŸÿö#þ}÷ÕçÙ[ŸÚÿô÷/ß.ñôíèâÓ['þÏö¿ê¹Ð´÷ï\u‹_uLéƒn>£õ®êƒîŸ£<»å­òßýèæ1[/}ðó¾0˳k>}ÐÕœ¦îÕþ»¼/hhö]eWT퇾洕ŸÚZŠýWÛÝOŸ ý­ÙÁ)}°†^ÿUŸòuÝUÏ™ú›Ú³ƒjý zߨCoÎUªöÁ]]§|ÿgW§Ýñîòú.^S¯ÓÅóUS½<µ¯nýð§é×íÒÓ=TÙ_Õ~¨Âç´:ªôÃiÜ«î÷©~¨Êƒº^V÷œ{ÈöGïÝS= @€ @€ @€ @€ @€ @`:o²0iÔx^íšIRÄ0 ó3þÿ žÀ8„©¢(2¶dÉÖÒ\8DRìnÉ–ëâ € @€ @€ @€ @€ @€ @ð%ðõq××~÷]ÕW h½Îæ­®|³¾¬ãlVO)kÚzÒu¯# õã§Û Y#ÞÞ¬êöÁs+/»êÈvGô]¾¬ßƒQk§êévß7ë”'¯÷ö5)Û¹«ºŸ?¿”~—QèmåǪŽÔï(¾ŸÑ¹[ùZ­3ò·ú|ŽFŸ¨U_«ù«>¥ù}̾ßéª7m¾Ô—u<þoZÚï5§ëVrïùwœÓž¼Þÿ÷ç¿÷(uñÿ =0{Kã¢Ì©×:*¸÷¸÷½xG«‹ÿÿÏÿhž¼ÖƒüWèé½>Š÷š·hu+¸çþ¿.m_áŸó¿BŒÎsésíÈ=ÕòÕ{ŸÙú@N°F}PÃãê.º÷Á*¿*ù»ú ÚýPÅßê>vûҫܪåŸêƒSýPÍŸÕ~N÷Á®~°âUµN”>ðꇪެ÷­þ®gôwþ§çÖœª×‹ÞÒõU÷åµ?)ç¨ñ^|ºÕêw´®nž¼÷;âí¹7®õ£y~ZOW?»÷µvsà}7(ý€NõCŒÝ³Š'Þ}ùÜVû#÷îY= @€ @€ @€ @€ @€ @¨Là£æ{x^í›AR+1 s3À"?U@1±,ëÙ#©Ù°K¶ºe…I?€ @€ @€ @€ @€ @€ @€  'ðùñÜcö·þdìA`Ö«u}ÄÙÈGÀê-j]ÜÉÉä!åÑ›Çsfbü¼žTqþJˆ´Py‹Êk©5vQ^vå±WÆÊwvùŠÞ«k¢}ìηV}ßèÝžTûõ58W¹Š¿:ï¿ÿß ^ýž£ÐoµÚÏjþ‘ßÑó~Fç*^õ?òè}>G£Ïê(oÞ<^Ÿ³q}ŒÚ*õúZ›õµÞF¥þªUÞø(Þ<õ;¯ÐëÍçõ¤ŠÃÿ“€×§5Nåo5oWÿVo«ëVý¨ãñ¯¹ÿjoQù»ù_½Ï£ø(/»òà?öþïòµOÿ£{»ú<ÊÇî<ø_»ÿ»}EïWÝÿê½ÅGûØÿ¾û¿Û“j?üã¿bŒæ¶÷¹êžÊ[ѽòûÜSžTûânþ«<œÊ‹›ÿS~ÔûVóïý\Å©=œÊî¥ÝcïóS÷S½o%÷Š¿ûÕüOçÇÿûùÚzüã¿Rx?߯âÔ÷ïtþJîùüߤÜ+ðÞ ¯¿Ì}»í§gü=ìs¯TùÏþ^˜Ûêüé郟s`ž`îü÷ö¯~/Ìöyû6ûO¯žYúÀO°Fd÷>¨aÑ_Å.ÿw~rµ"w÷Á]ú¡–ÅõjºõÁ:±šNõÁî¹PÓ^\U§û@Ýq¤jfº‹Õÿ§Õ´_ÕÝûÀÛñ¤zdÌÚ¿ÏÝÖ®Êì} #Ó+sÖ>èeI_m¶>Ðé½ÃÝû¡·ýÕß­ö`ÇïN÷6îE`w?Ü«zNsE@Õ¯AÀÛ5ª§ @€ @€ @€ @€ @€ @€ T#ð„FIx^íšËm1 ÝYúïÂ%¸„ž`CŠ?‰ÔäâƒDJœ!w7N¾¾ø@€ @€ @€ @€ @€ @€ @€@ Ÿ?¯s´?knÅ)^ZŸ«û¼÷">–Àª?ïþØÛ“m•€×Ÿ7~õ¾ì÷ðúŠŽ÷UC´D ÚWt>éþ¬ÛD{ÊÊg«Ž¨'Yž²òbÒG ËKU^_õ÷FWùÉ>ç^ƒ¶Ê³}Tç·Q¸/ªÚ‹õ¼ïßßÿjÞgr­b«‡ì8­_iß{vgûÓæ—üy×ï1ºV©ÖOÖ>¯Wmü•ù»³|Jyµ¾¢÷Í7ª«Pò“µís5ŸŽÎÜ]Y^Ÿò®úÉÞ?׬®²*ÿÙ­ùu”æíºÝû»_æ™ÕU„§i»ðþ2zëügû·¾‡wÅM›ï§z²½¿óïòh=ÿ/Þþ°ò߇ŸÿÝþ¼çO÷ïk)ÞËw<þ™ÿÉ= ͯu}÷ÜF?Ù}Äw]—ßã[ûÿ¶ç¿•÷iqSý[ŸëRÜiþ¼÷ÁÿÚü{yŸüOìé9n]?m~½÷™èžïþÏ¿ïIýžÿ{Àú|—â¤yê¶>Ñ=ÏžÿÒ[׻ͷt_æŸ÷ÿİη'ÍS·õ‰îyÿóþ÷µ4ÏÖõnsþtß©óÿ©Oñ¿öý×õÿù2ÿ6ÏÒûAš¯Ó×™__œîWºßtÿ|üÿ;ÿ¾ùïþ=€ÿÿ]ûàÿÙïü÷è$é{>j]úî:e½‡µø[Fy–òœâùÖ¿ÿ?uŽä-jÿñ³™1ʳ”çÔ>ˆdÙ1—ä-zý´>èè,ãÎÑžµùv÷CËÎ9µÞ¢÷íêƒÎ®2ïíw5_U?d2ìœ{ÕWÖþì>èì¨âîY^½y£ú¢‚á„3¼¾ªâWûb‚›Êª«zª @€ @€ @€ @€ @€ @€ 0À_j¾tx^íš]NA ¹÷¿EŽ# hE LÆmvÓvñ²ëŸvU{w¼½ñ@€ @€ @€ @€ @€ @€ @€ PGà÷û«×ÓkÝ‰è¤ ðäsõ}Å™¨¡'°êѯ?9=¼þ¢yž³’'õ¦ÊOB •/uËÙ‰ñPûR×óOF濨ýd×â†@¶§¬úšéçVÉòRUw®¹ØäU~²ûÄ(ÌËÎöQ]žAßÄÕ^Vûýúü}ÿê«Æœ¬UÙñ«~Ÿâç˜\›4Û£µþ“¿èûkTæD[ýdÅE½ZóçµMšåó©®Õ—:ÎF¥Ô“Ÿ¬÷Õ>Wëõ7k›0Ëï]ÝUOYñ6:}£¦z¿îS_³¶Éªügío´®R¿¨éÞ§ï?þûí´e"¼¿(MÝÿlÿÑïãê|ËÎtŠÁÿÌýÇûWïÓ>ÿñÿ?²îAõ÷¶ª_§ïöÿÍ‚wö?cÿU{¸«N÷ýÏÚû«î.oª¾øðÞ•‡]uðÿÎwÀ»×Ö¼]{«êÛÙ}äsÿ=n†Õ£7Nµ‡»êô°|?…׫5o—7U_üóü×ùX÷اÚÃ]u:»çùïïÿyÜݯîþ¯ù¼ûý”·koU}ñÏ÷ÿ„;ð´ÇÑ÷UûX]g‚{žîŸðûü?ýï€Süg?žzð¯ÙüŸq“¢ÏyÖüêç8o¿3¬éOiõóz©ÊÓ“=£bÔ«5¿Ê£·Ï¶òNiõóúÉÎË#{Få¨×ÕülŸ«õϰ”ÊUÑøUOYñùdÏèõéÍÏòj­{†ºSz=Fó¬¾ÔqudÏêõÍW{žþ÷ÿÕÛõ§ÊϾ«\¦Å«<ªê¨ïÃ4ŸÞyUþ²êxï…—ÇÔ¼,»êNõ{—/uß(‡éùjÕõ¦ûSÍ_íMÕO5?u^T^ªêà-—@•GoŸÜé©~ðúÉÎÃÐÙ^­õ÷LO×מּÔq˜ø™ÔžïêýÌé9Õõ½€tOÖ{Òsz¦‚ @€ @€ @€ @€ @€ @€ œJà˜œ"x^í›[nÃ0 {³Þÿ=BPi>j °¢Ý•Mqú1×â -ç~|ð@€ @€ @€ @€ @€ @€ @€ \Kàûóqþç㵫á쳎gŸÏžŸº5f½ŽÖ­é‚³Œõæ:nt]—!àò8›“éŠÔWf=¥ê0•%òæÊÍvß7Ýå'Ó×P¦ó´/w~†B¿T·—UyýLy;^å)u/>i)«sûótºÚû|_ßÿ?=TöOq{p罎>ßߜ֡ۓ+oÔïÙq}«]ž\9gg_ß× Ö™ËÛlάÏwë4JûUÏúrÕ½ëO=~?ƒZG.ïæ¨gë5ZûT¿ëËuü¬7WÝ>µN\>Gs\þÔZýêQ_®ãT_îúúµ\^ÏrÜÞ\y½ºÕg¾Ü¯»|¹sêÔVîöû*Ïí˧Q¬[ÿºî”•ãýA¯ëïøÇÿ/Ô¸ïÏéþ<\s‘Þ§Sù•öpe­.ÏU¿ç5? ÓŠµÌŸÿœûþqžRût*·â5¬¬™ëŸëŸëŸïRû@jŸNå*{iÅÚ”÷ªï*:TÖŒÿÞ÷ÿçì0ÊUT¿ÿõ*¬ò÷߇†;Ô®šƒÔûw5w‡J«üßuPØíTÛuvr¨ô²Úÿ]ö…ÙŽµÝæ`G‡JOWù¿j?PXí\Ûevv¨ôvµÿU¿'(Œ:ÔÞmÜ÷‰=ÞuÔyp°é”q÷98[ßñûÂNžq®òº“IǬ*ž_­³£³DÏUç Á¢sfµ9èì*Ù{•9H2 Û÷ÿ…©yÂÑZ)³¹k»çlG³Þ\u¹—×Ñœ{uÏjVï¯I`ôú>;®f÷¬z–Àqfs¨ƒ @€ @€ @€ @€ @€ @€ @pø–‰{%x^íÙÍqÜ0 Pw–þ»H )!ÇÏ8‚@"¡çË–?Àû ÝïLJ? @€ @€ @€ @€ @€ @€ @€ @`®ÀŸ_ŸçÝ}[…ÓV ÜÍ5º~U½Îˆæ7ºn¬J»g Œæ˜Ý?«~çÜÈæ5{ß½ª­Î ÌÎmÖyÙ~ì‹ ÌÊiÕ9±.¬º+°*¯ÙçÞíËúÿ ÌÎgõyòœ#°:§UçÏéþ½§¬Ê¥êÜ÷&7ÖyU>«ïSxßîÕyTŸÿ¾sWç’½ï÷¿ß¢¯9÷ìÊæ°z_4ß«uïI2×éê£ç_å˜}?§ÒW4—Õë²¹F÷õO2×áê\¯Îæ7º.§Ów×U.«ßÍóîþ¾Iæ:[ïOçßÍmÖúœR¿]oËýk~ú%™ë¨:ÿYÏïè99­>»Þš»çÿs†åßçYÎtR•ÿèçôªý³N{äß)Íx/U¹ݳêù=7.ÖkeUþ£ù¬Þß+Õx7ò[u\)ÿŽ©Æ{’ܪãJùwL5Þ“üãVWÊ¿cªñžä·ê¸RþS÷$ÿ¸UÇ•òï˜j¼'ùÇ­:®¬Êßï?{OOÕ¬þ'{þÞ鬯Nþëw¾¡*ÿ]¿vΦ¢6ùW(ïÇ[ç`ÿdj*¬Î—ïƒÝsnyÛœ“LM¥OåÿÔçAêy·¼eÎK¦¦â§ó¯ú<¨Ñ<÷–îspn25•ï’ÿ÷:²ÿïý¾¯Fñü[vƒÑ¹8?™ÚN™ƒhµz}n‹úî¾®O"Ït²{¾Wõ=£ÖïÖ+ç]ßï—ijíšóOu=«Õ÷öSæ o{t¶ûì¡Ô¿Š]ç ¿üžî2{꼯ª§æá}Ògt\5gh¨rÕ<í!Ýë‚ @€ @€ @€ @€ @€ @€ @€' ü"×Hx^íÙÁqÜ0 Ðt–þ»H )!“q|ˆ'A@‚ËçËLBàû ì±¿}óE€ @€ @€ @€ @€ @€ @€ @€ P/ðóûÇ3^}Öwà W¹F¿_Ñ›šyÑG×åuªR†ÀhŽ£û2zVc\`4·¬}ãÛ9"•[V‘3Øs_ +¯ì:÷ObÇì¼²ëÝ9‹µq윪êÅOdeD *§ªº‘3Ys-P•OuÝë“Yñ?ê|ªëKwL :—YõÇNî®Y¹ÌzιIÞ;ù¬<²ŸóãÏÿÿ^}ÞS8wuv.Ùõ®r–ÿØìfç”Uo4ï¯ûÆTÎÙ••×Ó:YyË?6»OóÊÚ_•ûgݘÆy«²ò­S»üÿ=Ó£ye훕»ü{å?;wùËÿ¼Ÿê¯Oœõþ¾[gÕ½wÿÿž…»¹e­—·PVžÑ:«swÿ×Þù÷¸÷Ÿ]DïmÖ:ù÷È?+Ïh.¹{ÿÌ_4·¬uòïqïOï»ÿ¸¦›¬÷z´Ž÷ÿšœ_=5š[Ö:ùËÿ·@—9è•Æün²îu´N—Üýþçïóo[ß'FïoÖº.ノÌí,+×hùÏÍ÷êiÑܲ׭žƒ+—S¾Ÿk´žü{MX4·ìu«æ —þún²sÖ“ÿúìWü?ðë|ÌžƒêýºˆÞÛªu³æ Ÿ|Žªr½[·zzh÷íân^Uë«æ ¯|Ϊò|Z7kz(÷ïâi^ÕûGç¡¿|«ó«ª5=t÷é¢*§Uu÷‘ïÕ骼²ŸÛKu¿n²ó˜]o?ñžÏÎ-ëy=5÷í*+—Yuö•îÝù¬üž>§·âþÝ=ͧzÿþÂ{œ :ÇÑú{è½O—£9Uí{Ù=OR•k´îžjïÛu4·¬uï+ù'ËÊùU÷P:ïYsqžÜY'¾š“³4œ– @€ @€ @€ @€ @€ @€ @€tøÖ‡8x^íÙ½yÐ0†Q6cÿ-( Á‘ô]ýX‡&E¬ké¼r¾}ó @€ @€ @€ @€ @€ @€ @€ @€Æ~~ÿ=£õçø›M˜)ÐÚ÷éù™{÷®¯ k/Mwéš÷Sïׯ©ì³»«WuîÑÞúûf«}ºöW»í?¦±ßê®~Õ¹úÏ|‹ÕNéý©î~ÿ×¾Ÿt¿ê<ý׺¥VU{¥ö§»ûý_ûBRýªsô_ë•ZUí•Þ¯ªìÚœt¿ê<ý׺¥VU{¥÷ëŸ*»6'ݯ:OÿµnéUÕn©ýú§Ë®ÍKõ«ÎÑ­WzUµ[zú;H{=m^º_užþ³_XµW×þÔw0«y¿ÓºúUçê?û-U{uí¯~³Š÷=­«_zîÑïá¾Efožî45ï§ïaVñþ§Mu›:çþEf_0ÕeêœY½çœ6Õ§ûœç™}Iw—©ù³jÏ;mªS×9Ï+òžuõéžû­çÚÝ©kþóJ¼÷E]ºæ¾Wë¹§wõJÏ}nk¼,Ý+=ïJÏ¿Eº[jÞóå¯õÂT·Ôœkéìw›Tdzsö¿æ‹Ïö«î»¦†[U»®î'}ÕžG×ÝãõnùÀÑÞ¯ëÉî% ÿ^½½– @€ @€ @€ @€ @€ @€ @€ @€+ üV£èDx^íÚÁmÛPPw–þ»H )!‡Ä‡p¾IͬeìóE€É?Ë}#é`øíÍ @€ @€ @€ @€ @€ @€ @€ @€ @€Y_?þä}ôš&mZàÔïéúôóšwOàÔãÝë÷žÆ©¶ÀÝ>¯žkï!ÿšÀÕþž½ÿÚÓ¹;-ðlÏžOï#ïsÏö–:ÿ¹§uWJ Õ[*'µ—œÿ ¤úJçè­+î+×Ý~ozº§VÞÞ†:›·zjåvö¦¶zjåîm*»y«ŸvnVa_Z»Ÿvþ¾Æ²·ûiçg5ö¤µ{™ÊßÓXvÓ©~Ús²*{ÒÚ½Låïi,³éT/Ss2*{R¦z™š³§¹Ì¦S½LÍɨìI™ê%=ççßÿ~|ÝÓÜs›¦û˜Êû¨÷÷ß?§²çôT_©9§Þõí½›êe*Gÿ×ú=Ý=Õ[jŽþO^»žêe*Gÿ×ú=Ý=Õ[jŽþO^»žêe*Gÿ×ú=Ý=Õ[zÎé}pÚÛõÒýLåùûOæ<Õ×ÔœŒÊž”©^¦æìi.³éT/Ss2*ûR¦úiÏÙ×\fãv/Sù})Sý´çìk.»q»Ÿv~Vc_Z»Ÿvþ¾Æ:·{jåw4ö¦¶zjåîmª³y«§VnGAj«¯t®¦ºé¾ÒyÝí¥¿ ¤{KåihV Õ[*gv{Ó^íû@#_+úßÍùÚíM¸ÛãÝsxM»}^=÷šÛ{ª©ïÒß[àêçýñþï½½§? œÞ§ó® @€ @€ @€ @€ @€ @€ @€ @€ @€¤~Ý;2Fx^íÔAR#A EAߌû߆#° Øvu}©l+ÙÌ‚n©•/†ÛÍ @€ @€ @€ @€ @€ @€ @€ @€ @€ øüøžó׿™-¦œ¸×÷ÞïO}·½×îõ\ýýµ¯ðV—ÀjÏÕç»î°ç1Õ~»Ï?öUžªØíxõýê»Ìÿ_àj·Ô{úô ¤º¥æô^o[ª[jŽ"=©^é9=×Ïݒ7·LíåéNUójæN¯ê•ž;·PÍåé>ÕójæN­î•ž?·Töòt—®yY…¹Óºz¥÷Ì-–¹<Ý£{^Faî”î^é}sËe.O÷èž—Q˜;¥»WzßÜr{—§;œš·§0÷íS½Ò{çÜ»<ÝáÔ¼=…¹oŸê•Þ;·àÞåé§æí)Ì}ûT¯ôÞ¹÷.Ow85oOaîÛ§z¥÷Î-˜¹<Ý£{^Faî”î^é}sËe.O÷èž—Q˜;¥»WzßÜrÙËÓ]ºæeæNëê•Þ3·XÍåé>ÕójæN­î•ž?·TíåéNUójæN¯ê•ž;·PÏåé^éy= ¶¤»¥æ)Ó+ê–šÓ{½m©n©9ŠœHu¼:çìõ¶ÿ\í·ûžÏ%°ÛsõýçºÞ×üXí¹ú<ñ×Xí{ïù׺Þצÿ>}oÿÿß»¯ë @€ @€ @€ @€ @€ @€ @€ @€ @€x/ÑUu9x^íÔAN1ÀüŒÿÿ†'p@¹D‚x×=³»¸ »ÇS-òxø!@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @ +ðýõ›÷üM—v•Àk¯£_õ^sçFû}wnînw ¼ëñìç]ï7ç˜ÀÙ>Þ;ö*§«Žö7{¾zùc³=ž½?ö:§ªÎö–ºWµ—ÜÿRýÍæè©W`¶¯ôýÞí÷–î-•·o#½›§úJçô*ì7-ÝW:o¿Fz7N÷•ÎëÕØgZº§ª¼}éÝ´ª¯tn¯ÊúÓÒýTç­ßHï†Õ}¥ó{uÖŸ–î§:oýFz7¬î+ß«³þ´t?Õyë7Ò³auOUù=:ëO©ê§:wýfz6¬î©*¿Ggý)UýTç®ßLï†Õ}¥ó{uÖŸ–î§:oýFz7¬î+ß«³þ´t?Õyë7Ò»au_éü^}¦¥{ªÊÛ§‘ÞM«úJçöªì7-ÝW:o¿Fz7N÷•ÎëÕØwZº·TÞ¾ônžê+Ó«`Zº¿Ù<\#0Û[êþ5Û›úHõx6G÷8Ûßì½{lïW}¿§Àìÿõèý{nïU¯£}=Gú3Žöü×ùÏÜÞ«Sß$÷xýØk{Û @€ @€ @€ @€ @€ @€ @€ @€ @€ @€:~$ó®Rx^íѱm1 ÀïÌýwãN º{Rð5Ÿ|p"Á}½ü @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€® |~|¿]ý_ßèe¢ÀªßÕ÷ÄLnú[`ÕçÝײַ!p·×«ïŸ‘þ¼+¯öW}wžlvâjŸwç³5ιîno]ïÏÎLÚÕã»{2UιêÝÞºæÎ‘ÎJÚÕ_uO–Êükª}uÏÏÏJØÝ_u_–ÎÜkª=횟+ž•lWÕ½YJs¯©ö´k~®xF²]½uíÍPš{EWO»öÌ•ÏH¶«·®½Js¯èêiמ¹òÉvõÖµ7Ciî]=íÚ3W>#Ù®Þºöf(ͽ¢«§]{æÊg$ÛÕ[×Þ ¥¹Wtõ´kÏ\ù¬d»ú«îÍRš{Mµ§]ósų’í꯺7Kiþ5Õ¾ºçç‹g%ì/Kçœkª½uÍŸ#ž™´«Çw÷dªœsÕ»½uÍ#´«Ï»{²UλînÕ÷ç ?#qµ×«óÏÐpåÕ>ï¾#û,»ý®Þ?+½k ¬ú]}':S`ÕûÏ÷™é¥"@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ÿ%ð–Šðãx^íØ1’Â0 PnÆýo³GØ‚¡Ù6v&²У¡ˆ‘¥÷•†ÇÇ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ø_àçùz~ôÍñ;Žrþôü;¦ï7ÅÙ¼ÿþ®ŸÜ='¾*oùß+ÿªÜßuï¥Ñ§ÛêÜ培K«r—¿ü3zvµú½÷þgí™ü³òXÕͮܽÿ«¾æÚª=ÉPè×EUž³uûÉgL<›SÕù ~]Tå9[·Ÿ|ÆÄ³9UÏÐè×EUž³uûÉgL<›SÕù ~]Tå9[·Ÿ|ÆÄ³9UÏÐè×EUž³uûÉgL<›SÕù ¾]Tå:Z·¯|Æä£9UËPÐEU¾GuÉgåTõ\s›* @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€è,ð bT%Ox^íÚA Â0†ÑÞÌû߯#¸n„Jhÿ”N湡Æä}Sݸm @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ 0.ð~}¯ÝŸÇßéÊJ¿^W:“½ Œövÿ¯1Eg{ë_»ª»ßÿZsþµzîvÖ}ïþ¯1Oú×è”Þåìîîÿt±ìzúg=«­¦µb™ýÞÕÝ÷¦WzýÓ¢µÖÓ¿V¯ônõO‹Ö\ï®9¨©³þ®õ_¿ñ¿êß»ÿ~úÙs@ùÙú?»Ï]»›5wíßç\Ðÿšß*ïNÏÁ*.ÝΑšƒnn«ž÷ì<¬êÑý\£óÐÝ©Ûùýÿ¿[qç%@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€˜#ðèY¬x^íØ1ƒ0EAnÆýo“#PÑ  –¥ÕßIC Þy†"ÛæC€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€Ü üvBÎÎÿŽ :ÍøÔûú}'›äYßv÷þÏÚ úgõækwÏÿ¨píóô¯Ýgõêô_-\óþ³Ý½ÿkv]•þ£RÙçÍîƒlüéôÏo|7¡þ½ûŸÓÝô2ôÏè8;ÅÛ}0û{®¯-ð´j¯ÞêV øÿg•¬û @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ 'p2l‰Mx^íЀ ý©)„  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`À€ 0PTREE9ñÜ‹ÈrÚȉs¢Ë{t+ÎRu¦ÐvøÒÊw Õ›xÔÖ3yoØ3zâå3{ç3|Hè3}{é3~®ê3áë3@í3@Gî3@zï3@­ð3@àñ3@ó3@Fô˜@yõÐ@÷@ áøF@ ñúu@ 7ý‘@ ¬ÿ¼@ =ß@ùì@Ø@Ä <@Å 9@B@:]@|{@Ù’@T€@æ!n@f%g@Ô(¥@;,o@à/‰@O3{@Ø6¤@S:@÷=Ñ@ ùAÖ@!ÊEÃ@" IÃ@#cMà@$&Q½@%UÁ@&ÃXÙ@'„\Ä@(]`ç@)!dÐ@*hÓ@+x^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íØA ƒ0†QoÖû߯#tU(BP3Âß×MÕ&ó>[Ámó"@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ëûçú±ŽÌøõ½çMl¢³þÇÏée Üíï~¡–À»§™ýýûȸnôÏè8;…þ³rçUû»¬}è¿v¿êî«ý«ë;¿W@ÿ^ÿîÕõï.лþlÿÞ][ý)ýŸ’\ó{îö_sJ» œõ'—-àùMv_Ó @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€@Uà {)‰Mx^íÚM Â0€QoæýoãDÄM±Ð´™a&}nDèÏä}¡ºðñð"@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @àšÀëù=ÿ÷~íjÎî&°í¿÷¹ÛºÌ{LàhωcžÝŽ:Ûß~èVúÿ¼³úûýÐs?ÌîoôÚú÷ê5{Ú¨þž³KÅ\Oÿ×.Wîï9P{'è_»OôtúG ×¾~Vß5÷þ5»dM¥–tÍûè_³KÖTYý³Öã>cúy­v´þ«[Otÿ±i- ¶x­ûEõ¯µJÓì èï½1»ÿ½5û­~Vÿ~+7ñGàlzkí¿Æj­b+àüö @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€FÞbËY¬x^íÙ1NÄ0†QnÆýoÃ(ˆ•³ÞøŸóh(‚3öû¬4¼½ù!@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€û>Þ¿Þµú}ßDoê$°êþèy§3ØË¾Ànÿßëöw`e¥À]ý݇ʊû³Oõÿ~ïþάLœîï$*îÏHõwö\©ÿIÝþïN÷÷èu'ôïÕ#½›ªþ¾éÒÏÓ¿G‡ª]T÷÷¨*íÿ>©ûQ«0wzªïjÎܵ'_uI=¯U˜;=Õw5gnÚ“¯º¤ž×*̞껚3·@íÉW]RÏkæNOõ]Í™[ öä«.©çµ s§§ú>š3W¾ÇÉõïÑ¡jUý«ÎkîOýg߈tÿÙÚýN¯¿&É¥ú'ÏdÖuÓý¯ïÄ_Vœê_q3Ÿ¸«ÿó“­è °Û¿ÃÞíáu«ý_Ÿä  @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @à? |SÄ%Ox^íÓ1R1 @~ÆÿÃ(B¸µ¥ÑÞ–›„àì‘ÕSûñá @€ @€ @€ @€ @€ @€ @€ @€ @€ @€È|}þä¾úŸ™.õnW½¿úýî÷›_xÕïêïµ×¸=-°ÚïÕóÓ{˜·'pµÏê¹½×¹•¨öºz?½ü5ÕþºÎ¯½Òé”@WŸ»9©½ä^Øí­ëÞµW:•è걚“ÚOîÿÕÞºïëkV »¿jÞìö¦UûJÝ×ÌŒ@ª¿jîÌö¦T{JÝ×ÌŒ@ª¿®Ü…s§tõ”Ê9·™™ÍS½uåÎ(œ;¥«§TιÍÌlžê­+wFáÜ)]=¥rÎmffóTo]¹3 çNéê)•sn33›§zëÊQ8wJWO©œs›™Ù<Õ[5wf{Sª=¥îkfF Õ_5wf{Sª=uß×Ȭ@wÕ¼ÙíM«öÕu_÷tõ·›sÏÖ¦þ ìöÖuO÷ tõ¸šsïÖ¦ßõý“/Õïv÷ü{mí5éïŸð3v¿ç¿î=ck¯ìúþI>[àê÷ÿì-½ž @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ 0!𠊺ðãx^íÓAN1@~ÆÿÃ8 \"¡$Þnï&S\ÂîñTk¿¾ü @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ô~¾ÿfÜ~÷'šp%ûþŸýûJ;x˺À³}?:·þ7ÏxÔëêÿÏÜÉìçVû}õÞó/rr§À«==¿s7³ ísõþã—9±C`µ¿Ô½;šñ¿@ªÇ£9::Gàhoéûç(Ìšî/•7·‘½›§úJçìU˜;-Ý[:on3{6O÷•ÎÛ£0wJº¯VÞ܆º›·úJçv榧{jçÍmª³y»¯t~GanjºŸvÞܦ:›·ûJçw榦ûiçÍmª³y»¯V~Gc^j«Ÿv:·{jåw4楶úiçÎkª³q»§t~GanjºŸvÞܦ:›·ûJçw榦ûiçÍmª³y»¯t~GanjºŸVÞ܆º›·úJçv榧{JçÍmfÏæé¾Òy{æNI÷•Ê›ÛÈÞÍS}¥sö*Ì–îíhÞÜ&ÎÙüh_©ûçlojª¿Õ œ+°ÚÛÑ{çnmúMàh¯Þ'-Wû[=­­½¦ýý~ÕïùþÞ{lë•÷«ý“ü ßñgôh  @€ @€ @€ @€ @€ @€ @€ @€ @€ @€« üÑ)®Rx^íÕAŽÛ0ÀýYþÿ›XìáTCð×— @€ @€ @€ @€ @€ @€ @€ @€ @€Ì üþõwÖwßs71i…ÀUÿW¿¯¸³™9«~ïþž»™¤ »ýÞ}~b3ž ÜíóÓçŸßÔɆÀ§}>=ߨEæ}§ý¥ÎÝ¿±IT©œän²®R½¥r®o쉤@ª·tNrGYß ¤{Kçé®+î«•×U87½ÕW:÷܆º›§{jçu5ÎKo÷•Î?¯¡îÆé~¦òº*ç¤Oõ•žsNCÝMÓ½LçuuöOŸî+=oÿ†º¦û˜ÎëêìŸ>ÝWzÞþ u7L÷±*¯«´oúª¾Òs÷m¨»Yº‡Uy]¥}ÓWõ•ž»oCÝÍÒ=¬Êë*훾ª¯ôÜ}ên–îaU^WißôU}¥çîÛPw³tÓy]ýÓ§ûJÏÛ¿¡î†é>¦óº:û§O÷•ž·CÝ Ó}LåuUÎIŸê+=眆º›¦{içu5ÎKo÷•Î?¯¡îÆé~Zy]…sÓ[}¥sÏm¨»yº§t^w{éé¾Ryš™Hõ•Ê™ÙÚ”©ÞR9š™Hõö4gv[ÓþxÚÛ§ç4ñO{¼{þ[»ÅÔÿ?éw Ü}¯ž÷¶n—þÿ'ú³¼Ï?»?·'@€ @€ @€ @€ @€ @€ @€ @€ @€ @€· ü]×u9x^íÚËMäPP2#ÿ,XÀlFj¹íúø•ßaÓíú Z||ø"@€ @€ @€ @€ @€ @€ @€ @€ @€èøþüùêµ#;Žò?z¿sW³òŽò½ú~þ¦:V\Í÷l]ÅîzÆÎæ}>¾±™Ñ<£õ™·èu^ š_VýùÍUddå—Õ'ã&=ÞÈÊ-»Ïûx2"[v¿Èmj²óªêw|‰'®TåUÕ÷Êj^ TåTÕW–¹U9U÷ÍUØ·[uNÕý÷M.çòê|ªûç(ìÛ¥:Ÿ®þû&»¼+Ÿê91…}««séê¿o‚±Ë»òéšÓدº+—®9û%»¸+—®91ýª¿þþÿ÷ÿ×®¼²çì—`ìâWùÿû~v>]ýb*ûTå?õç`Ÿc—Ê?æ7½ZþÓŒí/ÿ˜ßôjùOO0¶¿üc~Ó«òïú{-{Îô\ºö÷ùO—ôšs²ïîî·¦òº[ÝWöüu¥×Ü,Ûÿî~k*¯»ÕÝyeÍ_WxíͲüïî³¶òºÛÝ[Öüu…×Þ,Ëÿ®>k뮿Ý]¹eÍ]_xí ³rèî³¶êœíºs‹Î›#;cÓhÝõ3TçlÙßÕysDgmz5®ºYšó¶íÊñìœy’37>›Kõó3çn]ç»ýç ÎÞüÝ|ªž›­7ûª\_õ/ö¬ ºò–Ús®©Êÿ9BϾ$šÿ³užÝQþÏp! @€ @€ @€ @€ @€ @€ @€ @€ @€!—2Fx^íÚ]ŠAFÑÙ™ûß…Kp "Ú Cuf~‘õÇ—A¦2²â\{@ñëË/ @€ @€ @€ @€ @€ @€ @€ @€8Oà׿w¿¾ž÷&n>Cà½ÿèïÏxgwæF{ú|î Mªø´çês•;˜=/°Úuöüü;™˜í—:—ÜŬqTÇÔœñ œXHuKÏYÙÉÙÏÒÝÒó>ßÄ“3é^UófvsæX ªWÕÜã<1"PÕ©zîÈŽžý^ ºSÕ|M3U}vÍÍ(ô²«Sõ=} ®m^Ýe×ü5…¾§wõÙuOß’s›ïê²ëž9…¾§~þûÿ?ß}ÝÕ-}Oߢc›õÿ~ºSÕ¼1…¾Oö¿ËŸ‡¾EÇ6_íÿ:_õ9^;¦Ñïiýû5ÿãTÿ«þè]÷x{ýžüDºÿÕ~<¹]b7ýŠ÷ñôþ¯¿?Ü·Pí›ë_ë{õéú_½Píûé_ë{õéOïuÿ³ßOÿ³ œ{ºÿê¿×§ÏŸ«{ýÛõ¿~£Ê7LõOnSó*íž0[ÿ'Tœßaµêsšž3/Òëähÿt§ªy½*Îo{Ô¿ªOõÜy‘^'«;ìžß«Þú¶»ûTß·.ÒkBu]ó{UËm»«Oõ=9‘^“ª»TÏïU+¿muŸªùy‰ž«úTÏíY+¿uu§ôü¼@ï‰é>UózWªÛ¾ªWjnÝæ&ÿHuJÏQg@ºÛê¼=[»å%°Úkõ¼ç ¬ö›=îÖnßýù'~MÙÏïѹknë­ÞŽ:}Ÿè½ÞûÞ{oO€ @€ @€ @€ @€ @€ @€ @€ @€®.ð*ÕèDx^íÙÍmÜ0€Qwæþ»H )!'@`ÀØ¥8”^.>Xß·vþøð @€ @€ @€ @€ @€ @€ @€ @€^ߟ_ç¯~íÝÚéQ«Ý_=µ—95¯zF}¿æ6NYˆê»:guOÏç¬v‹~>çV¦¾+ÝswÞ»{{.F`·WÖû1·3å•@V¿¨¹¯ö÷ý=¨NÙsönéퟲ»EÏW2V ºOÕ¼X…çN«ê•uÎsËÅÜ<«KõÜçM©î”uÞóÊÅÜ8«G×Ü•çLùõ÷ï¿?}íê¸{îs îÝôUÿS?{*Ïyûjÿïíþœf¿ÿœ’×nºÛÿûûÙ=Wç_SyÎ[Ñý§ý^xNÉk7Íêïsp­Gõ[úW‹Ï:/»ÿ”ß³ÔçlSÕßç`Nóÿ7Ñf—ª­ô¯’žyNuÿîÿfVèÛJÿ>û 'ë?¡Bßú÷ÙO8Yÿ úvпÏ~ÂÉúO¨Ð·ƒþ}öN®î¿ú÷ûèç'˜OÚAÿI5êwÑ¿Þ|Ò‰Uý£¯Î›d>i—ìþ«²žŸd>iý'Õ¨ß%«ÖÏñêÜzѳNŒî¿Ú'ûù³jÔo»Û?»ßîüzѳN¼Ú·KöûgUèÛöUÿìNYóûDÏ:9Ë¿kîYúýÛvuŠ>·_òÌ ¢;tÍ;S¿ë®^Qçö ž½AT‡ê9g«ÏÙ¾ºÛîysäî±Énª÷ï¡=ïUý®ž3Oì^]í’ýÞ½”çÞ&»ãêü¹R÷ÜlµOôó÷T=çVÑ=ßwŽÐ½7}·×îs÷V<÷v»]¿¿®Ä37¿Úÿ™ZnM€ @€ @€ @€ @€ @€ @€ @€ @€?Êýx^íÚËmÝ0@Qw–þ»H )!bo çCJäÉÆ ‹#͹O/‹äãà @€ @€ @€ @€ @€ @€ @€ @à~ýŽŸ~>ã)=E—ÀUÿ»¿ïz>s{îö^×ûô¦g¢]£ç²Ïë|­@´cÕ¹ÚmL¨êX5gôù]Ÿ¨êV='·•Ówª»UÏ»»‡ëbÕ½ºæÅ¶sêJ «W×Ü«}ü~L «S÷ܱ-]ý“@w§îùÊæºûÌšŸS8÷ô¬>³îsnÉØæ¿?ÿýï§Ÿ³ºUß'¦qÞ©«þoý\œW2¶q´ÿ÷sÕïoÕ¼˜Ê9§ªú?õópNÉØ¦]ý¿æV½ÇÙ91ýOu÷Êç`ÿ’± gõ÷9ˆõé>5»ÿêÏA·çÛæëÿ¶bµÏ»ª¿ïÚŽÑiúGåö8·ºÿªï=êå·Ð?oøæ ú¿¹^þÙŸÒßßù–‘ úGÔö9£ÿ>-#›èQÛçŒþû´Œl¢DmŸ3úïÓ2²‰þµ}Î<¥öÿ÷Œžß§`nýs~o?­ÿÛ æžuÿÑïíªësjûœÖŸ–‘MVõ¯zGçDŒv>£ÿÎu¯w›Ýô}­¾þZä¬+fõ¯î8:שׁ÷·íî?Ú©ëúû"g]ÙÕ¿«ãèܳjŽo[մˬëÇEÎ:í?«_ô>gUŒo{Õ?ê¿ú\\䬓«;Ußÿ¬zùm«ýWÍËKœ9aU¯ªûžY­n몳çÔ œ=iv·ìýήU¿}¶Ç¬óõ››øO`V¿è}Têˆvé>×»µé_ÝGç+3W`´Oõõs·u·ïÕ=¯æ)ð,«^Ùß?k[OÓõþ“}§ÀÕûýέ<5 @€ @€ @€ @€ @€ @€ @€ @à „È8x^íÙKr1 EQïÌûßE–%¸R¶q%n6  Ñìã‰"Á{}¬·7 € € € € € € € € € € € € € € €}ü~ÿ¬åìcŸ¨d…ÀYï£ëWj²·ŽÀ¨ÏÕuu7rÒ«^g÷Ÿ©ÑÚ<³þ¢÷åÝPòO¢=Få±VC ÊWVN …çž’å-:÷¹†roí);/—ÆóÒ³}eå?ÏTγüTåæPyNj•§ìsžc,ö¦Ù^ªócéìŸöëë÷ŸÑÇjŸ³çío.憣ÞÖÍzÊÞCiß”#¯³Ïg{Íß×\ÌÍfýŽîõ”½.†Ö~)£W×eû=ÊßÏ\ÌV½žÝä)ûùjû¤œõµ>Ûóÿò÷1s“(Ÿ³9ú Æãlʬ·è}Õ}0Ëk·}ÑWóôAm‡­úŠÞÏÿ³ý¿ú©ªji÷;-z~£òø¯é•(_Ñ9Uþ_çÔÐîwJ´·è¼ª>èg¦¦¢h_ÑyüçöA´¯è<þùÿC »r)÷Mž×è¼lï>ÿ}öf´·¨<þs_;¢³Z^G¯§ü¥rö1zn¥£~ÏÎKyÍ9GeŸÑg=^?ê/5o³}+½êst~ÊçÙœ>*ûŒõ—šwÖ_jü>†¿^iÊãÕœ”×Þþ? \õ–žßë/5n÷}ö—ÊKùmåðïØy¤úuVN«S×wݳ¼¥rS~[9ü×|Xý¿änû Õ§³sZý›ºÎÿÞçÿüï´fŸÛéüÔ9ÿ.g'÷ÿkíþ³;´Å»Úuþ÷ö¿êû`–rÝ´jýÝ[s ³§zyWÇ?ÿ¿ÌÚºõSªõu«žY¾ÜúƲ¶xW»Î?ÿÎýܨÖß­zôνßÿþþü˜¥[?­ÕoU®Ïî{ŸÿöþßÿšþõýÜ÷*çû»:øßÓÿ*ﻞûÇ®ªÚÿ«üÏí®úéÕü¯ò¾{ßWëÞï9+îîÿÕÞõýŸûì.ÿ¼ßÓï¯w]íÿ.ïúþßûm•Þkôûªþ¿Û÷ëýkÒ¿¿ªTÿWóí¼ïÛ[£þ«úæ½Ïû»ïÿÕ½¶ê;·z£[<ŸrÉ1Oñû®Î±U›uxª3žæ?³j)OëÆæ¨ÞÿsV-µjÿ3³–ÀÝý¿vµîöJ`µj˜í¿ÖjU3«ÿ‘}&ÿ“>Ó›ª@@@@@@@@@@@@@@þ7?Ø{%x^íÛÁm#1DQgæü³Ø‚aÈ:ìÚá]jø|ñAì"ù·Fá? € € € € € € € € € € € € € °&¯ÏǹŽ~¯yz§%päýìë£çQ?—ÀY¿½ëçÞÊn­z}ŽÖµžÏº,QUõÙ[JE Ê_ucsT{«Î›Caß]ª}¥óö5•¹yÚW*?Cc¿Ô”ŸY¹û«½ñ,Oé}j©ì“–ö2;s57ýóûýÿÙß³½žÝ¯†ÎýSÎz?ZÖSzýý ŽÝðÈçèëi¿­ùc”î[=ê·µ¾ÕSjÝ} ŽÝ¬Õ_õº”ç£Ü1Z÷«®öz6ïÈWêõû™ì»ÑY_©õ)ϯrûhݯ*å³7WÌí±^OéºY}0—öz»¥=ŽæëƒlÏŒúI×ó¿·ÿgéƒL¤ç·:?ÝÊë¦VûIçñ_ÛKi_©|}PÓ)?é\þùÿ!ꃺ매ç4ÏÿX¥ý¤óùçßs ¿Òó™ÎOÍÿ3·Ÿì{T¦ý¤óùë³´Ÿt>ÿü{þ÷÷@z>Óùæ¿ßýOeÚO:Ÿþ½ÿ÷÷@z>Óùæ¿ß½÷ÿ㿌Ñ]¿:=ŸéüÔü¯o®æ„i?é|þÇú í'•Ÿò¾Ë÷¾Ï®IùIçò?6÷ü?ìþÿaé9­Î7÷5sÿ®óÏÿžþÓÞwûÜ÷.óÏ{í¼ÿ›Vý|®ÎãOÿ¼g½¯úþ?Ëû®ÏûUßÿyŸ3ï«ùŸíÝÜÿÝÕŸ×Zóx¿fÞ¯šÿ«|›÷ÿ÷Yë¼ö®»Ú;ÿsý¯â›÷¶çË»Ïõî¿k³üzÕjó:zžQ»Õò^¥~7oU÷]Å_ï9ª8ìšÓËýêº]}Ußûjg÷¯¾ÿîygùÏ^¿»ŸôýgûlÝ/}où­>Òëø¸†@Ú«ïg®ñÚºë,ÿ­ç±n.jÿsOo·Q­þG÷Q € € € € € € € € € € € € € pDàÚ}"x^í˜ÝmÃ0 »Y÷ߢ#t„"H ´ýII——þ„ìøŽ€Õ§5;¹¬þ¼ãsiœ·»·?ï|ç‰=±·/U¾X*çì¦ò¥Ê{Ž™˜“ªæ|Nâíu4_Ë—ê¹½õ³ŒúR¯Wù¾Ë»¾AÛ Ô>góGõÞúѳ~¢âèmEyœÝÿgû¿ú†>ÐôÁì\fÅ©û@C¹nÖ,³ûªý_ùëó­lÖCvœº|)×Í–íѺ¿ªêó­ÌÊ?;^åÿ”÷@¶?ëþø·ÝVþUâU}`£[?ºŠ?køŸë5+÷*ñø?Û¿ú;áÝúQUæ×«óâ^%þñÿ àÝcT×Y]en½êðö¾ûw /îUòàìî©âÍ«üãŸ÷xÍ]•<Ì¿ûÇÊ*Þ¼êÀ?þ¹ÿû{ÀkîªäaþûÝïtÿã}ÌûµºÊÜZëÀ?þyï÷€uî²ãUs¿ûwß]îüÏü߈ìùµî¯òo£ºN´•V¼Êû)÷þª÷¿Ú;þŸ‘5×­}Õþ×¹¹}*mñ®ò\íý´¹_åþǻϜße©2ßwuàÿLÿx×z¯zÿGy?õ}ÿÚUÙ÷´o¼ÿï€,ÿx¹ß[»DùÏòͼ¿ïµÿlïøñ_Åók­ûïôç£ó_Õ3Þç:yŸ½uÎQ87ª—kõuç´¼º×V}¶ÓÝâ[í9Æ| Tó{Wï©Év¨îSZÕükOKöWÙþ1’K ÚîiÙ=jþ!½Ñù_ãTT @€ @€ @€ @€ @€ @€ @X•Àóº¾tx^í›Qn1 s³Þÿ=BŽÆ…#‰z|K®&?ùX‘”fHyá ü@€ @€ @€ @€ @€ @€ @µ üùõØßêïÚ§bw³V½Ï®Ÿ­Ïºk ÌúT­»ö´T% ò̓‘k D½eÅ]Kã¼êYUyÏ3â=±Ê“+—Îý«¹¼©ëÜߌç„j/î|J÷­âö•UニrOöûëû¿w¿³|eçÍ¥vŸì#ÿ£çÙwóßÇTÎIF~£Ïw½©âs¨Ý'kÔïjœÊg4Ï}ŒiO²êQµ>êq7NK¯6•Ïhž]ŸÑøþæ4'ˆzËŠ‹úŒÆi(öÍ’åq7oÔçj\_sšïzÊŽ_õ]¯¡Ù/K¶?Uþ¨×Ù¸~æ4;Vùqå™õ]§¡Ú'‹Ë›ªNÔël\sšª¼¸óÌúŒ®ÓЭŸÅíM]/êwWßœf‡jî|#»Ï5”ëfqûʪ·ëù]|]sšeùpçŬܞ²ëÑk}íÃÿøÿK@ÝkTû¬vϧ«þçzÐåÃ]Gíÿ™oŽjŸUn/®zøŸëA—wüŸíÿÙoê>˜£Úg•{.Ýõðÿs/º}¸ëáÿÊïúÜìs;uÏ£»óÏü3ÿï{À=îzÌ?óÏüŸ7ÿê¹çûßG¹ïïh=üŸýþüó¹?îèýZ5޹;ÿwEUÑ}áÿÊ{ÿ®ïýÏ.‰ÎYµ8æ~mîñÿ 0ê›Õ>QÕæxu?#»Ïû˜Œít•wµõ»~Oý¿¯î÷–÷»¿ï½ÞÕæy´¼ÇîùwQ#ÞÕžãÿLÿx×zïòùŸíý´Ïû.ŸÿxÏ™÷êþ]ÞOŸû*÷¿Û7Þ¿ßW½ßãÝs¿ª¸ü_å›yÿ¹²ü_íï£É<ßõ_ÅóéßãÏÙþÕÈu¿xšŸûûwÿ{Îîâ—9ÏéÑ®þshœ—µ›ÿó 垸ºÿÜÓ“½šŒx \íß{Zª½pûÇ@-Yþk’ݼ#õQ@€ @€ @€ @€ @€ @€ @È"ð ÙFIx^íšAR$1 ùÙþÿ<'l0fwè–\RÉÝÉ…ÃØ’œiÙ†˜·7~ @€ @€ @€ @€ @€ @€æøøóUcö÷üRáo²ÞÏ΃þlg=ªÇͦrŸêÔ^³ñîC|ÖJ³¾ªçÍ¢tÝjª=ªâ_×€we*?]q¼´®—½Ë›:ÏõLxV¤öâŠç¡·V—¯ª¼ûé]Áû÷ÿÿ¢¿«ü©âöRÜ7[ÔûÑx•?Uœ}ÍôT~äsõs•ÇÕ8=4÷˲ê7:Õãêüý ÕVõ§¿ê3;¿–ê>ÑÕ>³ñ²³óö1T[iÖWõ¼¬×è¼Zºó£W{\õ™?ßTM…«~ºæg½FçÕPžµËŸ*OÔgtü\S5•©¼tljzޝ¡=/j·7U¾¨Ïèøy¦j*Rùpʼnzޝ¡>'ªË›:oÔëÙñsLÕT¢öàŠwÖgv\ }T—¯ª¼Y¿Góü¦j*¨òàŠ{ä1ûy }T—§ê¼YÏGóüÆ´T{pÅ?ò˜ý\KßÍå§+oÖó«y~cÚ º<¸òàÿ÷ýâòÒ•WíÿOÛ…¾h]ÜyÔûÀgL›Ùí¥+?þÿ¿oºø»óàÿŸTû@{ û¢¹û²+¿Ê;￯½ÚåM•ÿœÿœÿÿîUMCÿÓÿô?ý¯:|/vmæé綪>•wÞÿ¼ÿµè¦ê¯éqèÞ¼ÿî÷þS÷=÷ÿ^÷¿Ú¿÷¶ÖgŸ~o¯Ö‡ÿ{~ÿGíýjçþcW¬ö×Ôùø?wWLõ—­ ïç¼_µÿñOÿUÞ¯zï_­ÿ«üǺi¿ÑÙ{vʼ*ïWïûÝûïš³fJGëÀÿ=ýã]ã}·ó¿Úû]îûçÝ=w»Çã]Ûï»øÇ{­÷iç—ﻞó¯vS÷yþœ¯Û;þî„nÿ.ßx÷|ÿÏíï½ßÿ˜âïçÞÙóšççzέžQÓ=FëÃhŒ@”ïÔñ±U3úA`ªÏ£º0¨!pÄyÚçšUe·þÇX iýÍßm5ž_Eâ¿wÕdsŸÿ˜A ºÿg¬’*ªÎÈB€ @€ @€ @€ @€ @€ @þ*üæ{x^íšmn$! s³Üÿ9Âa%£Ý|ô4ØÃʟHlL†îQ^^ø @€ @€ @€ @€ @€ @ÀƒÀŸ×:£¿=VI•W¢Þ[ã ¿6VêqkS9§:µ×h¾sˆ¯µÒ¨¯ÑqkQÚ·šÑUù÷50we*?UyæÒÚoö*o£æÙÏHíŠFy©Î[KmŸÙÞ>¿ÿùþ»ÚŸj¾}ÌÔ¬äÊëßUÞÔyjèùÏÒê¹wœÚg6Ÿ¿©1+èõšŸõCÏ?kÖg4>ê1çoL»‚¨?u\Öko¼–¢o6µÇl¾^Ùñ¾æ4•g}ŽÏúm×ÐôË2ÚŸ*«Çè8?sšŠU~ªòDý¶Æi¨úd©ò¦ž§ÕgtœÁ\¥j/Õù¢~ïârT}¢«}©ç»ó˜ýÜÇd¬RµYù²ž¯âcT}¢fù5/û oïò0+/þÏöÿØw샶}0«O«æUïƒ6ª>£ª<ÌšÿÏ÷â,/Uóªý?òùtøÙþG=àÿƒ@UgçQŸøÇÿ{ ÛWnñœ_w­›¿l½øÇÿ;Õ>p¿²ýä¯ò¾Ë{ ›¿l½ø?ûüWÀùïõˆúŸç¿{ {ŸºÆ«ž8ÿ9ÿ÷€kÿfë¦ÿ=ûvï¼ÿ{îUßãÿÎ÷þ£öìyêOÿŸýþÿ3ýãý÷ÛÊíüŽÖ‹üó½ïÏ=í'—8ußïòÞwÊó¿Úÿï|ÿ¯Á¥£uâÿùŽr]=Ní}·s×óï}7Ôê}Ü[þÏô÷>ﻜÿ£¼ïzßß%½çëjãGùu“_Ôj>[ëåý”¾w=ÿñ®=cZûmö8¼k½»ôÿhï§÷«?ÿUù>Ýûjý÷1çû]ÖY÷zµoú}ÿÿÀû]GÖ~>ªÿgy¾š·–ªÏlYÿ«yæœïÛ{WþWõzWWßê}ÇÓåsLƸøå^ù½‹ró·>ï#àâ¿oUŒn%°ªÿÖú—#°ŠÿÜ*ˆŽ˜å?Z/qZ£ýk«%›š@Ö¿ºòA€ @€ @€ @€ @€ @€ <üôÅiÔx^íšKr! }³Üÿ9BŽJ9³°Ëc3ðH¢³ñ"ƒÝàÏíÆ @€ @€ @€ @€ @€ ä!ðóuϵ÷5ÏJÉô^ï­ã ›@«Gõs±©ì“Úko¼}ˆÇZi¯/÷¸X”êfãö¨Š_×ÀÚ•©üÌŽ³–ZÙg{SÏWÇÄš•¨}¬Š·†^þY¿ÿ?ÿùôºÊkï¼ùÍÌYÁ'ï­ÿïõä7‡bÞYZýö>çöÛ?¯!oæ½^{ǵúr=祙/z¯GÕ8—çOqó™òd¬ò¨ŠóÉ›úÿªy¢ª¼©ã¨=ŸÅËcÊ“©Ú›:uàñ~DUûrÇs׃—v¼èn_®øÔ¦–\~fÅuÕ†nü(³<¹æqù?âÆ78–¡ËË츮:£ôlOîù¨ƒk5çö±*¾º®QÍóô*?îyÕþ«ÞÜVÇW×AžÎnËtµ÷üjÿÕö7ÿ(ñÕuÐÖ]ñŸŠâÇþ_×¢›{´øÔÁcDóãÎÿøÿ# ªƒø'üû Ýý5>þïuÕ;/•ÿìïÝœ£ÆÇ?ýÏ=`ßýÿØ—Tû@Ö{`ÔýyV^øçPœôÎ:¢ÿszSøÇ?ûÿ¾ïèúŸþ§ÿG÷îÿ9÷‘Qï|þ¿·wüã?ëÞÏ÷¿üDõ9J¶8»ŸûÇž•ÍÛh¾*ïÙÏ}üß ŒÖCæ³ÇóÔ÷óxüç¸ÿãýu¥Žž§YÆãOÿjïUî}»ÜÿÔþ³Ÿ÷ÏùgÙ¿¯æ©ö^­ï«÷¿Úµ¾¯ê_í½jßWó÷¾êê¹íy—÷ê}_¥ÿ]þûº)ߨhýÜšËû.}Ÿµÿñ®ÝcZûmÕsnß»õ{–Ïð®íó³h«úúlÞYÞwïû(çÿlßxÜ f÷ÿ*ßx_óýïjßxPõÏÕ~Ÿå¾¶úê÷,/7·*ñ³yÅ·¶ò²û×ÒØ/ZVÿû™ò¬8‹Ïê‰Õ?fæˆâÎj™å™À*ÿ˜ˆAÀí?Æ*ÉâŒÀ¨ÈB€ @€ @€ @€ @€ @€ à$ð `Øù´x^í™Krã0 }³Üÿ9BŽr9^XeÅ”øðybÏ&‹! ¨ Dúvã @€ @€ @€ @€ @€ àEàçë‘ïÙ¿^OK¶[g½Îƒxo£ÕãzSY';µ×³ë­C¼×“žõ=¯¥ëfíQµþu Ô>™ÊOö:µÔ®=Û›:ÞuLÔ<‰ÚGõz5}£~ÿÝÿŒþ­ö;ß×Hnæ£ÞGÇúÉ—KÓ/Ú¨×ÙqY¾÷âø™ÉÉxÖëÙùUõCÕ'ÊYêyÙõàc(6SµÇÙõ¨ƒXßÛÕg}EÏϪ‡\ê}¢EûS­OÄÔŒÊOÖ:ÑuC¹ïªYÞÔq¨MM©½d¯GÌÕA¶¯¨xQu0G·ÿì(UëRÇj®ÊSt\u£ê3:ÚCÕújÿÏõ|ÌŽeZå'+®ºƨúŒÊòP‡:x_“Õ^²âãmÿÏ:£^ë «ÿºÄÁÿÚþÕû€Ï—ûÿ€Ú¿û¹°Ë¾œïÇ~ͽ[r›Oß¿V€›¿Ù|ñ¿¦¼¿?cÎö“Ë|ü¯éïÿß-¹ôïÑ<ÕÞÝÏù{Up”«ËxµÿºÚØÈ.>GóT{¿jß_íþïçö‰Ñ¾ê:.ÊûÕûÞ½ÿñ~®ßÝïñ®ñîÖÿÑÞWÙïÝúïÚ~ïî?Ë÷ªýÞÕ?Þcû¼Ûý_¶oú½ö÷Ÿ*ßxÏýý§Úó6~Í®êõè½^7¿{ùø¨ÍÔÅçhžµ4ý¢rí:Îx¯Œ»zý”W/оÙ|âÜåÿ} ÷μ‹_¾Ûkꤋÿš§'j•È÷ í¿ÇS’ÅYÿ… @€ @€ @€ @€ @€ h¿­‚‚©x^í˜A–! çf¹ÿ-r„!/ÏãÅøÙ¡i}¤/¨ÙÌ¢AUˆ¦ýõÅ @€ @€ @€ @€ @€@?~=jžýßo¥TüŽÀ¬÷ÙñP÷&0ëS5Þ›Ê9Õ©|FãœCÜk¥Qo«æ{QÚ·šUþÔq÷5P»2µ§¬xµÔöÉžåkUž}LÔ¬d•—ª¸5ûfýýýûÏìÿ*¿Wóö5’[ù¬÷«ã¯zZ=.—f¿lW}FÇ­ö<ŠßÏLNÅQ¯Ñù#oêç9Tûd‰úSÍW{Åëchm¥*ê8#ªçkéúGW{SÇSyÅñ7µ¦Bµ¯ÕñF£Ï×PöºÚתøQÏ£ù¾Æ´•­ò“wäñîs-eßhYžVç¹ëy4Ïל¦²Õ^²ã|Þ}®¡í%ÛOV¾»ž?Íó3§©(ËGvµÿg< uŸ(Ù^²ó©÷9M%Ù>ªò±Þï—*Ùyñ¶ÿç~cüÜÙ}è’Oµ4oáº(.>²ëPùïþ]ÍÝ%þgŽ‹ª:Tû îe®âî’Wå¿ë{ÀÅCuª}ëÆüÙÕÜ]òãÿìûþñÿ@täŸà±Œ.ç¯KQÿÝî.Ü]êÀ?ï“Þ.}çRýOÿÓÿçþ.LÿÓÿô?ý=b_åy³]î].uD½óýßûýÿÞþ¢çþñÏýû_ôÈ»ÁÅ2EÏËÝæG½sÿëùþ8ÍûóÔØ­ï®ÿ=ûö®ï×yøÇÿI÷~ÎÿUßw»÷á_ë?öV7[õþìçô¾?½ÿñÏ}Oñþ¯;¹5™»ÛÑzéûŸû&ʳË|¼¿?/ºø‹Ö‰ÿ3ýãýÿ÷„h_¹ÎW{ïúûÎè–èê/Z—Úÿˆc×çQÎnóÕÞwíûÝ~ÿÁû½È­gëÁû=ïÝû•÷ÝÏû×Ý2ÛoÕãñë÷nþWû>­ß»øÇ»¶Ï?E«>ÏŸù³|ŸÞïný÷œ>¯îÿlϯùj)ûf_uþWû朿¶çîúwñû©Žk«g”»ÇÙú0:G`–¯Ûø¹Õ2ú•€›Ï«õ`RCà*ïêqšÕ¥Kÿc*‡@u_ó–ãùS–*ÿµ«&û“Àjÿö&p׿÷ª¨€ @€ @€ @€ @€ @»ø v²dx^í™Avà s³Þÿ=BЗ—z¿¸$¾˜n²ˆbFâ>üA€ @€ @€ @€ @€  Iàçë•wë§æ*ÉúŠ@«÷Þç ®A ×«×ótêgéåÓ§>éœ+´z‹Ÿ“V½¬¢üyÇ­G>ÇŠ¼=ÍŠ—ƒž~³|EÏ£obÍ ¢½ÌŽ¿†¢î¬ßïF?gûmO×ÈÜÌG½ßkõýÜ\šz³Ýyôþ>Ú÷]|=C±{ûíwç+êûXª:Ñ{}E?åû*®Ž©˜L£}ŽÆ§b|Ÿ£Žú™=.ºæÐÎ7ËlÖù¨ß²úX5ž:ð©ƒUþ¼æ¥luàåauê`¬V{óž?ªÆèæåÍ?K<ê ­ö²øŠÊûÚ¨ê<Å=[\êàsMfó•þ÷öÔuð^Qý–=®Wèœôôÿ“ûýQªû@ö}::?¯sàˆ£VÑ|Uâ{Õþ_T¼{ßð¥PëÓè|w;¢yªÅÇ¿æþíUgøßÛ¿×}PåàÕ7ÕâX÷ükï#Vÿ*õ­×zð¯Ý¿Ö:À?þŸ¬uý`퓪ã­Þ9ÿµ÷ükû³îKøÇ?ç¿Þÿq­}ïõþó_{ÿ`ÿ×ögÝðÎÎÿÑ} û{Ÿ#?ë>Yuü¨w•{þ?ŸoVïø×¾7à_ÛŸõ<ÂÿžþwóÎùÿ^çøß³ïw{ß{þ]j=/ÕÇ{õ½Ú½ŸýÿEÀË¿Êûúß×»jßïÞÿ»÷ý®þ½¼«÷ýnþñþù†¢~oÍÿ{úÇûÿ¿LZûGí9oïUÎûê¿ÿðÞ÷&B­¯¯òò^µï«ÜÿñÞ×ïUö¼Û¼«ö?Þ}¼«ùÇ»¯÷ìþ£}ŸãÇÐÍ5ÛýïskfµÿÙ¾«ÿžë­žÙþWùÆûÜ÷ÿ«=ã»m'°öÏÜçÚ|ŸŸÊêo4¯1 ûŽåœmܾm+Ïæ±5Ûª}hå½ú9ŒÅXíõjþ˜Õ5ëý3k¬êÿ5«eÖYýi ½ý¯±*²„ @€ @€ @€ @€ @ ü‡’fx^í™Ûm#1 ÓÙõßÅ•p%Ï@lØæJ|‹“Ÿ|¬ž3$¥]}ñ@€ @€ @€ @€ @} üûs[»ô¿ïYù'’÷ÝçPïA`×ïn¿Tæ¬r×£u¿9ÄkíÔÚ£Õxµ(»+_Þãœk wgÞÞ¼ÆÏ¥vÎì^~¢Æ=ÇDÎN¢³I­Ÿ{{–Æïc&f¥Ö~WÇ“|y=¡[–U_Þí½|¿·¾!ßzûÔŽ¾”뎮õÕŸ8ð‰¡(Öóxǃíz£Z{‰Ï+ê™òYQ´/¯ùˆƒ½øðò‘5.q°Yž¼çµŽƒ5ª}Z{{ÈŸ8ø‹Ù~¢æ'^ÇAÿìyð?Ûÿ=þˆƒÇ8ÈÎˬù­â ÏMüÿ!@ ÿ=â kȪ¿Uæµ:îãt‹ƒ*²×aø¿Èö¹:?þ{z[õ,µŸiÏñ?»à¶ëïÕïƒÓêûÕýN©WyLk‡ÎÚ8 þ÷Ž#ü÷ö§=·ðÿ“Ïm~œÞ_›ÿÕ:ÝŸvø§þSÿûýž§Íû)ß­8:õŸúOý§þkë@ÕÖm«}i½óþ×ûüÀoÚ: õ_µîß×¥åszü“ÿšû?ùß3~´y_ýÞGýÿ—øï™·V÷üÏô?Å;õÿu|ãfÞOù½ïù}Äê¼ì>δ¼§þ?Ö9üϬûVÞ»¼ïSÿÉûß1ÐýÜÞ]ÿô¼Ÿ~þ[ù¯þ}_Zßnþtígå½ëy?õüÇûëJÐ5W×ÿ™þñþù°šG]Úã]ºùù½ÇÚû)÷¼wÑÐ%Ÿ¥uâýZ¾ŸvÿÇûž÷îß¼¼Ÿ^ï»æ¿·ïiÞ»ä?Þuõ]ê-Ý«¢ŸGùžšïÕê´o¼?FÀéùý_R=œöÜËV^¿›wš×«ûÕú¯æ™ú~Õü­]U»ëZÛ=­w9Wé‡A*WסÛ5½ïV¹gµÇ˜,ŸÒ¼>»eÔg’‡¨ç˜É!å—÷²¿Ò¬^þ¥yy^ƒÀUÿ5VË* @€ @€ @€ @€ @Ó |îó¿x^íšmNÃ0 @¹÷¿Gàh•Xµ‘¥þ¶Zã$ïÙnÓññÁ @€ @€ @€ @€ P›À÷ç}ýÇïÚ»aõ»Îþ¥ïÎÏõ±¤¾wÇÇî–ÙÏvýi_‘XÚ>µâÅR™3»–/ë8sŒøîÔÚ›U|_J}g³òã·¯ŸyyòšÇ‡ZŸY¾~ßÿ\ýíåuwž>†lwrÕûjÜ®/ëëm)Ö¾ò¨ý¹µçUüº¦lV®íw7ÞÊ—Õç64ëEÝõe}½•ïWqëÓ]±µOi|¯|Ð¥Z'šÔ×xòÀ&§¼üiÏc6´óEÕöâ<å”·/«ù¬ò@F7ÿh+QqɃ½œ‹òd=/yð^X{ˆŽ¯ïQ­sU´¯ùɃç9éÅ?Ë<äÁcdñâµüÏöäypϯºË:VÔyâ£þÿæ½–ÿ#Nµ<ÈZ—ÞëÒÊü×¼¯à¿¦7í>1-´ùU‡úÀÀ”<¨^¯VëÇÿì>€ÿÙþµÞf?ZõÏ.q»÷.ž¬öîç¬÷«ºéWÚð_»à¿¶?i?Â?þ%ÏôÿÚùCý×öÝÿ³þˆ”Ë”ñÒúÇíþÿÚþ¤} ÿøçùîÿ‹KëŸó_íþÿÚþ¢ïÿÔíü¡þkû£þŸw )—îã¥uŸõ½Ï‘ ÝýI÷‡ú~Çs?õÿ^w¯{üãÿF@zì:žúŸyߟâþÿ<¿ñOÝw~Þ?¿êzÿÞÝ×´º§ÿß hyÏþžïÕ÷O»uÒíz-ÿY¿ß[­«›Ïw÷£å½jÝOíÿxìïÖK—ëð?Ó?Þg~ÿ÷ÿŸ»ôõó>´½WΛrþÃûêÄ×óþ÷=ï]ÎVÞ»öû.ïÿñ~­Þ«ú·ö=¥Þ«ùÇ»NWyþ÷ò=µÞ³Õ¿·o¼Çžÿ¢|ãÝ÷ýo´çóü¶wѺѥï³y¦Î÷r1«¿«ëÚÛ=W_åœee²xÜ]‡l׌>ìrºc6¢|®æµÙ-QÏV¬?ÇH,k¿œÃcý®f×ö¿šÏs >sù`5€ @€ @€ @€ @€Ö~}px^íšÁU1 éŒþ» JàåAÉKX[–¥oi¸pˆmig,ÙYøøà€ @€ @€ @€ @8›À÷çoþ«¿Ï¦Ð7ûUï£óûÖ~òQ»ÆiÓ©ŸÝ.¯Öuë×zB«§èyZÔêdíq5^òO²ê#{¾Ås³Èöçÿ\9™ý}ÿŸýííÍ{½šçEõ>:ÞÛ§u½óŒÄf<êÓkœÕ£×¼XºúѼ¼Z×ñò:»Ž¾™˜ ­ÞvÍ›õ¸:>†²n”]½Ö]õ;:_×ÐÞ̼àÑâoôsUëO%¯ê}@…³jøç¨|¨ÖZ^«}`îTŽ­ÆY5ŸUÿªPå­–þ¹T¼¨Õ™z>«} îd‹¤Î[-?ü÷>ðÿ•{ÀXWŽ¥Ö_Õó¡þ©ê¿ïÿ QÿÔ?õOý[û@ÜÍn,’ú}K-?«wÞÿ×87ð_㵯àÿÜÿúÝÿVëžóÿì¾ÿ³ýYÏûû¼Uÿc߯âG­ré2ÿ=ëÕ»ê¹ï4]ê×úœøïY÷ÕÏ}êÿÿ}]½îñÿë¹Xu^—º§þ_×?þ{Þ÷ºy§þ÷9þ©û•¿ë©¿çy÷f¹ê=nô¹ºÖ}÷þßÝ{Wÿ^ÞOí÷ÏçÀhŸ<}Þ_ßN÷z•¿·÷*u_½ÿã}ìI®êç´Ïñ>æ½Zýã}Î{ÿ»¼W;竽ÿÁ»­ÞOÿþ‡wï§ôÿݾ»ôùSú”ïîÞUê?Ú7Þ;Aô÷û,ßx}ÿ›íßc÷Dký«ø}—ÇØÓ3JÝãl~#0ËWmüÜÓ2ú™€šÏ«|0èKàŠ·Êç¾OÍjw*~¹¯çìÉlÿ9OMÔèú‡¸&¯ú×|:²‚ @€ @€ @€ @€ P‘ÀÂ>ø@€ @€ @€ @€ 0ŸÀŸ_¯5ìþ=å¬@ã}rÖ¾Ùõé5þ,ŠsWãåÓ+Î\’3+÷òg&Õ9UGy‹Š;‡ìŒJ£{–§¬<»~¥ããMÔdÈò’GêU:®ÆN|Öl/Ùù¤~¥ãâäfÈöQ•OêW:.×R\¶*Uy¥~WãâŒäF®òP•wåu÷y®-ÿlUªóîz^÷7“±ÚCuþ•Wéó[þYªùwÉ/õ¼ço(6bþÕu¬¼JŸÇÚò^ͽ[~©çÕ8S1»ñ¯®gåUú<Æ–ÔjÞ]óK=¯ÆùóØ•u]+¯Òç¾¶ü£Usîž_êùiœ¿1߈ÝùW×gõÿžïkÍ/Z5ß)ù­ûÀϘo¤)ü«ëÄÿkßU{¨Êoõßõ¨â95¯uøvo{´©ªêÆ?÷À?Ú}`?±¾ªÎÑÔ¼ZïÜÿgô «ÿnû`ê9¬®Ûº|»¸>Z5Ç©ùñF?×î?üãÿ„ïíþ¿}çŸóÏùçß´}@ÿÆî;óö>®]¿Ö;ßÿgÜø?Ã#çÿnøÇ¿æÿµÐÿïÜ7Vï¼ÿÍÞ7Vÿ¾_oöhÚûïÖyøŸ}~­ûÿø?áwß÷Ía=·Ì·žûnï}øßëcVÿö7µ˜·œ_ë:ñ¿w^¬¼»Ì·zïÚ÷éÿ²ýlõÓµý¢v9gÝê°zï~î9ÿ?ŸüËúc·sk­çïœÿï÷7þ9÷'ý¾·zS´öËSæßvîéÿ/·z¿Ý¿—÷)ßyO÷À)ý[º¼ÿ¿¤ÜNçåõ^5åù)^Wëðò>½ßÝ—+nÓŸãýçN4ÝïSýx—Ý@§ù÷ö~Z¿?­ÿGù>Ýûôï¼ËúûjÔ´þ÷•ѽçÝýGû¾¥ÏOùý/Ë÷íÞ»ÜÿÙ¾ñ^ûûo•o¼DÝÿÕžñ-{ÜõßÅëªÙêµâ8å9&u¦ø}ªS·jf½ Ló9_Ýýû®–h_ tó¡\UþsWI¶'Ñþ!ß›€ÖïUQ @€ @€ @€ @€N'ð{±zðx^íšÍuÜ0 ÝYúï"%¤„<¿v¤%Eü} Ç—=H"Á¤výñÁ @€ @€ @€ @{øó뵎»Ï=VÉ*îŒü¯^‡x«~WŸëAåœ(W=z?wq­•z{ôO‹Ö~ÑxûŠo?µ+Šö5~-µ}fò“=î>FrWòûßûÿè3Û§u¾\Š}gyzÝêÍûù¾fr"êwõ~o¯OÇË¡Ùo–UŸ^Ï=õèu?S1{yôÇËïì81TûŒêå-jœYÖûúó4Ê[Ô¸VÏ£ç}éêå)zÜ‘Gëu}s>F{Šßêyô¼eÝQ¢ýd?ò¹z]× -²l?Yó­z¾{ÎFY÷é,Uóïs¯ÊKö¼äÁud{¨ž<øžÕ>ªæ÷ÊÝ}.²*þÕózùÿg޶Þ]Õªç÷Ê=³sUóW™ÿÔí=ŸÕ»Zß§þŸå±Õ¿ZßÇ?þ? x÷ÉÝÆ³Ö½jß§þçêßê_µïãÿ½«wõºÇ?þÙÿÿ?ÿœR÷Ôÿuýãî\Ä9ÿÅég¾¨Ÿ÷~Æ·›Ç§ë9­Þñ]·Ö<èV÷§ïÿVß]ÞïFyù´_v¿ßË;þ{ñ~Ý º×ó(~¼¿ßFüº^ÇûhçïÕ¿góÐÛû.ûü]6ÌrU½/Ê÷îÞ»¿ÿá}®¿îR­ë»¸ð>2ú캺ÿhß§ôù.û–ïÓ½«ìÿÙ¾ñþ½dõÿ*ÏøÎýþ§Ús÷ßãŸÞìwÏÖ¿š×QµÆíúÓzΗnþlZµâhyÞ“ßí µ¼YÅÙõ(}ΖrÞèVÞ´ãJýîŽÏkÊffÚž¼âíú¼}Άv¾¨^¾¬òÜúÝ—Ϙ¼DÅÝõzúœ.õ<Ñ¢1Ý™X{ˆŽ¿òzúº.ýøhÑ~¼òŸz^=oNg^ü³äYy=}]ÇB\”,^¼çqêùéù8s:™½¹gË7}dóá=-ÿ¯8:UéÅ›wÖ|ZûÀÏœN¦¬>¢æ5mDqΚÿ_}%«¯yMÙ^<«æ‘îSÚ.JU/^ó–úÏ~/ðâX=tØU°,ru/^óÇ?ï ? tÛ^õÓ%þg÷üÏöÿêc]öA—¾ì½üÏîøÇ¿ä> »µëöî›ÝòUïÝ|x¯ÿœ•Ïïzé–ú§þ©ÿ¹ß¡þg׿ôó@½›Ü]¤nçqÔznûÀ5½QQ¼ºåÅÿìsÿø¿¹èuò»HÝúpÔz¨ÿ™õë=Ë÷‚£ê¥K^üϬ{齟úï±o¨ÿoÏ#üãÿæÞGÿ¯½oª×ýëÓ‚Û¾7}þkׯtÿâÿ•Ï}úÿÝþíR÷øÇÿ'é98m<õW7Õ÷I7ïôÿ³}Œÿ3^Õë]ëÿ;Y>ç{úvHOVëèZ÷ôÿŸû™Ô{öºÇÿ÷þ§xÇ?þ¹ÿÿÿücZÝSÿ_´¼W9ïßïVÄÕòÿt¿Êþ÷*ž´ç9ÝûÔþ¯å½j¿ŸÚÿñþýI¤ÝW³ÅÃûÏï@²ùÒšÞ÷ÞyjñÎGÛ{—s¾ëÿ¬|w÷^ýý?Þ÷úûê©,}{5kßSê½Úýï« –½¾ª;ï×½|O­÷lõoYýJG{Õ··ç÷|RN]Çkùöû”¿«7­u=ùÏês5/-.Sâ¬xVy}Š/íuVñËy®mÞæûOVûÉfõDµò%‹ROÒñ>«$Ë©¿ÝñÈI`×ß깜«cV€ @€ @€ @€ @ 3¿œ¶Ox^íšËqë0 ÓYúï"%¤„ŒÇÑÁÛ¢ð±ï’ƒE‚Ü šÏ__üƒ @€ @€ @€ }ü~ß÷rõï>fïäª÷«ÏϦ[÷W}Z=_ŸÌŒZù´šgõ:»´òæ5OR{®ÄË›×¼{ZÈÛ•—§¨yóÈí9ÊSTœ=¬Äíâçÿû¿ôo”׫qâöŽ$õ¾:îª7¯ç{[ò[ýªGëç¼<ŸÍëG²çÌÖ^¥óy³þ¼§-ûUK}y³ö}6Ÿ=Ù3z{´šÿÌŸÕç=¬Ù­ÒÊOôè(×íË+¾uÔ5¨[™ÿ*ó’Ÿó£Š'ïu¯óÀ›{µùɃÇ<¨æ'j=Vy {ûæŽâ]5Îô<¨ê%z]Só šsÕxø¿¿‹ªú‰Z×´<ˆâÚ-Ž6òOvk+èæ%j½ZÿÇø5 yOEñìG›yf×"wõµn­ÿê} Šc÷8ÚÅo—8Ú<ˆ¯pþÿÏò^ÿÜÝì’»ôåè}àvÀÿlÿG¿éžÑ}s·xøŸÝðÍ÷ìû€ÝúqÖ~¤}ÿ{ô©ÿì{á¬zÙ-.þ÷¨ci^âÿÏÒ|gÜc¾SÿÔ?õ?÷÷ãÔ?õOýSÿÒ>uÄ9ΦoI½sÿcÃ?;ñ¿‡Giáÿœÿ8ÿIûç¿Þý£›÷#ߤï;ÆÝ J½gŸûñoÓoðoñk?Á?þ5ï¬sý_—·Ýëÿø¿èúÞÍ^7õ¯«ŸlÒø»x§ÿËòÿ2nÒz«2Në½Ê}Ïó÷*|«¯Cë?û{Þ»øÕ¹g¯Oë½jÝóþ_{iýW­{üö¯õ^½îñïë¿zÝãÿµÿ)uÿGÿÓ¼ãÿÜÿë¿ÓåœÇ÷êýUd߯DÇ·zÏw¯ûi_~D×_t<¼¾‰ˆöïk7PQ>¢âX{ßå=¿ëùßË÷îÞ»Ÿÿð¾ÖßÏžŠêËÚ8Þ¾§Ô{—ßÿDùžê½ZÿÇ÷Y§öù\Û—¥ã£}O¯ó¬ó–çç¸>ÕÓViýãªøÅ·,«ú“®KFaî()ç*ãæš³Ùy«ë°Ù5³V¹g=‡)_Y^ßÅõÝ-³?Èò‰¼ýר%«xG@ꢀ @€ @€ @€ T ðÉ®¾?x^í[rÛ0 }³Þÿ=BŽÐé8ú°F2)¼Il2‰¯]¤'y½ø@€ @€ @€ ìEàçÏ{=Ç×½VÇjFÎþµÿÇõZ´¾Ÿ¶¯µzfóÔŸ×ý˜È!àåÓªß*}FµòÕO31+òæ5N ¥}GñòÝニ|Wö÷÷ùÿé×h¿OÇó¥¶OïO½ÏÞÿÔ—÷ýû³]ɬO«û¼=ú·¥·~oV^µýŒ¼Y__ßœÍ ´Þ¼Ú[û¾ëφ⺽xù³î×;Ö5¨›¹µ§¨þˆ÷£u”/¯qˆ]xy‰î×+ttë·Žö5žu<Ô7)›a”¬qˆƒïq‘å%z\âà:¢=dG|ÆA¶¬ñ‰ƒwdñ¯2®UÈN_ù­ªxÈžG×8Èæ^e|ü³ü'Ð-ªä_µyt‰ƒjÜ«ÍGù'<Þÿhžs´þöUã Z¾U6ð¿öùRë¿j¨šoU祃ju *çªóÂÿÚuÜ*®v‰+ÝúÁï:€ÿÞþz·zt«ÛÖëÅï:€ÿÞþµû@öûëzص¿Uë@W_ÖëÆï}ÿ½ý¯z°®ƒÝû“Ö¬s`w_ÖëÇï}ÿø—|Ž˜ú¿GÜHó?ëóAÖû_÷þð¿GKãÿø—ìÿÔÿ=âFšÿœÿðŸÒ}Žv×ñJþï‘ÇÒøÆ?þ%翌گù¹Hi~ìÚNš÷Yçþ#Þvõ½.üS÷%uŸüß#n¤ùŸµïSÿmâNê=;ïñÎÿúßIþÛäQôy];ÞêÞ©ÿº¸Å¿ŽŸ6ÿ²Ûã¿§ÿ]¼Sÿeñ‹·ìz­_ë½Êóþù}“–K—öZÿÙïùîÆïâOºN­÷ªyÏþ?·iýWÍ{ü÷¯õ^=ïñí¿‹wüúïæÿøçûýþÞÏÿoVõ~•sÏÿx¿Šé{‘ÕÚ‘ï×`5Oç‹÷ïo žò\å~¼Ï½y\ÅçhžÖ¾W?×ÍÙ×þqäÅûº—wüÏ}ÄÛï]ÿxŸÍð=ö|Ûø^åý·ï.õ}5YõûW?¡­·j´Ÿ¬õW=ݧgÌ7£,?YqŽ|®~îK]gµ,/UqV}¿»OÇœO&U^²ãR¯ë%ÛCu<ê౪}TÅ÷ªŸ)\·J•¸»×ЇêÖ:8×ùW©òVËËê_u¨qVÏÇZùþ9¢:oµü¬þÕæ€ß.ùXë@etá­–'þo¬æ%;ŸîuÍkZ<ü3þX­ƒêç€iýXµŸUÿÕïU¼¦ÅÅÿÞçþ÷öŸg«uPõ0mWïÿ{ÏUÿUÏÕý2->þéÿNßLë¿êýÐÿ{÷·÷€ê~™ud¿Nå_½/üï}àÿ+ïÌÿuCÿÏð¸úü3ÿ÷ûÿÀÕ¾çûÿóÿ3ݽÓÿ¶ºÅ¿ßQ©~nõ^ý¾ÏóŸ­n­þ«¾ç{WµÏÔò²zWë{ÎÿksÀê_­ïñοջjßã?Ç¿jßãÿ³ÿé}ß¿ß?׋zßãÿÑ¿W¿«Ÿ÷¼ÿÓ÷ÿ×€Ú{vv>^}ßeÞÓÿ7»{ßõü÷òÞíœßýû_¼¿®€ìó6;Þ??™dûˆŽçí{ÊœŸ>ÿñ¾öÝÑëã}Í{×çÿ(ßÓç|×ùí{WïªýŸå{wï*þ³}ãýñ$ˆ~>{^ß¶ç5﻽ýWù}×›×´õŽü«ù<›Ï4OQû9ËSýº(>Ó×U÷Ê\­À.þc)컺ªÿ}äî¼Úîn‰öL Ë?ä5 Xýk @€ @€ @€ ìDà´ÍŒvx^í›[R+1 ÙûßK` T*LI%ÌØÖÃGêûÃÉî–d'á~|ð€ @€ @€ @ ïÏûžÞý¬·cvô—À™ÿÙßCYƒÀ¬ßÕ×iЩ¿ÊUÖ¯¯O|¯Zû³Ž·­z«±ö寞ÜyûòŽŸKO?»·ŸèøúFbwðõûþög´ßÑ|±4õ²Íz¿úºQ_^Ï뙉YñUÖÏyy>‹CU'‹µ×Õxgþ¬¯cÊg¥«¾¼_oí›ï9ëÈÛŸu|ïzðé²}£Zû‰ŠGØÔT”/ï<^õ`Cyß(Þ^¢ãScµí'*up­¢|dç±®‡kt÷*ÛKt~ê@ûýŸU½XÕÁþþÿ ­xªÆé^ªÞ¬×ݵ¬9ªÆ³òÄQ9T}y­Ûªð'àåÉ;n—:ðæ¨µvŸê~¼×¿ê÷û€7¿*ñWë`×9PÅOÔ>ªÕA·*yð¯}¯·ªÃ*u`Å£[ü3nÔë [ßZïÿ½çþ{û?æ‰jXÏîñðß{à¿·ÿÕs ëóá®óÚkßjsÀ‹C׸øï}à¿·µ{@×9í½ïÙ9}ôæÐ5>þ{ŸøÇÿÊ÷ƒQç@×ùì½ïÙþþ{Qo]ããŸùÏü×ýÿ?«s‹þ§ÿéúvpÿמ³Þ¹ÿk{çóÿþ²îQsÿȳºO^ÿºÞgç?þkÌü×ð8:ßf½Gßû˜ÿ>õ‰®£}˜õ<þñ?ó¹_ô½ùo[§j}üßd“ÕòÒÿ¶ý¤RªÞ™ÿ6õŠŽ*ýþ¼Nü÷ô¯îù¿V·ø_ãǼÏúÄç1¯ª‡¬uWé{æÿØüªæÿ×ü¯zÏú^÷êé’5GUò®ú¿ê!ë9Ñë\õ¾{ß3ÿ_Ïÿ.ÞñÿèßÊ»JßãßÇÖ9>›7ú\Ý-ŸUßÏòÏ~Ýn>¢ÖÓÝ{×ùoå]íœ7g¢ú-;Þ_W@¶¯üÖ¾«ôûsxñÏŠ‹÷±e–'ë¼xó®~ÿóò]uÎW¹ÿá}®ÏUý{ûîÖï»ßÿ¢|w÷ž}þG{ÆwÎûÿ,ÏÏymOÍ:ÑV߇íâ÷Ý:ê˜òÙÉîþF×çC©nÔQ¾»=_×LÌÎvóy¶ž*}²œñÎþ}9;ÍöË==Çû‘5Ëî®ÉîíÂÎú_c¬€ @€ @€ @Ý ü&Þòx^íIR1 ùÿÿOà ¨"0¶$km.9Ä‹Ü-Ù3Ηþ @€ @€ @€úx½­IúÙȬI½ïö›EµÎjw=Z·¯Cªg¤Ö>­ÆëI;ߪ¬|'¹övjüôãWqÊ÷¸ñ$kFðöõþ·úéíU:_MþQ¯zßm'õfÝÏŸh­w½jÛ[ûݯ–óÑj}Zõßõ¨mžl¬üY£õ»Ú¿†¥sQZ{;5ÞªOm»s¤sŽ|Ê×éqµž¯úç´eÕiO§Ç¿ò¨ýÞžx®Oûñ_ëûYÿ\Öì¢ñöã5y°–#^>¢æ!þσ(/QóZåÃZuåoå!z^òà–›Ñ¢çŸžÑü³Ì?5²ðŽÿœŸ¦åAtÝeJdåŸ%.mdÌÂ9kZÿßý³æAVîÙâÒæþ{<_v˃lu–=ü÷¨cmžuÉ-‡©ýñÏ>`q_ý\8µ~­Ö]}°â0}iPÿ=Ωÿèû¡éukµ~ü÷¨cm>Hó êЮ—þ÷y/õuàÏvß¿-Ϫù)Íïs *ßìqãö>€ükÎìûhÕø¤õïýP•o•¸¥y@ý÷8?ðßãt¿Á?þ%Ïìÿ=ò†úïá‘ý¶Güãÿ“Àn°ÿÏÎüãŸçÿý}swŸÍÚžú§þ©ÿyõ/­{~ÿé±_࿇Gés…Ô¿×½ï÷<ÒõÑïÿüÆ?õŸù¹ú?“ŸÒº÷~îÃ?þ%÷Ùœû=Î}êß¶þ«íûøÇ?û¿Ý½¤´þ½ß÷çã<×íRïQÏûø×ù~¬üÛò¬²U÷ÎóŸ.oñ¯ãW¥Î»í÷œÿº¼íR÷ìÿ{yÐÍ;þ×ük½gyÏ{vÏPõöŠ[ë?ú~çj~/ŽÕæÑzÏ^÷ìÿïÿS¼ãÿÞÿ4ïøÇ?¿ÿÍûŸÜÿÜLÝï§ûÇû}T{/Û×Ê÷ã8WïÕU¾ßåY¥=Þ×2°ŠÏÕ8ñ¾æ½Ëûß)ßUîïölÿn½ZWÙÚá]k~í÷¯,ÞOûžRïUÞÿðmSßW£L«ï©užå÷¯º~6ÏU=LûþTýG{¦Î×2yׯWq¬­žVW«|I*~»Þ¿Ë¬ÙõªâßnÅŒô“@VÿXò!íßg•ÌòŒ€— ä$ õŸsUD@€ @€ @€ i>/í='x^í›]n1 }³Þÿ=BP‰Qذ³2ÅßO“—oß|ÏñXOY½Æ‰®‡ØnëÝËKUœ¨zèg*&£*oÞãFÕÁ=n ýú¨ÞºÄó®‡zS1tñ•uðsÝDqï—:x]ÝWóŠò>Ϥ-ãUÎ]®‹ò=õýÝfýÿ]]¼¾Ë#Ú÷©Þ»¾ÿgù>Ý{ÿÙ¾ñ^sþSåß9ïÿÕ~ß¿»?R¿¿«7k^ê¾¼çgåÜå>o§Åëâq5ÓüDÏw•{ÕuÑó?=~•Wök=*¯ÊÙ“E”ÈÎ påÆ,È€ @€ @€ @àvû šux^íœMRì0 ¹÷¿Gà,†©‚)†‰õûÙj6,Hl¹[’äÕ{{〠@€ @€ 3 |¾ßÖeý}&•9«²z_½oѽVºê1úú½hm´Ï¨ñÎ#­¹¢(_ÙãhÒÛ?ªloYãïO^cY~ªÇÕ ¹_ßÏ«¿«ý®Î·Ÿ‰žˆW½¯^¿ê-ëúºú³®úŒº>Ëó«qõÔFåÓ;Î+oѯ¥¬;›×[öýÑÞŸ§k(7²lÑãgçC.m½Ñ£ýTGÄäR•¯ìy²ò!†²î(Ù^ªÇ'Ör­ÚOõ|Ñù°FWÿêj]ó‘çb—®yɃßyÐå¡{^òà–ݺçÊýžþÿ_¾O̓îúS›Z¨ñW‰gJ¨ðVÛêçUî*qyýßïWÍÎêqxóÿgêŸú§þ©ÿÕ>ÀûŸ3úƪw¾ÿÍöŽÿÙþ³ûþ}üªóïÔyTû>þkú þk8«öüÏôoõ^uî£ÿçæ¥ÕÕ¹ÿø·|ÇTÝgUâ²Ö}uß§þsêß꿺ïã?Ö¿Õ{WÝãÿìÿqß#­õßÕ÷©ÿ˜ú·zïîûøïõß]÷ø÷ùß½îñoóŠwüãŸóÿõóÿiuOý_«¯w•sþ³ó¦Ê{sÕ8¼þUÎùø¿VïÞÿ·ë1_ð¿Æ½»xë]½ß?æc7o•ù§yçüw#0ÕûtÿQÞwë÷Óû?Þg€Êþ›Þÿ~ÉæÞ=>Þÿíö=´ïÝ÷÷Wï¢ùw‡÷W¦ÏêÿY¾O¯÷ÝÏÿx·Õù®ïÿ³}O«wÕú¯ò<Ý·Šÿjßxï9ÿuyÞí;lì®~}4ëóšŠ×gq\'0ûJuWã›mѾú«|Õ®³¯˜;PóJ?¯ÍOUÿµæÎÖí.y•Wù×X-Q<ðú‡( @€ @€ @€€ /Z²éx^í›KrÜ0 }³Üÿ9BŽJM´•=’x;—kÄ_÷EÉ“¯/þA€ @€ @€À\~½Övüœ»RVö³ïïPîEÀëûiû^tæÏö©¿èëç×Za´OoÿZ´æÍÆë'»ý<µ+Êö·z¼ZzýGÿýÿùïø¹ÚOvýä®àìßû{¶ï«ñriöÍëûnû+OÑŸ÷3“3ã»þ¢®‹öþSÿ9tõG‰òêí7+ú†bgèõ”Õ>:±”u{Ïò·zò°&S«½d÷G|9Èö=^T|”u[Gû¨êŸÜË\•ŸìqWçá]ý«²=TGÞ3Yí£jüU9ЯðÏ3¬â¯2îî9PñP=]sPÍ]müÝr Æ_e>Þt9¨ðVÇô¨rW™—×ÿÑ^u?Pá¬>oðÿ" îùj~Órpµ^>Ï-þgÔ±7×Sràå°{{kTλûó®ßê_å¹À»~Ú¿*Ùšƒê}kÎ3VÿÕûþ×ø?8ZsPµà_ÃÕ>€üOx/§–ã.÷5nSæƒÿµûj·\XýgŸºqí6_k²žºñì6_üs°¼¤þgåFuè¶Ÿv/þgÕóÓâÿŠç€§9æz[Ž©·)yÃ?þÙÿûÿÛºQÿ{׿ê÷C¬y¦-Ïjûm­ÜðŸËÛê)ªþ÷ô¯æýøûRTÎé÷=çøß³îUÏýÔn©ÿ\Þj÷üãŸ÷¿¼ÿ}ºDLmŸœ:Ÿ§Þ³¾>•·ÊºT½sþÏ9à?‡³J½Ÿçÿ=ý[½gÝ÷Ùÿcsiõ}Þ?÷¯ºovŸþcëK5VïÙû>ûL>ñÃUµÞ»÷¹ÿÇä³[ݳÿ¯ÍÕöyŸú×ð^uÞÿ†ÿêºgÿ÷åÀºß«Ô=þmþ§xÇ?þÿèò\]=ÏiuOýß«¯wµû=çÿ{Þ½ßÛW÷NýηîUžï®æQ}_Ußë½KÝSÿïõ¿›wü¿ìê}wÿ»{ßÕÿ*ïÝîó?ÕÎ_QóÁû÷ ˆâ]ÝïjßSê}úû¼_=ñ¿^]§Þñ£|O­÷)õ÷guÞýü‡ï5¾»Ô´ï]ö÷«Ôxï¿ÞöYžÏã\qÙås¯¿»í«ÊOö¸:DÏZÉßï÷¿Ý¯Ù~wç;ËBÝjw½ï>¿ë-êù:ÂÚ3ïúôz>Êó«qµmä¯Î˧×8¯üyÿ>q­½¼Eãíûj<-+y«‰ò5ntòÈkÌå)kܨý?6—§Ô=ï¾9°zϾïQÿ¾ÞUÿ®oõ©z:e]§Ö=ýß§Xý¯Ögôs§Ô™Ú:­Þ«Ï{Îÿ÷꾋wú¿-ø·qSëß»ëéæú_Ëñ»ÞÕÎ{μÿŸÝ>ØýùîõNý?¯ü¯õÇn}`šwî7S½Oõïå[ý^¿ú}ãn}üj?xÿ=Ýýãýy'èêïk'@ÿÞ¾»œï¯Rpº¼¿2Ü«ÿGùžRï§}ÿ/Ú÷TïªïÿY¾§{¯öŸíß5ïÿUžç}ï–Ô÷ÓÖû¿Š×«uô5æ»3u«ëó¥2g´U¾jÏÍ1»S5¯ôóXߣ«úÏ¥0w¶jÿsÉkì<Ú¿Æ.YÅ«ˆB€ @€ @€  Hà—ÏŠìx^íšÑ‘Û0 D¯³ôßEJH ™ŒÏ3±'Ž(€ໟû°HBûvAJö×(€(€(€(€(€(€(€(€(€(€søõãqoÞÿsš}g^îwÇÏV³ßÝÝåu}?åfTÅs×¼3TÖ½‹]œ²æÑU²geYÜ¢Ö驺NÕQ\ªæÕQ¶G%?¿Ÿÿ¼ÿ«x_­ÛƒB]•^î«ã¯8E^§°öÊ«ü¢®‹æþi~m*yÕEqõΛå‹<¥5WòrÊbü“Åo÷:Ñ~ˆQ[oÖÝ\²çÃ>OeóŠ^/Ê>•uGG󨚬y®ŠOöº»ý°¦®þUÙª×æQµ>>xø J•uO÷ ‡ê:vù@ǧÿÿ¯ïæƒêÜ©®ŠTõW©Ëëõý@Egõ:¦ú@]w•ú¼üŸãÕúо]êðúþ3Þ;LñA—Ü©Õ ÿ9öúª»¼÷ÏøG¬>¨>ÀoO³ò¯~.€ÿþO­>¨êð×à_Õà¿—·>øó;ý¿ƒ±ž²÷ò“ÿ.ûüáOÿßßÿ½ùÏÚÈlþ½>ˆ~/øÓÿãú?ùÏÉ—z³>ÒÿgùGÍê¹™Vügåù®?áËïE¢ÎwýËõ>ÿ’Ÿ~ÝýøÓÿãß·¨ö òOþÉ?ù¿Û8ÿÏèw¹G¬ºON­ þ3rlõ'üáÏùóßÝ>ÀùoF߸Ëóß îª¿²žcgó%ù·éÖÝojÜŸç‰îºv©þgæ^uß'ÿ¹~$ÿ¹z«ì VîÑÏ}ä?ÇVþQï{ÞçUÉÉÔ:àŸ“35ÿX¹gõ}ú¬/­ü³ú>üáÿGµ¾Ù½kî³û>ùÉ¿•v߇ÿ^þVîU¹‡¿ÿªÜÃÿ®¹‡¿wîð‡?Ï÷Ÿ§äžüßË¿—{õ9ÿÓ9³ûû–¬ú½ü«Ïùð¿—wïïvTóÎ÷¿k>ðæþk:gõïÕuNáÎùïÕŸ§q‡ÿC]Ü»ô{ö¸ÿíÕ}qÊu§çýÔüÃýßo¦äúý>vó_½wšÆîWÄ_?ïÎ?Š÷Ô¼OÙÿá~/çÝßÿGó>%ï]òï=ù¾š¥zÿÏâü¾Î•.§|žÅ¿Šó©}}Õ¿»øWóý´þª§^§ÊÍZש­÷mÕYeœõ¾ó=X´/à¶Wh^Þù÷Þ-³½+àå³k{Ý®?ëûsèê¯:ëÕêzk¿£ñôÄfhåÓ*ΨG«ëbië­fåÍ+Ž•çOqôÌÄdäåÍ+î'»?¡®³Š—§¨¸»¾_ݯcÈ7“(OÞëPkuâí%+¾u=¬ÑÕ¿+ËOÔºÔÁûŒò½uð¼²½d­oUúžþ¿xUgÝë «ÿÔÖíZj²óéVÙ¼U×ïRªüUòÚ­õó¡ gÕ]â¯ÖAöy ‹ï}®úÏ~>ôæÒ-þjdÍn~¼÷»ê?kxóèµ¢ç@W?ÞûÆ?χWêuàÝÝãã¿÷Àoÿ÷ù§ZÝçsÔþñß{àÿ+ÏÞï¢æëÜLªÍ¼ÄÎ%üÇòV«oüãå÷€×9@­?NχþïÝÿ«ïƒèÿ³êFeœ>oU÷‡ÿ³úy¶Îð…ç€Ùºåz›º¥ÿm8V­GüãŸùß÷ïÇèúŸþ§ÿgç€õ{Àªç§êyg{¿×QuŽÕòŸõîýwaÕøUÏÿœûÎ}Ìÿœ:¤ÿs¸«üÞÀOÿjÞ™ÿ±uˆÿXÞ*ó~õó^ÞÏ}ôl=Òÿ±¼Uú_Õ;ýSøá¬Òïyà¿§uïÌߺĿ/ßÓæ}ÔóÞãçT9VÍ«Jß3ÿmçÓª÷¬¾Ç¿†ëÏsÍÆ«:gUò®Ú÷ôÿ^ÿW÷Žü_ ¨ÌÑ*yœÒ÷ôÿ\ÿïzÏ>ç¿:V黬wÿAt=œkÌvgÑ^¼Ö³¥Ò'š—ï¸} ùîÔÛ“U|_ }£[ù±ŠÓ×DÎέ¼ÍÆÉÙ-«>˜õ6z=¤kÀg Od @€ @€ @`œÀ/¥Ùx^í›Ûqë0 ÝYúï"%¤„LÆñLì‰L‰ÄãÜû“[$¸ €”ä{»ñ€ @€ @èMàëã¾¾Ù¿½éô_ݬ÷«×õ'Ys…W=Z¿&µ>Q[û´¯aí•XùŠG›f½è¢¼yÍS¸VÄ^^²ÆÕ¢«Íçïýßêß,ߣyõ äF¸êýìõ#OÞŸçRÖý¬?¯ïy{?_×Hld^^WÇÊ‹XÚz³­zŠºÞ;ôÌÄDåÏzòÁ&?¬½DG¬åA´/ïù¼òa²îÕÞ>²Æ'Îå\–Ÿèy­óá]ýoE{ÈžÿsEWΪëRóÿz¾TåÖ%.uÿôßó)þ}ùª÷‰*þé>yŠ®êu¯þ>˜s`L^V©ÿ*õT-NuÿÕxV‹WÍ5~ÕãUñ_cÕøñsÎRÍüãŸ÷¿þ¿»ïRÿüþ§G¿Péû<×ÍÉ'üçpWÙð…sý?6Õêÿøÿ! ²/vƒú­7•|RõNÿÉGüÇpV©÷×8ð¿§uïô߼Ŀ/ßnýÞëÿ÷Þ©r¬W•º§ÿÛö§YïYuÿU½ãÿ<ÿþ]½î©ÿ¹ú_õž½ß¿ÞT=ggŽêt?ýyÇjóvóNÿ?×ÿW½«õ{ú?Þÿæ@µ>ìo÷z§þÿ¯ÿݼ³ÿß XyWßçî+¼û©êø»{ßµþñþÜ TëÓ:.¼ÿ¿XsVÏÚwÕý}ôëqô=üÄyðþØÕå”ù»üŸóÕúÁ)ü¬ö¹ëø?°â‘µNdé×%nutáµø÷èç»þ©êƒÝ}3ÿáøÓ*ú€úõñíj?ˆ~>„?ü;<«úX½¨êÖ%/øûô×*þXåõyA«ç¹êïû`u]«äΕ÷Ô/ߨõ*ý³KžðïUϳ¾„ÿÙüŸ~™õ×=`Ö¿Œ·ñï,¯÷ð´á9«£ ÿg_™ÍŸñ{¾Qãu¾Àyó•~UøÓàÿ³7ÐlüP­þ_Ï|ã¯çÀÙuá¾7´ÿL³çÀlz§ìõøïéWÝUùW×]%øSÿ3ß ñ>ÏG×W©ŸêyT«ÿêz«ä?ËÝëó?ê=çü©Â_¥^ºå¡Æ¿›¾êûQᯮS×üàŸsîªø þðWxîW©‡SòP©{¾×“ÓàŸ£»Jÿ™üÕ¸Óÿc}ÿX½Uúýêÿ{E½ïWÓ©[>«uÿýb•ÿèçr»ãºÕ›Ú~àߣŽg}¥Îû¿¯/áï«ïl=F_åuß{½/DéÒ=N5îôÛþ[=«ô‹ªÜ©¿ÂßFÇ*õ¾û>7ëžwõž¨šîÙùV¯wîÿkýj—»ZÝsþÏù`—ÿî{z¯ùÙýT=~WîÔÿûúßå®Úï9ÿáþÓêý7*¿Sêúÿ»þOå~úù:÷Óø[ñ®r¯}^Œ:_³âÀý½²¸xÇ…ûXðæµ¾5ïn}¾Ûç?^¼Oá^õþ÷±¾>:*ª?¯Æñæ}Z½«¾ÿ‰â|:ïlþÑœ_ãöÅSÆ­öå»yÙœ©ó1Wá8ë§±Ý3jVWÕñ\S@•ç]^k»eÖ«w:gÿb¾ dóå~îË÷nõhþwùð÷X¬ùÇfO4@@@@@@@@@@ø£q5cx^íšAr1Es³Üÿ9BŽr9^xÊSH4|>ð³ÉBj@ï ©gÆ¿~韈€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆÀ|®Ñú>‰+´¼{ÇwÒì·j¯ß§Ïõ#5³â§£ŸŸI™wUÑþ²âñì]Y–¯ì¸½©óTŸí Ÿ‡h¯Jþüÿüwû?Ê«7O/ uÕÞz¿ïõý\aîÌ·>£æGû=Çm_]”Ϩ8§£æá‰seŒò–'ʳ‡Ë ®š,oYq-OÇqä92eyBÅ}êÛzžÃR^(O¨<–OïxžÚÈ(/è<^ÏÖsµ¶â³£½Tå³¼ÞŽÇ›¨‰Xå£*ï­gk~µ¸¬UªóZ^oÇãŒ`#U{¨ÎëÙšµ÷<[5–ü–×ÓñçF°Xø³ÔqêÙš‡µèÏÆÂ­Ëïé¸ß æI6îlõœz¶æalÞgaãÍZå׿7ƒy‚•7k]–gkcõ< +göº,ÏÖø¹¡Ü™ìœYë³üZã¹VÏ£³òíR—åÙ?7•3³ gö:-ÏïÆs¬žGeçÚ¥>¯ÿ¯çÎÅÎì·KÞ}kõºðìZ§wÄt·¥+×.uËÿçìâ+«NÖ}µ^Åý¾ïå÷9 ÿ»ý‡lû@ç4v_Ê?–7Ûþ–ÿÝþ½÷€ýIÞ7ƒ­?¶Ô£s`÷9ÀæÿÝé±¥Ñëìâÿu_ 9MÍ×Õÿ×~˜ê½®Û}à{ËË{ ÍkZ>ù×{àíƒûàÖ;úïCNoŽiç2j=ò¿³ï½ß²ö¿>øöñ”þ—ÿÝþQ÷å´Ó=T­Í‡­yYüoå_½nù×{?Ã{ulËÏÒ÷ú½¦æü‘ÿî,çŒüïôÏæ]ç?vÊ?–7ËyÏþw^lœ¦ÕÃÚ÷:ÿ1ç‘üc8³^ïèßyظM©Çëßúý5z| o–ux½£û^÷νäõÝ×§ñXú¦{^ïU}¯þí¯ÿÓ>Íš×½ïªë÷z¯î{õÿ³þïî]þåÿƒ@õùÙ-ÿ”¾Wÿßõÿ4ïòæÿ©w–÷¼wŸº¿¨z§{WÿÿÜÿ[¼ËÿwÿÛ¼Ëÿ'(ïì÷¼îÿœ~ïê}kÿoï÷×sõ>]GÞ¾ª½dåöÝýœßrÿËûÝ/ÅYý‡Š›å{j¿w¿ÿåû®¿­Ù¨>õæÉö½¥Ï»Üÿ(ßÛ½WþG{–ïšÏUž_óZ÷àÖqö{Ù»¶ú¼]·—/Ûs·ëÖüœß¿PûBþb |=ͳZEy%ðÔKôó2„%íï4v•ÊöŽÀ©¯Ûy"ÞƒÀ©×«Q•" " " " " " " " " Ÿþüd8x^íšKrÛ@ }³Üÿ9BŽr)ZX‰3C|€öÆ ‘L7R¤¾¾øƒ @€ @èOàϯÇwÿ÷'3c‡»ÞOŸA³Þ.O}ZWX¯Œ­¨Ú'ÝóR›ïž7u÷µ¿*þ_ë"‹W·¸Uý?ë¡›¬ýìÖAÔ{Õ8YܺĭîŸypïûÌ®ÿè÷ës€:8«üŸq›:ÿÕûŸ9°WÏÝúÿøç÷$ë¿'¡ÿ÷ú¥Ëu_õ}ðîý>ÏïÕ/ý_õy€üŸ¼¶šÓ^ëTï˨ü»õ·.qðÏüï0ÿ»ôcô>ª÷4¯nñvý{ݯí®ÛÍCÖ~ªùÏâÔ-î®÷¬÷~ݸ«ìGÅ¿ iyàŸï} ßû¦õ]ö~UúžßiäÌüçpÏî{Õ÷ý*\ºç¡Ö÷ÌÿØ9„ÿXÞjóÿ3ý«zgþÇÔ#þc8w™÷ÑÏùÕ¸uÉG½ï™ÿ>séÔ{tßã_Ëÿîï/¬Žï2o³÷Q­ïé›þ¯êÿøÿ&=7«Æ¯Þ÷ôÿYÿwñŽü3ÿׯÝúžþ_ëÿ®ÞñÿÙÿ]ïYÏóvŸ U½ÿöÊ{ŠwúÿgÿOóŽÿ©Þ§ûŸî}š+ßUîëVï½î£TÖÅûçJPñd•‡µïnýþZ Vܳ×ÁûêÄÿy\¶·Óø^¾»÷{Õþ÷ö=Í»úý?¾ÏæùîY§ó×ê¼(ϯqv9u=ÞÊãÕ:Yž§ÎõÕz½ò¶úy¶ßwñW9L=NÕÛi^S=žîû”³Êy§ûæ<Û÷_Qõ€7[QÞvãØî’ÕÞØõb}<×çnôSÿ»q8€ @€ @ˆ$ðÐ[6Cx^íœMRì0 ¹÷¿Gà5Ì‚)B"G?Ÿ¬~“Ør·$Í+ÞÞø@€ @€ °?Ï÷ÇïþÜŸÔž+¼ëÝúþžû®Êê/êù¾{GåÓkÜÞtõ£÷ò”5Ž>Ñ^fy‹ž§uh?~¾ÿýŒö5¾aíHÎü¯~åÕ:®6ýúèVý®¾gõçý|=q­V=z¿çíùl<- uÑx{ôïÌ£×çujgöö=ž—ï£qjmäÏí+zü¨|È7Q3c´Ÿ¬ñ£òà9nøY³üdÏ•ñFrgÈö’=yð>eû¨žÏ;r«Õ¶jUó“\ªâ¯2ïôpvs´ w•utóÿš*»Ça̓³:Íþ¼;ÿêø»ûæ[5Ç®ó[ý«ÿ=Š®ªâÞÍ?ýÀv¾Å¿WUFÍ»«úÀµ¼Æÿ5NQõ§2®5²Ïy«ó©ðUÿ³ûþñoùÿ¢«ý8û=õ¾«õOýSÿsÿõOýSÿóêßZ÷ê¿ÿáÞÏÖÇðoã¥ò}Ý+ŽÝü{q™2Î.þ§øò^gwÿÞ<¦gõŸ}w4ß4OQëíâ?jýSǵz:÷Må_½nüsî³Ü÷Qÿ{ä‹JÝsOW“Oø¯á^½ß?çÇÿLÿjÞéÿ¹yˆÿ\Þ*ý^µïSÿ9ù¨Z÷øÇÿ7µ>¹K<êuOýÇÖ?þcùªö‰UïQ÷»g¿/VåØ-®nÞéÿ¾ý ÿ¾<»ÔWïÔÿ½|íîÿøçüo¿ÿØ¥î©[ýïæÿ×üßõ^u®?;÷ãÿÿ»{Çÿßþ§xÇÿoÿÓ¼ãÿAÀË»ú>ô} Ë=›wœÓ½O­¼ÿîÞu¥6ž·ï®}~ZÿÇûµµz]'Ê÷nõþš«¼UÞ‹ò~­zú?¥âñjQ¾w¯ó®û´ï©ÞÕÎYž§û®Þÿ³=¿Î×Çö]ÁÕ}×ú\µgêüZžœyUñhãÚêyÊÊUõyL®Põy×Ú*y눀ºÌÅPñ»JFW©Lhðª­U  @€ @€¦øíY8Ax^íkŽ1 „÷fÜÿ# 4Œ„†éö»Ê)þ¬P'~Ôg;f%¾¾ôG H) ¤€R@ H) ¤€8C_?y>ž‘µ²|*ðÊ?ëïR˜C,ÞV;êìÒÊ­zý~ű2¬æ™eKµ=Ñdñé¶³‡Àl&ÝܪüͪÈëýçßûŸõgÇ,»¼Dz#·r·®ÏâµÓ«*7+ϬõQžÞýÏ¡ÑoeJ4¿VÞïÖ÷ŒyDã0Ïiu0­7ªÿSêU”¸¢u›Îõ»QtFck ëŽ_”ÿs}GÛ< èËG´ltêW³èŽç–:@Ó•-ö:`Ó5^oÔOøÏPõd‹ËËú½Mgôx½u05Ðõd‹ÏËj°é˯·ºç‹žlqzùwÏ6]ÙâõÖA×`Ó“-^/ÿ®9À¦'k¼Þ:¨ž¬z²Å-þJfã–/Zdç'{Ÿë\üÏžâ6ÿç|D©Íë™zDá÷~¡:É­6þ¯u¢zÈ©kÜí×®uªƒXXùw}ôÖêÁVâoÓk[}mãÿœÛ8U峕¿êÀ6׬uà=Ÿ»÷UõÍ6»[ùkÜ›âO§m}ïýÜ=Ç£þ¶rËÊKý¯þÿ£ÀÖ:Èê“­v¬ÜÑ¿êß lóLümzm›â/þ–ó?ú>Þ½[¿fçcíÿn~QÙzm³·•ÿ6NUùˆ¿ÎÿMçUŸl³kí{ô{ÿ6>Õùˆ¿æ¾eî£õul·ÏÚÿÛ¹tåg彇{÷wéqŠ+÷ª¹ŠÞhyŠ¿ÞûÞûÐúb{<(}¯ßËœ™?â?£;Ê\ÿ3ù£q×üï­CñïÕ›}ÞWÝ÷õ{™½uˆÚ÷šÿµuàåÞÕ÷âÉßû}Þ»åœÜKß«ÿkúßËßÛ¿Ñ}[ún:/÷îó^ïÿêûk`ºoØý³ö½ÎÿØ`ç.þ>þ[¸‹¿”ûô{Þ»{ûùÛ”ôžVµ¿K?V?[¹kþžÿQî¨ó^÷q×ýÿÿÿ‡ì”~Wÿ?ÈâÍ2çõþ/îßÕë{¹5nõû÷Àª#Ûzqÿüå€çU¼Ù¼ÙÏ÷«ïFWz¢?¯â½;ë÷ŸjÞ§pgá/ÞW<öm¾wñ>­ÏÑîÿÝœÅ{æþ7ÅùÕolJîÝíÿ(\ßű—XnfèïÆ—«Ê9Öîê‹¶îBµ™¢qÕ<¯åýjoöò6Å_Êc(PÅ#;Eq¥€îÉW é¹R@ H) ¤€R@ H) Xø C—TREE9¢Ù6{Ó@+ØkÙ@,«oØ@-„sù@.\w]@/U{ú@0²è@1¬ƒH@2”‡<@3˜#@4Xœ%@5{ #@6 ¤T@7èY@8­(@9p±*@:˜µ.@;¹2@<ð½n@="Âz@>Æo@? Ëo@@yÏz@AèÓn@BbØ2@CÐÜ.@Dá*@E0å(@FZéY@G‚íT@HÛñ#@I/ö%@JRú#@Kwþ<@LšH@MÖè@N ú@O]@Pù@Q]Ø@RVÙ@S.Ó@T#Ð@UÚ&ç@Vª*Ä@W‘.Ù@XU2Á@Y.6½@Zï9à@[¬=Ã@\ŒAÃ@]OEÖ@^IÑ@_èL@`¹P¤@a»T{@b_X‰@cÚ[o@dx^í›[r1E½³ìYB–rM¦*qf,Ð.èäÇÜHÝv>>ø‡(€(€(€(€(€(€(p‡¿~<òý¼Cû²q_}~Ÿ¢µ2^建¾–Zý¢]åwj}?¥53:Åo÷¾šêÕj7§èýêÈÍ š×i¹jÖóþóÏûßìÏÓ±½žævjÿY¾Öu{UÖÝíŸè}­\½vºäöDÍé´?/_«ýµõv9Í#{+_«Áµˆ²ùDù·òµÚ­©®³:J5?VÎ#;’s‘¨q‰ŽgÄ×ú|NýüUÑz«ú³rÙåõE Ê#+®_ës…<ë,ÕýZ9ìòÈÚ<«sÈŽoÄ×úÜF#Þ*[ß*þ­œßÙÅ“µy¬¢¿JœÝê@E×jqt©ƒjº«Ä ÿÇù¢Â#+Žêu¥[7¿Uë ‡¬|àÏyð©@µ:Èê—î~gëÀö־Ϫ;‡¬üfù?×í#üýNYúÜâw¶àßã>ÿWç•j¬æÅz[}ÃߦS÷zR«ƒîz«åÿ»çüïæÿœG*u 6o‰G…¿õ;Ã-\¢ò¬ÆÿkDéÔÝ·¬ým×Ó©ü¼ü£?à­£S:uÝ·ÿg½tåµ;¯®ü©Û{.üm:íî;µý¼uà=—³íÕôV‹þwÏîü¹|_ß^þêïïε¹«ü™ÿ3?ž}¯óúWé7µ8èúŸþ¿÷ÿ“Òÿô?ýOÿ{ç€÷þ•m¯vïR‰ÇË÷ÿ^çEwþ*}¦‡—ö÷úWÕ]%®®üUôUþ½Îsk½y¹W¹÷Yó¿Ý®ÿÛyzóïÂß›7öó®*øí¹¯xù{ß»vÙÃ{ï§Ž^î§î}pÝËÕª'üst·ò9mÿ;ù«pçïqsêþ9ºŸžçÖýá'5îÌÿØ:„¬ÞÖy|ÚN•;ýSðÑùt{÷Ÿå~ê;ß»ïÀÞ¼°·Õó,ÿ]ßë­ûÀÓÆÓªÓ,÷è¾çü¿›;üáÿ©€u®a÷º^ªÍû¯÷¸ÎÍêÜ™ÿsÜWÿ~#ëžÇûßïnÜé_=t™÷œÿ±ÜÕæ=ümüWû];óÿÌû\îðÿ—ÿ-ýÎü‡ûß5pë÷Ÿ[ûýÖþßÅ»Úù>ú=p÷þ‡û÷ÐÿnÞÝú½ëü‡ûhÒ¿~^µÿOñîÞïUûÿ4ïÛ¸«ÿ÷Ü<÷®ÊžÿQœ¿úñêÔÕ>Šç[纵^Wùgsù·êp«ÝH¿jÏoå8›w5¾œã³¤_¯«ÂoÖìöT@•?„bÈæ“%^Þ)pš?Êk+`å¯Ñ¡               ü¯Ào{ðDx^íœAr1 ý³üÿyBžrÉ:He{I,@ €ÎÅ-A`zÀ%);üC@@@@@@˜¡À¿?:wÎP§•»Ü­Ï÷W²f…Vž^ãjªÖ'k/ŽÞqú(¬]‰7·èxÚjÖË.šWtüzŠkeÍçt|-uõ³ùûuþóúyš÷Õ|úr3ôâ¾çŠWôç¹jë;Ê-ú¹hîïñõHädÍÕ?œñƒ•OÖ¸h_œQ]g–,Ž^óFùA‡Pl&^²ãà›O²¹EÍÖü¥¿J\|ð»T8ÊÃÛk]¦ûÔ)ÝÕæÁOªq9ÏtœÖ[u>/è®ôßg¦Ê#+¯i>ÈÒY}Þ)>Pç_wdë[eþ®>¨¢¿Jžw} ¶?TѵZ]|PMwµ|«û@MÏjùÀŸ{ÄOªú Z¿©çkõAÖ¾P]ÏjùYù?ÇöA5}«äkõü{í'Ô}P¥Ÿªæ ÿ^ýlõ¡ª¬õ0nÏ×ðßÓ«›¿à?›ÿÓÏj>èÖgêõÀŸuÀr_u/ Þ/]óS[®üÕ•CV]Õø¿û#K·nóîúàªO³>ïÆåT=»ü³¾'ZõÕ)ݺÌÓÿÓ']øD×Ñ•?>Ø;çîú`u=Vy.ºªÇ‡ÿ^¿Tçýžwþ¼~÷÷.õsÀOïn}ëUÏþ¬߯ðçý¯ô½Pô¹ÁkÝì‡þ§ÿéÿ¹ÿýOÿÓÿôÿî:½O‹ŠßeßæUÇ.÷ª÷?œÿ9ÿ*àÕ7]âLéÿ.¼¼ë€?ûÿÎûï~éo·ÿ£öåQq»ñò®§+oºÆëÆ¿+'ïºv¹«Ÿû½õé¯ ÿê«Ê?JiqwùGíϯâNã]ï.÷¨÷~tÄ×¾ï‡Oνãnÿ_­ÏÖÏá–ÿ.÷¨uŸïaÏröüstÏ^çÔ¸Óÿg}ÿ³zg÷ûûüðŸÉ_•;ëÿ?ÂÿŒÎ¬÷¶5ݪçcí÷èsþO·ZþVþ¶î½?JM¿ªùX¹gõ=û?Ÿ}IUîð‡?‡cÿ;¤ê}OÿÛú¿ wøïñ¿Ë={ŸÇùo÷Ýïkßýrÿ¤¡êy+:ï®ýþî¢h«ÅŸÂ÷ÿëû`wø?ð⮺¿»Ú5T[Ÿ½òÎ}jÿÃýuEðê'Õ8Þ¼«®óÓÎÿp¿zóÛîCèó5]«<¥Ês5/úüžÓVuVy.Šw·÷úª+T¸zý¾¼Õ«zu{N…¿•›u\7ŽÖzNó·òògÕ©ë8oþ^œ¼âtåæU——Î*q¼t™G…›5)œ¢ê´êž5.J‡©q³8^Í;•Ç麯8D~º^æ{U šïÔ{µ*>³ò¯Ry¢              À\þ¬«;?x^íšËqc1 ™óÏÂ!l[.Ù©ôü@ïÅñL@RÚþ¡             ôPàßç#ÏÕ¿=Ôª—å*wëüz æÎÈÊo×øÜ*æ~O¯uó*›#r/NQëäP5O”QÜvï“Gq­H¿~îÿWwsÛµ¾–ʺÑÜñŸý|W뺺ÊkD6Ëwuž•£×x Õu¢Xåè=ß‹óè::$ÎDâÍÏ{½QŽ«ãΨ~Wo^»×[å|7ÿ<‘ØvóÚ½þÏÕÏciÄï¶›Oôú«¼¯æÇ“‰Ù1šOÔ~ø`Ì?Qxöd´þ*ûჇTxœŽÃËúŸúÿË÷Ý|pºîT÷ïâUýUâZõúy ¢³zU} ®»Z|Õ| ¦¯z<«ü竜 êz«Æ·êø×øþ!»Të+[\³>8ݲé¬ï,ÿÓ÷U=³Æ•ÍYuV{ÖÑ纎Yã›å}dÕ7Kܳ>ˆêYtÌ'ük¼óWý§êƒÕ¼˜?æoøéTÝOj>¨®·Z~ð§|+`õÁ®÷€Z}t‰ÇÊ?ú{_¿uágþW}'Z¯ªûY}°ë˜]·*—¨¼¬üO£þˆÒ­Ê>Õøso°½oªòÇ{}0ÚUÆUé×»ò°ö®£qìҭʺVþê÷@Þ{ûVþÜ Þû¢KýÃþß T9·½òèVÿôŸ>0zÿVçU?UÖ±öU®£qUáæ•ümï&/ÝUÖ?ü-ÿ?h´ÏªŽS©;•8¨êŸúïûý@—úWé·*qX¹óý­sþµxZûJþV]ºŒ¯Î¿ ÇÙ<áOÿ¯øî›­‡nó¬õ¯ú}¿ëÚú˜•»ú»¯[Ý®æ›ÿjþÝçgåß›WþVþÑç¾Wž¬ó|/°rßuîÃÅv_óÒ þgt÷â·ºŽ•ÿ®¾¿šóm>¶rßÕ÷y§Û¸yùþgt÷â7»Žwê?Ö‡ðÕ{¶N½ç©r§þcüÿ½ëvu½Yî»ïû¯ïÈÕ<™ÿÞß³üw½ó¯Ö…Ÿošå]÷œÿ½¹Ã_ƒt¿çü×à~ªßÃ߇¶sžûŸw•ºçüŸóC•º‡¿5îðã_•;üÿæ¿Ê]íœçþSïY¸SÿÏ~èRï¼ÿ xñÎVïÝùÃýÙ]~ÿƒûû`UþÞ¼³÷ù.÷¸Û~QÌ^ÿ»xW­÷ì÷?xÛêûn´zýïæÝ¥Î³œÿQ¼»s?ýý_4gxǾÿNñ}Ý÷îüëþùÝù¯ÂÑGw®£ù[uU?š/ãžPåyü|Pçï›-«½* Â2gˆæ&Kv½RÀ‹? £               ¨)ðc{>ûx^íœKr1 }3ßÿ>BŽRɳJc’ ˆogã…†ðúC*N¾¾øƒ(€(€(€(€(€(€(ÐCßÏ:µ~öP­N•ZÜg÷©£\Jf¹Y=WCÕøìo.ÖññÁ«¬õO˲éë¿*¯|ºúÀKï¨q»ù *ZøÀ[çèñ«û ºþQòÛõÿIûßC]?UóÁ®]×WñAW~»uÃÿù>ÙÕ1ûúì>È®”ü³ú Š~UòúÀë~PE÷(uHù_ë¬}E·jyH}ÿçJ)ë9P­ï¢Õ#õÕˆ¦WÕ|¢ú ªÞÑê‚÷ºÔWðïÍÿòM4HýÌ:™Ÿá/Ó­šßðAoDã?{߬և^õdåÿî/ýªÄ]õÁlŸz=W…‹U«ü­¿–úÈJ¿ìqªò¿|“Uþ«>ö¥×:+³Æ©ÎŸyð÷}w•–s÷…¹ï9ºðg|öüçú$ëû}”w7þÌ9àuž×Š;ê‹nŸ¯Î-^ûtã;ªþœ ÌúÀ«oµâŽú¡Ûç³Ü³Þÿï|Óó]½ðgþ3ÿûþ»ònýÏÜwðgþ3ÿ™ÿ³s@ëæµó¿çü‡û÷ì÷ø÷äw¿÷ËÚÿðïÉÿçu^_ ÷¹ï3fï{Yæ>ÜÏpÎîsÜ/Vû>x¯ñ~×k•ÿêûWûyxïñŽÚ÷pÕá:«ãjߟšû³ùòœ®?௫gFá~ ²èV%OøÓ÷‘~¿£J_E¯#Zß3ÿmçümõŽ2¢r§ÿÏúQÊýÔ=ÿî{À(}R-)íïkGûUÓÝ»)wë¾gþŸ™ÿRþ£>=õ¹w¿T‰/åîÕ÷ô¿Nÿgåÿ=þÙ¹Ãþª¼­ê¨Ò÷ôÿZÿWãÿ9þ»Ü½Ï÷£{£ÕÜ̧:wúÿsÿwáÿWþݸÃÿ©@Wî]ùkñŽ~®ûºñ‡ûgGd;—¯æ ÷¿'ÁªžÑŸ×æ]eÎß¹ :ÏQ~§xWçžýý÷Ù^îùšs—>Ï2ÿ­xwçî=ÿ­9ÃÛçþçÅù=®ÎÛ²Þ.£óõîÿWâÅ¿©3yñÑŽ{Fú»js°Ú¯>› ­xIãØ¨Ð7Š”Ë©u}IøT~Šãݾ>UõNmþ((€(€(€(€(€(€(€(Eÿ@J3…x^í›]r1„}³Üÿ9BŽrÙ[o¼ ~è¼äAB@ÀŒf“·7þ¡T€ P*@¨ T€ P= üùµ'Wfú¿þÞSë xs—ž×CùQJye훯8V†Y\oý`©6'š[.UösÔfRÅÏÛo­Š}½ÿþ¼ÿYÿöæèu^_"¹‘[¹Kí¼xÞž“«joRŽQûn¹ZíûŠ4Šëí¹V®Z»XuñO¿å”e¯åjÝOÌ7Â,~Þ~¬|¥v¾*ãžæÍ¥ê<)Wí>\r>‘UñŠò«å+Ýï£6Þ)QPΕò•îÃ#x §è8¤|¥ûîTDZŽÖõ|)çÓ>’¶HPùdÅuâ+]·©_o•¥3º)çÓ¾z¢ºйdÇwâ+]×Q¨Û­oRΧ}udež»ð¨ŠóÄWº.£‘¿«J×n~¥œOûò ÿ챇êxO|Oëäÿ¡@5Ç[ÿ'Χu”:¸Õa»ý‰ói½º¶ó»ÍÿÄ÷´Nþ|TÖÀmýÓþkýžúýÕzU Ÿïü±òØe×ùûòèi­òáQUçèuP¥Ë¿ä?«Ÿ­u‹ZÖ|h§«kò×é5µ¾Ðê`ªÎ¨y‘?çÀ»Ú:ˆº¢öÉô¸´üù}hÖÜèÂÿÕܙޟYùië ê9p{n–^ÓühùW=¤õ1Ot>Óø?×I´~ÝÏŸÎÿQÝ9Eǯ­éh»hvÖy9Ín[ÿó>ðuÞ‘?ç?çÿÿn}.iû¿ëw@þNÄï@ÿÖ€µ_¦Ùiûí=ÞÏ4ŽÖ|ÈŸï›Þÿ¬}2Õn[ÿOåhÍ‹ü9ÿ7ÌkL·ÛÒÿÓ9jóÓrïúÝG«Ë–ýÓùoáhÍ“üùÞ7ñ½ÏÚ[ì¦õýn^yNáï¥Ç¶sºò߯)*_-ëïkV»¨¼·Ÿ«åußßΡ*òç}á¾WUÿ[ý¢ô=ÿÝuÍü!ÿÝ«ç wön’®ÞÕýþðÊýŸS䟣3J¿wé{öL]Zû=êûÞé»/ZßtÇÊÿÄ)j½»Þ(ñ[¹Wõ=ç¿ÏüïÊüÉÿ]”ùÙ-Žî}Ïþ·õÿîä¯ã˽ú=ïÕý¡ÛÜÍŽw*wöÿÏý?;ùÏ wòÿÊwòÿP`+÷íü·s߯ߋ7ê=ÎúûPö}*Û¹ÿ\Ù<¢ýyóžÖïÏÕÍ#ë|r·=²øxû‰â=½ß»ö4ïmÜÑßÿÉÛ6ϵVÞsY{^çg?Z¦î×òÒî¯âKÞ²ŠÕò|Þ—¼e¼Ÿw¡òÓÆeËžVZQö“œ(Ivooü¡t€Ð:@è t€Ð9|¾?öz÷{Ž3wºâ¯}>ÓÍz»Öò=WÏ©žŠO9zÍïé6Þ®¼øY¯‹ç\EÖœ¢×ëA!oѼ¼ãå9Y3òÇÏûßéoo®ÚõkR‰S}Ê}w¾–ŸÕ¼8GkEÚåç5Ί¯tZ”üÔzq=]WÊS;ÞÏÙ+ŸrŠš¯å+WƒšÊ(~Öq¤\¥ãíÆ^ÉšKÖzR¾»ã±é«Ëâåw—ïî¸s§1Wðæ½þ._é8LšrUÙ|¢ãK9¯ÆËÇší?J¼Wés,ªûjPxdér^ßwcd–ïhqW\wŸcPÝWÆEÏ.ï»qûrG¢øª£{ úަ«k ùŒªç”ÿs~n·ÿÕoT]§y@þPùîêê’»û帿ó¶z«M?ÒæAöy@þ¹ü³ï…äoÃÿ飶dåùÛò?̓èó€ü±øG÷ò÷á_¥?&ÿ¨>@þ¾üÑûù“‡ï±UòXû~èõ^PÅ·.:É?¦ß¢ç ó`v ñß=_Ð몊¾ªü¯yRÅoTÒ<Ø­Ó¬q¨>£ê’òú.tš?¨~£éêÊÿ™?h~£ê‘æÁi}FÏGõEWwþìÿ¿ïJùW¹Üõ”ºCÑAþ³¿iÿN}Ž[ÇC©?Ò>`Í#z=ßQtLãÏ{áë¹Gþ³ïäOþßLË”ó7[‡”{õï<ÿÏÎÿ.ü™¦Ö?ùÏæŸ}î¢ÄŸZÿ(þgë ¾ÿiîÑßm­ãe×J|iý[sÈZÅÿlÓøgûŸüyþKÎÿ¬~}­îPôL©¿QtH¹Wýî‹â7šŽîüÑüFÓ#åzþFÍGóUO7þ¨>£é’rG?÷ÑüE×S?º¿èú¤ü£Îí»8è~VÑ'åîÝ÷«øÖE' ÿ.~VÛùÏüÎÂÿo—“äŸã{öù€Æõ›‡äëwv½?ãk¹{¿ï±þcòQË?ê;JtÓ¡åU÷¬ßú×òª{ò÷á¯å]÷äoË¿wòÇàÝï¯ñºÝ»¢÷SµîYÿgõ_;ùëøwáNþ2þ§Ü³î÷«ûEôyY5Þ)ÿ‡¬çUyDéîÊýÿÿþß;ù¿ò?åz¾¯Î•¨>ŠÇŠ;ùËîÓÙù0ûÔþOî¯'BvzÇ·æ]µÏßݼýÏZßšûêUõyë¸Ö¼»Õy·ú÷â=…{µûŸ7ïiÜÑù“wÌÂú–®Åù'Æ]ü(R^ÚñYœ§öõÝÌÓò¼ÎËæ{ׇ©ãP¹iuMå¨Ý·Ög”yÚ}sÞÃŽ+äåãÀÊ÷¬ç>»åªWÈwvNxñŸíjݯø×Ù •Ò:@è t€Ð:@è ¿ø‚ÏA™x^íœKr1 D}³Üÿ9BŽJ)ZhJÒ$A6€çæè×9c•~øA@@@@@è£ÀŸ_\­Ÿ}”é‘©•ûè¸*æÍr”ëªyy•«ù*ޫש¡®~«¹E¯§¯h®£yE¯ŸKm½h£ùì^_Oaíˆ~ÿþ›ýÜÍÙºŸ¶ú磛åîoå5î¼âZxùEâýi]- 碉â9».~Øã‰YN»çGûbê:»ìæ·z¿(?èŠd5Sëáƒ1Ÿœâ½/~°ù!šƒÊú«ý`SW” Ÿ]qàƒWOîÒ]m|ðð—Ýñt÷Án½Õ÷[åý“ŸúÿÖÿf}ÿþªîõ~¬_U¨è›%Žj>È¢»Jœ³üŸóUî*ºf‹cÖðç~¨àlu§oÖ~ ªgÖ¸F}pªdÕY5îQþ§î…ª:fkÔ»û@vUãå¿»¨êW%®QìêUtVÏCÕêºU‰þ5Þ÷ÌúQͳù0ßçkøûôªæ/ø÷æÿô³š>=oT«?•|²ðDZýÊëƒ]ï¼û¨ÔU¶8¼üw¿ôúà9>‡SñVå|ç†×£uyjÞ©úʲouþôƒïýÀË?Ë=€çÛ9›NYú¹7Înü9ÞûÝëƒS÷¹Uûzë¤úønüé¯}þÜþ)ÐÍÕûº5?/÷ìÏ×{„U§ªãàOÿïØÿ¹> þ©ÿ¬z?½NÕsÝšõOýSÿ}ÿ/%õOýw¬ëùX}\×ú¯ÎÕšüéÿžþúymvk]tç­ÿYýOÏïÂÕšgþV=ºŒórÏþw¿.\­yváoÕ£Û¸êü»ñôæëåúÞfÝß«C×ñÕøwåèÍÛË]õÞçÍ›ñ¹¿ï¿5ï)½õo=wWƒóÎW½Ü£ú>|cøÞé ÿ3ºßq‰þ½ w¾o{Æð?£{t][ׇOþjÜéÿ{}ÿ½z[ûqô8UîÔ¬G¹G=çzíÿ®ëò_õÞκNW>Qyrß]÷ôÿ˜þ?ÊßZ¯«ÇEÕA·uG¹Ÿª{êMýgåÿ9þٹÌÿ,÷ÓýþzèvNÏæ;Ëõýmv½Y=ºÌ¯ÆþoëÿU¹Ãÿ;ÿêÜáÿžÿ,wµûÝÝý Ëùý)ÏU¼³qï^ÿݹwå÷סKÿ‡ûû›@Uþ«yg=ß»Üÿà}GºfýÃ}Œ{Öû_ïªýýΪç4çëúw:Uý½ xŸqØnþ»9Sçß}µŠÿi®Ÿö?SUyvUå6Wå5"ÕYež†Šy£Páh#¯Òš‘[uß=NS­zQíæÊ}\ËCÑüµ²%š«^þ(ˆ(€(€(€(€(€(€(I¿‚øDWx^íšAv1 Cs³Þÿ=BÐççxá<;J$Pè&‹Ìˆ>@ÍLóõåVÀ X+`¬€°VÀ X+pŽÿþÜ÷ýyŽB³wå½~¶zú»‹ò̾^_AídóÌZO[Uî³xu­££¬F§]ܪêh¨ÌÛeÔº¼Jsvö÷ûý/ë'Šû»ºœªót•Åýê:hð(ÏÑÉUnÕ×uû‚C}|Õ\w×ïòž¦ƒ]>Ý÷WûCWµ›_v½*?àˆôVÎæ^Ï~ˆùÍ«ª¾}pÍUú³­›í‡kêò_ÅÆ©ºûàÙ“Õz³®oÜ}Àʧ»¯,?ðO|çÿ7ߟæƒîœ©Ô;Å*˜¢?Ë>V}€zOdÑmJ«ü÷uû`ŠîlûXõùÏz®d÷[n¦õcþ³ò¼êOV¬îÇ÷Å|mþ1½¦ú‹ÍSufÝ—ù{¬|7®z/dÍÉô¾ØæÀ'Mçѽ?5þïüÑ­Û´zQ|Ê)ú÷ÓøTï'ÊõÿQ_Uë6eý©ü~™Â©zQD󈾾Z?õõ§ó÷<øý»G”¿Ês€ß®}ï:¿çÁk_D}€>×w뫟ÛÙýŸÆßsày˜ÿµó2;w,ëEù«?:ÿ{ùŸÂß>¸+pjþÍßüo °œÃ¨>œ?ÿ­œ»ïá,÷£rÇR×ùwþÿsŸœçßùwþ£s€åùm·–ç0TQîS¾ÿ¡ôf«kþ>ÿO<ÿÙrˆê'šÿÝó}?JgÖºæïù™ÿèü®Ögͪ¯hîUŸûQú²×Ο]tQþ«s·û>´®*õ§ñWÑÝg”;û¹ÖS­¾:5½Ùúòï>¿ýw™5ß#¢Ü«æ>[NéÇükr¥âŸ(ÿª¹¯¢×”>£Ü«æ¾ÏuÌü1ŒîèùÁÆÝùïõ¡ù÷êÎû£>+wç¿Çæß£3KÞUrïü×ør5ïÕïyï¾°åF½ŸUþUßw>­«®7Kÿ«ÜQ¹÷üÏ™ÿªÜÍ¿:wó7ÿ›,ç§JSrïüÇò?»ù_ã?•»ùÿΗ;ú½îÓ{¿ù¿æ wóæ÷ÓùgñV™óþþWÀÜŸ òÞ½Û§¹¿ž»º²ÞŸÍ[}Ο2ÿÍýê›ßµï¬ùÞý;›O>‰©¨{5;ߟý}â¶û{]’k³óßåyõþ5õôïbá•SÖuúärvÐÍ?‹ßê:9ªÍY%›ÿ*—ªûæªÙI•î¨ukTš»*ŠSVݹdzv–Å¡kUΩÒÅ-ZçØF¹d_ݽ«góü¹žæV`•?÷®Ü°VÀ X+`¬€°VÀ X+pWà?Út?[x^íšKRÄ0 D¹÷¿Gà,BIM*–¬OKj6T1¶%÷kÉ΄¯/þP*@¨ T€ P*@¨À~¿½JÏQ¨÷N¥Üµã{«XwwZžÖóê*X;skŽÖëÕV?{k^Þëá+Z+Co^Þë×R/ÛŸÿûÿý·77¯õñÆÎ艿öï^\µëb«ŸŸ–³vž–£Õ¼|ű2Ðr´žgÅwu, yÙXs´^o•çî¸<¹‘­yy¯·Ëùm~.øèÞ¼¼×ã¹ûy<‘؈Þ|¢×ßåý4?–J\´h>Qñèƒ5EñÈŽcí‡5uñGes‰ŽO\=­?Z<+?àWúç Ñxdå3ÕYz£ÆæTÙyMñA¶Îèñ»û]”üv}€z?DÑ·JÝ|PEw´<»øM×jùh}€rTÓ-_-ÿs^¶Ðô¬šOUTÕ5o­²úªŽUóÒòÏ:ªêŒž·ÖÑ}]Çêù¡û º¾èù“ÿÑÑÐ9yç‡êï}sýÃÿäÏ>€èÖg¬/ÑúùÇò?õ¦rtGñ;ÿÕïPô«žGUþwŸT眾ÈÖ³ZünüÙd÷šîüÙÖü õÁê} m\µþ•¯”Öûb+?EéZ%ù¯õÉ*<¥yNãÏ{Ág¿K}`Õ³Ö‘ÖI÷ñÓø³\ûùó yOœÕ¿­ãvïïoû“Öõç@~_¸×ÿÉ¿×yÁúïÅó­ß?}.õõ9œµžV¯nó¦òçóà¡ùÏ>¦òïÖǵû!Ö¿äȺ¯YÇÕÖK—yÒºçó¯>Aþ½xJûÒTþRºŽ'Öÿ¤{_×:ÖîKZÿÖ÷ïèõ´:u7…W~Ú}I¹WîÓêÔuÞþ]ùíî«;ÿ]}ºÏ—ò¾§iãuç¶»?)wôsWió«óŸÆËz¿Õø[ïêzhܧrÈÚ7 ÿ¬ýOKþ3ßó pçÿÙæøüstÏ>oи³þc}Hþ±zg×û•;ë?Æä£3J½W©{Ö¿/µõžõ^­nªç£å¯}¿º;¯ºÞ(ùk¹gÕ=û¿Mÿ¯Êüsùïöm«ù(ý³ZÕëžõ¯«ÿ.ÜÉ_Æ¿wò_ã¿Ë=û~ÿvO¨vîFåÛ;ëÿsýOáNþWþÓ¸“ÿ¡ÀTîSù[ñF¿×½Ýû¦ñ'÷ÏŽˆºOGDZæÝ¥Þï.ˆæâÜW;ÿÚ÷Þ¼v×÷âݵޫ×?yËêûmônýyÏ÷æ=¥ÎŸ|àÍouý(ÎÓyg÷ÿhÎ÷xoýpÚç«õ¹:.›/yËüÄãj>²Ýsôª®¨ãHpOT®Oyíí–³ï  ó'1_Pøûî’«?)ÅŸ0ÐòÇÜ ³¢T€ P*@¨ T€ P*@® ü¹?[x^íœQr[1E³³î]B—Ðé8ž©=vü.B€àô'–܈÷œôë‹(€(€(€(€(€(€søóë«õç¥zFjå®îë©ÞùQ©w­?_É3#ØÅÓëÜ3U=Çk/NQ眣ìžFqÛmç µëyùû{þ÷s7·]ç×Sº¦GŸø[?ßÅU=·¦êu¼²ò]ݧrôZ_Gùž¬rôÞïÅùê95(äyáÍÏû¼«W×åȵìÍk÷y«œ?íÏ¥o}7¯Ýçâ¹úy<‘X‹»ùDŸ¿ÊûÝþX*qÖ¢ùDÛó·821–¢ydÙ#^çS,»äÁcdq¨b×+bºµ¿•*²ý˜šÙºW³?-ªé_ÅŸ)yPEïª~tσªºWók5ü'7Ÿ«é\ÝŸnyP]ïªþuɃªúžâ×éypŠÎUý„ÿm©Ê'ʯSó JŸ)v¬yà3Íë§Láç)y­Ë{Vþ÷}z%ÛvLá‘§5l4õ]YºL±kåÕ¦pÈŽÓšzEk;²u™bþ¼'ø§@µ<˜RUâ„?} bxžªÔKW?ªõmjä}¿W^ªy rŠZï¥Ç´sTþQïVófGk¼]ùßóǪ˴}j¬Ögôþi<Õx»ó§üüÜ«ò?ex×gÔúè¾~úÁë~ æAô=îm¯{]«ñMãOxìðŸýý€Êÿô9ïÖêþ½úõß‹çîùúï™/jð~.Ë>O­›nëáß³®¯æ)üá¯üžXv¿ö¶µNº®£þ©êîïN­ÿ®ý\ þ3û¿Ê÷?½òþ½xîîû]ê_Õ©ëú©õß•§—Êßû½Kôyª>Ý×ßûÂ{Ÿîu¬Æ§Öý©sŸªË”õÝùOáhSå=§YíYõ˜¶¯ÿiü¬ñªÜ«ÞûÖø§ï;•ÿtn^ñ«ü­÷°ºÏ+>Îñù;Ï]}>9ï›Ôº‡'ïú¨Â¿»ÍÉ'øçèî]ÇêyÕ¸Sÿ±yÿX½ÕúܵÞÊ}×¼÷ü¸+nν)må¯>¯[×ÃiO_²rª{îÿ=Üïõdåo­cë>êß7NáNý×àÝï™ÿàþÐÿ×òÁÚï³ëžþ?›;ümüO¯wî¸sÿ_ÿ%Vë½Ê=ÿîýóßë~Ð;÷ÿlîðä?¥Þ™ÿà>yþóªóêsÝÕÌpÝùÃýçNп7ï.}¾ëóÿ.ÞݹŸúü䀘ݵuÕûÿnÞSêü”þÅ{:÷ìþÍÞ±ÏY|Ÿí^»ç®útÿWá¨ú1—¨¹ªkÕõZÔ¬¾+P•ç;¿ ç«@uþ¾ÑrÚ³UøC&G(þ9Ñaõ“«ü?Ïç(€(€(€(€(€(€(€™ üV5:Ÿx^íš[r\1D½³ìYB–J§*ž›‡5Ðùɇ$„ú4Hs“þ¡T€ P*@¨ T€ P= üùõ8«÷ï=JÍ<©—»uÝLõúŸÊÊ1k~%{ž ‹gTÜžªöÉ:ŠSUœ>ÊöÈ´Š[ö>=ÔÆËò÷çû_ú;›_t|<¥13’¸{Ç£yzãaªŽ“•—ïé:/ÏÓu8ÊcdrÊ1zý)_ëz ÷²ˆæÏÊÓ;ÿ»;GóÊŽçå«]w—FýîÙ¼²ãk¹zçÕ©Ý1›Ou|/gi]-•ºÝªùTï'qµŽ×‘©Ù©šÇ­ý¬œ¥ù5tòw¹Åãö¾_íx>¡Üns¸½¿–³4/—R^ôÛú£ì/ñÕŽç‘ʉŒ¢?JZÎÒ¼ZñQQtGËCâ«'Mw´|´œ¥y±Ô⢡éšÄW# UoÔ¼$ÎÒx,½óh¨:£ç%q–ÆÏÉÅD@×=?‰³4CÑ]ß.ùIœ¿÷“‹YÙE_ô<½üŸëbhÚ£ ëÚ-?¯ìäbVtÓ·K¾]|ÐEÏnyzùWßÝtí–¯×1Ý]ŽÒMÏnùzùWõnzvÍ×빂ÏftÕ³[Þäÿði7nÑù¢ù ú|Œ÷³ÏÉŸ}àŸh>øîÁzÎñkþôEÿg]Y}pöÚÏ[Í>áó‰•ÕwS§Ð:?Låÿô}ãƒÓú¬^OÄþ.¬æµ}ðÞÓïWÿÐ_}°?ß1} ª/ߊÃ>pÖnq‹Þ—>x(j½¢9ÜŠGþ>þ]¾i}µÝÖú'Ý÷•.¾"ÿY<½¾³ú@Û_»Ìóê6eùïîäOþ–߃]úº6Ï)}Ü{Ö?ëŸõ¿÷ÿ‹³þYÿ¬Ö¿¶hßUèó¼ï¥ië´ÜùýwÖ=aåNþäÞÓ5ùMëßÞól­¯^ÓÖYùkj yÎ4~§ç!ÿY÷¹Õ[ø[u™>ßʽë»:Gïù¦ó÷ê²e•?ò›îÿܶð;=ç4þ§zlYoåŽzïoá}ήü£uØÏÊ¿úÞßÊ%ûÜVîY}?ûœŒÿþ{ùïü·Âýy°>k}Hþµz£ø;ë¿Ö‡ä_«w÷ºÏzï¿þ~DÑij¨uÏþŸÛ¼Ü«êžü1ùóû^.—ì{Æ[÷ÕÜYÿ±>ór¯î÷|ÿÅrö/ÿ[uÏúñ—ûíº'ÿ3þݹ“¿ÿîäoã?;ùëøŸrG¹ç¿{gfÿî:wÖÿûúßÂü¿ò߯ü lå¾ÿvîÛøGñFÏ[¿'w}Ÿkó&÷Ÿ¡Õ±Ë¼hÞÓê}Ú¿ÿeñžÎ½ëýOÞÖ¾wÿÏæ½¥Î»|ÿ­â½ûíþ_Í™¼ßw€¬wý-¾¯ûÆÞ–ó¢iù£ðÔæ1TΉ´z¢ÏËQg~Tt®ìç¹DçŸ{zFGáOw¨âçtÜURà”¿ŸãT€ P*@¨ T€ P*@¨ÀMþH:5£x^íšËq1 D•™óÏÂ!8Ö[åÝÒŠˆOh]t ña?Ãéë‹?T€ P*@¨ T€ P*°G?¿gµþÞ£ÔÌ“Z¹[ífªØ÷TVŽÞv}ì¹7Go½ÕÅÏÞ›W–?|e{d˜Å+:Nµñ²üýïþúÍÏÛ?žÒ˜¸[×½yÞúÃT¿>++ß[»[žVûzű2¸åèmoåjµÃ¢‘Ÿ7?oV®Z»|å1"zóÊò§å«ÝA'>‹,^Qq´\µûã ÔFˆâRåWËWº¿–R\ô*NYq¥|¥ûâHÔxÎâPGÊWº¯†–Ôj.Uñ¥œOûü‰äz¬Ò%t=—š_4ÕyH9Ÿöù‘ÉñT­;Zü_éz½û(hú£ä#å|ÚwO(ÖŠÞ¨yœøJ×c)Ú½£êŽ–—”óiŸTŒ%šÎèùœøžÖc(Ú½¢ëšß‰óiÝNÌ×Uß.y8ŸÖ}iê½uÑ=ÏçOëzb¾èºvÉÏÊÿiçKUî­‹¾]òìV]tí–§µäë³³›®]òµòÏ~tѳkžÖ:ðéî³—®ºvÉÛÊ?ktѱ{žÖ:8wðÝŽîºvË­ºé×=_òÌ«î­ù“ÿnþϺA«ƒO·kÓîç:ïŸu;¯´upwÛ³f¿ÛêDË?ë»Àm¥°dõ0•ÿ³~X1upÛŸÙö¬ƒÙ÷Ai=±¾¯ƒéÏ÷ú`¼ÖÁ6þ¼øÌéÜEÝÇ9p7P¹jób<Ó>´:£î'ÿ.߃¤u·½´ýOþ²ï+]êŠügñ´Ö¶¤óµË>«nSìÈ÷ ò×¼v™ëÒ<§Ìqë9Øÿìö?ÿOX:¤sµË>ëÜœb'å>íûÿ.hûÜ¥¯OyNéßÛslíÿ[ݺÛk¹O›ÿÝùÝæ¿•ÿ­nSìÉŸïý›Þû§ô­×9´ýºO£¯{é6ÅÏþSxyC˽ë½ßK¯i~¦óŸÆËûùó¾1ûÙ¹u…2÷ùV¹ÜŸ}Fþ5ºWÏ94îìÿœ:´rºï½ß!ªûbz|+ÿˆ»Þw>§ë_u>+÷¬¾çüÿVþY}Oþ1ü­Ü³ûžü}ùwãNþü³ç=ïÿÜ«æ=ùûðï:ïÉÿŽÿî|þëêà–;ʼgÿ“ûÿ5Põ} =îÔ~gÿÿÜÿ[¸óùÿZÛ¸“ÿC­Ü·óßÎ}/Þ¨ïqÖïÈè÷ðÛüÈýçʸÕÍÞ›÷´~ŸöþÅ{:÷îÏr·>ñ_íÐæ÷{>Ñœ·ôù§jAãŸÅ{;÷êùŸÍ™¼¿ŸQý_Å÷S\Ÿ§åøC¨ T€ P*@¨ ÷(ðç×c¯Þß÷(¶k§^îÚù»ÔÃß–_Öx|%1wÅ3*.¦ª8YGqªŽƒ£ðìL«¹e­7[å¹Ùýþÿ?ýÎâ–w®â³2;q·>Ïâj;Kõ9ÙXùzçY9zçÍQ~F&^ŽÑó½|µógPèË"š_t<-Oëø>½+GóªŠgå,×K¥nõ*^YëHyZÇÕ‘èY)‹KW\+çÓ¼:ù«vqªZ÷ÄUû<ŸHí Uº×Ñr>¯¥”·Z7—®õO|¥ÏóÈÔDîÒʺRΧq5´âW™Â¡;_éóxB¹»uŸ¶¾”ói\.µ¸èÓôŸ’ω¯ôy©œHSôžš‡”ói\=Ô©ºOËëÄWúÜO,6Â4§ç#åün\,=´ézOÍo‹¦ê‹’ºPtžž§Õþî‹0]W”ü¬üŸó|í³QôEÉÍ(º¢åiõ½’m3ÑtEÉ×Ê¿ú<@Ñ5O«lÕ¬Ÿ…ª+JÞVþU}EGô<­>ÐW´nº®hùOóš~èù’ÿ£?¡s´æOþwóúfšN·«ß9ï{¿£ñçòõõ3­NuÚýœ~ÐùA˿껀×GôÌ[ù?ýCäøÀ[ŸÕó郟}°½°ÿçžÃ~ðê‡[êÿë¹Cø|P}Žg­G<”Õö,ÕqÉ߯å{€ÔO·û@[ÿä/{¿Fñùïâ©õÝíüùÀvž¯(ã´u³m¼¶ p•æ¹§v?äÏ{€æ{€´®PÆiëeÛxÖ?ëŸõÏÿ–ö”¾.Ís[?×îGÊ}Û÷?¾ÿóýÿŸÚzÙ6þÖú߯Q»-÷mý_«×¶ñäÏ÷>Í{ß–úßVÇÖýÜZÿV½¶ÍÓò—¾OO·Ÿw?·ð÷ê´m¾–;깿[Ô~¶óÒikœ­ü·òŠÞ—–ÿÔû¿ßë¾_h¹O?÷£ëb{øC¨ T€ P*@¨ ÷(ðç×c¯Þß÷(¶k§^îÚù»ÔÃß–_Öx|%1wÅ3*.¦ª8YGqªŽƒ£ðìL«¹e­7[å¹Ùýþÿ?ýÎâ–w®â³2;q·>Ïâj;Kõ9ÙXùzçY9zçÍQ~F&^ŽÑó½|µógPèË"š_t<-Oëø>½+GóªŠgå,×K¥nõ*^YëHyZÇÕ‘èY)‹KW\+çÓ¼:ù«vqªZ÷ÄUû<ŸHí Uº×Ñr>¯¥”·Z7—®õO|¥ÏóÈÔDîÒʺRΧq5´âW™Â¡;_éóxB¹»uŸ¶¾”ói\.µ¸èÓôŸ’ω¯ôy©œHSôžš‡”ói\=Ô©ºOËëÄWúÜO,6Â4§ç#åün\,=´ézOÍo‹¦ê‹’ºPtžž§Õþî‹0]W”ü¬üŸó|í³QôEÉÍ(º¢åiõ½’m3ÑtEÉ×Ê¿ú<@Ñ5O«lÕ¬Ÿ…ª+JÞVþU}EGô<­>ÐW´nº®hùOóš~èù’ÿ£?¡s´æOþwóúfšN·«ß9ï{¿£ñçòõõ3­NuÚýœ~ÐùA˿껀×GôÌ[ù?ýCäøÀ[ŸÕó郟}°½°ÿçžÃ~ðê‡[êÿë¹Cø|P}Žg­G<”Õö,ÕqÉ߯å{€ÔO·û@[ÿä/{¿Fñùïâ©õÝíüùÀvž¯(ã´u³m¼¶ p•æ¹§v?äÏ{€æ{€´®PÆiëeÛxÖ?ëŸõÏÿ–ö”¾.Ís[?×îGÊ}Û÷?¾ÿóýÿŸÚzÙ6þÖú߯Q»-÷mý_«×¶ñäÏ÷>Í{ß–úßVÇÖýÜZÿV½¶ÍÓò—¾OO·Ÿw?·ð÷ê´m¾–;깿[Ô~¶óÒikœ­ü·òŠÞ—–ÿÔû¿ßë¾_h¹O?÷£ëb{++ß[»[žVûzű2¸åèmoåjµÃ¢‘Ÿ7?oV®Z»|å1"zóÊò§å«ÝA'>‹,^Qq´\µûã ÔFˆâRåWËWº¿–R\ô*NYq¥|¥ûâHÔxÎâPGÊWº¯†–Ôj.Uñ¥œOûü‰äz¬Ò%t=—š_4ÕyH9Ÿöù‘ÉñT­;Zü_éz½û(hú£ä#å|ÚwO(ÖŠÞ¨yœøJ×c)Ú½£êŽ–—”óiŸTŒ%šÎèùœøžÖc(Ú½¢ëšß‰óiÝNÌ×Uß.y8ŸÖ}iê½uÑ=ÏçOëzb¾èºvÉÏÊÿiçKUî­‹¾]òìV]tí–§µäë³³›®]òµòÏ~tѳkžÖ:ðéî³—®ºvÉÛÊ?ktѱ{žÖ:8wðÝŽîºvË­ºé×=_òÌ«î­ù“ÿnþϺA«ƒO·kÓîç:ïŸu;¯´upwÛ³f¿ÛêDË?ë»Àm¥°dõ0•ÿ³~X1upÛŸÙö¬ƒÙ÷Ai=±¾¯ƒéÏ÷ú`¼ÖÁ6þ¼øÌéÜEÝÇ9p7P¹jób<Ó>´:£î'ÿ.߃¤u·½´ýOþ²ï+]êŠügñ´Ö¶¤óµË>«nSìÈ÷ ò×¼v™ëÒ<§Ìqë9Øÿìö?ÿOX:¤sµË>ëÜœb'å>íûÿ.hûÜ¥¯OyNéßÛslíÿ[ݺÛk¹O›ÿÝùÝæ¿•ÿ­nSìÉŸïý›Þû§ô­×9´ýºO£¯{é6ÅÏþSxyC˽ë½ßK¯i~¦óŸÆËûùó¾1ûÙ¹u…2÷ùV¹ÜŸ}Fþ5ºWÏ94îìÿœ:´rºï½ß!ªûbz|+ÿˆ»Þw>§ë_u>+÷¬¾çüÿVþY}Oþ1ü­Ü³ûžü}ùwãNþü³ç=ïÿÜ«æ=ùûðï:ïÉÿŽÿî|þëêà–;ʼgÿ“ûÿ5Põ} =îÔ~gÿÿÜÿ[¸óùÿZÛ¸“ÿC­Ü·óßÎ}/Þ¨ïqÖïÈè÷ðÛüÈýçʸÕÍÞ›÷´~ŸöþÅ{:÷îÏr·>ñ_íÐæ÷{>Ñœ·ôù§jAãŸÅ{;÷êùŸÍ™¼¿ŸQý_Å÷S\Ÿ§åÊöÈ´Š[ö>=ÔÆËò÷çû_ú;›_t|<¥13’¸{Ç£yzãaªŽ“•—ïé:/ÏÓu8ÊcdrÊ1zý)_ëz ÷²ˆæÏÊÓ;ÿ»;GóÊŽçå«]w—FýîÙ¼²ãk¹zçÕ©Ý1›Ou|/gi]-•ºÝªùTï'qµŽ×‘©Ù©šÇ­ý¬œ¥ù5tòw¹Åãö¾_íx>¡Üns¸½¿–³4/—R^ôÛú£ì/ñÕŽç‘ʉŒ¢?JZÎÒ¼ZñQQtGËCâ«'Mw´|´œ¥y±Ô⢡éšÄW# UoÔ¼$ÎÒx,½óh¨:£ç%q–ÆÏÉÅD@×=?‰³4CÑ]ß.ùIœ¿÷“‹YÙE_ô<½üŸëbhÚ£ ëÚ-?¯ìäbVtÓ·K¾]|ÐEÏnyzùWßÝtí–¯×1Ý]ŽÒMÏnùzùWõnzvÍ×빂ÏftÕ³[Þäÿði7nÑù¢ù ú|Œ÷³ÏÉŸ}àŸh>øîÁzÎñkþôEÿg]Y}pöÚÏ[Í>áó‰•ÕwS§Ð:?Låÿô}ãƒÓú¬^OÄþ.¬æµ}ðÞÓïWÿÐ_}°?ß1} ª/ߊÃ>pÖnq‹Þ—>x(j½¢9ÜŠGþ>þ]¾i}µÝÖú'Ý÷•.¾"ÿY<½¾³ú@Û_»Ìóê6eùïîäOþ–߃]úº6Ï)}Ü{Ö?ëŸõ¿÷ÿ‹³þYÿ¬Ö¿¶hßUèó¼ï¥ië´ÜùýwÖ=aåNþäÞÓ5ùMëßÞól­¯^ÓÖYùkj yÎ4~§ç!ÿY÷¹Õ[ø[u™>ßʽë»:Gïù¦ó÷ê²e•?ò›îÿܶð;=ç4þ§zlYoåŽzïoá}ήü£uØÏÊ¿úÞßÊ%ûÜVîY}?ûœŒÿþ{ùïü·Âýy°>k}Hþµz£ø;ë¿Ö‡ä_«w÷ºÏzï¿þ~DÑij¨uÏþŸÛ¼Ü«êžü1ùóû^.—ì{Æ[÷ÕÜYÿ±>ór¯î÷|ÿÅrö/ÿ[uÏúñ—ûíº'ÿ3þݹ“¿ÿîäoã?;ùëøŸrG¹ç¿{gfÿî:wÖÿûúßÂü¿ò߯ü lå¾ÿvîÛøGñFÏ[¿'w}Ÿkó&÷Ÿ¡Õ±Ë¼hÞÓê}Ú¿ÿeñžÎ½ëýOÞÖ¾wÿÏæ½¥Î»|ÿ­â½ûíþ_Í™¼ßw€¬wý-¾¯ûÆÞ–ó¢iù£ðÔæ1TΉ´z¢ÏËQg~Tt®ìç¹DçŸ{zFGáOw¨âçtÜURà”¿ŸãT€ P*@¨ T€ P*@¨ÀMþH:5£x^íœQr[1E³³î]B—Ðé8ž©=vü.B€àô'–܈÷œôë‹(€(€(€(€(€(€søóë«õç¥zFjå®îë©ÞùQ©w­?_É3#ØÅÓëÜ3U=Çk/NQ眣ìžFqÛmç µëyùû{þ÷s7·]ç×Sº¦GŸø[?ßÅU=·¦êu¼²ò]ݧrôZ_Gùž¬rôÞïÅùê95(äyáÍÏû¼«W×åȵìÍk÷y«œ?íÏ¥o}7¯Ýçâ¹úy<‘X‹»ùDŸ¿ÊûÝþX*qÖ¢ùDÛó·821–¢ydÙ#^çS,»äÁcdq¨b×+bºµ¿•*²ý˜šÙºW³?-ªé_ÅŸ)yPEïª~tσªºWók5ü'7Ÿ«é\ÝŸnyP]ïªþuɃªúžâ×éypŠÎUý„ÿm©Ê'ʯSó JŸ)v¬yà3Íë§Láç)y­Ë{Vþ÷}z%ÛvLá‘§5l4õ]YºL±kåÕ¦pÈŽÓšzEk;²u™bþ¼'ø§@µ<˜RUâ„?} bxžªÔKW?ªõmjä}¿W^ªy rŠZï¥Ç´sTþQïVófGk¼]ùßóǪ˴}j¬Ögôþi<Õx»ó§üüÜ«ò?ex×gÔúè¾~úÁë~ æAô=îm¯{]«ñMãOxìðŸýý€Êÿô9ïÖêþ½úõß‹çîùúï™/jð~.Ë>O­›nëáß³®¯æ)üá¯üžXv¿ö¶µNº®£þ©êîïN­ÿ®ý\ þ3û¿Ê÷?½òþ½xîîû]ê_Õ©ëú©õß•§—Êßû½Kôyª>Ý×ßûÂ{Ÿîu¬Æ§Öý©sŸªË”õÝùOáhSå=§YíYõ˜¶¯ÿiü¬ñªÜ«ÞûÖø§ï;•ÿtn^ñ«ü­÷°ºÏ+>Îñù;Ï]}>9ï›Ôº‡'ïú¨Â¿»ÍÉ'øçèî]ÇêyÕ¸Sÿ±yÿX½ÕúܵÞÊ}×¼÷ü¸+nν)må¯>¯[×ÃiO_²rª{îÿ=Üïõdåo­cë>êß7NáNý×àÝï™ÿàþÐÿ×òÁÚï³ëžþ?›;ümüO¯wî¸sÿ_ÿ%Vë½Ê=ÿîýóßë~Ð;÷ÿlîðä?¥Þ™ÿà>yþóªóêsÝÕÌpÝùÃýçNп7ï.}¾ëóÿ.ÞݹŸúü䀘ݵuÕûÿnÞSêü”þÅ{:÷ìþÍÞ±ÏY|Ÿí^»ç®útÿWá¨ú1—¨¹ªkÕõZÔ¬¾+P•ç;¿ ç«@uþ¾ÑrÚ³UøC&G(þ9Ñaõ“«ü?Ïç(€(€(€(€(€(€(€™ üV5:Ÿx^íšKRÄ0 D¹÷¿Gà,BIM*–¬OKj6T1¶%÷kÉ΄¯/þP*@¨ T€ P*@¨À~¿½JÏQ¨÷N¥Üµã{«XwwZžÖóê*X;skŽÖëÕV?{k^Þëá+Z+Co^Þë×R/ÛŸÿûÿý·77¯õñÆÎ艿öï^\µëb«ŸŸ–³vž–£Õ¼|ű2Ðr´žgÅwu, yÙXs´^o•çî¸<¹‘­yy¯·Ëùm~.øèÞ¼¼×ã¹ûy<‘؈Þ|¢×ßåý4?–J\´h>Qñèƒ5EñÈŽcí‡5uñGes‰ŽO\=­?Z<+?àWúç Ñxdå3ÕYz£ÆæTÙyMñA¶Îèñ»û]”üv}€z?DÑ·JÝ|PEw´<»øM×jùh}€rTÓ-_-ÿs^¶Ðô¬šOUTÕ5o­²úªŽUóÒòÏ:ªêŒž·ÖÑ}]Çêù¡û º¾èù“ÿÑÑÐ9yç‡êï}sýÃÿäÏ>€èÖg¬/ÑúùÇò?õ¦rtGñ;ÿÕïPô«žGUþwŸT眾ÈÖ³ZünüÙd÷šîüÙÖü õÁê} m\µþ•¯”Öûb+?EéZ%ù¯õÉ*<¥yNãÏ{Ág¿K}`Õ³Ö‘ÖI÷ñÓø³\ûùó yOœÕ¿­ãvïïoû“Öõç@~_¸×ÿÉ¿×yÁúïÅó­ß?}.õõ9œµžV¯nó¦òçóà¡ùÏ>¦òïÖǵû!Ö¿äȺ¯YÇÕÖK—yÒºçó¯>Aþ½xJûÒTþRºŽ'Öÿ¤{_×:ÖîKZÿÖ÷ïèõ´:u7…W~Ú}I¹WîÓêÔuÞþ]ùíî«;ÿ]}ºÏ—ò¾§iãuç¶»?)wôsWió«óŸÆËz¿Õø[ïêzhܧrÈÚ7 ÿ¬ýOKþ3ßó pçÿÙæøüstÏ>oи³þc}Hþ±zg×û•;ë?Æä£3J½W©{Ö¿/µõžõ^­nªç£å¯}¿º;¯ºÞ(ùk¹gÕ=û¿Mÿ¯Êüsùïöm«ù(ý³ZÕëžõ¯«ÿ.ÜÉ_Æ¿wò_ã¿Ë=û~ÿvO¨vîFåÛ;ëÿsýOáNþWþÓ¸“ÿ¡ÀTîSù[ñF¿×½Ýû¦ñ'÷ÏŽˆºOGDZæÝ¥Þï.ˆæâÜW;ÿÚ÷Þ¼v×÷âݵޫ×?yËêûmônýyÏ÷æ=¥ÎŸ|àÍouý(ÎÓyg÷ÿhÎ÷xoýpÚç«õ¹:.›/yËüÄãj>²Ýsôª®¨ãHpOT®Oyíí–³ï  ó'1_Pøûî’«?)ÅŸ0ÐòÇÜ ³¢T€ P*@¨ T€ P*@® ü¹?[x^íšAv1 Cs³Þÿ=BÐççxá<;J$Pè&‹Ìˆ>@ÍLóõåVÀ X+`¬€°VÀ X+pŽÿþÜ÷ýyŽB³wå½~¶zú»‹ò̾^_AídóÌZO[Uî³xu­££¬F§]ܪêh¨ÌÛeÔº¼Jsvö÷ûý/ë'Šû»ºœªót•Åýê:hð(ÏÑÉUnÕ×uû‚C}|Õ\w×ïòž¦ƒ]>Ý÷WûCWµ›_v½*?àˆôVÎæ^Ï~ˆùÍ«ª¾}pÍUú³­›í‡kêò_ÅÆ©ºûàÙ“Õz³®oÜ}Àʧ»¯,?ðO|çÿ7ߟæƒîœ©Ô;Å*˜¢?Ë>V}€zOdÑmJ«ü÷uû`ŠîlûXõùÏz®d÷[n¦õcþ³ò¼êOV¬îÇ÷Å|mþ1½¦ú‹ÍSufÝ—ù{¬|7®z/dÍÉô¾ØæÀ'Mçѽ?5þïüÑ­Û´zQ|Ê)ú÷ÓøTï'ÊõÿQ_Uë6eý©ü~™Â©zQD󈾾Z?õõ§ó÷<øý»G”¿Ês€ß®}ï:¿çÁk_D}€>×w뫟ÛÙýŸÆßsày˜ÿµó2;w,ëEù«?:ÿ{ùŸÂß>¸+pjþÍßüo °œÃ¨>œ?ÿ­œ»ïá,÷£rÇR×ùwþÿsŸœçßùwþ£s€åùm·–ç0TQîS¾ÿ¡ôf«kþ>ÿO<ÿÙrˆê'šÿÝó}?JgÖºæïù™ÿèü®Ögͪ¯hîUŸûQú²×Ο]tQþ«s·û>´®*õ§ñWÑÝg”;û¹ÖS­¾:5½Ùúòï>¿ýw™5ß#¢Ü«æ>[NéÇükr¥âŸ(ÿª¹¯¢×”>£Ü«æ¾ÏuÌü1ŒîèùÁÆÝùïõ¡ù÷êÎû£>+wç¿Çæß£3KÞUrïü×ør5ïÕïyï¾°åF½ŸUþUßw>­«®7Kÿ«ÜQ¹÷üÏ™ÿªÜÍ¿:wó7ÿ›,ç§JSrïüÇò?»ù_ã?•»ùÿΗ;ú½îÓ{¿ù¿æ wóæ÷ÓùgñV™óþþWÀÜŸ òÞ½Û§¹¿ž»º²ÞŸÍ[}Ο2ÿÍýê›ßµï¬ùÞý;›O>‰©¨{5;ߟý}â¶û{]’k³óßåyõþ5õôïbá•SÖuúärvÐÍ?‹ßê:9ªÍY%›ÿ*—ªûæªÙI•î¨ukTš»*ŠSVݹdzv–Å¡kUΩÒÅ-ZçØF¹d_ݽ«góü¹žæV`•?÷®Ü°VÀ X+`¬€°VÀ X+pWà?Út?[x^íœKr1 D}³Üÿ9BŽJ)ZhJÒ$A6€çæè×9c•~øA@@@@@è£ÀŸ_\­Ÿ}”é‘©•ûè¸*æÍr”ëªyy•«ù*ޫש¡®~«¹E¯§¯h®£yE¯ŸKm½h£ùì^_Oaíˆ~ÿþ›ýÜÍÙºŸ¶ú磛åîoå5î¼âZxùEâýi]- 碉â9».~Øã‰YN»çGûbê:»ìæ·z¿(?èŠd5Sëáƒ1Ÿœâ½/~°ù!šƒÊú«ý`SW” Ÿ]qàƒWOîÒ]m|ðð—Ýñt÷Án½Õ÷[åý“ŸúÿÖÿf}ÿþªîõ~¬_U¨è›%Žj>È¢»Jœ³üŸóUî*ºf‹cÖðç~¨àlu§oÖ~ ªgÖ¸F}pªdÕY5îQþ§î…ª:fkÔ»û@vUãå¿»¨êW%®QìêUtVÏCÕêºU‰þ5Þ÷ÌúQͳù0ßçkøûôªæ/ø÷æÿô³š>=oT«?•|²ðDZýÊëƒ]ï¼û¨ÔU¶8¼üw¿ôúà9>‡SñVå|ç†×£uyjÞ©úʲouþôƒïýÀË?Ë=€çÛ9›NYú¹7Înü9ÞûÝëƒS÷¹Uûzë¤úønüé¯}þÜþ)ÐÍÕûº5?/÷ìÏ×{„U§ªãàOÿïØÿ¹> þ©ÿ¬z?½NÕsÝšõOýSÿ}ÿ/%õOýw¬ëùX}\×ú¯ÎÕšüéÿžþúymvk]tç­ÿYýOÏïÂÕšgþV=ºŒórÏþw¿.\­yváoÕ£Û¸êü»ñôæëåúÞfÝß«C×ñÕøwåèÍÛË]õÞçÍ›ñ¹¿ï¿5ï)½õo=wWƒóÎW½Ü£ú>|cøÞé ÿ3ºßq‰þ½ w¾o{Æð?£{t][ׇOþjÜéÿ{}ÿ½z[ûqô8UîÔ¬G¹G=çzíÿ®ëò_õÞκNW>Qyrß]÷ôÿ˜þ?ÊßZ¯«ÇEÕA·uG¹Ÿª{êMýgåÿ9þٹÌÿ,÷ÓýþzèvNÏæ;Ëõýmv½Y=ºÌ¯ÆþoëÿU¹Ãÿ;ÿêÜáÿžÿ,wµûÝÝý Ëùý)ÏU¼³qï^ÿݹwå÷סKÿ‡ûû›@Uþ«yg=ß»Üÿà}GºfýÃ}Œ{Öû_ïªýýΪç4çëúw:Uý½ xŸqØnþ»9Sçß}µŠÿi®Ÿö?SUyvUå6Wå5"ÕYež†Šy£Páh#¯Òš‘[uß=NS­zQíæÊ}\ËCÑüµ²%š«^þ(ˆ(€(€(€(€(€(€(I¿‚øDWx^íšÍUÄ0 „éŒþ» Jàñ–=l Ø’õ3’† ‡ØÖx>Ivooü¡t€Ð:@è t€Ð9|¾?öz÷{Ž3wºâ¯}>ÓÍz»Öò=WÏ©žŠO9zÍïé6Þ®¼øY¯‹ç\EÖœ¢×ëA!oѼ¼ãå9Y3òÇÏûßéoo®ÚõkR‰S}Ê}w¾–ŸÕ¼8GkEÚåç5Ί¯tZ”üÔzq=]WÊS;ÞÏÙ+ŸrŠš¯å+WƒšÊ(~Öq¤\¥ãíÆ^ÉšKÖzR¾»ã±é«Ëâåw—ïî¸s§1Wðæ½þ._é8LšrUÙ|¢ãK9¯ÆËÇší?J¼Wés,ªûjPxdér^ßwcd–ïhqW\wŸcPÝWÆEÏ.ï»qûrG¢øª£{ úަ«k ùŒªç”ÿs~n·ÿÕoT]§y@þPùîêê’»û帿ó¶z«M?ÒæAöy@þ¹ü³ï…äoÃÿ飶dåùÛò?̓èó€ü±øG÷ò÷á_¥?&ÿ¨>@þ¾üÑûù“‡ï±UòXû~èõ^PÅ·.:É?¦ß¢ç ó`v ñß=_Ð몊¾ªü¯yRÅoTÒ<Ø­Ó¬q¨>£ê’òú.tš?¨~£éêÊÿ™?h~£ê‘æÁi}FÏGõEWwþìÿ¿ïJùW¹Üõ”ºCÑAþ³¿iÿN}Ž[ÇC©?Ò>`Í#z=ßQtLãÏ{áë¹Gþ³ïäOþßLË”ó7[‡”{õï<ÿÏÎÿ.ü™¦Ö?ùÏæŸ}î¢ÄŸZÿ(þgë ¾ÿiîÑßm­ãe×J|iý[sÈZÅÿlÓøgûŸüyþKÎÿ¬~}­îPôL©¿QtH¹Wýî‹â7šŽîüÑüFÓ#åzþFÍGóUO7þ¨>£é’rG?÷ÑüE×S?º¿èú¤ü£Îí»8è~VÑ'åîÝ÷«øÖE' ÿ.~VÛùÏüÎÂÿo—“äŸã{öù€Æõ›‡äëwv½?ãk¹{¿ï±þcòQË?ê;JtÓ¡åU÷¬ßú×òª{ò÷á¯å]÷äoË¿wòÇàÝï¯ñºÝ»¢÷SµîYÿgõ_;ùëøwáNþ2þ§Ü³î÷«ûEôyY5Þ)ÿ‡¬çUyDéîÊýÿÿþß;ù¿ò?åz¾¯Î•¨>ŠÇŠ;ùËîÓÙù0ûÔþOî¯'BvzÇ·æ]µÏßݼýÏZßšûêUõyë¸Ö¼»Õy·ú÷â=…{µûŸ7ïiÜÑù“wÌÂú–®Åù'Æ]ü(R^ÚñYœ§öõÝÌÓò¼ÎËæ{ׇ©ãP¹iuMå¨Ý·Ög”yÚ}sÞÃŽ+äåãÀÊ÷¬ç>»åªWÈwvNxñŸíjݯø×Ù •Ò:@è t€Ð:@è ¿ø‚ÏA™x^í›]r1„}³Üÿ9BŽrÙ[o¼ ~è¼äAB@ÀŒf“·7þ¡T€ P*@¨ T€ P= üùµ'Wfú¿þÞSë xs—ž×CùQJye훯8V†Y\oý`©6'š[.UösÔfRÅÏÛo­Š}½ÿþ¼ÿYÿöæèu^_"¹‘[¹Kí¼xÞž“«joRŽQûn¹ZíûŠ4Šëí¹V®Z»XuñO¿å”e¯åjÝOÌ7Â,~Þ~¬|¥v¾*ãžæÍ¥ê<)Wí>\r>‘UñŠò«å+Ýï£6Þ)QPΕò•îÃ#x §è8¤|¥ûîTDZŽÖõ|)çÓ>’¶HPùdÅuâ+]·©_o•¥3º)çÓ¾z¢ºйdÇwâ+]×Q¨Û­oRΧ}udež»ð¨ŠóÄWº.£‘¿«J×n~¥œOûò ÿ챇êxO|Oëäÿ¡@5Ç[ÿ'Χu”:¸Õa»ý‰ói½º¶ó»ÍÿÄ÷´Nþ|TÖÀmýÓþkýžúýÕzU Ÿïü±òØe×ùûòèi­òáQUçèuP¥Ë¿ä?«Ÿ­u‹ZÖ|h§«kò×é5µ¾Ðê`ªÎ¨y‘?çÀ»Ú:ˆº¢öÉô¸´üù}hÖÜèÂÿÕܙޟYùië ê9p{n–^ÓühùW=¤õ1Ot>Óø?×I´~ÝÏŸÎÿQÝ9Eǯ­éh»hvÖy9Ín[ÿó>ðuÞ‘?ç?çÿÿn}.iû¿ëw@þNÄï@ÿÖ€µ_¦Ùiûí=ÞÏ4ŽÖ|ÈŸï›Þÿ¬}2Õn[ÿOåhÍ‹ü9ÿ7ÌkL·ÛÒÿÓ9jóÓrïúÝG«Ë–ýÓùoáhÍ“üùÞ7ñ½ÏÚ[ì¦õýn^yNáï¥Ç¶sºò߯)*_-ëïkV»¨¼·Ÿ«åußßΡ*òç}á¾WUÿ[ý¢ô=ÿÝuÍü!ÿÝ«ç wön’®ÞÕýþðÊýŸS䟣3J¿wé{öL]Zû=êûÞé»/ZßtÇÊÿÄ)j½»Þ(ñ[¹Wõ=ç¿ÏüïÊüÉÿ]”ùÙ-Žî}Ïþ·õÿîä¯ã˽ú=ïÕý¡ÛÜÍŽw*wöÿÏý?;ùÏ wòÿÊwòÿP`+÷íü·s߯ߋ7ê=ÎúûPö}*Û¹ÿ\Ù<¢ýyóžÖïÏÕÍ#ë|r·=²øxû‰â=½ß»ö4ïmÜÑßÿÉÛ6ϵVÞsY{^çg?Z¦î×òÒî¯âKÞ²ŠÕò|Þ—¼e¼Ÿw¡òÓÆeËžVZQö“œ(BŽRɳJc’ ˆogã…†ðúC*N¾¾øƒ(€(€(€(€(€(€(ÐCßÏ:µ~öP­N•ZÜg÷©£\Jf¹Y=WCÕøìo.ÖññÁ«¬õO˲éë¿*¯|ºúÀKï¨q»ù *ZøÀ[çèñ«û ºþQòÛõÿIûßC]?UóÁ®]×WñAW~»uÃÿù>ÙÕ1ûúì>È®”ü³ú Š~UòúÀë~PE÷(uHù_ë¬}E·jyH}ÿçJ)ë9P­ï¢Õ#õÕˆ¦WÕ|¢ú ªÞÑê‚÷ºÔWðïÍÿòM4HýÌ:™Ÿá/Ó­šßðAoDã?{߬և^õdåÿî/ýªÄ]õÁlŸz=W…‹U«ü­¿–úÈJ¿ìqªò¿|“Uþ«>ö¥×:+³Æ©ÎŸyð÷}w•–s÷…¹ï9ºðg|öüçú$ëû}”w7þÌ9àuž×Š;ê‹nŸ¯Î-^ûtã;ªþœ ÌúÀ«oµâŽú¡Ûç³Ü³Þÿï|Óó]½ðgþ3ÿûþ»ònýÏÜwðgþ3ÿ™ÿ³s@ëæµó¿çü‡û÷ì÷ø÷äw¿÷ËÚÿðïÉÿçu^_ ÷¹ï3fï{Yæ>ÜÏpÎîsÜ/Vû>x¯ñ~×k•ÿêûWûyxïñŽÚ÷pÕá:«ãjߟšû³ùòœ®?௫gFá~ ²èV%OøÓ÷‘~¿£J_E¯#Zß3ÿmçümõŽ2¢r§ÿÏúQÊýÔ=ÿî{À(}R-)íïkGûUÓÝ»)wë¾gþŸ™ÿRþ£>=õ¹w¿T‰/åîÕ÷ô¿Nÿgåÿ=þÙ¹Ãþª¼­ê¨Ò÷ôÿZÿWãÿ9þ»Ü½Ï÷£{£ÕÜ̧:wúÿsÿwáÿWþݸÃÿ©@Wî]ùkñŽ~®ûºñ‡ûgGd;—¯æ ÷¿'ÁªžÑŸ×æ]eÎß¹ :ÏQ~§xWçžýý÷Ù^îùšs—>Ï2ÿ­xwçî=ÿ­9ÃÛçþçÅù=®ÎÛ²Þ.£óõîÿWâÅ¿©3yñÑŽ{Fú»js°Ú¯>› ­xIãØ¨Ð7Š”Ë©u}IøT~Šãݾ>UõNmþ((€(€(€(€(€(€(€(Eÿ@J3…x^íšËqc1 ™óÏÂ!l[.Ù©ôü@ïÅñL@RÚþ¡             ôPàßç#ÏÕ¿=Ôª—å*wëüz æÎÈÊo×øÜ*æ~O¯uó*›#r/NQëäP5O”QÜvï“Gq­H¿~îÿWwsÛµ¾–ʺÑÜñŸý|W뺺ÊkD6Ëwuž•£×x Õu¢Xåè=ß‹óè::$ÎDâÍÏ{½QŽ«ãΨ~Wo^»×[å|7ÿ<‘ØvóÚ½þÏÕÏciÄï¶›Oôú«¼¯æÇ“‰Ù1šOÔ~ø`Ì?Qxöd´þ*ûჇTxœŽÃËúŸúÿË÷Ý|pºîT÷ïâUýUâZõúy ¢³zU} ®»Z|Õ| ¦¯z<«ü竜 êz«Æ·êø×øþ!»Të+[\³>8ݲé¬ï,ÿÓ÷U=³Æ•ÍYuV{ÖÑ纎Yã›å}dÕ7Kܳ>ˆêYtÌ'ük¼óWý§êƒÕ¼˜?æoøéTÝOj>¨®·Z~ð§|+`õÁ®÷€Z}t‰ÇÊ?ú{_¿uágþW}'Z¯ªûY}°ë˜]·*—¨¼¬üO£þˆÒ­Ê>Õøso°½oªòÇ{}0ÚUÆUé×»ò°ö®£qìҭʺVþê÷@Þ{ûVþÜ Þû¢KýÃþß T9·½òèVÿôŸ>0zÿVçU?UÖ±öU®£qUáæ•ümï&/ÝUÖ?ü-ÿ?h´ÏªŽS©;•8¨êŸúïûý@—úWé·*qX¹óý­sþµxZûJþV]ºŒ¯Î¿ ÇÙ<áOÿ¯øî›­‡nó¬õ¯ú}¿ëÚú˜•»ú»¯[Ý®æ›ÿjþÝçgåß›WþVþÑç¾Wž¬ó|/°rßuîÃÅv_óÒ þgt÷â·ºŽ•ÿ®¾¿šóm>¶rßÕ÷y§Û¸yùþgt÷â7»Žwê?Ö‡ðÕ{¶N½ç©r§þcüÿ½ëvu½Yî»ïû¯ïÈÕ<™ÿÞß³üw½ó¯Ö…Ÿošå]÷œÿ½¹Ã_ƒt¿çü×à~ªßÃ߇¶sžûŸw•ºçüŸóC•º‡¿5îðã_•;üÿæ¿Ê]íœçþSïY¸SÿÏ~èRï¼ÿ xñÎVïÝùÃýÙ]~ÿƒûû`UþÞ¼³÷ù.÷¸Û~QÌ^ÿ»xW­÷ì÷?xÛêûn´zýïæÝ¥Î³œÿQ¼»s?ýý_4gxǾÿNñ}Ý÷îüëþùÝù¯ÂÑGw®£ù[uU?š/ãžPåyü|Pçï›-«½* Â2gˆæ&Kv½RÀ‹? £               ¨)ðc{>ûx^íœAr1 ý³üÿyBžrÉ:He{I,@ €ÎÅ-A`zÀ%);üC@@@@@@˜¡À¿?:wÎP§•»Ü­Ï÷W²f…Vž^ãjªÖ'k/ŽÞqú(¬]‰7·èxÚjÖË.šWtüzŠkeÍçt|-uõ³ùûuþóúyš÷Õ|úr3ôâ¾çŠWôç¹jë;Ê-ú¹hîïñõHädÍÕ?œñƒ•OÖ¸h_œQ]g–,Ž^óFùA‡Pl&^²ãà›O²¹EÍÖü¥¿J\|ð»T8ÊÃÛk]¦ûÔ)ÝÕæÁOªq9ÏtœÖ[u>/è®ôßg¦Ê#+¯i>ÈÒY}Þ)>Pç_wdë[eþ®>¨¢¿Jžw} ¶?TѵZ]|PMwµ|«û@MÏjùÀŸ{ÄOªú Z¿©çkõAÖ¾P]ÏjùYù?ÇöA5}«äkõü{í'Ô}P¥Ÿªæ ÿ^ýlõ¡ª¬õ0nÏ×ðßÓ«›¿à?›ÿÓÏj>èÖgêõÀŸuÀr_u/ Þ/]óS[®üÕ•CV]Õø¿û#K·nóîúàªO³>ïÆåT=»ü³¾'ZõÕ)ݺÌÓÿÓ']øD×Ñ•?>Ø;çîú`u=Vy.ºªÇ‡ÿ^¿Tçýžwþ¼~÷÷.õsÀOïn}ëUÏþ¬߯ðçý¯ô½Pô¹ÁkÝì‡þ§ÿéÿ¹ÿýOÿÓÿôÿî:½O‹ŠßeßæUÇ.÷ª÷?œÿ9ÿ*àÕ7]âLéÿ.¼¼ë€?ûÿÎûï~éo·ÿ£öåQq»ñò®§+oºÆëÆ¿+'ïºv¹«Ÿû½õé¯ ÿê«Ê?JiqwùGíϯâNã]ï.÷¨÷~tÄ×¾ï‡Oνãnÿ_­ÏÖÏá–ÿ.÷¨uŸïaÏröüstÏ^çÔ¸Óÿg}ÿ³zg÷ûûüðŸÉ_•;ëÿ?ÂÿŒÎ¬÷¶5ݪçcí÷èsþO·ZþVþ¶î½?JM¿ªùX¹gõ=û?Ÿ}IUîð‡?‡cÿ;¤ê}OÿÛú¿ wøïñ¿Ë={ŸÇùo÷Ýïkßýrÿ¤¡êy+:ï®ýþî¢h«ÅŸÂ÷ÿëû`wø?ð⮺¿»Ú5T[Ÿ½òÎ}jÿÃýuEðê'Õ8Þ¼«®óÓÎÿp¿zóÛîCèó5]«<¥Ês5/úüžÓVuVy.Šw·÷úª+T¸zý¾¼Õ«zu{N…¿•›u\7ŽÖzNó·òògÕ©ë8oþ^œ¼âtåæU——Î*q¼t™G…›5)œ¢ê´êž5.J‡©q³8^Í;•Ç麯8D~º^æ{U šïÔ{µ*>³ò¯Ry¢              À\þ¬«;?x^í›[r1E½³ìYB–rM¦*qf,Ð.èäÇÜHÝv>>ø‡(€(€(€(€(€(€(p‡¿~<òý¼Cû²q_}~Ÿ¢µ2^建¾–Zý¢]åwj}?¥53:Åo÷¾šêÕj7§èýêÈÍ š×i¹jÖóþóÏûßìÏÓ±½žævjÿY¾Öu{UÖÝíŸè}­\½vºäöDÍé´?/_«ýµõv9Í#{+_«Áµˆ²ùDù·òµÚ­©®³:J5?VÎ#;’s‘¨q‰ŽgÄ×ú|NýüUÑz«ú³rÙåõE Ê#+®_ës…<ë,ÕýZ9ìòÈÚ<«sÈŽoÄ×úÜF#Þ*[ß*þ­œßÙÅ“µy¬¢¿JœÝê@E×jqt©ƒjº«Ä ÿÇù¢Â#+Žêu¥[7¿Uë ‡¬|àÏyð©@µ:Èê—î~gëÀö־Ϫ;‡¬üfù?×í#üýNYúÜâw¶àßã>ÿWç•j¬æÅz[}ÃߦS÷zR«ƒîz«åÿ»çüïæÿœG*u 6o‰G…¿õ;Ã-\¢ò¬ÆÿkDéÔÝ·¬ým×Ó©ü¼ü£?à­£S:uÝ·ÿg½tåµ;¯®ü©Û{.üm:íî;µý¼uà=—³íÕôV‹þwÏîü¹|_ß^þêïïε¹«ü™ÿ3?ž}¯óúWé7µ8èúŸþ¿÷ÿ“Òÿô?ýOÿ{ç€÷þ•m¯vïR‰ÇË÷ÿ^çEwþ*}¦‡—ö÷úWÕ]%®®üUôUþ½Îsk½y¹W¹÷Yó¿Ý®ÿÛyzóïÂß›7öó®*øí¹¯xù{ß»vÙÃ{ï§Ž^î§î}pÝËÕª'üst·ò9mÿ;ù«pçïqsêþ9ºŸžçÖýá'5îÌÿØ:„¬ÞÖy|ÚN•;ýSðÑùt{÷Ÿå~ê;ß»ïÀÞ¼°·Õó,ÿ]ßë­ûÀÓÆÓªÓ,÷è¾çü¿›;üáÿ©€u®a÷º^ªÍû¯÷¸ÎÍêÜ™ÿsÜWÿ~#ëžÇûßïnÜé_=t™÷œÿ±ÜÕæ=ümüWû];óÿÌû\îðÿ—ÿ-ýÎü‡ûß5pë÷Ÿ[ûýÖþßÅ»Úù>ú=p÷þ‡û÷ÐÿnÞÝú½ëü‡ûhÒ¿~^µÿOñîÞïUûÿ4ïÛ¸«ÿ÷Ü<÷®ÊžÿQœ¿úñêÔÕ>Šç[纵^Wùgsù·êp«ÝH¿jÏoå8›w5¾œã³¤_¯«ÂoÖìöT@•?„bÈæ“%^Þ)pš?Êk+`å¯Ñ¡               ü¯Ào{ðDx^íkŽ1 „÷fÜÿ# 4Œ„†éö»Ê)þ¬P'~Ôg;f%¾¾ôG H) ¤€R@ H) ¤€8C_?y>ž‘µ²|*ðÊ?ëïR˜C,ÞV;êìÒÊ­zý~ű2¬æ™eKµ=Ñdñé¶³‡Àl&ÝܪüͪÈëýçßûŸõgÇ,»¼Dz#·r·®ÏâµÓ«*7+ϬõQžÞýÏ¡ÑoeJ4¿VÞïÖ÷ŒyDã0Ïiu0­7ªÿSêU”¸¢u›Îõ»QtFck ëŽ_”ÿs}GÛ< èËG´ltêW³èŽç–:@Ó•-ö:`Ó5^oÔOøÏPõd‹ËËú½Mgôx½u05Ðõd‹ÏËj°é˯·ºç‹žlqzùwÏ6]ÙâõÖA×`Ó“-^/ÿ®9À¦'k¼Þ:¨ž¬z²Å-þJfã–/Zdç'{Ÿë\üÏžâ6ÿç|D©Íë™zDá÷~¡:É­6þ¯u¢zÈ©kÜí×®uªƒXXùw}ôÖêÁVâoÓk[}mãÿœÛ8U峕¿êÀ6׬uà=Ÿ»÷UõÍ6»[ùkÜ›âO§m}ïýÜ=Ç£þ¶rËÊKý¯þÿ£ÀÖ:Èê“­v¬ÜÑ¿êß lóLümzm›â/þ–ó?ú>Þ½[¿fçcíÿn~QÙzm³·•ÿ6NUùˆ¿ÎÿMçUŸl³kí{ô{ÿ6>Õùˆ¿æ¾eî£õul·ÏÚÿÛ¹tåg彇{÷wéqŠ+÷ª¹ŠÞhyŠ¿ÞûÞûÐúb{<(}¯ßËœ™?â?£;Ê\ÿ3ù£q×üï­CñïÕ›}ÞWÝ÷õ{™½uˆÚ÷šÿµuàåÞÕ÷âÉßû}Þ»åœÜKß«ÿkúßËßÛ¿Ñ}[ún:/÷îó^ïÿêûk`ºoØý³ö½ÎÿØ`ç.þ>þ[¸‹¿”ûô{Þ»{ûùÛ”ôžVµ¿K?V?[¹kþžÿQî¨ó^÷q×ýÿÿÿ‡ì”~Wÿ?ÈâÍ2çõþ/îßÕë{¹5nõû÷Àª#Ûzqÿüå€çU¼Ù¼ÙÏ÷«ïFWz¢?¯â½;ë÷ŸjÞ§pgá/ÞW<öm¾wñ>­ÏÑîÿÝœÅ{æþ7ÅùÕolJîÝíÿ(\ßű—XnfèïÆ—«Ê9Öîê‹¶îBµ™¢qÕ<¯åýjoöò6Å_Êc(PÅ#;Eq¥€îÉW é¹R@ H) ¤€R@ H) Xø C—x^íœMRì0 ¹÷¿Gà5Ì‚)B"G?Ÿ¬~“Ør·$Í+ÞÞø@€ @€ °?Ï÷ÇïþÜŸÔž+¼ëÝúþžû®Êê/êù¾{GåÓkÜÞtõ£÷ò”5Ž>Ñ^fy‹ž§uh?~¾ÿýŒö5¾aíHÎü¯~åÕ:®6ýúèVý®¾gõçý|=q­V=z¿çíùl<- uÑx{ôïÌ£×çujgöö=ž—ï£qjmäÏí+zü¨|È7Q3c´Ÿ¬ñ£òà9nøY³üdÏ•ñFrgÈö’=yð>eû¨žÏ;r«Õ¶jUó“\ªâ¯2ïôpvs´ w•utóÿš*»Ça̓³:Íþ¼;ÿêø»ûæ[5Ç®ó[ý«ÿ=Š®ªâÞÍ?ýÀv¾Å¿WUFÍ»«úÀµ¼Æÿ5NQõ§2®5²Ïy«ó©ðUÿ³ûþñoùÿ¢«ý8û=õ¾«õOýSÿsÿõOýSÿóêßZ÷ê¿ÿáÞÏÖÇðoã¥ò}Ý+ŽÝü{q™2Î.þ§øò^gwÿÞ<¦gõŸ}w4ß4OQëíâ?jýSǵz:÷Må_½nüsî³Ü÷Qÿ{ä‹JÝsOW“Oø¯á^½ß?çÇÿLÿjÞéÿ¹yˆÿ\Þ*ý^µïSÿ9ù¨Z÷øÇÿ7µ>¹K<êuOýÇÖ?þcùªö‰UïQ÷»g¿/VåØ-®nÞéÿ¾ý ÿ¾<»ÔWïÔÿ½|íîÿøçüo¿ÿØ¥î©[ýïæÿ×üßõ^u®?;÷ãÿÿ»{Çÿßþ§xÇÿoÿÓ¼ãÿAÀË»ú>ô} Ë=›wœÓ½O­¼ÿîÞu¥6ž·ï®}~ZÿÇûµµz]'Ê÷nõþš«¼UÞ‹ò~­zú?¥âñjQ¾w¯ó®û´ï©ÞÕÎYž§û®Þÿ³=¿Î×Çö]ÁÕ}×ú\µgêüZžœyUñhãÚêyÊÊUõyL®Põy×Ú*y눀ºÌÅPñ»JFW©Lhðª­U  @€ @€¦øíY8Ax^íšKrÛ@ }³Üÿ9BŽr)ZX‰3C|€öÆ ‘L7R¤¾¾øƒ @€ @èOàϯÇwÿ÷'3c‡»ÞOŸA³Þ.O}ZWX¯Œ­¨Ú'ÝóR›ïž7u÷µ¿*þ_ë"‹W·¸Uý?ë¡›¬ýìÖAÔ{Õ8YܺĭîŸypïûÌ®ÿè÷ës€:8«üŸq›:ÿÕûŸ9°WÏÝúÿøç÷$ë¿'¡ÿ÷ú¥Ëu_õ}ðîý>ÏïÕ/ý_õy€üŸ¼¶šÓ^ëTï˨ü»õ·.qðÏüï0ÿ»ôcô>ª÷4¯nñvý{ݯí®ÛÍCÖ~ªùÏâÔ-î®÷¬÷~ݸ«ìGÅ¿ iyàŸï} ßû¦õ]ö~UúžßiäÌüçpÏî{Õ÷ý*\ºç¡Ö÷ÌÿØ9„ÿXÞjóÿ3ý«zgþÇÔ#þc8w™÷ÑÏùÕ¸uÉG½ï™ÿ>séÔ{tßã_Ëÿîï/¬Žï2o³÷Q­ïé›þ¯êÿøÿ&=7«Æ¯Þ÷ôÿYÿwñŽü3ÿׯÝúžþ_ëÿ®ÞñÿÙÿ]ïYÏóvŸ U½ÿöÊ{ŠwúÿgÿOóŽÿ©Þ§ûŸî}š+ßUîëVï½î£TÖÅûçJPñd•‡µïnýþZ Vܳ×ÁûêÄÿy\¶·Óø^¾»÷{Õþ÷ö=Í»úý?¾ÏæùîY§ó×ê¼(ϯqv9u=ÞÊãÕ:Yž§ÎõÕz½ò¶úy¶ßwñW9L=NÕÛi^S=žîû”³Êy§ûæ<Û÷_Qõ€7[QÞvãØî’ÕÞØõb}<×çnôSÿ»q8€ @€ @ˆ$ðÐ[6Cx^íšAr1Es³Üÿ9BŽr9^xÊSH4|>ð³ÉBj@ï ©gÆ¿~韈€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆÀ|®Ñú>‰+´¼{ÇwÒì·j¯ß§Ïõ#5³â§£ŸŸI™wUÑþ²âñì]Y–¯ì¸½©óTŸí Ÿ‡h¯Jþüÿüwû?Ê«7O/ uÕÞz¿ïõý\aîÌ·>£æGû=Çm_]”Ϩ8§£æá‰seŒò–'ʳ‡Ë ®š,oYq-OÇqä92eyBÅ}êÛzžÃR^(O¨<–OïxžÚÈ(/è<^ÏÖsµ¶â³£½Tå³¼ÞŽÇ›¨‰Xå£*ï­gk~µ¸¬UªóZ^oÇãŒ`#U{¨ÎëÙšµ÷<[5–ü–×ÓñçF°Xø³ÔqêÙš‡µèÏÆÂ­Ëïé¸ß æI6îlõœz¶æalÞgaãÍZå׿7ƒy‚•7k]–gkcõ< +göº,ÏÖø¹¡Ü™ìœYë³üZã¹VÏ£³òíR—åÙ?7•3³ gö:-ÏïÆs¬žGeçÚ¥>¯ÿ¯çÎÅÎì·KÞ}kõºðìZ§wÄt·¥+×.uËÿçìâ+«NÖ}µ^Åý¾ïå÷9 ÿ»ý‡lû@ç4v_Ê?–7Ûþ–ÿÝþ½÷€ýIÞ7ƒ­?¶Ô£s`÷9ÀæÿÝé±¥Ñëìâÿu_ 9MÍ×Õÿ×~˜ê½®Û}à{ËË{ ÍkZ>ù×{àíƒûàÖ;úïCNoŽiç2j=ò¿³ï½ß²ö¿>øöñ”þ—ÿÝþQ÷å´Ó=T­Í‡­yYüoå_½nù×{?Ã{ulËÏÒ÷ú½¦æü‘ÿî,çŒüïôÏæ]ç?vÊ?–7ËyÏþw^lœ¦ÕÃÚ÷:ÿ1ç‘üc8³^ïèßyظM©Çëßúý5z| o–ux½£û^÷νäõÝ×§ñXú¦{^ïU}¯þí¯ÿÓ>Íš×½ïªë÷z¯î{õÿ³þïî]þåÿƒ@õùÙ-ÿ”¾Wÿßõÿ4ïòæÿ©w–÷¼wŸº¿¨z§{WÿÿÜÿ[¼ËÿwÿÛ¼Ëÿ'(ïì÷¼îÿœ~ïê}kÿoï÷×sõ>]GÞ¾ª½dåöÝýœßrÿËûÝ/ÅYý‡Š›å{j¿w¿ÿåû®¿­Ù¨>õæÉö½¥Ï»Üÿ(ßÛ½WþG{–ïšÏUž_óZ÷àÖqö{Ù»¶ú¼]·—/Ûs·ëÖüœß¿PûBþb |=ͳZEy%ðÔKôó2„%íï4v•ÊöŽÀ©¯Ûy"ÞƒÀ©×«Q•" " " " " " " " " Ÿþüd8x^í›Yr1 D}3ßÿ9BŽàJɪr”Œ† –øüã“دrFòÇ?(€(€(€(€(€(€(€(€(€½øýùØŸÕïÞjõÛ÷Ñuú)X{G£Ü¢ÆÕV³^öQ\WãÔS´VÆ«\²çÕRY7ÛlŽVñuÖÎì×÷ýÿê·Ÿèu´U×ÉîŽÿêߣy_ÅÓQZ3“U¾«ó²}¡I!/«UŽÖó¢}‘§¸VdkŽÖëEùB‹J\6Ö¼¼×óöCœò‘¼yy­lüãÅ'z]/?ب¬»J4§¨xÖ~Ð%¸—Y¬8øà½?²¸DÇÅÿ÷A4•xV~Øë¾ù³Uxdåqº²tW‹{ªÔ8dçsš²õVŠTõWÉk×ù7<žÿþ(°ë§®>ØÕå”ù»üŸóÕúÁ)ü¬ö¹ëø?°â‘µNdé×%nutáµø÷èç»þ©êƒÝ}3ÿáøÓ*ú€úõñíj?ˆ~>„?ü;<«úX½¨êÖ%/øûô×*þXåõyA«ç¹êïû`u]«äΕ÷Ô/ߨõ*ý³KžðïUϳ¾„ÿÙüŸ~™õ×=`Ö¿Œ·ñï,¯÷ð´á9«£ ÿg_™ÍŸñ{¾Qãu¾Àyó•~UøÓàÿ³7ÐlüP­þ_Ï|ã¯çÀÙuá¾7´ÿL³çÀlz§ìõøïéWÝUùW×]%øSÿ3ß ñ>ÏG×W©ŸêyT«ÿêz«ä?ËÝëó?ê=çü©Â_¥^ºå¡Æ¿›¾êûQᯮS×üàŸsîªø þðWxîW©‡SòP©{¾×“ÓàŸ£»Jÿ™üÕ¸Óÿc}ÿX½Uúýêÿ{E½ïWÓ©[>«uÿýb•ÿèçr»ãºÕ›Ú~àߣŽg}¥Îû¿¯/áï«ïl=F_åuß{½/DéÒ=N5îôÛþ[=«ô‹ªÜ©¿ÂßFÇ*õ¾û>7ëžwõž¨šîÙùV¯wîÿkýj—»ZÝsþÏù`—ÿî{z¯ùÙýT=~WîÔÿûúßå®Úï9ÿáþÓêý7*¿Sêúÿ»þOå~úù:÷Óø[ñ®r¯}^Œ:_³âÀý½²¸xÇ…ûXðæµ¾5ïn}¾Ûç?^¼Oá^õþ÷±¾>:*ª?¯Æñæ}Z½«¾ÿ‰â|:ïlþÑœ_ãöÅSÆ­öå»yÙœ©ó1Wá8ë§±Ý3jVWÕñ\S@•ç]^k»eÖ«w:gÿb¾ dóå~îË÷nõhþwùð÷X¬ùÇfO4@@@@@@@@@@ø£q5cx^í›Ûqë0 ÝYúï"%¤„LÆñLì‰L‰ÄãÜû“[$¸ €”ä{»ñ€ @€ @èMàëã¾¾Ù¿½éô_ݬ÷«×õ'Ys…W=Z¿&µ>Q[û´¯aí•XùŠG›f½è¢¼yÍS¸VÄ^^²ÆÕ¢«Íçïýßêß,ߣyõ äF¸êýìõ#OÞŸçRÖý¬?¯ïy{?_×Hld^^WÇÊ‹XÚz³­zŠºÞ;ôÌÄDåÏzòÁ&?¬½DG¬åA´/ïù¼òa²îÕÞ>²Æ'Îå\–Ÿèy­óá]ýoE{ÈžÿsEWΪëRóÿz¾TåÖ%.uÿôßó)þ}ùª÷‰*þé>yŠ®êu¯þ>˜s`L^V©ÿ*õT-NuÿÕxV‹WÍ5~ÕãUñ_cÕøñsÎRÍüãŸ÷¿þ¿»ïRÿüþ§G¿Péû<×ÍÉ'üçpWÙð…sý?6Õêÿøÿ! ²/vƒú­7•|RõNÿÉGüÇpV©÷×8ð¿§uïô߼Ŀ/ßnýÞëÿ÷Þ©r¬W•º§ÿÛö§YïYuÿU½ãÿ<ÿþ]½î©ÿ¹ú_õž½ß¿ÞT=ggŽêt?ýyÇjóvóNÿ?×ÿW½«õ{ú?Þÿæ@µ>ìo÷z§þÿ¯ÿݼ³ÿß XyWßçî+¼û©êø»{ßµþñþÜ TëÓ:.¼ÿ¿XsVÏÚwÕý}ôëqô=üÄyðþ{Ý®?ëûsèê¯:ëÕêzk¿£ñôÄfhåÓ*ΨG«ëbië­fåÍ+Ž•çOqôÌÄdäåÍ+î'»?¡®³Š—§¨¸»¾_ݯcÈ7“(OÞëPkuâí%+¾u=¬ÑÕ¿+ËOÔºÔÁûŒò½uð¼²½d­oUúžþ¿xUgÝë «ÿÔÖíZj²óéVÙ¼U×ïRªüUòÚ­õó¡ gÕ]â¯ÖAöy ‹ï}®úÏ~>ôæÒ-þjdÍn~¼÷»ê?kxóèµ¢ç@W?ÞûÆ?χWêuàÝÝãã¿÷Àoÿ÷ù§ZÝçsÔþñß{àÿ+ÏÞï¢æëÜLªÍ¼ÄÎ%üÇòV«oüãå÷€×9@­?NχþïÝÿ«ïƒèÿ³êFeœ>oU÷‡ÿ³úy¶Îð…ç€Ùºåz›º¥ÿm8V­GüãŸùß÷ïÇèúŸþ§ÿgç€õ{Àªç§êyg{¿×QuŽÕòŸõîýwaÕøUÏÿœûÎ}Ìÿœ:¤ÿs¸«üÞÀOÿjÞ™ÿ±uˆÿXÞ*ó~õó^ÞÏ}ôl=Òÿ±¼Uú_Õ;ýSøá¬Òïyà¿§uïÌߺĿ/ßÓæ}ÔóÞãçT9VÍ«Jß3ÿmçÓª÷¬¾Ç¿†ëÏsÍÆ«:gUò®Ú÷ôÿ^ÿW÷Žü_ ¨ÌÑ*yœÒ÷ôÿ\ÿïzÏ>ç¿:V黬wÿAt=œkÌvgÑ^¼Ö³¥Ò'š—ï¸} ùîÔÛ“U|_ }£[ù±ŠÓ×DÎέ¼ÍÆÉÙ-«>˜õ6z=¤kÀg Od @€ @€ @`œÀ/¥Ùx^íšÑ‘Û0 D¯³ôßEJH ™ŒÏ3±'Ž(€ໟû°HBûvAJö×(€(€(€(€(€(€(€(€(€(€søõãqoÞÿsš}g^îwÇÏV³ßÝÝåu}?åfTÅs×¼3TÖ½‹]œ²æÑU²geYÜ¢Ö驺NÕQ\ªæÕQ¶G%?¿Ÿÿ¼ÿ«x_­ÛƒB]•^î«ã¯8E^§°öÊ«ü¢®‹æþi~m*yÕEqõΛå‹<¥5WòrÊbü“Åo÷:Ñ~ˆQ[oÖÝ\²çÃ>OeóŠ^/Ê>•uGG󨚬y®ŠOöº»ý°¦®þUÙª×æQµ>>xø J•uO÷ ‡ê:vù@ǧÿÿ¯ïæƒêÜ©®ŠTõW©Ëëõý@Egõ:¦ú@]w•ú¼üŸãÕúо]êðúþ3Þ;LñA—Ü©Õ ÿ9öúª»¼÷ÏøG¬>¨>ÀoO³ò¯~.€ÿþO­>¨êð×à_Õà¿—·>øó;ý¿ƒ±ž²÷ò“ÿ.ûüáOÿßßÿ½ùÏÚÈlþ½>ˆ~/øÓÿãú?ùÏÉ—z³>ÒÿgùGÍê¹™Vügåù®?áËïE¢ÎwýËõ>ÿ’Ÿ~ÝýøÓÿãß·¨ö òOþÉ?ù¿Û8ÿÏèw¹G¬ºON­ þ3rlõ'üáÏùóßÝ>ÀùoF߸Ëóß îª¿²žcgó%ù·éÖÝojÜŸç‰îºv©þgæ^uß'ÿ¹~$ÿ¹z«ì VîÑÏ}ä?ÇVþQï{ÞçUÉÉÔ:àŸ“35ÿX¹gõ}ú¬/­ü³ú>üáÿGµ¾Ù½kî³û>ùÉ¿•v߇ÿ^þVîU¹‡¿ÿªÜÃÿ®¹‡¿wîð‡?Ï÷Ÿ§äžüßË¿—{õ9ÿÓ9³ûû–¬ú½ü«Ïùð¿—wïïvTóÎ÷¿k>ðæþk:gõïÕuNáÎùïÕŸ§q‡ÿC]Ü»ô{ö¸ÿíÕ}qÊu§çýÔüÃýßo¦äúý>vó_½wšÆîWÄ_?ïÎ?Š÷Ô¼OÙÿá~/çÝßÿGó>%ï]òï=ù¾š¥zÿÏâü¾Î•.§|žÅ¿Šó©}}Õ¿»øWóý´þª§^§ÊÍZש­÷mÕYeœõ¾ó=X´/à¶Wh^Þù÷Þ-³½+àå³kÊOö¸:DÏZÉßï÷¿Ý¯Ù~wç;ËBÝjw½ï>¿ë-êù:ÂÚ3ïúôz>Êó«qµmä¯Î˧×8¯üyÿ>q­½¼Eãíûj<-+y«‰ò5ntòÈkÌå)kܨý?6—§Ô=ï¾9°zϾïQÿ¾ÞUÿ®oõ©z:e]§Ö=ýß§Xý¯Ögôs§Ô™Ú:­Þ«Ï{Îÿ÷꾋wú¿-ø·qSëß»ëéæú_Ëñ»ÞÕÎ{μÿŸÝ>ØýùîõNý?¯ü¯õÇn}`šwî7S½Oõïå[ý^¿ú}ãn}üj?xÿ=Ýýãýy'èêïk'@ÿÞ¾»œï¯Rpº¼¿2Ü«ÿGùžRï§}ÿ/Ú÷TïªïÿY¾§{¯öŸíß5ïÿUžç}ï–Ô÷ÓÖû¿Š×«uô5æ»3u«ëó¥2g´U¾jÏÍ1»S5¯ôóXߣ«úÏ¥0w¶jÿsÉkì<Ú¿Æ.YÅ«ˆB€ @€ @€  Hà—ÏŠìx^í›KrÜ0 }³Üÿ9BŽJM´•=’x;—kÄ_÷EÉ“¯/þA€ @€ @€À\~½Övüœ»RVö³ïïPîEÀëûiû^tæÏö©¿èëç×Za´OoÿZ´æÍÆë'»ý<µ+Êö·z¼ZzýGÿýÿùïø¹ÚOvýä®àìßû{¶ï«ñriöÍëûnû+OÑŸ÷3“3ã»þ¢®‹öþSÿ9tõG‰òêí7+ú†bgèõ”Õ>:±”u{Ïò·zò°&S«½d÷G|9Èö=^T|”u[Gû¨êŸÜË\•ŸìqWçá]ý«²=TGÞ3Yí£jüU9ЯðÏ3¬â¯2îî9PñP=]sPÍ]müÝr Æ_e>Þt9¨ðVÇô¨rW™—×ÿÑ^u?Pá¬>oðÿ" îùj~Órpµ^>Ï-þgÔ±7×Sràå°{{kTλûó®ßê_å¹À»~Ú¿*Ùšƒê}kÎ3VÿÕûþ×ø?8ZsPµà_ÃÕ>€üOx/§–ã.÷5nSæƒÿµûj·\XýgŸºqí6_k²žºñì6_üs°¼¤þgåFuè¶Ÿv/þgÕóÓâÿŠç€§9æz[Ž©·)yÃ?þÙÿûÿÛºQÿ{׿ê÷C¬y¦-Ïjûm­ÜðŸËÛê)ªþ÷ô¯æýøûRTÎé÷=çøß³îUÏýÔn©ÿ\Þj÷üãŸ÷¿¼ÿ}ºDLmŸœ:Ÿ§Þ³¾>•·ÊºT½sþÏ9à?‡³J½Ÿçÿ=ý[½gÝ÷Ùÿcsiõ}Þ?÷¯ºovŸþcëK5VïÙû>ûL>ñÃUµÞ»÷¹ÿÇä³[ݳÿ¯ÍÕöyŸú×ð^uÞÿ†ÿêºgÿ÷åÀºß«Ô=þmþ§xÇ?þÿèò\]=ÏiuOýß«¯wµû=çÿ{Þ½ßÛW÷NýηîUžï®æQ}_Ußë½KÝSÿïõ¿›wü¿ìê}wÿ»{ßÕÿ*ïÝîó?ÕÎ_QóÁû÷ ˆâ]ÝïjßSê}úû¼_=ñ¿^]§Þñ£|O­÷)õ÷guÞýü‡ï5¾»Ô´ï]ö÷«Ôxï¿ÞöYžÏã\qÙås¯¿»í«Ëó«qõÔFåÓ;Î+oѯ¥¬;›×[öýÑÞŸ§k(7²lÑãgçC.m½Ñ£ýTGÄäR•¯ìy²ò!†²î(Ù^ªÇ'Ör­ÚOõ|Ñù°FWÿêj]ó‘çb—®yɃßyÐå¡{^òà–ݺçÊýžþÿ_¾O̓îúS›Z¨ñW‰gJ¨ðVÛêçUî*qyýßïWÍÎêqxóÿgêŸú§þ©ÿÕ>ÀûŸ3úƪw¾ÿÍöŽÿÙþ³ûþ}üªóïÔyTû>þkú þk8«öüÏôoõ^uî£ÿçæ¥ÕÕ¹ÿø·|ÇTÝgUâ²Ö}uß§þsêß꿺ïã?Ö¿Õ{WÝãÿìÿqß#­õßÕ÷©ÿ˜ú·zïîûøïõß]÷ø÷ùß½îñoóŠwüãŸóÿõóÿiuOý_«¯w•sþ³ó¦Ê{sÕ8¼þUÎùø¿VïÞÿ·ë1_ð¿Æ½»xë]½ß?æc7o•ù§yçüw#0ÕûtÿQÞwë÷Óû?Þg€Êþ›Þÿ~ÉæÞ=>Þÿíö=´ïÝ÷÷Wï¢ùw‡÷W¦ÏêÿY¾O¯÷ÝÏÿx·Õù®ïÿ³}O«wÕú¯ò<Ý·Šÿjßxï9ÿuyÞí;lì®~}4ëóšŠ×gq\'0ûJuWã›mѾú«|Õ®³¯˜;PóJ?¯ÍOUÿµæÎÖí.y•Wù×X-Q<ðú‡( @€ @€ @€€ /Z²éx^í›]n1 }³Þÿ=BP‰Qذ³2ÅßO“—oß|ÏñXOY½Æ‰®‡ØnëÝËKUœ¨zèg*&£*oÞãFÕÁ=n ýú¨ÞºÄó®‡zS1tñ•uðsÝDqï—:x]ÝWóŠò>Ϥ-ãUÎ]®‹ò=õýÝfýÿ]]¼¾Ë#Ú÷©Þ»¾ÿgù>Ý{ÿÙ¾ñ^sþSåß9ïÿÕ~ß¿»?R¿¿«7k^ê¾¼çgåÜå>o§Åëâq5ÓüDÏw•{ÕuÑó?=~•Wök=*¯ÊÙ“E”ÈÎ påÆ,È€ @€ @€ @àvû šux^íIR1 ùÿÿOà ¨"0¶$km.9Ä‹Ü-Ù3Ηþ @€ @€ @€úx½­IúÙȬI½ïö›EµÎjw=Z·¯Cªg¤Ö>­ÆëI;ߪ¬|'¹övjüôãWqÊ÷¸ñ$kFðöõþ·úéíU:_MþQ¯zßm'õfÝÏŸh­w½jÛ[ûݯ–óÑj}Zõßõ¨mžl¬üY£õ»Ú¿†¥sQZ{;5ÞªOm»s¤sŽ|Ê×éqµž¯úç´eÕiO§Ç¿ò¨ýÞžx®Oûñ_ëûYÿ\Öì¢ñöã5y°–#^>¢æ!þσ(/QóZåÃZuåoå!z^òà–›Ñ¢çŸžÑü³Ì?5²ðŽÿœŸ¦åAtÝeJdåŸ%.mdÌÂ9kZÿßý³æAVîÙâÒæþ{<_v˃lu–=ü÷¨cmžuÉ-‡©ýñÏ>`q_ý\8µ~­Ö]}°â0}iPÿ=Ωÿèû¡éukµ~ü÷¨cm>Hó êЮ—þ÷y/õuàÏvß¿-Ϫù)Íïs *ßìqãö>€ükÎìûhÕø¤õïýP•o•¸¥y@ý÷8?ðßãt¿Á?þ%Ïìÿ=ò†úïá‘ý¶Güãÿ“Àn°ÿÏÎüãŸçÿý}swŸÍÚžú§þ©ÿyõ/­{~ÿé±_࿇Gés…Ô¿×½ï÷<ÒõÑïÿüÆ?õŸù¹ú?“ŸÒº÷~îÃ?þ%÷Ùœû=Î}êß¶þ«íûøÇ?û¿Ý½¤´þ½ß÷çã<×íRïQÏûø×ù~¬üÛò¬²U÷ÎóŸ.oñ¯ãW¥Î»í÷œÿº¼íR÷ìÿ{yÐÍ;þ×ük½gyÏ{vÏPõöŠ[ë?ú~çj~/ŽÕæÑzÏ^÷ìÿïÿS¼ãÿÞÿ4ïøÇ?¿ÿÍûŸÜÿÜLÝï§ûÇû}T{/Û×Ê÷ã8WïÕU¾ßåY¥=Þ×2°ŠÏÕ8ñ¾æ½Ëûß)ßUîïölÿn½ZWÙÚá]k~í÷¯,ÞOûžRïUÞÿðmSßW£L«ï©užå÷¯º~6ÏU=LûþTýG{¦Î×2yׯWq¬­žVW«|I*~»Þ¿Ë¬ÙõªâßnÅŒô“@VÿXò!íßg•ÌòŒ€— ä$ õŸsUD@€ @€ @€ i>/í='x^í›[R+1 ÙûßK` T*LI%ÌØÖÃGêûÃÉî–d'á~|ð€ @€ @€ @ ïÏûžÞý¬·cvô—À™ÿÙßCYƒÀ¬ßÕ×iЩ¿ÊUÖ¯¯O|¯Zû³Ž·­z«±ö寞ÜyûòŽŸKO?»·ŸèøúFbwðõûþög´ßÑ|±4õ²Íz¿úºQ_^Ï뙉YñUÖÏyy>‹CU'‹µ×Õxgþ¬¯cÊg¥«¾¼_oí›ï9ëÈÛŸu|ïzðé²}£Zû‰ŠGØÔT”/ï<^õ`Cyß(Þ^¢ãScµí'*up­¢|dç±®‡kt÷*ÛKt~ê@ûýŸU½XÕÁþþÿ ­xªÆé^ªÞ¬×ݵ¬9ªÆ³òÄQ9T}y­Ûªð'àåÉ;n—:ðæ¨µvŸê~¼×¿ê÷û€7¿*ñWë`×9PÅOÔ>ªÕA·*yð¯}¯·ªÃ*u`Å£[ü3nÔë [ßZïÿ½çþ{û?æ‰jXÏîñðß{à¿·ÿÕs ëóá®óÚkßjsÀ‹C׸øï}à¿·µ{@×9í½ïÙ9}ôæÐ5>þ{ŸøÇÿÊ÷ƒQç@×ùì½ïÙþþ{Qo]ããŸùÏü×ýÿ?«s‹þ§ÿéúvpÿמ³Þ¹ÿk{çóÿþ²îQsÿȳºO^ÿºÞgç?þkÌü×ð8:ßf½Gßû˜ÿ>õ‰®£}˜õ<þñ?ó¹_ô½ùo[§j}üßd“ÕòÒÿ¶ý¤RªÞ™ÿ6õŠŽ*ýþ¼Nü÷ô¯îù¿V·ø_ãǼÏúÄç1¯ª‡¬uWé{æÿØüªæÿ×ü¯zÏú^÷êé’5GUò®ú¿ê!ë9Ñë\õ¾{ß3ÿ_Ïÿ.ÞñÿèßÊ»JßãßÇÖ9>›7ú\Ý-ŸUßÏòÏ~Ýn>¢ÖÓÝ{×ùoå]íœ7g¢ú-;Þ_W@¶¯üÖ¾«ôûsxñÏŠ‹÷±e–'ë¼xó®~ÿóò]uÎW¹ÿá}®ÏUý{ûîÖï»ßÿ¢|w÷ž}þG{ÆwÎûÿ,ÏÏymOÍ:ÑV߇íâ÷Ý:ê˜òÙÉîþF×çC©nÔQ¾»=_×LÌÎvóy¶ž*}²œñÎþ}9;ÍöË==Çû‘5Ëî®ÉîíÂÎú_c¬€ @€ @€ @Ý ü&Þòx^íšÝqã0 ÓYúïâJ¸27ŽgÎÛ’ˆ¿àæ%–p%'__ü@€ @€ @€ @³üý¾ígõ÷,ûífÕûÕûö#ÛcÇW=z_߃ÒÜ,½}z­7—¸Öμ|E¯£EmN6ÑÞ¢ÖŸc v'Q~²×­¥Ø7úŸß÷¿w¿³=zÅëk$7ó#ÿW?÷òçµN.Í~Ñ®úµ^ïåõê:ýÌädlõi½ÿªG¯ësèêG±úó¾ßËïÙuô ÅfèíÏ{½³­×ÅRÖ]ÝÛWôzVÏG÷뚊É,ÚWôúG>W?¡­·j´Ÿ¬õW=ݧgÌ7£,?YqŽ|®~îK]gµ,/UqV}¿»OÇœO&U^²ãR¯ë%ÛCu<ê౪}TÅ÷ªŸ)\·J•¸»×ЇêÖ:8×ùW©òVËËê_u¨qVÏÇZùþ9¢:oµü¬þÕæ€ß.ùXë@etá­–'þo¬æ%;ŸîuÍkZ<ü3þX­ƒêç€iýXµŸUÿÕïU¼¦ÅÅÿÞçþ÷öŸg«uPõ0mWïÿ{ÏUÿUÏÕý2->þéÿNßLë¿êýÐÿ{÷·÷€ê~™ud¿Nå_½/üï}àÿ+ïÌÿuCÿÏð¸úü3ÿ÷ûÿÀÕ¾çûÿóÿ3ݽÓÿ¶ºÅ¿ßQ©~nõ^ý¾ÏóŸ­n­þ«¾ç{WµÏÔò²zWë{ÎÿksÀê_­ïñοջjßã?Ç¿jßãÿ³ÿé}ß¿ß?׋zßãÿÑ¿W¿«Ÿ÷¼ÿÓ÷ÿ×€Ú{vv>^}ßeÞÓÿ7»{ßõü÷òÞíœßýû_¼¿®€ìó6;Þ??™dûˆŽçí{ÊœŸ>ÿñ¾öÝÑëã}Í{×çÿ(ßÓç|×ùí{WïªýŸå{wï*þ³}ãýñ$ˆ~>{^ß¶ç5﻽ýWù}×›×´õŽü«ù<›Ï4OQû9ËSýº(>Ó×U÷Ê\­À.þc)컺ªÿ}äî¼Úîn‰öL Ë?ä5 Xýk @€ @€ @€ ìDà´ÍŒvx^í[rÛ0 }³Þÿ=BŽÐé8ú°F2)¼Il2‰¯]¤'y½ø@€ @€ @€ ìEàçÏ{=Ç×½VÇjFÎþµÿÇõZ´¾Ÿ¶¯µzfóÔŸ×ý˜È!àåÓªß*}FµòÕO31+òæ5N ¥}GñòÝニ|Wö÷÷ùÿé×h¿OÇó¥¶OïO½ÏÞÿÔ—÷ýû³]ɬO«û¼=ú·¥·~oV^µýŒ¼Y__ßœÍ ´Þ¼Ú[û¾ëφ⺽xù³î×;Ö5¨›¹µ§¨þˆ÷£u”/¯qˆ]xy‰î×+ttë·Žö5žu<Ô7)›a”¬qˆƒïq‘å%z\âà:¢=dG|ÆA¶¬ñ‰ƒwdñ¯2®UÈN_ù­ªxÈžG×8Èæ^e|ü³ü'Ð-ªä_µyt‰ƒjÜ«ÍGù'<Þÿhžs´þöUã Z¾U6ð¿öùRë¿j¨šoU祃ju *çªóÂÿÚuÜ*®v‰+ÝúÁï:€ÿÞþz·zt«ÛÖëÅï:€ÿÞþµû@öûëzص¿Uë@W_ÖëÆï}ÿ½ý¯z°®ƒÝû“Ö¬s`w_ÖëÇï}ÿø—|Ž˜ú¿GÜHó?ëóAÖû_÷þð¿GKãÿø—ìÿÔÿ=âFšÿœÿðŸÒ}Žv×ñJþï‘ÇÒøÆ?þ%翌گù¹Hi~ìÚNš÷Yçþ#Þvõ½.üS÷%uŸüß#n¤ùŸµïSÿmâNê=;ïñÎÿúßIþÛäQôy];ÞêÞ©ÿº¸Å¿ŽŸ6ÿ²Ûã¿§ÿ]¼Sÿeñ‹·ìz­_ë½Êóþù}“–K—öZÿÙïùîÆïâOºN­÷ªyÏþ?·iýWÍ{ü÷¯õ^=ïñí¿‹wüúïæÿøçûýþÞÏÿoVõ~•sÏÿx¿Šé{‘ÕÚ‘ï×`5Oç‹÷ïo žò\å~¼Ï½y\ÅçhžÖ¾W?×ÍÙ×þqäÅûº—wüÏ}ÄÛï]ÿxŸÍð=ö|Ûø^åý·ï.õ}5Yõûfïäª÷«ÏϦ[÷W}Z=_ŸÌŒZù´šgõ:»´òæ5OR{®ÄË›×¼{ZÈÛ•—§¨yóÈí9ÊSTœ=¬Äíâçÿû¿ôo”׫qâöŽ$õ¾:îª7¯ç{[ò[ýªGëç¼<ŸÍëG²çÌÖ^¥óy³þ¼§-ûUK}y³ö}6Ÿ=Ù3z{´šÿÌŸÕç=¬Ù­ÒÊOôè(×íË+¾uÔ5¨[™ÿ*ó’Ÿó£Š'ïu¯óÀ›{µùɃÇ<¨æ'j=Vy {ûæŽâ]5Îô<¨ê%z]Só šsÕxø¿¿‹ªú‰Z×´<ˆâÚ-Ž6òOvk+èæ%j½ZÿÇø5 yOEñìG›yf×"wõµn­ÿê} Šc÷8ÚÅo—8Ú<ˆ¯pþÿÏò^ÿÜÝì’»ôåè}àvÀÿlÿG¿éžÑ}s·xøŸÝðÍ÷ìû€ÝúqÖ~¤}ÿ{ô©ÿì{á¬zÙ-.þ÷¨ci^âÿÏÒ|gÜc¾SÿÔ?õ?÷÷ãÔ?õOýSÿÒ>uÄ9ΦoI½sÿcÃ?;ñ¿‡Giáÿœÿ8ÿIûç¿Þý£›÷#ߤï;ÆÝ J½gŸûñoÓoðoñk?Á?þ5ï¬sý_—·Ýëÿø¿èúÞÍ^7õ¯«ŸlÒø»x§ÿËòÿ2nÒz«2Në½Ê}Ïó÷*|«¯Cë?û{Þ»øÕ¹g¯Oë½jÝóþ_{iýW­{üö¯õ^½îñïë¿zÝãÿµÿ)uÿGÿÓ¼ãÿÜÿë¿ÓåœÇ÷êýUd߯DÇ·zÏw¯ûi_~D×_t<¼¾‰ˆöïk7PQ>¢âX{ßå=¿ëùßË÷îÞ»Ÿÿð¾ÖßÏžŠêËÚ8Þ¾§Ô{—ßÿDùžê½ZÿÇ÷Y§öù\Û—¥ã£}O¯ó¬ó–çç¸>ÕÓViýãªøÅ·,«ú“®KFaî()ç*ãæš³Ùy«ë°Ù5³V¹g=‡)_Y^ßÅõÝ-³?Èò‰¼ýר%«xG@ꢀ @€ @€ @€ T ðÉ®¾?x^íšYn[1 s³Þÿ=BP©QÀÈ‹îää'@üDJ3"e9þøà€ @€ @€ @€ þüúZ‡ôwóV!õ¾;~Ù+Þõgõ\ J}giåU·/ñ\+“zòŸ‹ZŸÙxùÓÎÓÇ@ìJ´½DÅ‹¥X7ûï÷¿÷ßQ¥y뚈™ù“ÿÓ¿K½Y¡Z'ë©çÛç­üîÆ­cÄw¦·>µÆíúÓzΗnþlZµâhyÞ“ßí µ¼YÅÙõ(}ΖrÞèVÞ´ãJýîŽÏkÊffÚž¼âíú¼}Άv¾¨^¾¬òÜúÝ—Ϙ¼DÅÝõzúœ.õ<Ñ¢1Ý™X{ˆŽ¿òzúº.ýøhÑ~¼òŸz^=oNg^ü³äYy=}]ÇB\”,^¼çqêùéù8s:™½¹gË7}dóá=-ÿ¯8:UéÅ›wÖ|ZûÀÏœN¦¬>¢æ5mDqΚÿ_}%«¯yMÙ^<«æ‘îSÚ.JU/^ó–úÏ~/ðâX=tØU°,ru/^óÇ?ï ? tÛ^õÓ%þg÷üÏöÿêc]öA—¾ì½üÏîøÇ¿ä> »µëöî›ÝòUïÝ|x¯ÿœ•Ïïzé–ú§þ©ÿ¹ß¡þg׿ôó@½›Ü]¤nçqÔznûÀ5½QQ¼ºåÅÿìsÿø¿¹èuò»HÝúpÔz¨ÿ™õë=Ë÷‚£ê¥K^üϬ{齟úï±o¨ÿoÏ#üãÿæÞGÿ¯½oª×ýëÓ‚Û¾7}þkׯtÿâÿ•Ï}úÿÝþíR÷øÇÿ'é98m<õW7Õ÷I7ïôÿ³}Œÿ3^Õë]ëÿ;Y>ç{úvHOVëèZ÷ôÿŸû™Ô{öºÇÿ÷þ§xÇ?þ¹ÿÿÿücZÝSÿ_´¼W9ïßïVÄÕòÿt¿Êþ÷*ž´ç9ÝûÔþ¯å½j¿ŸÚÿñþýI¤ÝW³ÅÃûÏï@²ùÒšÞ÷ÞyjñÎGÛ{—s¾ëÿ¬|w÷^ýý?Þ÷úûê©,}{5kßSê½Úýï« –½¾ª;ï×½|O­÷lõoYýJG{Õ··ç÷|RN]Çkùöû”¿«7­u=ùÏês5/-.Sâ¬xVy}Š/íuVñËy®mÞæûOVûÉfõDµò%‹ROÒñ>«$Ë©¿ÝñÈI`×ß깜«cV€ @€ @€ @€ @ 3¿œ¶Ox^íšÍuÜ0 ÝYúï"%¤„<¿v¤%Eü} Ç—=H"Á¤výñÁ @€ @€ @€ @{øó뵎»Ï=VÉ*îŒü¯^‡x«~WŸëAåœ(W=z?wq­•z{ôO‹Ö~ÑxûŠo?µ+Šö5~-µ}fò“=î>FrWòûßûÿè3Û§u¾\Š}gyzÝêÍûù¾fr"êwõ~o¯OÇË¡Ùo–UŸ^Ï=õèu?S1{yôÇËïì81TûŒêå-jœYÖûúó4Ê[Ô¸VÏ£ç}éêå)zÜ‘Gëu}s>F{Šßêyô¼eÝQ¢ýd?ò¹z]× -²l?Yó­z¾{ÎFY÷é,Uóïs¯ÊKö¼äÁud{¨ž<øžÕ>ªæ÷ÊÝ}.²*þÕózùÿg޶Þ]Õªç÷Ê=³sUóW™ÿÔí=ŸÕ»Zß§þŸå±Õ¿ZßÇ?þ? x÷ÉÝÆ³Ö½jß§þçêßê_µïãÿ½«wõºÇ?þÙÿÿ?ÿœR÷Ôÿuýãî\Ä9ÿÅég¾¨Ÿ÷~Æ·›Ç§ë9­Þñ]·Ö<èV÷§ïÿVß]ÞïFyù´_v¿ßË;þ{ñ~Ý º×ó(~¼¿ßFüº^ÇûhçïÕ¿góÐÛû.ûü]6ÌrU½/Ê÷îÞ»¿ÿá}®¿îR­ë»¸ð>2ú캺ÿhß§ôù.û–ïÓ½«ìÿÙ¾ñþ½dõÿ*ÏøÎýþ§Ús÷ßãŸÞìwÏÖ¿š×Q>ø@€ @€ @€ @€ 0ŸÀŸ_¯5ìþ=å¬@ã}rÖ¾Ùõé5þ,ŠsWãåÓ+Î\’3+÷òg&Õ9UGy‹Š;‡ìŒJ£{–§¬<»~¥ããMÔdÈò’GêU:®ÆN|Öl/Ùù¤~¥ãâäfÈöQ•OêW:.×R\¶*Uy¥~WãâŒäF®òP•wåu÷y®-ÿlUªóîz^÷7“±ÚCuþ•Wéó[þYªùwÉ/õ¼ço(6bþÕu¬¼JŸÇÚò^ͽ[~©çÕ8S1»ñ¯®gåUú<Æ–ÔjÞ]óK=¯ÆùóØ•u]+¯Òç¾¶ü£Usîž_êùiœ¿1߈ÝùW×gõÿžïkÍ/Z5ß)ù­ûÀϘo¤)ü«ëÄÿkßU{¨Êoõßõ¨â95¯uøvo{´©ªêÆ?÷À?Ú}`?±¾ªÎÑÔ¼ZïÜÿgô «ÿnû`ê9¬®Ûº|»¸>Z5Ç©ùñF?×î?üãÿ„ïíþ¿}çŸóÏùçß´}@ÿÆî;óö>®]¿Ö;ßÿgÜø?Ã#çÿnøÇ¿æÿµÐÿïÜ7Vï¼ÿÍÞ7Vÿ¾_oöhÚûïÖyøŸ}~­ûÿø?áwß÷Ía=·Ì·žûnï}øßëcVÿö7µ˜·œ_ë:ñ¿w^¬¼»Ì·zïÚ÷éÿ²ýlõÓµý¢v9gÝê°zï~î9ÿ?ŸüËúc·sk­çïœÿï÷7þ9÷'ý¾·zS´öËSæßvîéÿ/·z¿Ý¿—÷)ßyO÷À)ý[º¼ÿ¿¤ÜNçåõ^5åù)^Wëðò>½ßÝ—+nÓŸãýçN4ÝïSýx—Ý@§ù÷ö~Z¿?­ÿGù>Ýûôï¼ËúûjÔ´þ÷•ѽçÝýGû¾¥ÏOùý/Ë÷íÞ»ÜÿÙ¾ñ^ûûo•o¼DÝÿÕžñ-{ÜõßÅëªÙêµâ8å9&u¦ø}ªS·jf½ Ló9_Ýýû®–h_ tó¡\UþsWI¶'Ñþ!ß›€ÖïUQ @€ @€ @€ @€N'ð{±zðx^íšÁU1 éŒþ» JàåAÉKX[–¥oi¸pˆmig,ÙYøøà€ @€ @€ @€ @8›À÷çoþ«¿Ï¦Ð7ûUï£óûÖ~òQ»ÆiÓ©ŸÝ.¯Öuë×zB«§èyZÔêdíq5^òO²ê#{¾Ås³Èöçÿ\9™ý}ÿŸýííÍ{½šçEõ>:ÞÛ§u½óŒÄf<êÓkœÕ£×¼XºúѼ¼Z×ñò:»Ž¾™˜ ­ÞvÍ›õ¸:>†²n”]½Ö]õ;:_×ÐÞ̼àÑâoôsUëO%¯ê}@…³jøç¨|¨ÖZ^«}`îTŽ­ÆY5ŸUÿªPå­–þ¹T¼¨Õ™z>«} îd‹¤Î[-?ü÷>ðÿ•{ÀXWŽ¥Ö_Õó¡þ©ê¿ïÿ QÿÔ?õOý[û@ÜÍn,’ú}K-?«wÞÿ×87ð_㵯àÿÜÿúÝÿVëžóÿì¾ÿ³ýYÏûû¼Uÿc߯âG­ré2ÿ=ëÕ»ê¹ï4]ê×úœøïY÷ÕÏ}êÿÿ}]½îñÿë¹Xu^—º§þ_×?þ{Þ÷ºy§þ÷9þ©û•¿ë©¿çy÷f¹ê=nô¹ºÖ}÷þßÝ{Wÿ^ÞOí÷ÏçÀhŸ<}Þ_ßN÷z•¿·÷*u_½ÿã}ìI®êç´Ïñ>æ½Zýã}Î{ÿ»¼W;竽ÿÁ»­ÞOÿþ‡wï§ôÿݾ»ôùSú”ïîÞUê?Ú7Þ;Aô÷û,ßx}ÿ›íßc÷Dký«ø}—ÇØÓ3JÝãl~#0ËWmüÜÓ2ú™€šÏ«|0èKàŠ·Êç¾OÍjw*~¹¯çìÉlÿ9OMÔèú‡¸&¯ú×|:²‚ @€ @€ @€ @€ P‘ÀµâÅR™3»–/ë8sŒøîÔÚ›U|_J}g³òã·¯ŸyyòšÇ‡ZŸY¾~ßÿ\ýíåuwž>†lwrÕûjÜ®/ëëm)Ö¾ò¨ý¹µçUüº¦lV®íw7ÞÊ—Õç64ëEÝõe}½•ïWqëÓ]±µOi|¯|Ð¥Z'šÔ×xòÀ&§¼üiÏc6´óEÕöâ<å”·/«ù¬ò@F7ÿh+QqɃ½œ‹òd=/yð^X{ˆŽ¯ïQ­sU´¯ùɃç9éÅ?Ë<äÁcdñâµüÏöäypϯºË:VÔyâ£þÿæ½–ÿ#Nµ<ÈZ—ÞëÒÊü×¼¯à¿¦7í>1-´ùU‡úÀÀ”<¨^¯VëÇÿì>€ÿÙþµÞf?ZõÏ.q»÷.ž¬öîç¬÷«ºéWÚð_»à¿¶?i?Â?þ%ÏôÿÚùCý×öÝÿ³þˆ”Ë”ñÒúÇíþÿÚþ¤} ÿøçùîÿ‹KëŸó_íþÿÚþ¢ïÿÔíü¡þkû£þŸw )—îã¥uŸõ½Ï‘ ÝýI÷‡ú~Çs?õÿ^w¯{üãÿF@zì:žúŸyߟâþÿ<¿ñOÝw~Þ?¿êzÿÞÝ×´º§ÿß hyÏþžïÕ÷O»uÒíz-ÿY¿ß[­«›Ïw÷£å½jÝOíÿxìïÖK—ëð?Ó?Þg~ÿ÷ÿŸ»ôõó>´½WΛrþÃûêÄ×óþ÷=ï]ÎVÞ»öû.ïÿñ~­Þ«ú·ö=¥Þ«ùÇ»NWyþ÷ò=µÞ³Õ¿·o¼Çžÿ¢|ãÝ÷ýo´çóü¶wѺѥï³y¦Î÷r1«¿«ëÚÛ=W_åœee²xÜ]‡l׌>ìrºc6¢|®æµÙ-QÏV¬?ÇH,k¿œÃcý®f×ö¿šÏs >sù`5€ @€ @€ @€ @€Ö~}px^í™Ûm#1 ÓÙõßÅ•p%Ï@lØæJ|‹“Ÿ|¬ž3$¥]}ñ@€ @€ @€ @€ @} üûs[»ô¿ïYù'’÷ÝçPïA`×ïn¿Tæ¬r×£u¿9ÄkíÔÚ£Õxµ(»+_Þãœk wgÞÞ¼ÆÏ¥vÎì^~¢Æ=ÇDÎN¢³I­Ÿ{{–Æïc&f¥Ö~WÇ“|y=¡[–U_Þí½|¿·¾!ßzûÔŽ¾”뎮õÕŸ8ð‰¡(Öóxǃíz£Z{‰Ï+ê™òYQ´/¯ùˆƒ½øðò‘5.q°Yž¼çµŽƒ5ª}Z{{ÈŸ8ø‹Ù~¢æ'^ÇAÿìyð?Ûÿ=þˆƒÇ8ÈÎˬù­â ÏMüÿ!@ ÿ=â kȪ¿Uæµ:îãt‹ƒ*²×aø¿Èö¹:?þ{z[õ,µŸiÏñ?»à¶ëïÕïƒÓêûÕýN©WyLk‡ÎÚ8 þ÷Ž#ü÷ö§=·ðÿ“Ïm~œÞ_›ÿÕ:ÝŸvø§þSÿûýž§Íû)ß­8:õŸúOý§þkë@ÕÖm«}i½óþ×ûüÀoÚ: õ_µîß×¥åszü“ÿšû?ùß3~´y_ýÞGýÿ—øï™·V÷üÏô?Å;õÿu|ãfÞOù½ïù}Äê¼ì>δ¼§þ?Ö9üϬûVÞ»¼ïSÿÉûß1ÐýÜÞ]ÿô¼Ÿ~þ[ù¯þ}_Zßnþtígå½ëy?õüÇûëJÐ5W×ÿ™þñþù°šG]Úã]ºùù½ÇÚû)÷¼wÑÐ%Ÿ¥uâýZ¾ŸvÿÇûž÷îß¼¼Ÿ^ï»æ¿·ïiÞ»ä?Þuõ]ê-Ý«¢ŸGùžšïÕê´o¼?FÀéùý_R=œöÜËV^¿›wš×«ûÕú¯æ™ú~Õü­]U»ëZÛ=­w9Wé‡A*WסÛ5½ïV¹gµÇ˜,ŸÒ¼>»eÔg’‡¨ç˜É!å—÷²¿Ò¬^þ¥yy^ƒÀUÿ5VË* @€ @€ @€ @€ @Ó |îó¿x^í™Avà s³Þÿ=BЗ—z¿¸$¾˜n²ˆbFâ>üA€ @€ @€ @€ @€  Iàçë•wë§æ*ÉúŠ@«÷Þç ®A ×«×ótêgéåÓ§>éœ+´z‹Ÿ“V½¬¢üyÇ­G>ÇŠ¼=ÍŠ—ƒž~³|EÏ£obÍ ¢½ÌŽ¿†¢î¬ßïF?gûmO×ÈÜÌG½ßkõýÜ\šz³Ýyôþ>Ú÷]|=C±{ûíwç+êûXª:Ñ{}E?åû*®Ž©˜L£}ŽÆ§b|Ÿ£Žú™=.ºæÐÎ7ËlÖù¨ß²úX5ž:ð©ƒUþ¼æ¥luàåauê`¬V{óž?ªÆèæåÍ?K<ê ­ö²øŠÊûÚ¨ê<Å=[\êàsMfó•þ÷öÔuð^Qý–=®Wèœôôÿ“ûýQªû@ö}::?¯sàˆ£VÑ|Uâ{Õþ_T¼{ßð¥PëÓè|w;¢yªÅÇ¿æþíUgøßÛ¿×}PåàÕ7ÕâX÷ükï#Vÿ*õ­×zð¯Ý¿Ö:À?þŸ¬uý`퓪ã­Þ9ÿµ÷ükû³îKøÇ?ç¿Þÿq­}ïõþó_{ÿ`ÿ×ögÝðÎÎÿÑ} û{Ÿ#?ë>Yuü¨w•{þ?ŸoVïø×¾7à_ÛŸõ<ÂÿžþwóÎùÿ^çøß³ïw{ß{þ]j=/ÕÇ{õ½Ú½ŸýÿEÀË¿Êûúß×»jßïÞÿ»÷ý®þ½¼«÷ýnþñþù†¢~oÍÿ{úÇûÿ¿LZûGí9oïUÎûê¿ÿðÞ÷&B­¯¯òò^µï«ÜÿñÞ×ïUö¼Û¼«ö?Þ}¼«ùÇ»¯÷ìþ£}ŸãÇÐÍ5ÛýïskfµÿÙ¾«ÿžë­žÙþWùÆûÜ÷ÿ«=ã»m'°öÏÜçÚ|ŸŸÊêo4¯1 ûŽåœmܾm+Ïæ±5Ûª}hå½ú9ŒÅXíõjþ˜Õ5ëý3k¬êÿ5«eÖYýi ½ý¯±*²„ @€ @€ @€ @€ @ ü‡’fx^í˜A–! çf¹ÿ-r„!/ÏãÅøÙ¡i}¤/¨ÙÌ¢AUˆ¦ýõÅ @€ @€ @€ @€ @€@?~=jžýßo¥TüŽÀ¬÷ÙñP÷&0ëS5Þ›Ê9Õ©|FãœCÜk¥Qo«æ{QÚ·šUþÔq÷5P»2µ§¬xµÔöÉžåkUž}LÔ¬d•—ª¸5ûfýýýûÏìÿ*¿Wóö5’[ù¬÷«ã¯zZ=.—f¿lW}FÇ­ö<ŠßÏLNÅQ¯Ñù#oêç9Tûd‰úSÍW{Åëchm¥*ê8#ªçkéúGW{SÇSyÅñ7µ¦Bµ¯ÕñF£Ï×PöºÚתøQÏ£ù¾Æ´•­ò“wäñîs-eßhYžVç¹ëy4Ïל¦²Õ^²ã|Þ}®¡í%ÛOV¾»ž?Íó3§©(ËGvµÿg< uŸ(Ù^²ó©÷9M%Ù>ªò±Þï—*Ùyñ¶ÿç~cüÜÙ}è’Oµ4oáº(.>²ëPùïþ]ÍÝ%þgŽ‹ª:Tû îe®âî’Wå¿ë{ÀÅCuª}ëÆüÙÕÜ]òãÿìûþñÿ@täŸà±Œ.ç¯KQÿÝî.Ü]êÀ?ï“Þ.}çRýOÿÓÿçþ.LÿÓÿô?ý=b_åy³]î].uD½óýßûýÿÞþ¢çþñÏýû_ôÈ»ÁÅ2EÏËÝæG½sÿëùþ8ÍûóÔØ­ï®ÿ=ûö®ï×yøÇÿI÷~ÎÿUßw»÷á_ë?öV7[õþìçô¾?½ÿñÏ}Oñþ¯;¹5™»ÛÑzéûŸû&ʳË|¼¿?/ºø‹Ö‰ÿ3ýãýÿ÷„h_¹ÎW{ïúûÎè–èê/Z—Úÿˆc×çQÎnóÕÞwíûÝ~ÿÁû½È­gëÁû=ïÝû•÷ÝÏû×Ý2ÛoÕãñë÷nþWû>­ß»øÇ»¶Ï?E«>ÏŸù³|ŸÞïný÷œ>¯îÿlϯùj)ûf_uþWû朿¶çîúwñû©Žk«g”»ÇÙú0:G`–¯Ûø¹Õ2ú•€›Ï«õ`RCà*ïêqšÕ¥Kÿc*‡@u_ó–ãùS–*ÿµ«&û“Àjÿö&p׿÷ª¨€ @€ @€ @€ @€ @»ø v²dx^í™Krã0 }³Üÿ9BŽr9^XeÅ”øðybÏ&‹! ¨ Dúvã @€ @€ @€ @€ @€ àEàçë‘ïÙ¿^OK¶[g½Îƒxo£ÕãzSY';µ×³ë­C¼×“žõ=¯¥ëfíQµþu Ô>™ÊOö:µÔ®=Û›:ÞuLÔ<‰ÚGõz5}£~ÿÝÿŒþ­ö;ß×Hnæ£ÞGÇúÉ—KÓ/Ú¨×ÙqY¾÷âø™ÉÉxÖëÙùUõCÕ'ÊYêyÙõàc(6SµÇÙõ¨ƒXßÛÕg}EÏϪ‡\ê}¢EûS­OÄÔŒÊOÖ:ÑuC¹ïªYÞÔq¨MM©½d¯GÌÕA¶¯¨xQu0G·ÿì(UëRÇj®ÊSt\u£ê3:ÚCÕújÿÏõ|ÌŽeZå'+®ºƨúŒÊòP‡:x_“Õ^²âãmÿÏ:£^ë «ÿºÄÁÿÚþÕû€Ï—ûÿ€Ú¿û¹°Ë¾œïÇ~ͽ[r›Oß¿V€›¿Ù|ñ¿¦¼¿?cÎö“Ë|ü¯éïÿß-¹ôïÑ<ÕÞÝÏù{Up”«ËxµÿºÚØÈ.>GóT{¿jß_íþïçö‰Ñ¾ê:.ÊûÕûÞ½ÿñ~®ßÝïñ®ñîÖÿÑÞWÙïÝúïÚ~ïî?Ë÷ªýÞÕ?Þcû¼Ûý_¶oú½ö÷Ÿ*ßxÏýý§Úó6~Í®êõè½^7¿{ùø¨ÍÔÅçhžµ4ý¢rí:Îx¯Œ»zý”W/оÙ|âÜåÿ} ÷μ‹_¾Ûkꤋÿš§'j•È÷ í¿ÇS’ÅYÿ… @€ @€ @€ @€ @€ h¿­‚‚©x^íšKr! }³Üÿ9BŽJ9³°Ëc3ðH¢³ñ"ƒÝàÏíÆ @€ @€ @€ @€ @€ ä!ðóuϵ÷5ÏJÉô^ï­ã ›@«Gõs±©ì“Úko¼}ˆÇZi¯/÷¸X”êfãö¨Š_×ÀÚ•©üÌŽ³–ZÙg{SÏWÇÄš•¨}¬Š·†^þY¿ÿ?ÿùôºÊkï¼ùÍÌYÁ'ï­ÿïõä7‡bÞYZýö>çöÛ?¯!oæ½^{ǵúr=祙/z¯GÕ8—çOqó™òd¬ò¨ŠóÉ›úÿªy¢ª¼©ã¨=ŸÅËcÊ“©Ú›:uàñ~DUûrÇs׃—v¼èn_®øÔ¦–\~fÅuÕ†nü(³<¹æqù?âÆ78–¡ËË츮:£ôlOîù¨ƒk5çö±*¾º®QÍóô*?îyÕþ«ÞÜVÇW×AžÎnËtµ÷üjÿÕö7ÿ(ñÕuÐÖ]ñŸŠâÇþ_×¢›{´øÔÁcDóãÎÿøÿ# ªƒø'üû Ýý5>þïuÕ;/•ÿìïÝœ£ÆÇ?ýÏ=`ßýÿØ—Tû@Ö{`ÔýyV^øçPœôÎ:¢ÿszSøÇ?ûÿ¾ïèúŸþ§ÿG÷îÿ9÷‘Qï|þ¿·wüã?ëÞÏ÷¿üDõ9J¶8»ŸûÇž•ÍÛh¾*ïÙÏ}üß ŒÖCæ³ÇóÔ÷óxüç¸ÿãýu¥Žž§YÆãOÿjïUî}»ÜÿÔþ³Ÿ÷ÏùgÙ¿¯æ©ö^­ï«÷¿Úµ¾¯ê_í½jßWó÷¾êê¹íy—÷ê}_¥ÿ]þûº)ߨhýÜšËû.}Ÿµÿñ®ÝcZûmÕsnß»õ{–Ïð®íó³h«úúlÞYÞwïû(çÿlßxÜ f÷ÿ*ßx_óýïjßxPõÏÕ~Ÿå¾¶úê÷,/7·*ñ³yÅ·¶ò²û×ÒØ/ZVÿû™ò¬8‹Ïê‰Õ?fæˆâÎj™å™À*ÿ˜ˆAÀí?Æ*ÉâŒÀ¨ÈB€ @€ @€ @€ @€ @€ à$ð `Øù´x^íšmn$! s³Üÿ9Âa%£Ý|ô4ØÃʟHlL†îQ^^ø @€ @€ @€ @€ @€ @ÀƒÀŸ×:£¿=VI•W¢Þ[ã ¿6VêqkS9§:µ×h¾sˆ¯µÒ¨¯ÑqkQÚ·šÑUù÷50we*?UyæÒÚoö*o£æÙÏHíŠFy©Î[KmŸÙÞ>¿ÿùþ»ÚŸj¾}ÌÔ¬äÊëßUÞÔyjèùÏÒê¹wœÚg6Ÿ¿©1+èõšŸõCÏ?kÖg4>ê1çoL»‚¨?u\Öko¼–¢o6µÇl¾^Ùñ¾æ4•g}ŽÏúm×ÐôË2ÚŸ*«Çè8?sšŠU~ªòDý¶Æi¨úd©ò¦ž§ÕgtœÁ\¥j/Õù¢~ïârT}¢«}©ç»ó˜ýÜÇd¬RµYù²ž¯âcT}¢fù5/û oïò0+/þÏöÿØw샶}0«O«æUïƒ6ª>£ª<ÌšÿÏ÷â,/Uóªý?òùtøÙþG=àÿƒ@UgçQŸøÇÿ{ ÛWnñœ_w­›¿l½øÇÿ;Õ>p¿²ýä¯ò¾Ë{ ›¿l½ø?ûüWÀùïõˆúŸç¿{ {ŸºÆ«ž8ÿ9ÿ÷€kÿfë¦ÿ=ûvï¼ÿ{îUßãÿÎ÷þ£öìyêOÿŸýþÿ3ýãý÷ÛÊíüŽÖ‹üó½ïÏ=í'—8ußïòÞwÊó¿Úÿï|ÿ¯Á¥£uâÿùŽr]=Ní}·s×óï}7Ôê}Ü[þÏô÷>ﻜÿ£¼ïzßß%½çëjãGùu“_Ôj>[ëåý”¾w=ÿñ®=cZûmö8¼k½»ôÿhï§÷«?ÿUù>Ýûjý÷1çû]ÖY÷zµoú}ÿÿÀû]GÖ~>ªÿgy¾š·–ªÏlYÿ«yæœïÛ{WþWõzWWßê}ÇÓåsLƸøå^ù½‹ró·>ï#àâ¿oUŒn%°ªÿÖú—#°ŠÿÜ*ˆŽ˜å?Z/qZ£ýk«%›š@Ö¿ºòA€ @€ @€ @€ @€ @€ <üôÅiÔx^íšAR$1 ùÙþÿ<'l0fwè–\RÉÝÉ…ÃØ’œiÙ†˜·7~ @€ @€ @€ @€ @€ @€æøøóUcö÷üRáo²ÞÏ΃þlg=ªÇͦrŸêÔ^³ñîC|ÖJ³¾ªçÍ¢tÝjª=ªâ_×€we*?]q¼´®—½Ë›:ÏõLxV¤öâŠç¡·V—¯ª¼ûé]Áû÷ÿÿ¢¿«ü©âöRÜ7[ÔûÑx•?Uœ}ÍôT~äsõs•ÇÕ8=4÷˲ê7:Õãêüý ÕVõ§¿ê3;¿–ê>ÑÕ>³ñ²³óö1T[iÖWõ¼¬×è¼Zºó£W{\õ™?ßTM…«~ºæg½FçÕPžµËŸ*OÔgtü\S5•©¼tljzޝ¡=/j·7U¾¨Ïèøy¦j*Rùpʼnzޝ¡>'ªË›:oÔëÙñsLÕT¢öàŠwÖgv\ }T—¯ª¼Y¿Góü¦j*¨òàŠ{ä1ûy }T—§ê¼YÏGóüÆ´T{pÅ?ò˜ý\KßÍå§+oÖó«y~cÚ º<¸òàÿ÷ýâòÒ•WíÿOÛ…¾h]ÜyÔûÀgL›Ùí¥+?þÿ¿oºø»óàÿŸTû@{ û¢¹û²+¿Ê;￯½ÚåM•ÿœÿœÿÿîUMCÿÓÿô?ý¯:|/vmæé綪>•wÞÿ¼ÿµè¦ê¯éqèÞ¼ÿî÷þS÷=÷ÿ^÷¿Ú¿÷¶ÖgŸ~o¯Ö‡ÿ{~ÿGíýjçþcW¬ö×Ôùø?wWLõ—­ ïç¼_µÿñOÿUÞ¯zï_­ÿ«üǺi¿ÑÙ{vʼ*ïWïûÝûïš³fJGëÀÿ=ýã]ã}·ó¿Úû]îûçÝ=w»Çã]Ûï»øÇ{­÷iç—ﻞó¯vS÷yþœ¯Û;þî„nÿ.ßx÷|ÿÏíï½ßÿ˜âïçÞÙóšççzέžQÓ=FëÃhŒ@”ïÔñ±U3úA`ªÏ£º0¨!pÄyÚçšUe·þÇX iýÍßm5ž_Eâ¿wÕdsŸÿ˜A ºÿg¬’*ªÎÈB€ @€ @€ @€ @€ @€ @þ*üæ{x^í›Qn1 s³Þÿ=BŽÆ…#‰z|K®&?ùX‘”fHyá ü@€ @€ @€ @€ @€ @€ @µ üùõØßêïÚ§bw³V½Ï®Ÿ­Ïºk ÌúT­»ö´T% ò̓‘k D½eÅ]Kã¼êYUyÏ3â=±Ê“+—Îý«¹¼©ëÜߌç„j/î|J÷­âö•UニrOöûëû¿w¿³|eçÍ¥vŸì#ÿ£çÙwóßÇTÎIF~£Ïw½©âs¨Ý'kÔïjœÊg4Ï}ŒiO²êQµ>êq7NK¯6•Ïhž]ŸÑøþæ4'ˆzËŠ‹úŒÆi(öÍ’åq7oÔçj\_sšïzÊŽ_õ]¯¡Ù/K¶?Uþ¨×Ù¸~æ4;Vùqå™õ]§¡Ú'‹Ë›ªNÔël\sšª¼¸óÌúŒ®ÓЭŸÅíM]/êwWßœf‡jî|#»Ï5”ëfqûʪ·ëù]|]sšeùpçŬܞ²ëÑk}íÃÿøÿK@ÝkTû¬vϧ«þçzÐåÃ]Gíÿ™oŽjŸUn/®zøŸëA—wüŸíÿÙoê>˜£Úg•{.Ýõðÿs/º}¸ëáÿÊïúÜìs;uÏ£»óÏü3ÿï{À=îzÌ?óÏüŸ7ÿê¹çûßG¹ïïh=üŸýþüó¹?îèýZ5޹;ÿwEUÑ}áÿÊ{ÿ®ïýÏ.‰ÎYµ8æ~mîñÿ 0ê›Õ>QÕæxu?#»Ïû˜Œít•wµõ»~Oý¿¯î÷–÷»¿ï½ÞÕæy´¼ÇîùwQ#ÞÕžãÿLÿx×zïòùŸíý´Ïû.ŸÿxÏ™÷êþ]ÞOŸû*÷¿Û7Þ¿ßW½ßãÝs¿ª¸ü_å›yÿ¹²ü_íï£É<ßõ_ÅóéßãÏÙþÕÈu¿xšŸûûwÿ{Îîâ—9ÏéÑ®þshœ—µ›ÿó 垸ºÿÜÓ“½šŒx \íß{Zª½pûÇ@-Yþk’ݼ#õQ@€ @€ @€ @€ @€ @€ @È"ð ÙFIx^í˜ÝmÃ0 »Y÷ߢ#t„"H ´ýII——þ„ìøŽ€Õ§5;¹¬þ¼ãsiœ·»·?ï|ç‰=±·/U¾X*çì¦ò¥Ê{Ž™˜“ªæ|Nâíu4_Ë—ê¹½õ³ŒúR¯Wù¾Ë»¾AÛ Ô>góGõÞúѳ~¢âèmEyœÝÿgû¿ú†>ÐôÁì\fÅ©û@C¹nÖ,³ûªý_ùëó­lÖCvœº|)×Í–íѺ¿ªêó­ÌÊ?;^åÿ”÷@¶?ëþø·ÝVþUâU}`£[?ºŠ?køŸë5+÷*ñø?Û¿ú;áÝúQUæ×«óâ^%þñÿ àÝcT×Y]en½êðö¾ûw /îUòàìî©âÍ«üãŸ÷xÍ]•<Ì¿ûÇÊ*Þ¼êÀ?þ¹ÿû{ÀkîªäaþûÝïtÿã}ÌûµºÊÜZëÀ?þyï÷€uî²ãUs¿ûwß]îüÏü߈ìùµî¯òo£ºN´•V¼Êû)÷þª÷¿Ú;þŸ‘5×­}Õþ×¹¹}*mñ®ò\íý´¹_åþǻϜße©2ßwuàÿLÿx×z¯zÿGy?õ}ÿÚUÙ÷´o¼ÿï€,ÿx¹ß[»DùÏòͼ¿ïµÿlïøñ_Åók­ûïôç£ó_Õ3Þç:yŸ½uÎQ87ª—kõuç´¼º×V}¶ÓÝâ[í9Æ| Tó{Wï©Év¨îSZÕükOKöWÙþ1’K ÚîiÙ=jþ!½Ñù_ãTT @€ @€ @€ @€ @€ @€ @X•Àóº¾tTREEÜ‹ÿÿÿÿÿÿÿÿo@dc_¥@eÒbg@fwfn@gÞi€@hLm’@iÌp{@j^t]@kÙwB@lv‡9@m¸Š<@nñ@o-‘ì@p.”ß@q—¼@rù™‘@sµœu@tFŸF@u»¡@v¤Ð@w¦˜@xá§3@yy©3@z¬ª3@{ß«3@|­3@}E®3@~x¯3@«°€@‚íT@HÛñ#@I/ö%@JRú#@Kwþ<@LšH@MÖè@N ú@O]@Pù@Q]Ø@RVÙ@S.Ó@T#Ð@UÚ&ç@Vª*Ä@W‘.Ù@XU2Á@Y.6½@Zï9à@[¬=Ã@\ŒAÃ@]OEÖ@^IÑ@_èL@`¹P¤@a»T{@b_X‰@cÚ[o@dx^íÛÁm#1DQgæü³Ø‚aÈ:ìÚá]jø|ñAì"ù·Fá? € € € € € € € € € € € € € °&¯ÏǹŽ~¯yz§%päýìë£çQ?—ÀY¿½ëçÞÊn­z}ŽÖµžÏº,QUõÙ[JE Ê_ucsT{«Î›Caß]ª}¥óö5•¹yÚW*?Cc¿Ô”ŸY¹û«½ñ,Oé}j©ì“–ö2;s57ýóûýÿÙß³½žÝ¯†ÎýSÎz?ZÖSzýý ŽÝðÈçèëi¿­ùc”î[=ê·µ¾ÕSjÝ} ŽÝ¬Õ_õº”ç£Ü1Z÷«®öz6ïÈWêõû™ì»ÑY_©õ)ϯrûhݯ*å³7WÌí±^OéºY}0—öz»¥=ŽæëƒlÏŒúI×ó¿·ÿgéƒL¤ç·:?ÝÊë¦VûIçñ_ÛKi_©|}PÓ)?é\þùÿ!ꃺ매ç4ÏÿX¥ý¤óùçßs ¿Òó™ÎOÍÿ3·Ÿì{T¦ý¤óùë³´Ÿt>ÿü{þ÷÷@z>Óùæ¿ßýOeÚO:Ÿþ½ÿ÷÷@z>Óùæ¿ß½÷ÿ㿌Ñ]¿:=ŸéüÔü¯o®æ„i?é|þÇú í'•Ÿò¾Ë÷¾Ï®IùIçò?6÷ü?ìþÿaé9­Î7÷5sÿ®óÏÿžþÓÞwûÜ÷.óÏ{í¼ÿ›Vý|®ÎãOÿ¼g½¯úþ?Ëû®ÏûUßÿyŸ3ï«ùŸíÝÜÿÝÕŸ×Zóx¿fÞ¯šÿ«|›÷ÿ÷Yë¼ö®»Ú;ÿsý¯â›÷¶çË»Ïõî¿k³üzÕjó:zžQ»Õò^¥~7oU÷]Å_ï9ª8ìšÓËýêº]}Ußûjg÷¯¾ÿîygùÏ^¿»ŸôýgûlÝ/}où­>Òëø¸†@Ú«ïg®ñÚºë,ÿ­ç±n.jÿsOo·Q­þG÷Q € € € € € € € € € € € € € pDàÚ}"x^íÙ]’Û EáÙYö¿‹,!KH¥&zˆ]P+|~q¹WÍ94þûøð@@@@@@@@@@@@@@¨GàÇ·ÏšŽçzªh&Wÿ£¯gÖ({Qß½óæU.9A ×cj\¢f9)¯£9¹•H!0êmÖ¼‘5˜3N`–ÇTîøÊÌì!ò4;§g-Æœ'0Û[:ÿü ÍøŠ@ÚϪ³Z^G¯§ü¥rö1zn¥£~ÏÎKyÍ9GeŸÑg=^?ê/5o³}+½êst~ÊçÙœ>*ûŒõ—šwÖ_jü>†¿^iÊãÕœ”×Þþ? \õ–žßë/5n÷}ö—ÊKùmåðïØy¤úuVN«S×wݳ¼¥rS~[9ü×|Xý¿änû Õ§³sZý›ºÎÿÞçÿüï´fŸÛéüÔ9ÿ.g'÷ÿkíþ³;´Å»Úuþ÷ö¿êû`–rÝ´jýÝ[s ³§zyWÇ?ÿ¿ÌÚºõSªõu«žY¾ÜúƲ¶xW»Î?ÿÎýܨÖß­zôνßÿþþü˜¥[?­ÕoU®Ïî{ŸÿöþßÿšþõýÜ÷*çû»:øßÓÿ*ﻞûÇ®ªÚÿ«üÏí®úéÕü¯ò¾{ßWëÞï9+îîÿÕÞõýŸûì.ÿ¼ßÓï¯w]íÿ.ïúþßûm•Þkôûªþ¿Û÷ëýkÒ¿¿ªTÿWóí¼ïÛ[£þ«úæ½Ïû»ïÿÕ½¶ê;·z£[<ŸrÉ1Oñû®Î±U›uxª3žæ?³j)OëÆæ¨ÞÿsV-µjÿ3³–ÀÝý¿vµîöJ`µj˜í¿ÖjU3«ÿ‘}&ÿ“>Ó›ª@@@@@@@@@@@@@@þ7?Ø{%x^íÙKr1 EQïÌûßE–%¸R¶q%n6  Ñìã‰"Á{}¬·7 € € € € € € € € € € € € € € €}ü~ÿ¬åìcŸ¨d…ÀYï£ëWj²·ŽÀ¨ÏÕuu7rÒ«^g÷Ÿ©ÑÚ<³þ¢÷åÝPòO¢=Få±VC ÊWVN …çž’å-:÷¹†roí);/—ÆóÒ³}eå?ÏTγüTåæPyNj•§ìsžc,ö¦Ù^ªócéìŸöëë÷ŸÑÇjŸ³çío.憣ÞÖÍzÊÞCiß”#¯³Ïg{Íß×\ÌÍfýŽîõ”½.†Ö~)£W×eû=ÊßÏ\ÌV½žÝä)ûùjû¤œõµ>Ûóÿò÷1s“(Ÿ³9ú Æãlʬ·è}Õ}0Ëk·}ÑWóôAm‡­úŠÞÏÿ³ý¿ú©ªji÷;-z~£òø¯é•(_Ñ9Uþ_çÔÐîwJ´·è¼ª>èg¦¦¢h_ÑyüçöA´¯è<þùÿC »r)÷Mž×è¼lï>ÿ}öf´·¨<þs_;¢ã)=E—ÀUÿ»¿ïz>s{îö^×ûô¦g¢]£ç²Ïë|­@´cÕ¹ÚmL¨êX5gôù]Ÿ¨êV='·•Ówª»UÏ»»‡ëbÕ½ºæÅ¶sêJ «W×Ü«}ü~L «S÷ܱ-]ý“@w§îùÊæºûÌšŸS8÷ô¬>³îsnÉØæ¿?ÿýï§Ÿ³ºUß'¦qÞ©«þoý\œW2¶q´ÿ÷sÕïoÕ¼˜Ê9§ªú?õópNÉØ¦]ý¿æV½ÇÙ91ýOu÷Êç`ÿ’± gõ÷9ˆõé>5»ÿêÏA·çÛæëÿ¶bµÏ»ª¿ïÚŽÑiúGåö8·ºÿªï=êå·Ð?oøæ ú¿¹^þÙŸÒßßù–‘ úGÔö9£ÿ>-#›èQÛçŒþû´Œl¢DmŸ3úïÓ2²‰þµ}Î<¥öÿ÷Œžß§`nýs~o?­ÿÛ æžuÿÑïíªësjûœÖŸ–‘MVõ¯zGçDŒv>£ÿÎu¯w›Ýô}­¾þZä¬+fõ¯î8:שׁ÷·íî?Ú©ëúû"g]ÙÕ¿«ãèܳjŽo[մˬëÇEÎ:í?«_ô>gUŒo{Õ?ê¿ú\\䬓«;Ußÿ¬zùm«ýWÍËKœ9aU¯ªûžY­n몳çÔ œ=iv·ìýήU¿}¶Ç¬óõ››øO`V¿è}Têˆvé>×»µé_ÝGç+3W`´Oõõs·u·ïÕ=¯æ)ð,«^Ùß?k[OÓõþ“}§ÀÕûýέ<5 @€ @€ @€ @€ @€ @€ @€ @à „È8x^íÙÍmÜ0€Qwæþ»H )!'@`ÀØ¥8”^.>Xß·vþøð @€ @€ @€ @€ @€ @€ @€ @€^ߟ_ç¯~íÝÚéQ«Ý_=µ—95¯zF}¿æ6NYˆê»:guOÏç¬v‹~>çV¦¾+ÝswÞ»{{.F`·WÖû1·3å•@V¿¨¹¯ö÷ý=¨NÙsönéퟲ»EÏW2V ºOÕ¼X…çN«ê•uÎsËÅÜ<«KõÜçM©î”uÞóÊÅÜ8«G×Ü•çLùõ÷ï¿?}íê¸{îs îÝôUÿS?{*Ïyûjÿïíþœf¿ÿœ’×nºÛÿûûÙ=Wç_SyÎ[Ñý§ý^xNÉk7Íêïsp­Gõ[úW‹Ï:/»ÿ”ß³ÔçlSÕßç`Nóÿ7Ñf—ª­ô¯’žyNuÿîÿfVèÛJÿ>û 'ë?¡Bßú÷ÙO8Yÿ úvпÏ~ÂÉúO¨Ð·ƒþ}öN®î¿ú÷ûèç'˜OÚAÿI5êwÑ¿Þ|Ò‰Uý£¯Î›d>i—ìþ«²žŸd>iý'Õ¨ß%«ÖÏñêÜzѳNŒî¿Ú'ûù³jÔo»Û?»ßîüzѳN¼Ú·KöûgUèÛöUÿìNYóûDÏ:9Ë¿kîYúýÛvuŠ>·_òÌ ¢;tÍ;S¿ë®^Qçö ž½AT‡ê9g«ÏÙ¾ºÛîysäî±Énª÷ï¡=ïUý®ž3Oì^]í’ýÞ½”çÞ&»ãêü¹R÷ÜlµOôó÷T=çVÑ=ßwŽÐ½7}·×îs÷V<÷v»]¿¿®Ä37¿Úÿ™ZnM€ @€ @€ @€ @€ @€ @€ @€ @€?Êýx^íÚ]ŠAFÑÙ™ûß…Kp "Ú Cuf~‘õÇ—A¦2²â\{@ñëË/ @€ @€ @€ @€ @€ @€ @€ @€8Oà׿w¿¾ž÷&n>Cà½ÿèïÏxgwæF{ú|î Mªø´çês•;˜=/°Úuöüü;™˜í—:—ÜŬqTÇÔœñ œXHuKÏYÙÉÙÏÒÝÒó>ßÄ“3é^UófvsæX ªWÕÜã<1"PÕ©zîÈŽžý^ ºSÕ|M3U}vÍÍ(ô²«Sõ=} ®m^Ýe×ü5…¾§wõÙuOß’s›ïê²ëž9…¾§~þûÿ?ß}ÝÕ-}Oߢc›õÿ~ºSÕ¼1…¾Oö¿ËŸ‡¾EÇ6_íÿ:_õ9^;¦Ñïiýû5ÿãTÿ«þè]÷x{ýžüDºÿÕ~<¹]b7ýŠ÷ñôþ¯¿?Ü·Pí›ë_ë{õéú_½Píûé_ë{õéOïuÿ³ßOÿ³ œ{ºÿê¿×§ÏŸ«{ýÛõ¿~£Ê7LõOnSó*íž0[ÿ'Tœßaµêsšž3/Òëähÿt§ªy½*Îo{Ô¿ªOõÜy‘^'«;ìžß«Þú¶»ûTß·.ÒkBu]ó{UËm»«Oõ=9‘^“ª»TÏïU+¿muŸªùy‰ž«úTÏíY+¿uu§ôü¼@ï‰é>UózWªÛ¾ªWjnÝæ&ÿHuJÏQg@ºÛê¼=[»å%°Úkõ¼ç ¬ö›=îÖnßýù'~MÙÏïѹknë­ÞŽ:}Ÿè½ÞûÞ{oO€ @€ @€ @€ @€ @€ @€ @€ @€®.ð*ÕèDx^íÚËMäPP2#ÿ,XÀlFj¹íúø•ßaÓíú Z||ø"@€ @€ @€ @€ @€ @€ @€ @€ @€èøþüùêµ#;Žò?z¿sW³òŽò½ú~þ¦:V\Í÷l]ÅîzÆÎæ}>¾±™Ñ<£õ™·èu^ š_VýùÍUddå—Õ'ã&=ÞÈÊ-»Ïûx2"[v¿Èmj²óªêw|‰'®TåUÕ÷Êj^ TåTÕW–¹U9U÷ÍUØ·[uNÕý÷M.çòê|ªûç(ìÛ¥:Ÿ®þû&»¼+Ÿê91…}««séê¿o‚±Ë»òéšÓدº+—®9û%»¸+—®91ýª¿þþÿ÷ÿ×®¼²çì—`ìâWùÿû~v>]ýb*ûTå?õç`Ÿc—Ê?æ7½ZþÓŒí/ÿ˜ßôjùOO0¶¿üc~Ó«òïú{-{Îô\ºö÷ùO—ôšs²ïîî·¦òº[ÝWöüu¥×Ü,Ûÿî~k*¯»ÕÝyeÍ_WxíͲüïî³¶òºÛÝ[Öüu…×Þ,Ëÿ®>k뮿Ý]¹eÍ]_xí ³rèî³¶êœíºs‹Î›#;cÓhÝõ3TçlÙßÕysDgmz5®ºYšó¶íÊñìœy’37>›Kõó3çn]ç»ýç ÎÞüÝ|ªž›­7ûª\_õ/ö¬ ºò–Ús®©Êÿ9BϾ$šÿ³užÝQþÏp! @€ @€ @€ @€ @€ @€ @€ @€ @€!—2Fx^íÕAŽÛ0ÀýYþÿ›XìáTCð×— @€ @€ @€ @€ @€ @€ @€ @€ @€Ì üþõwÖwßs71i…ÀUÿW¿¯¸³™9«~ïþž»™¤ »ýÞ}~b3ž ÜíóÓçŸßÔɆÀ§}>=ߨEæ}§ý¥ÎÝ¿±IT©œän²®R½¥r®o쉤@ª·tNrGYß ¤{Kçé®+î«•×U87½ÕW:÷܆º›§{jçu5ÎKo÷•Î?¯¡îÆé~¦òº*ç¤Oõ•žsNCÝMÓ½LçuuöOŸî+=oÿ†º¦û˜ÎëêìŸ>ÝWzÞþ u7L÷±*¯«´oúª¾Òs÷m¨»Yº‡Uy]¥}ÓWõ•ž»oCÝÍÒ=¬Êë*훾ª¯ôÜ}ên–îaU^WißôU}¥çîÛPw³tÓy]ýÓ§ûJÏÛ¿¡î†é>¦óº:û§O÷•ž·CÝ Ó}LåuUÎIŸê+=眆º›¦{içu5ÎKo÷•Î?¯¡îÆé~Zy]…sÓ[}¥sÏm¨»yº§t^w{éé¾Ryš™Hõ•Ê™ÙÚ”©ÞR9š™Hõö4gv[ÓþxÚÛ§ç4ñO{¼{þ[»ÅÔÿ?éw Ü}¯ž÷¶n—þÿ'ú³¼Ï?»?·'@€ @€ @€ @€ @€ @€ @€ @€ @€ @€· ü]×u9x^íÓAN1@~ÆÿÃ8 \"¡$Þnï&S\ÂîñTk¿¾ü @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ô~¾ÿfÜ~÷'šp%ûþŸýûJ;x˺À³}?:·þ7ÏxÔëêÿÏÜÉìçVû}õÞó/rr§À«==¿s7³ ísõþã—9±C`µ¿Ô½;šñ¿@ªÇ£9::Gàhoéûç(Ìšî/•7·‘½›§úJçìU˜;-Ý[:on3{6O÷•ÎÛ£0wJº¯VÞ܆º›·úJçv榧{jçÍmª³y»¯t~GanjºŸvÞܦ:›·ûJçw榦ûiçÍmª³y»¯V~Gc^j«Ÿv:·{jåw4楶úiçÎkª³q»§t~GanjºŸvÞܦ:›·ûJçw榦ûiçÍmª³y»¯t~GanjºŸVÞ܆º›·úJçv榧{JçÍmfÏæé¾Òy{æNI÷•Ê›ÛÈÞÍS}¥sö*Ì–îíhÞÜ&ÎÙüh_©ûçlojª¿Õ œ+°ÚÛÑ{çnmúMàh¯Þ'-Wû[=­­½¦ýý~ÕïùþÞ{lë•÷«ý“ü ßñgôh  @€ @€ @€ @€ @€ @€ @€ @€ @€ @€« üÑ)®Rx^íÓ1R1 @~ÆÿÃ(B¸µ¥ÑÞ–›„àì‘ÕSûñá @€ @€ @€ @€ @€ @€ @€ @€ @€ @€È|}þä¾úŸ™.õnW½¿úýî÷›_xÕïêïµ×¸=-°ÚïÕóÓ{˜·'pµÏê¹½×¹•¨öºz?½ü5ÕþºÎ¯½Òé”@WŸ»9©½ä^Øí­ëÞµW:•è걚“ÚOîÿÕÞºïëkV »¿jÞìö¦UûJÝ×ÌŒ@ª¿jîÌö¦T{JÝ×ÌŒ@ª¿®Ü…s§tõ”Ê9·™™ÍS½uåÎ(œ;¥«§TιÍÌlžê­+wFáÜ)]=¥rÎmffóTo]¹3 çNéê)•sn33›§zëÊQ8wJWO©œs›™Ù<Õ[5wf{Sª=¥îkfF Õ_5wf{Sª=uß×Ȭ@wÕ¼ÙíM«öÕu_÷tõ·›sÏÖ¦þ ìöÖuO÷ tõ¸šsïÖ¦ßõý“/Õïv÷ü{mí5éïŸð3v¿ç¿î=ck¯ìúþI>[àê÷ÿì-½ž @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ 0!𠊺ðãx^íÙ1NÄ0†QnÆýoÃ(ˆ•³ÞøŸóh(‚3öû¬4¼½ù!@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€û>Þ¿Þµú}ßDoê$°êþèy§3ØË¾Ànÿßëöw`e¥À]ý݇ʊû³Oõÿ~ïþάLœîï$*îÏHõwö\©ÿIÝþïN÷÷èu'ôïÕ#½›ªþ¾éÒÏÓ¿G‡ª]T÷÷¨*íÿ>©ûQ«0wzªïjÎܵ'_uI=¯U˜;=Õw5gnÚ“¯º¤ž×*̞껚3·@íÉW]RÏkæNOõ]Í™[ öä«.©çµ s§§ú>š3W¾ÇÉõïÑ¡jUý«ÎkîOýg߈tÿÙÚýN¯¿&É¥ú'ÏdÖuÓý¯ïÄ_Vœê_q3Ÿ¸«ÿó“­è °Û¿ÃÞíáu«ý_Ÿä  @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @à? |SÄ%Ox^íÚM Â0€QoæýoãDÄM±Ð´™a&}nDèÏä}¡ºðñð"@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @àšÀëù=ÿ÷~íjÎî&°í¿÷¹ÛºÌ{LàhωcžÝŽ:Ûß~èVúÿ¼³úûýÐs?ÌîoôÚú÷ê5{Ú¨þž³KÅ\Oÿ×.Wîï9P{'è_»OôtúG ×¾~Vß5÷þ5»dM¥–tÍûè_³KÖTYý³Öã>cúy­v´þ«[Otÿ±i- ¶x­ûEõ¯µJÓì èï½1»ÿ½5û­~Vÿ~+7ñGàlzkí¿Æj­b+àüö @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€FÞbËY¬x^íØA ƒ0†QoÖû߯#tU(BP3Âß×MÕ&ó>[Ámó"@€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ëûçú±ŽÌøõ½çMl¢³þÇÏée Üíï~¡–À»§™ýýûȸnôÏè8;…þ³rçUû»¬}è¿v¿êî«ý«ë;¿W@ÿ^ÿîÕõï.лþlÿÞ][ý)ýŸ’\ó{îö_sJ» œõ'—-àùMv_Ó @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€@Uà {)‰Mx^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Px^íЀ ý©)„  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`À€ 0Pastra-toolbox-2.3.0/matlab/algorithms/DART/tools/000077500000000000000000000000001475635207100216275ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/algorithms/DART/tools/DARToptimizer.m000066400000000000000000000063431475635207100245100ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef DARToptimizer < handle %---------------------------------------------------------------------- properties (SetAccess=public,GetAccess=public) % optimization options max_evals = 100; % SETTING: Maximum number of function evaluations during optimization. tolerance = 0.1; % SETTING: Minimum tolerance to achieve. display = 'off'; % SETTING: Optimization output. {'off','on','iter'} verbose = 'yes'; % SETTING: verbose? {'yes','no} metric = ProjDiffOptimFunc(); % SETTING: Optimization object. Default: ProjDiffOptimFunc. % DART options DART_iterations = 20; % SETTING: number of DART iterations in each evaluation. D_base = []; end %---------------------------------------------------------------------- properties (SetAccess=private,GetAccess=public) stats = Statistics(); end %---------------------------------------------------------------------- methods (Access=public) %------------------------------------------------------------------ % Constructor function this = DARToptimizer(D_base) this.D_base = D_base; % statistics this.stats = Statistics(); this.stats.register('params'); this.stats.register('values'); this.stats.register('score'); end %------------------------------------------------------------------ function opt_values = run(this, params, initial_values) if nargin < 3 for i = 1:numel(params) initial_values(i) = eval(['this.D_base.' params{i} ';']); end end % fminsearch options = optimset('display', this.display, 'MaxFunEvals', this.max_evals, 'TolX', this.tolerance); opt_values = fminsearch(@this.optim_func, initial_values, options, params); % save to D_base for i = 1:numel(params) eval(sprintf('this.D_base.%s = %d;',params{i}, opt_values(i))); end end %------------------------------------------------------------------ end %---------------------------------------------------------------------- methods (Access=protected) %------------------------------------------------------------------ function score = optim_func(this, values, params) % copy DART D = this.D_base.deepcopy(); % set parameters for i = 1:numel(params) eval(sprintf('D.%s = %d;',params{i}, values(i))); D.output.pre = [D.output.pre num2str(values(i)) '_']; end % evaluate if D.initialized == 0 D.initialize(); end rng('default'); D.iterate(this.DART_iterations); % compute score score = this.metric.calculate(D, this); % statistics this.stats.add('params',params); this.stats.add('values',values); this.stats.add('score',score); % output if strcmp(this.verbose,'yes') disp([num2str(values) ': ' num2str(score)]); end end %------------------------------------------------------------------ end end astra-toolbox-2.3.0/matlab/algorithms/DART/tools/DARToptimizerBoneStudy.m000066400000000000000000000057101475635207100263420ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef DARToptimizerBoneStudy < handle %---------------------------------------------------------------------- properties (SetAccess=public,GetAccess=public) % optimization options max_evals = 100; tolerance = 0.1; display = 'off'; % DART options DART_iterations = 50; D_base = []; end %---------------------------------------------------------------------- properties (SetAccess=private,GetAccess=public) stats = struct(); end %---------------------------------------------------------------------- methods (Access=public) %------------------------------------------------------------------ % Constructor function this = DARToptimizerBoneStudy(D_base) this.D_base = D_base; this.stats.params = {}; this.stats.values = []; this.stats.rmse = []; this.stats.f_250 = []; this.stats.f_100 = []; this.stats.w_250 = []; this.stats.w_125 = []; end %------------------------------------------------------------------ function opt_values = run(this, params, initial_values) if nargin < 3 for i = 1:numel(params) initial_values(i) = eval(['this.D_base.' params{i} ';']); end end % fminsearch options = optimset('display', this.display, 'MaxFunEvals', this.max_evals, 'TolX', this.tolerance); opt_values = fminsearch(@optim_func, initial_values, options, this.D_base, params, this); % save to D_base for i = 1:numel(params) eval(sprintf('this.D_base.%s = %d;',params{i}, opt_values(i))); end end %------------------------------------------------------------------ end end %-------------------------------------------------------------------------- function rmse = optim_func(values, D_base, params, Optim) % copy DART D = D_base.deepcopy(); % set parameters for i = 1:numel(params) eval(sprintf('D.%s = %d;',params{i}, values(i))); D.output.pre = [D.output.pre num2str(values(i)) '_']; end % evaluate if D.initialized == 0 D.initialize(); end rng('default'); D.iterate(Optim.DART_iterations); % compute rmse ROI = load('roi.mat'); [rmse, f_250, f_100, w_250, w_125] = compute_rmse(D.S, ROI); %projection = D.tomography.project(D.S); %proj_diff = sum((projection(:) - D.base.sinogram(:)).^2); % save Optim.stats.params{end+1} = params; Optim.stats.values(end+1,:) = values; Optim.stats.rmse(end+1) = rmse; Optim.stats.f_250(end+1) = f_250; Optim.stats.f_100(end+1) = f_100; Optim.stats.w_250(end+1) = w_250; Optim.stats.w_125(end+1) = w_125; disp([num2str(values) ': ' num2str(rmse)]); end astra-toolbox-2.3.0/matlab/algorithms/DART/tools/ProjDiffOptimFunc.m000066400000000000000000000016271475635207100253430ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef ProjDiffOptimFunc < handle %---------------------------------------------------------------------- properties (SetAccess=private, GetAccess=public) end %---------------------------------------------------------------------- methods (Access=public) function proj_diff = calculate(~, D, ~) projection = D.tomography.project(D.S); proj_diff = sum((projection(:) - D.base.sinogram(:)).^2); end end %---------------------------------------------------------------------- end astra-toolbox-2.3.0/matlab/algorithms/DART/tools/dart_create_base_phantom.m000066400000000000000000000015541475635207100270070ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- function base = dart_create_base_phantom(Im, angle_count, angle_range, gpu_core) base.phantom = imreadgs(Im); base.proj_geom = astra_create_proj_geom('parallel', 1, size(base.phantom,1), linspace2(angle_range(1),angle_range(2),angle_count)); base.vol_geom = astra_create_vol_geom(size(base.phantom,1),size(base.phantom,2)); [sino_id, base.sinogram] = astra_create_sino_cuda(base.phantom, base.proj_geom, base.vol_geom, gpu_core); astra_mex_data2d('delete',sino_id); astra-toolbox-2.3.0/matlab/algorithms/DART/tools/dart_scheduler.m000066400000000000000000000021311475635207100247720ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- function dart_scheduler(D_tmpl, iterations, settings) for base_index = 1:numel(settings.base_loop) % create new DART object D = DART(settings.base_loop{base_index}); dart_scheduler1D(D, iterations, settings.parameter1); % copy from templates D.tomography = D_tmpl.tomography; D.smoothing = D_tmpl.smoothing; D.segmentation = D_tmpl.segmentation; D.masking = D_tmpl.masking; D.statistics = D_tmpl.statistics; D.output = D_tmpl.output; % set output options D.output = OutputScheduler(); D.output.directory = output_folder{base_index}; % run DART D = D.initialize(); D = D.iterate(iterations); end end function dart_scheduler1d(D, iterations, parameter_loop1) end astra-toolbox-2.3.0/matlab/algorithms/DART/tools/rNMPOptimFunc.m000066400000000000000000000016361475635207100244540ustar00rootroot00000000000000%-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef rNMPOptimFunc < handle %---------------------------------------------------------------------- properties (SetAccess=private, GetAccess=public) end %---------------------------------------------------------------------- methods (Access=public) function rnmp = calculate(~, D, ~) if isfield(D.stats,'rnmp'); rnmp = D.stats.rnmp; else rnmp = compute_rnmp(D.base.phantom, D.S); end end end %---------------------------------------------------------------------- end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/000077500000000000000000000000001475635207100217225ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/000077500000000000000000000000001475635207100227325ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/draw_cad_phantom.m000066400000000000000000000021341475635207100264020ustar00rootroot00000000000000function [] = draw_cad_phantom(filename, magn) %% draw_cad_phantom.m % brief render an stl model into a 3d axis object % param vol_geom volume geometry describing the phantom % param h_ax handle to axis to plot into % param magn magnification multiplier of the phantom. default = 1 % % date 02.07.2018 % author Alice Presenti % imec VisionLab % University of Antwerp % Modified by Tim Elberfeld %% h_ax = gca; if nargin == 1 magn = 1; end [v,f,~,~] = stlTools.stlRead(filename); m = mean(v); % to center the CAD model! for i=1:3 v(:,i) = (v(:,i)- m(i)) .* magn; end object.vertices = v; object.faces = f; patch(h_ax, object,'FaceColor', [0.8 0.8 1.0], ... 'EdgeColor', 'none', ... 'FaceLighting', 'gouraud', ... 'AmbientStrength', 0.15); % Add a camera light, and tone down the specular highlighting camlight('headlight'); material('dull'); hold off; endastra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/draw_cone_geom.m000066400000000000000000000060021475635207100260560ustar00rootroot00000000000000function [] = draw_cone_geom(h_ax, geom, options) %% draw_cone_geom.m % draw an astra cone beam projection geometry % % param h_ax handle to axis to draw into % param geom the geometry to draw % param options struct containing the options for this function % SourceMarker marker to use to mark the source location % SourceMarkerColor Color of the marker for the source % DetectorLineColor Color of the lines that draw the detector % DetectorLineWidth Width of the lines that draw the detector % OpticalAxisColor color of the lines representing the optical axis % % date 21.06.2018 % author Tim Elberfeld % imec VisionLab % University of Antwerp %% hold on; % draw origin h_origin = scatter3(h_ax, 0,0,0, '+k'); h_origin.SizeData = 120; h_origin.LineWidth = 2; % draw lines between source, origin and detector line(h_ax, [0, 0], [0, geom.DistanceOriginDetector], [0, 0], 'Color', options.OpticalAxisColor); line(h_ax, [0, 0], [0, -geom.DistanceOriginSource], [0, 0], 'Color', options.OpticalAxisColor); % draw source scatter3(h_ax, 0, -geom.DistanceOriginSource, 0, options.SourceMarker,... options.SourceMarkerColor); detector = draw_detector(h_ax, geom, options); % connect source to detector edges for idx = 1:4 line(h_ax,[0, detector.vertices(1, idx)], ... [-geom.DistanceOriginSource, geom.DistanceOriginDetector],... [0, detector.vertices(3, idx)],... 'Color', 'k', 'LineStyle', ':'); end % draw rotation axis line(h_ax, [0,0],[0,0], 0.6*[-detector.height, detector.height],... 'LineWidth', options.DetectorLineWidth, 'Color',... 'k', 'LineStyle', '--'); perc = 0.05; text(h_ax, perc*detector.width, perc*detector.width,... 0.8*detector.height, 'rotation axis'); text(h_ax, detector.width*perc, 0, perc*detector.height, 'origin'); text(h_ax, detector.width*perc, -geom.DistanceOriginSource,... perc*detector.height, 'x-ray source'); text(h_ax, 0, geom.DistanceOriginDetector, 0, 'detector'); hold off; function [detector] = draw_detector(h_ax, geom, options) detector = struct; detector.height = geom.DetectorRowCount * geom.DetectorSpacingY; detector.width = geom.DetectorColCount * geom.DetectorSpacingX; vertices = zeros(3, 5); vertices(1, :) = 0.5*[-detector.width, -detector.width,... detector.width, detector.width, -detector.width]; vertices(2, :) = repmat([geom.DistanceOriginDetector], 5, 1); vertices(3, :) = 0.5*[detector.height, -detector.height,... -detector.height, detector.height, detector.height]; detector.vertices = vertices; plot3(h_ax, detector.vertices(1, :), detector.vertices(2, :),... detector.vertices(3, :), options.DetectorLineColor); end end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/draw_cone_vec_geom.m000066400000000000000000000114051475635207100267160ustar00rootroot00000000000000function [] = draw_cone_vec_geom(h_ax, geom, options) %% draw_cone_vec_geom.m % draw an astra cone beam vectorized projection geometry % param h_ax handle to axis to draw into % param geom the geometry to draw % param options struct holding options for drawing % RotationAxis if specified, will change the rotation axis % from z-axis to provided axis. % Must be 3-vector. Default value [0, 0, 1] % VectorIdx the index of the angle to draw % Color global color for all lines and markers. % is overwritten by specialized color options % SourceMarker Marker to use to mark the source % SourceMarkerColor Color of the source marker % DetectorMarker Marker to use to mark the source % DetectorMarkerColor Color of the source marker % DetectorLineColor Color of the outline of the detector % OpticalAxisColor color for drawing the optical axis % % date 21.06.2018 % author Tim Elberfeld, Van Nguyen % imec VisionLab % University of Antwerp % % - last update 07.11.2018 %% vectors = geom.Vectors; % source xray_source = vectors(:, 1:3); % center of detector detector_center = vectors(:, 4:6); % draw the points and connect with lines hold on; num_angles = size(vectors, 1); s_source = scatter3(h_ax, xray_source(:, 1), xray_source(:, 2),... xray_source(:, 3)); s_source.Marker = options.SourceMarker; s_source.MarkerEdgeColor = options.SourceMarkerColor; s_det = scatter3(h_ax, detector_center(:, 1),... detector_center(:, 2), detector_center(:, 3)); s_det.MarkerEdgeColor = options.DetectorMarkerColor; s_det.Marker = options.DetectorMarker; detector = struct; detector.u = vectors(options.VectorIdx, 7:9); detector.v = vectors(options.VectorIdx, 10:12); detector.height = geom.DetectorColCount; detector.width = geom.DetectorRowCount; detector.origin = detector_center(options.VectorIdx, :); vertices = draw.draw_detector_vec(h_ax, detector, options); connect_source_detector(h_ax, vertices, detector_center, ... xray_source, options); % rotation axis will be roughly as long as the source detector distance distances = eucl_dist3d(detector_center, xray_source); mean_sdd = mean(distances(:)); % mean source detector distance draw_rotation_axis(h_ax, mean_sdd, options); text(h_ax, xray_source(options.VectorIdx, 1),... xray_source(options.VectorIdx, 2),... xray_source(options.VectorIdx, 3), 'x-ray source'); text(h_ax, detector_center(options.VectorIdx, 1),... detector_center(options.VectorIdx, 2),... detector_center(options.VectorIdx, 3), 'detector'); hold off; function [] = connect_source_detector(h_ax, vertices,... detector_center, xray_source, options) % connect source to detector origin idx = options.VectorIdx; line(h_ax, [detector_center(idx, 1), xray_source(idx, 1)],... [detector_center(idx, 2), xray_source(idx, 2)],... [detector_center(idx, 3), xray_source(idx, 3)],... 'Color', options.OpticalAxisColor, 'LineStyle', '--'); % connect source to detector edges for kk = 1:4 line(h_ax,[xray_source(idx, 1), vertices(1, kk)], ... [xray_source(idx, 2), vertices(2, kk)],... [xray_source(idx, 3), vertices(3, kk)],... 'Color', 'k', 'LineStyle', ':'); end end function [] = draw_rotation_axis(h_ax, scaling, options) % draw rotation axis rot_axis = options.RotationAxis; if(~isnan(rot_axis(1))) rot_axis = options.RotationAxis + options.RotationAxisOffset; origin = options.RotationAxisOffset; % origin of the geometry is assumed to be [0, 0, 0] always! line(h_ax, [origin(1), (scaling/2)*rot_axis(1)],... [origin(2), (scaling/2)*rot_axis(2)],... [origin(3), (scaling/2)*rot_axis(3)],... 'Color', options.OpticalAxisColor,... 'LineStyle', '-.'); line(h_ax, [origin(1), -(scaling/2)*rot_axis(1)],... [origin(2), -(scaling/2)*rot_axis(2)],... [origin(3), -(scaling/2)*rot_axis(3)],... 'Color', options.OpticalAxisColor,... 'LineStyle', '-.'); end end end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/draw_detector_vec.m000066400000000000000000000037641475635207100266050ustar00rootroot00000000000000function [ vertices] = draw_detector_vec( h_ax, detector, options) %% draw_detector_vec.m % draw a detector for a vector geometry % param h_ax handle to axis to draw into % param detector the struct specifying the detector % .origin 3d coordinates of the detector origin % .u vector pointing from detector origin to % pixel (1,0) % .v vector pointing from detector origin to % pixel (0,1) % .width width of the detector (number of px in u % direction) % .height height of the detector (number of px in v % direction) % param options struct with options % .Color Color for the line work % .DetectorLineColor Color of the detector rectangle outline % return The vertices of the detector rectangle that % were plotted % % date 09.07.2018 % author Tim Elberfeld % imec VisionLab % University of Antwerp %% % draw the detector rectangle vertices = zeros(3, 5); vertices(:, 1) = detector.origin - detector.u * detector.width / 2 + ... detector.v * detector.height / 2; vertices(:, 2) = detector.origin + detector.u * detector.width / 2 + ... detector.v * detector.height / 2; vertices(:, 3) = detector.origin + detector.u * detector.width / 2 - ... detector.v * detector.height / 2; vertices(:, 4) = detector.origin - detector.u * detector.width / 2 - ... detector.v * detector.height / 2; vertices(:, 5) = vertices(:, 1); detector.vertices = vertices; plot3(h_ax, detector.vertices(1, :), detector.vertices(2, :),... detector.vertices(3, :), options.DetectorLineColor); end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/draw_fanflat_geom.m000066400000000000000000000022101475635207100265420ustar00rootroot00000000000000function [] = draw_fanflat_geom( h_ax, geom, options) %% draw_fanflat_geom.m % draw an astra cone beam projection geometry % param h_ax handle to axis to draw into % param geom the geometry to draw % param options struct holding options for drawing % SourceMarker Marker to use to mark the source % SourceMarkerColor Color of the source marker % DetectorMarker Marker to use to mark the source % DetectorMarkerColor Color of the source marker % DetectorLineColor Color of the outline of the detector % OpticalAxisColor color for drawing the optical axis % date 28.06.2018 % author Tim Elberfeld % imec VisionLab % University of Antwerp %% % convert to faux cone geometry so we don't have to write more code :)! cone_geom = astra_create_proj_geom('cone', geom.DetectorWidth,... geom.DetectorWidth, 1, geom.DetectorCount, geom.ProjectionAngles,... geom.DistanceOriginSource, geom.DistanceOriginDetector); draw.draw_cone_geom(h_ax, cone_geom, options); end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/draw_fanflat_vec_geom.m000066400000000000000000000105321475635207100274050ustar00rootroot00000000000000function [ output_args ] = draw_fanflat_vec_geom( h_ax, geom, options) %% draw_fanflat_vec_geom.m % draw an astra cone beam projection geometry % param h_ax handle to axis to draw into % param geom the geometry to draw % param options struct holding options for drawing % VectorIdx the index of the angle to draw % SourceMarker Marker to use to mark the source % SourceMarkerColor Color of the source marker % DetectorMarker Marker to use to mark the source % DetectorMarkerColor Color of the source marker % DetectorLineColor Color of the outline of the detector % OpticalAxisColor color for drawing the optical axis % % date 28.06.2018 % author Tim Elberfeld % imec VisionLab % University of Antwerp % % - last update 09.07.2018 %% vectors = geom.Vectors; % source num_angles = size(vectors, 1); xray_source = [vectors(:, 1:2), zeros(num_angles, 1)]; % center of detector detector_center = [vectors(:, 3:4), zeros(num_angles, 1)]; % draw the points and connect with lines hold on; scatter3(h_ax, xray_source(:, 1), xray_source(:, 2),... xray_source(:, 3), options.SourceMarker,... options.SourceMarkerColor); scatter3(h_ax, detector_center(:, 1),... detector_center(:, 2), detector_center(:, 3),... options.DetectorMarker, options.DetectorMarkerColor); detector = struct; detector.u = [vectors(options.VectorIdx, 5:6), 0]; detector.v = fliplr(detector.u); detector.height = 1; detector.width = geom.DetectorCount; detector.origin = detector_center(options.VectorIdx, :); vertices = draw.draw_detector_vec(h_ax, detector, options); connect_source_detector(h_ax, vertices, detector_center,... xray_source, options); % rotation axis will be roughly as long as the source detector distance distances = eucl_dist3d(detector_center, xray_source); mean_sdd = mean(distances(:)); % mean source detector distance draw_rotation_axis(h_ax, mean_sdd, options); text(h_ax, xray_source(options.VectorIdx, 1),... xray_source(options.VectorIdx, 2),... xray_source(options.VectorIdx, 3), 'x-ray source'); text(h_ax, detector_center(options.VectorIdx, 1),... detector_center(options.VectorIdx, 2),... detector_center(options.VectorIdx, 3), 'detector'); hold off; function [] = connect_source_detector(h_ax, vertices,... detector_center, xray_source, options) idx = options.VectorIdx; % connect source to detector origin line(h_ax, [detector_center(idx, 1), xray_source(idx, 1)],... [detector_center(idx, 2), xray_source(idx, 2)],... [detector_center(idx, 3), xray_source(idx, 3)],... 'Color', options.OpticalAxisColor, 'LineStyle', '--'); % connect source to detector edges for kk = 1:4 line(h_ax,[xray_source(idx, 1), vertices(1, kk)], ... [xray_source(idx, 2), vertices(2, kk)],... [xray_source(idx, 3), vertices(3, kk)],... 'Color', 'k', 'LineStyle', ':'); end end function [] = draw_rotation_axis(h_ax, scaling, options) % draw rotation axis rot_axis = options.RotationAxis; if(~isnan(rot_axis(1))) rot_axis = options.RotationAxis + options.RotationAxisOffset; origin = options.RotationAxisOffset; % origin of the geometry is assumed to be [0, 0, 0] always! line(h_ax, [origin(1), (scaling/2)*rot_axis(1)],... [origin(2), (scaling/2)*rot_axis(2)],... [origin(3), (scaling/2)*rot_axis(3)],... 'Color', options.OpticalAxisColor,... 'LineStyle', '-.'); line(h_ax, [origin(1), -(scaling/2)*rot_axis(1)],... [origin(2), -(scaling/2)*rot_axis(2)],... [origin(3), -(scaling/2)*rot_axis(3)],... 'Color', options.OpticalAxisColor,... 'LineStyle', '-.'); end end end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/draw_parallel3d_geom.m000066400000000000000000000071011475635207100271560ustar00rootroot00000000000000function [ ] = draw_parallel3d_geom( h_ax, geom, options) %% draw_parallel3d_geom.m % draw an astra parallel3d projection geometry % param h_ax handle to axis to draw into % param geom the geometry to draw % param options options struct with additional settings % .SourceDistance distance of source to origin % .SourceMarker marker for the source locations % .SourceMarkerColor color specifier for the source marker % .DetectorMarker marker for the detector locations. % Default = '.' % .DetectorMarkerColor color specifier for the detector marker. % Default = 'k' % .DetectorLineColor color for the lines drawing the detector % outline % .OpticalAxisColor Color of the line representing the optical % axis. % date 22.06.2018 % author Tim Elberfeld % imec VisionLab % University of Antwerp % % - last update 09.07.2018 %% dist_origin_detector = options.SourceDistance; dist_origin_source = options.SourceDistance; hold on; % draw source scatter3(h_ax, 0, -dist_origin_source, 0, options.SourceMarker,... options.SourceMarkerColor); % draw detector detector = draw_detector(h_ax, geom, dist_origin_detector, options); % draw origin h_origin = scatter3(h_ax, 0,0,0, '+k'); h_origin.SizeData = 120; h_origin.LineWidth = 2; % draw lines between source, origin and detector line(h_ax, [0, 0], [0, dist_origin_detector], [0, 0],... 'Color', options.OpticalAxisColor); line(h_ax, [0, 0], [0, -dist_origin_source], [0, 0],... 'Color', options.OpticalAxisColor); % connect source to detector edges for idx = 1:4 line(h_ax,[detector.vertices(1, idx),... detector.vertices(1, idx)],... [-dist_origin_source, dist_origin_detector],... [detector.vertices(3, idx), detector.vertices(3, idx)],... 'Color', 'k', 'LineStyle', ':'); end % draw rotation axis line(h_ax, [0,0],[0,0], 0.6*[-detector.height, detector.height],... 'LineWidth', 2, 'Color', 'k', 'LineStyle', '--'); perc = 0.05; text(h_ax, perc*detector.width, perc*detector.width,... 0.8*detector.height, 'rotation axis'); text(h_ax, detector.width*perc, 0, perc*detector.height, 'origin'); text(h_ax, detector.width*perc, -dist_origin_source,... perc*detector.height, 'x-ray source'); text(h_ax, 0, dist_origin_detector, 0, 'detector'); hold off; function [detector] = draw_detector(h_ax, geom,... dist_origin_detector, options) detector = struct; detector.height = geom.DetectorRowCount * geom.DetectorSpacingY; detector.width = geom.DetectorColCount * geom.DetectorSpacingX; vertices = zeros(3, 5); vertices(1, :) = 0.5*[-detector.width, -detector.width,... detector.width, detector.width, -detector.width]; vertices(2, :) = repmat(dist_origin_detector, 5, 1); vertices(3, :) = 0.5*[detector.height, -detector.height,... -detector.height, detector.height, detector.height]; detector.vertices = vertices; plot3(h_ax, detector.vertices(1, :), detector.vertices(2, :),... detector.vertices(3, :), options.DetectorLineColor); end end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/draw_parallel3d_vec_geom.m000066400000000000000000000102231475635207100300120ustar00rootroot00000000000000function [ ] = draw_parallel3d_vec_geom( h_ax, geom, options) %% draw_parallel3d_vec_geom.m % draw an astra parallel3d projection geometry % param h_ax handle to axis to draw into % param geom the geometry to draw % param options struct containing the options for this function % idx index of the vector to draw in more detail % SourceDistance distance of the source to the origin % % date 22.06.2018 % author Tim Elberfeld % imec VisionLab % University of Antwerp %% vectors = geom.Vectors; % source xray_source = vectors(:, 1:3)*options.SourceDistance; % center of detector detector_center = vectors(:, 4:6); % draw the points and connect with lines hold on; num_angles = size(vectors, 1); scatter3(h_ax, xray_source(:, 1), xray_source(:, 2),... xray_source(:, 3), options.SourceMarkerColor,... options.SourceMarker); scatter3(h_ax, detector_center(:, 1), detector_center(:, 2),... detector_center(:, 3), 'k.'); detector = struct; detector.u = vectors(options.VectorIdx, 7:9); detector.v = vectors(options.VectorIdx, 10:12); detector.height = geom.DetectorColCount; detector.width = geom.DetectorRowCount; detector.origin = detector_center(options.VectorIdx, :); detector.vertices = draw.draw_detector_vec(h_ax, detector, options); connect_source_detector(h_ax, detector, detector_center, xray_source,... options); % rotation axis will be roughly as long as the source detector distance distances = eucl_dist3d(detector_center, xray_source); mean_sdd = mean(distances(:)); % mean source detector distance draw_rotation_axis(h_ax, mean_sdd, options); text(h_ax, xray_source(options.VectorIdx, 1),... xray_source(options.VectorIdx, 2), ... xray_source(options.VectorIdx, 3), 'x-ray source'); text(h_ax, detector_center(options.VectorIdx, 1),... detector_center(options.VectorIdx, 2),... detector_center(options.VectorIdx, 3), 'detector'); hold off; function [] = connect_source_detector(h_ax, detector,... detector_center, xray_source, options) % connect source to detector origin idx = options.VectorIdx; line(h_ax, [detector_center(idx, 1), xray_source(idx, 1)],... [detector_center(idx, 2), xray_source(idx, 2)],... [detector_center(idx, 3), xray_source(idx, 3)],... 'Color', options.OpticalAxisColor, 'LineStyle', '--'); % compute normal of detector plane n = null([detector.u; detector.v]); % connect source to detector edges for kk = 1:4 a = detector.vertices(1, kk) - n(1)*xray_source(idx, 1); b = detector.vertices(2, kk) - n(2)*xray_source(idx, 2); c = detector.vertices(3, kk) - n(3)*xray_source(idx, 3); line(h_ax,[a, detector.vertices(1, kk)], ... [b, detector.vertices(2, kk)],... [c, detector.vertices(3, kk)],... 'Color', 'k',... 'LineStyle', ':'); end end function [] = draw_rotation_axis(h_ax, scaling, options) % draw rotation axis rot_axis = options.RotationAxis; if(~isnan(rot_axis(1))) rot_axis = options.RotationAxis + options.RotationAxisOffset; origin = options.RotationAxisOffset; % origin of the geometry is assumed to be [0, 0, 0] always! line(h_ax, [origin(1), (scaling/2)*rot_axis(1)],... [origin(2), (scaling/2)*rot_axis(2)],... [origin(3), (scaling/2)*rot_axis(3)],... 'Color', options.OpticalAxisColor,... 'LineStyle', '-.'); line(h_ax, [origin(1), -(scaling/2)*rot_axis(1)],... [origin(2), -(scaling/2)*rot_axis(2)],... [origin(3), -(scaling/2)*rot_axis(3)],... 'Color', options.OpticalAxisColor,... 'LineStyle', '-.'); end end end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/draw_proj_geom.m000066400000000000000000000144321475635207100261120ustar00rootroot00000000000000function [] = draw_proj_geom(geom, varargin) %% draw_proj_geom.m % brief rendering function for astra geometries. % param geom the geometry to plot. If geometry type % is not supported, throws error % ------------------------------ % optional parameters that can be provided as string value pairs: % % param RotationAxis if specified, will change the drawn % rotation axis to provided axis. % Must be 3-vector. Default value is % [NaN, NaN, NaN], (meaning do not draw). % param RotationAxisOffset if specified, will translate the drawn % rotation axis by the provided vector. % Default = [0, 0, 0] % param VectorIdx index of the vector to visualize if geom % is a vector geometry type. Default = 1 % param Color Color for all markers and lines if not % otherwise specified % param DetectorMarker marker for the detector locations. % Default = '.' % param DetectorMarkerColor color specifier for the detector marker. % Default = 'k' % param DetectorLineColor color for the lines drawing the detector % outline % param DetectorLineWidth line width of detector rectangle % param SourceMarker marker for the source locations % param SourceMarkerColor color specifier for the source marker % param SourceDistance (only for parallel3d and parallel3d_vec) % distance of source to origin % param OpticalAxisColor Color for drawing the optical axis % % date 20.06.2018 % author Tim Elberfeld % imec VisionLab % University of Antwerp % % - last update 07.11.2018 %% h_ax = gca; options = parseoptions(varargin); switch geom.type case 'parallel3d' disp('type: parallel3d') disp(['detector spacing: [' num2str(geom.DetectorSpacingX), ', '... num2str(geom.DetectorSpacingY) ']']); disp(['detector px: [' num2str(geom.DetectorRowCount), ', ' ... num2str(geom.DetectorColCount) ']']); disp(['angle lo: ' num2str(geom.ProjectionAngles(1))]); disp(['angle hi: ' num2str(geom.ProjectionAngles(end))]); disp(['# angles: ' num2str(numel(geom.ProjectionAngles))]); disp('DistanceOriginDetector inf'); disp('DistanceOriginSource inf'); draw.draw_parallel3d_geom(h_ax, geom, options); case 'parallel3d_vec' disp('type: parallel3d_vec') disp(['detector px: [' num2str(geom.DetectorRowCount), ', '... num2str(geom.DetectorColCount) ']']); disp(['# angles: ' num2str(size(geom.Vectors, 1))]); draw.draw_parallel3d_vec_geom(h_ax, geom, options); case 'cone' disp('type: cone'); disp(['detector spacing: [' num2str(geom.DetectorSpacingX), ', '... num2str(geom.DetectorSpacingY) ']']); disp(['detector px: [' num2str(geom.DetectorRowCount), ', ' ... num2str(geom.DetectorColCount) ']']); disp(['angle lo: ' num2str(geom.ProjectionAngles(1))]); disp(['angle hi: ' num2str(geom.ProjectionAngles(end))]); disp(['# angles: ' num2str(numel(geom.ProjectionAngles))]); disp(['DistanceOriginDetector ' num2str(geom.DistanceOriginDetector)]); disp(['DistanceOriginSource ' num2str(geom.DistanceOriginSource)]); draw.draw_cone_geom(h_ax, geom, options); case 'cone_vec' disp('type: cone_vec'); disp(['detector px: [' num2str(geom.DetectorRowCount), ', ' ... num2str(geom.DetectorColCount) ']']); disp(['# angles: ' num2str(size(geom.Vectors, 1))]); draw.draw_cone_vec_geom(h_ax, geom, options); case 'fanflat' disp('type: fanflat'); disp(['detector px: ' num2str(geom.DetectorCount)]); disp(['angle lo: ' num2str(geom.ProjectionAngles(1))]); disp(['angle hi: ' num2str(geom.ProjectionAngles(end))]); disp(['# angles: ' num2str(numel(geom.ProjectionAngles))]); disp(['DistanceOriginDetector '... num2str(geom.DistanceOriginDetector)]); disp(['DistanceOriginSource '... num2str(geom.DistanceOriginSource)]); draw.draw_fanflat_geom(h_ax, geom, options); case 'fanflat_vec' disp('type: fanflat_vec'); disp(['detector px: ' num2str(geom.DetectorCount)]); disp(['# angles: ' num2str(size(geom.Vectors, 1))]); draw.draw_fanflat_vec_geom(h_ax, geom, options); otherwise error(['Unknown geometry type ' geom.type]) end view(45, 25); % gives nicer default view angle function [options] = parseoptions(input_args) % make an options struct options = struct; options.RotationAxis = [NaN, NaN, NaN]; options.RotationAxisOffset = [0, 0, 0]; options.VectorIdx = 1; options.Color = 'k'; options.DetectorMarker = '.'; options.DetectorMarkerColor = ''; options.DetectorLineColor = ''; options.DetectorLineWidth = 1; options.SourceMarker = '*'; options.SourceMarkerColor = ''; options.SourceDistance = 100; options.OpticalAxisColor = ''; options = parseargs.parseargs(options, input_args{:}); % if the color is still empty, replace by global color if strcmpi(options.DetectorMarkerColor , '') options.DetectorMarkerColor = options.Color; end if strcmpi(options.DetectorLineColor , '') options.DetectorLineColor = options.Color; end if strcmpi(options.SourceMarkerColor , '') options.SourceMarkerColor = options.Color; end if strcmpi(options.OpticalAxisColor , '') options.OpticalAxisColor = options.Color; end end end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/draw_vol_geom.m000066400000000000000000000102271475635207100257360ustar00rootroot00000000000000function [] = draw_vol_geom( vol_geom, varargin) %% draw_vol_geom.m % brief rendering function for astra volume geometries % describing a phantom. % param vol_geom volume geometry describing the phantom % param vx_size voxel size in unit of preference. must be same unit % that was used to scale the projection geometry % ------------------------------ % optional parameters that can be provided as string value pairs: % % param Magnification magnification factor for the phantom. for small % phantoms it might be necessary to scale the render % up as otherwise it won't show up in the plot. % Default = 1 % param LineWidth line width for the box wireframe. Default = 2 % param Color color of the wireframe. Default = 'r' % % date 20.06.2018 % author Tim Elberfeld % imec VisionLab % University of Antwerp % % - last update 16.11.2018 %% h_ax = gca; if mod(size(varargin), 2) ~= 0 vx_size = varargin{1}; varargin = varargin(2:end); % consumed vx_size from arg list else vx_size = 1; end options = struct; options.Color = 'r'; options.LineWidth = 2; options.Magnification = 1; options = parseargs.parseargs(options, varargin{:}); hold on; phantom_height = vol_geom.GridRowCount * vx_size; phantom_width = vol_geom.GridColCount * vx_size; phantom_depth = vol_geom.GridSliceCount * vx_size; if isfield(vol_geom, 'option') minx = vol_geom.option.WindowMinX * vx_size; maxx = vol_geom.option.WindowMaxX * vx_size; miny = vol_geom.option.WindowMinY * vx_size; maxy = vol_geom.option.WindowMaxY * vx_size; minz = vol_geom.option.WindowMinZ * vx_size; maxz = vol_geom.option.WindowMaxZ * vx_size; else minx = phantom_width / 2 * vx_size; maxx = phantom_width / 2 * vx_size; miny = phantom_height / 2 * vx_size; maxy = phantom_height / 2 * vx_size; minz = phantom_depth / 2 * vx_size; maxz = phantom_depth / 2 * vx_size; end xx_phantom = options.Magnification*[minx, minx, minx, minx, maxx, maxx, maxx, maxx]; yy_phantom = options.Magnification*[miny, miny, maxy, maxy, miny, miny, maxy, maxy]; zz_phantom = options.Magnification*[minz, maxz, minz, maxz, minz, maxz, minz, maxz]; face1 = [xx_phantom(1:4); yy_phantom(1:4); zz_phantom(1:4)]; face2 = [[xx_phantom(1:2), xx_phantom(5:6)];... [yy_phantom(1:2), yy_phantom(5:6)];... [zz_phantom(1:2), zz_phantom(5:6)]]; face3 = [[xx_phantom(3:4), xx_phantom(7:8)];... [yy_phantom(3:4), yy_phantom(7:8)];... [zz_phantom(3:4), zz_phantom(7:8)]]; face4 = [[xx_phantom(5:6), xx_phantom(7:8)];... [yy_phantom(5:6), yy_phantom(7:8)];... [zz_phantom(5:6), zz_phantom(7:8)]]; % as we draw only a wire frame, we need only to draw 4 of the faces draw_face(h_ax, face1, options); draw_face(h_ax, face2, options); draw_face(h_ax, face3, options); draw_face(h_ax, face4, options); hold off; function [] = draw_face(h_ax, face_coords, options) line(h_ax, face_coords(1, 1:2), face_coords(2, 1:2),... face_coords(3, 1:2), 'LineWidth', options.LineWidth,... 'Color', options.Color); line(h_ax, face_coords(1, 3:4), face_coords(2, 3:4),... face_coords(3, 3:4), 'LineWidth', options.LineWidth,... 'Color', options.Color); line(h_ax, [face_coords(1, 4),face_coords(1, 2)],... [face_coords(2, 4),face_coords(2, 2)],... [face_coords(3, 4),face_coords(3, 2)],... 'LineWidth', options.LineWidth, 'Color', options.Color); line(h_ax, [face_coords(1, 1),face_coords(1, 3)],... [face_coords(2, 1),face_coords(2, 3)],... [face_coords(3, 1),face_coords(3, 3)],... 'LineWidth', options.LineWidth, 'Color', options.Color); end end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/private/000077500000000000000000000000001475635207100244045ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+draw/private/eucl_dist3d.m000066400000000000000000000007521475635207100267700ustar00rootroot00000000000000function [dist] = eucl_dist3d(a, b) %% eucl_dist3d.m % 3d euclidean distance for a nx3 matrix holding n 3-vectors % param a - first vectors % param b - second vectors % date 07.11.2018 % author Tim Elberfeld % imec VisionLab % University of Antwerp % last update 07.11.2018 %% dist = sqrt((a(:, 1) - b(:, 1)).^2 + ... (a(:, 2) - b(:, 2)).^2 + ... (a(:, 3) - b(:, 3)).^2); end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+parseargs/000077500000000000000000000000001475635207100237645ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+parseargs/license.txt000066400000000000000000000027661475635207100261620ustar00rootroot00000000000000Copyright (c) 2016, The MathWorks, Inc. All rights reserved. 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. * In all cases, the software is, and all modifications and derivatives of the software shall be, licensed to you solely for use in conjunction with MathWorks products and service offerings. 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. astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+parseargs/parseargs.m000066400000000000000000000072111475635207100261320ustar00rootroot00000000000000function X = parseargs(X,varargin) %PARSEARGS - Parses name-value pairs % % Behaves like setfield, but accepts multiple name-value pairs and provides % some additional features: % 1) If any field of X is an cell-array of strings, it can only be set to % one of those strings. If no value is specified for that field, the % first string is selected. % 2) Where the field is not empty, its data type cannot be changed % 3) Where the field contains a scalar, its size cannot be changed. % % X = parseargs(X,name1,value1,name2,value2,...) % % Intended for use as an argument parser for functions which multiple options. % Example usage: % % function my_function(varargin) % X.StartValue = 0; % X.StopOnError = false; % X.SolverType = {'fixedstep','variablestep'}; % X.OutputFile = 'out.txt'; % X = parseargs(X,varargin{:}); % % Then call (e.g.): % % my_function('OutputFile','out2.txt','SolverType','variablestep'); % The various #ok comments below are to stop MLint complaining about % inefficient usage. In all cases, the inefficient usage (of error, getfield, % setfield and find) is used to ensure compatibility with earlier versions % of MATLAB. % Copyright 2006-2010 The MathWorks, Inc. remaining = nargin-1; % number of arguments other than X count = 1; fields = fieldnames(X); modified = zeros(size(fields)); % Take input arguments two at a time until we run out. while remaining>=2 fieldname = varargin{count}; fieldind = find(strcmp(fieldname,fields)); if ~isempty(fieldind) oldvalue = getfield(X,fieldname); %#ok newvalue = varargin{count+1}; if iscell(oldvalue) % Cell arrays must contain strings, and the new value must be % a string which appears in the list. if ~iscellstr(oldvalue) error(sprintf('All allowed values for "%s" must be strings',fieldname)); %#ok end if ~ischar(newvalue) error(sprintf('New value for "%s" must be a string',fieldname)); %#ok end if isempty(find(strcmp(oldvalue,newvalue))) %#ok error(sprintf('"%s" is not allowed for field "%s"',newvalue,fieldname)); %#ok end elseif ~isempty(oldvalue) % The caller isn't allowed to change the data type of a non-empty property, % and scalars must remain as scalars. if ~strcmp(class(oldvalue),class(newvalue)) error(sprintf('Cannot change class of field "%s" from "%s" to "%s"',... fieldname,class(oldvalue),class(newvalue))); %#ok elseif numel(oldvalue)==1 & numel(newvalue)~=1 %#ok error(sprintf('New value for "%s" must be a scalar',fieldname)); %#ok end end X = setfield(X,fieldname,newvalue); %#ok modified(fieldind) = 1; else error(['Not a valid field name: ' fieldname]); end remaining = remaining - 2; count = count + 2; end % Check that we had a value for every name. if remaining~=0 error('Odd number of arguments supplied. Name-value pairs required'); end % Now find cell arrays which were not modified by the above process, and select % the first string. notmodified = find(~modified); for i=1:length(notmodified) fieldname = fields{notmodified(i)}; oldvalue = getfield(X,fieldname); %#ok if iscell(oldvalue) if ~iscellstr(oldvalue) error(sprintf('All allowed values for "%s" must be strings',fieldname)); %#ok elseif isempty(oldvalue) error(sprintf('Empty cell array not allowed for field "%s"',fieldname)); %#ok end X = setfield(X,fieldname,oldvalue{1}); %#ok end end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/000077500000000000000000000000001475635207100236205ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/license.txt000066400000000000000000000035411475635207100260060ustar00rootroot00000000000000Copyright (c) 2017, Pau Micó Copyright (c) 2015, Sven Holcombe Copyright (c) 2011, Eric Johnson Copyright (c) 2013, Adam H. Aitkenhead Copyright (c) 2011, Francis Esmonde-White All rights reserved. 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 the The MathWorks, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * Neither the name of the The Christie NHS Foundation Trust 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. astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/readme.txt000066400000000000000000000046071475635207100256250ustar00rootroot00000000000000The 'stlTools' toolbox is a collection of functions, samples and demos to illustrate how to deal with STL files. Some of them are contributions published in Matlab Central and properly referenced here. This toolbox contains the following files: stlGetFormat: identifies the format of the STL file and returns 'binary' or 'ascii'. This file is inspired in the 'READ-stl' file written and published by Adam H. Aitkenhead (http://www.mathworks.com/matlabcentral/fileexchange/27390-mesh-voxelisation). Copyright (c) 2013, Adam H. Aitkenhead. stlReadAscii: reads an STL file written in ascii format. This file is inspired in the 'READ-stl' file written and published by Adam H. Aitkenhead (http://www.mathworks.com/matlabcentral/fileexchange/27390-mesh-voxelisation). Copyright (c) 2013, Adam H. Aitkenhead stlReadBinary: reads an STL file written in binary format. This file is inspired in the 'READ-stl' file written and published by Adam H. Aitkenhead (http://www.mathworks.com/matlabcentral/fileexchange/27390-mesh-voxelisation). Copyright (c) 2013, Adam H. Aitkenhead stlRead: uses 'stlGetFormat', 'stlReadAscii' and 'stlReadBinary' to make STL reading independent of the format of the file stlWrite: writes an STL file in 'ascii' or 'binary' formats. This is written and published by Sven Holcombe (http://www.mathworks.com/matlabcentral/fileexchange/20922-stlwrite-filename--varargin-). Copyright (c) 2012, Grant Lohsen. Copyright (c) 2015, Sven Holcombe. stlSlimVerts: finds and removes duplicated vertices. This function is written and published by Francis Esmonde-White as PATCHSLIM (http://www.mathworks.com/matlabcentral/fileexchange/29986-patch-slim--patchslim-m-). Copyright (c) 2011, Francis Esmonde-White. stlGetVerts: returns a list of vertices that are 'opened' or 'closed' depending on the 'mode' input parameter. An 'open' vertice is the one that defines an open side. An open side is the one that only takes part of one triangle stlDelVerts: removes a list of vertices from STL files stlAddVerts: adds the new vertices from a list (and consequently, new faces) to a STL object stlPlot: is an easy way to plot an STL object stlDemo: is a collection of examples about how to use stlTools femur_binary: is an ascii STL sample used in 'stlDemo'. It is published by Eric Johnson (http://www.mathworks.com/matlabcentral/fileexchange/22409-stl-file-reader). Copyright (c) 2011, Eric Johnson. sphere_ascii: is a binary STL sampleastra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/stlAddVerts.m000066400000000000000000000012271475635207100262370ustar00rootroot00000000000000function [vnew, fnew] = stlAddVerts(v, f, list) %STLADDVERTS adds new vertices (and consequently, new faces) to a STL object %V is the Nx3 array of vertices %F is the Mx3 array of faces %LIST is the list of vertices to be added to the object %VNEW is the new array of vertices %FNEW is the new array of faces import stlTools.* % triangulation just with the slice faces = delaunay(list(:,1),list(:,2)); % calculate new faces % update object nvert = length(v); % number of original vertices v = [v; list]; % update vertices with the ones in the list f = [f; faces+nvert]; % update faces with the new ones [vnew,fnew] = stlSlimVerts(v,f); % clear repeated verticesastra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/stlDelVerts.m000066400000000000000000000014451475635207100262550ustar00rootroot00000000000000function [vnew, fnew] = stlDelVerts(v, f, list) %STLDELVERT removes a list of vertices from STL files %V is the Nx3 array of vertices %F is the Mx3 array of faces %LIST are the vertices (rows) to delete, where length(LIST) < N %VNEW is the new array of vertices %FNEW is the new array of faces % find (on the global set) the position (rows) of the vertices to be deleted [~,vdel] = ismember(list,v,'rows'); % delete vertices and get new tags vnew = v; tags = 1:length(v); vnew(vdel,:) = []; tags(vdel) = []; % delete faces fnew = f; [fdel,~] = find(ismember(f,vdel)); % find the position (rows) of the faces to delete fnew(fdel,:) = []; % rename faces, as some of the vertices have been deleted flist = reshape(fnew,numel(fnew),1); [~,ind] = ismember(flist,tags); fnew = reshape(ind,numel(flist)/3,3);astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/stlDemo.m000066400000000000000000000025171475635207100254120ustar00rootroot00000000000000%% STLDEMO shows how to use the functions included in the toolbox STLTOOLS import stlTools.* %% EXAMPLE 1.- How to cut a sphere and close the base to get a semisphere % load an ascii STL sample file (STLGETFORMAT and STLREADASCII) [vertices,faces,normals,name] = stlRead('sphere300faces.stl'); stlPlot(vertices,faces,name); % the sphere is centered in the origin % now we get a list of vertices to be deleted if (x,y,z<0) minZ = 0; [rows, ~] = find(vertices(:,3) < minZ); list = vertices(rows,:); % if we delete the list of vertices with z<0, we get an opened semisphere % (as the base is not closed) [newv,newf] = stlDelVerts(vertices,faces,list); stlPlot(newv,newf,name); % the next step is to identify a new list with the faces that are opened % (that means all the sides that belong only to a unique triangle) list = stlGetVerts(newv,newf,'opened'); % finally we generate all the new faces that are needed just to close the % base of the semisphere [vsemi,fsemi] = stlAddVerts(newv,newf,list); stlPlot(vsemi,fsemi,'closed semisphere'); %% EXAMPLE 2.- How to get a section of a femur [vertices,faces,normals,name] = stlRead('femur_binary.stl'); stlPlot(vertices,faces,name); minX = 1.2; [rows, ~] = find(vertices(:,1) < minX); list = vertices(rows,:); [newv,newf] = stlDelVerts(vertices,faces,list); stlPlot(newv,newf,'section of the femur'); astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/stlGetFormat.m000066400000000000000000000032271475635207100264150ustar00rootroot00000000000000function format = stlGetFormat(fileName) %STLGETFORMAT identifies the format of the STL file and returns 'binary' or %'ascii' fid = fopen(fileName); % Check the file size first, since binary files MUST have a size of 84+(50*n) fseek(fid,0,1); % Go to the end of the file fidSIZE = ftell(fid); % Check the size of the file if rem(fidSIZE-84,50) > 0 format = 'ascii'; else % Files with a size of 84+(50*n), might be either ascii or binary... % Read first 80 characters of the file. % For an ASCII file, the data should begin immediately (give or take a few % blank lines or spaces) and the first word must be 'solid'. % For a binary file, the first 80 characters contains the header. % It is bad practice to begin the header of a binary file with the word % 'solid', so it can be used to identify whether the file is ASCII or % binary. fseek(fid,0,-1); % go to the beginning of the file header = strtrim(char(fread(fid,80,'uchar')')); % trim leading and trailing spaces isSolid = strcmp(header(1:min(5,length(header))),'solid'); % take first 5 char fseek(fid,-80,1); % go to the end of the file minus 80 characters tail = char(fread(fid,80,'uchar')'); isEndSolid = findstr(tail,'endsolid'); % Double check by reading the last 80 characters of the file. % For an ASCII file, the data should end (give or take a few % blank lines or spaces) with 'endsolid '. % If the last 80 characters contains the word 'endsolid' then this % confirms that the file is indeed ASCII. if isSolid & isEndSolid format = 'ascii'; else format = 'binary'; end end fclose(fid);astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/stlGetVerts.m000066400000000000000000000020531475635207100262640ustar00rootroot00000000000000function list = stlGetVerts(v, f, mode) %GETVERTS returns the vertices that are 'opened' or 'closed' depending on %the 'mode'. An 'open' vertice is the one that defines an open side. An %open side is the one that only takes part of one triangle %V is the Nx3 array of vertices %F is the Mx3 array of faces %MODE can be 'opened' or 'closed' depending of the kind of vertices to list %LIST is the list of 'opened' or 'closed' vertices sides = sort([[f(:,1) f(:,2)]; ... [f(:,2) f(:,3)]; ... [f(:,3) f(:,1)]],2); [C,ia,ic] = unique(sides,'rows'); ind_all = sort(ic); % open and closed sides ind_rep = find(diff(ind_all) == 0); ind_cls = ind_all(ind_rep); % closed sides sides_cls = C(ind_cls,:); ind_rep = [ind_rep; ind_rep+1]; ind_opn = ind_all; ind_opn(ind_rep) = []; % open sides sides_opn = C(ind_opn,:); switch mode, case'opened', list = v(unique(sides_opn(:)),:); case 'closed', list = v(unique(sides_cls(:)),:); otherwise, error('getVerts:InvalidMode','The ''mode'' valid values are ''opened'' or ''closed'''); endastra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/stlPlot.m000066400000000000000000000012001475635207100254300ustar00rootroot00000000000000function stlPlot(v, f, name) %STLPLOT is an easy way to plot an STL object %V is the Nx3 array of vertices %F is the Mx3 array of faces %NAME is the name of the object, that will be displayed as a title figure; object.vertices = v; object.faces = f; patch(object,'FaceColor', [0.8 0.8 1.0], ... 'EdgeColor', 'none', ... 'FaceLighting', 'gouraud', ... 'AmbientStrength', 0.15); % Add a camera light, and tone down the specular highlighting camlight('headlight'); material('dull'); % Fix the axes scaling, and set a nice view angle axis('image'); view([-135 35]); grid on; title(name); astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/stlRead.m000066400000000000000000000006441475635207100254000ustar00rootroot00000000000000function [v, f, n, name] = stlRead(fileName) %STLREAD reads any STL file not depending on its format %V are the vertices %F are the faces %N are the normals %NAME is the name of the STL object (NOT the name of the STL file) import stlTools.* format = stlGetFormat(fileName); if strcmp(format,'ascii') [v,f,n,name] = stlReadAscii(fileName); elseif strcmp(format,'binary') [v,f,n,name] = stlReadBinary(fileName); endastra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/stlReadAscii.m000066400000000000000000000031271475635207100263500ustar00rootroot00000000000000function [v, f, n, name] = stlReadAscii(fileName) %STLREADASCII reads a STL file written in ASCII format %V are the vertices %F are the faces %N are the normals %NAME is the name of the STL object (NOT the name of the STL file) import stlTools.* %====================== % STL ascii file format %====================== % ASCII STL files have the following structure. Technically each facet % could be any 2D shape, but in practice only triangular facets tend to be % used. The present code ONLY works for meshes composed of triangular % facets. % % solid object_name % facet normal x y z % outer loop % vertex x y z % vertex x y z % vertex x y z % endloop % endfacet % % % % endsolid object_name fid = fopen(fileName); cellcontent = textscan(fid,'%s','delimiter','\n'); % read all the file and put content in cells content = cellcontent{:}(logical(~strcmp(cellcontent{:},''))); % remove all blank lines fclose(fid); % read the STL name line1 = char(content(1)); if (size(line1,2) >= 7) name = line1(7:end); else name = 'Unnamed Object'; end % read the vector normals normals = char(content(logical(strncmp(content,'facet normal',12)))); n = str2num(normals(:,13:end)); % read the vertex coordinates (vertices) vertices = char(content(logical(strncmp(content,'vertex',6)))); v = str2num(vertices(:,7:end)); nvert = size(vertices,1); % number of vertices nfaces = sum(strcmp(content,'endfacet')); % number of faces if (nvert == 3*nfaces) f = reshape(1:nvert,[3 nfaces])'; % create faces end % slim the file (delete duplicated vertices) [v,f] = stlSlimVerts(v,f);astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/stlReadBinary.m000066400000000000000000000043441475635207100265460ustar00rootroot00000000000000function [v, f, n, name] = stlReadBinary(fileName) %STLREADBINARY reads a STL file written in BINARY format %V are the vertices %F are the faces %N are the normals %NAME is the name of the STL object (NOT the name of the STL file) import stlTools.* %======================= % STL binary file format %======================= % Binary STL files have an 84 byte header followed by 50-byte records, each % describing a single facet of the mesh. Technically each facet could be % any 2D shape, but that would screw up the 50-byte-per-facet structure, so % in practice only triangular facets are used. The present code ONLY works % for meshes composed of triangular facets. % % HEADER: % 80 bytes: Header text % 4 bytes: (int) The number of facets in the STL mesh % % DATA: % 4 bytes: (float) normal x % 4 bytes: (float) normal y % 4 bytes: (float) normal z % 4 bytes: (float) vertex1 x % 4 bytes: (float) vertex1 y % 4 bytes: (float) vertex1 z % 4 bytes: (float) vertex2 x % 4 bytes: (float) vertex2 y % 4 bytes: (float) vertex2 z % 4 bytes: (float) vertex3 x % 4 bytes: (float) vertex3 y % 4 bytes: (float) vertex3 z % 2 bytes: Padding to make the data for each facet 50-bytes in length % ...and repeat for next facet... fid = fopen(fileName); header = fread(fid,80,'int8'); % reading header's 80 bytes name = deblank(native2unicode(header,'ascii')'); if isempty(name) name = 'Unnamed Object'; % no object name in binary files! end nfaces = fread(fid,1,'int32'); % reading the number of facets in the stl file (next 4 byters) nvert = 3*nfaces; % number of vertices % reserve memory for vectors (increase the processing speed) n = zeros(nfaces,3); v = zeros(nvert,3); f = zeros(nfaces,3); for i = 1 : nfaces % read the data for each facet tmp = fread(fid,3*4,'float'); % read coordinates n(i,:) = tmp(1:3); % x,y,z components of the facet's normal vector v(3*i-2,:) = tmp(4:6); % x,y,z coordinates of vertex 1 v(3*i-1,:) = tmp(7:9); % x,y,z coordinates of vertex 2 v(3*i,:) = tmp(10:12); % x,y,z coordinates of vertex 3 f(i,:) = [3*i-2 3*i-1 3*i]; % face fread(fid,1,'int16'); % Move to the start of the next facet (2 bytes of padding) end fclose(fid); % slim the file (delete duplicated vertices) [v,f] = stlSlimVerts(v,f);astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/stlSlimVerts.m000066400000000000000000000015571475635207100264610ustar00rootroot00000000000000function [vnew, fnew]= stlSlimVerts(v, f) % PATCHSLIM removes duplicate vertices in surface meshes. % % This function finds and removes duplicate vertices. % % USAGE: [v, f]=patchslim(v, f) % % Where v is the vertex list and f is the face list specifying vertex % connectivity. % % v contains the vertices for all triangles [3*n x 3]. % f contains the vertex lists defining each triangle face [n x 3]. % % This will reduce the size of typical v matrix by about a factor of 6. % % For more information see: % http://www.esmonde-white.com/home/diversions/matlab-program-for-loading-stl-files % % Francis Esmonde-White, May 2010 if ~exist('v','var') error('The vertex list (v) must be specified.'); end if ~exist('f','var') error('The vertex connectivity of the triangle faces (f) must be specified.'); end [vnew, indexm, indexn] = unique(v, 'rows'); fnew = indexn(f); astra-toolbox-2.3.0/matlab/algorithms/plot_geom/+stlTools/stlWrite.m000066400000000000000000000234621475635207100256220ustar00rootroot00000000000000function stlWrite(filename, varargin) %STLWRITE Write STL file from patch or surface data. % % STLWRITE(FILE, FV) writes a stereolithography (STL) file to FILE for a % triangulated patch defined by FV (a structure with fields 'vertices' % and 'faces'). % % STLWRITE(FILE, FACES, VERTICES) takes faces and vertices separately, % rather than in an FV struct % % STLWRITE(FILE, X, Y, Z) creates an STL file from surface data in X, Y, % and Z. STLWRITE triangulates this gridded data into a triangulated % surface using triangulation options specified below. X, Y and Z can be % two-dimensional arrays with the same size. If X and Y are vectors with % length equal to SIZE(Z,2) and SIZE(Z,1), respectively, they are passed % through MESHGRID to create gridded data. If X or Y are scalar values, % they are used to specify the X and Y spacing between grid points. % % STLWRITE(...,'PropertyName',VALUE,'PropertyName',VALUE,...) writes an % STL file using the following property values: % % MODE - File is written using 'binary' (default) or 'ascii'. % % TITLE - Header text (max 80 chars) written to the STL file. % % TRIANGULATION - When used with gridded data, TRIANGULATION is either: % 'delaunay' - (default) Delaunay triangulation of X, Y % 'f' - Forward slash division of grid quads % 'b' - Back slash division of quadrilaterals % 'x' - Cross division of quadrilaterals % Note that 'f', 'b', or 't' triangulations now use an % inbuilt version of FEX entry 28327, "mesh2tri". % % FACECOLOR - Single colour (1-by-3) or one-colour-per-face (N-by-3) % vector of RGB colours, for face/vertex input. RGB range % is 5 bits (0:31), stored in VisCAM/SolidView format % (http://en.wikipedia.org/wiki/STL_(file_format)#Color_in_binary_STL) % % Example 1: % % Write binary STL from face/vertex data % tmpvol = false(20,20,20); % Empty voxel volume % tmpvol(8:12,8:12,5:15) = 1; % Turn some voxels on % fv = isosurface(~tmpvol, 0.5); % Make patch w. faces "out" % stlwrite('test.stl',fv) % Save to binary .stl % % Example 2: % % Write ascii STL from gridded data % [X,Y] = deal(1:40); % Create grid reference % Z = peaks(40); % Create grid height % stlwrite('test.stl',X,Y,Z,'mode','ascii') % % Example 3: % % Write binary STL with coloured faces % cVals = fv.vertices(fv.faces(:,1),3); % Colour by Z height. % cLims = [min(cVals) max(cVals)]; % Transform height values % nCols = 255; cMap = jet(nCols); % onto an 8-bit colour map % fColsDbl = interp1(linspace(cLims(1),cLims(2),nCols),cMap,cVals); % fCols8bit = fColsDbl*255; % Pass cols in 8bit (0-255) RGB triplets % stlwrite('testCol.stl',fv,'FaceColor',fCols8bit) % Original idea adapted from surf2stl by Bill McDonald. Huge speed % improvements implemented by Oliver Woodford. Non-Delaunay triangulation % of quadrilateral surface courtesy of Kevin Moerman. FaceColor % implementation by Grant Lohsen. % % Author: Sven Holcombe, 11-24-11 % Check valid filename path path = fileparts(filename); if ~isempty(path) && ~exist(path,'dir') error('Directory "%s" does not exist.',path); end % Get faces, vertices, and user-defined options for writing [faces, vertices, options] = parseInputs(varargin{:}); asciiMode = strcmp( options.mode ,'ascii'); % Create the facets facets = single(vertices'); facets = reshape(facets(:,faces'), 3, 3, []); % Compute their normals V1 = squeeze(facets(:,2,:) - facets(:,1,:)); V2 = squeeze(facets(:,3,:) - facets(:,1,:)); normals = V1([2 3 1],:) .* V2([3 1 2],:) - V2([2 3 1],:) .* V1([3 1 2],:); clear V1 V2 normals = bsxfun(@times, normals, 1 ./ sqrt(sum(normals .* normals, 1))); facets = cat(2, reshape(normals, 3, 1, []), facets); clear normals % Open the file for writing permissions = {'w','wb+'}; fid = fopen(filename, permissions{asciiMode+1}); if (fid == -1) error('stlwrite:cannotWriteFile', 'Unable to write to %s', filename); end % Write the file contents if asciiMode % Write HEADER fprintf(fid,'solid %s\r\n',options.title); % Write DATA fprintf(fid,[... 'facet normal %.7E %.7E %.7E\r\n' ... 'outer loop\r\n' ... 'vertex %.7E %.7E %.7E\r\n' ... 'vertex %.7E %.7E %.7E\r\n' ... 'vertex %.7E %.7E %.7E\r\n' ... 'endloop\r\n' ... 'endfacet\r\n'], facets); % Write FOOTER fprintf(fid,'endsolid %s\r\n',options.title); else % BINARY % Write HEADER fprintf(fid, '%-80s', options.title); % Title fwrite(fid, size(facets, 3), 'uint32'); % Number of facets % Write DATA % Add one uint16(0) to the end of each facet using a typecasting trick facets = reshape(typecast(facets(:), 'uint16'), 12*2, []); % Set the last bit to 0 (default) or supplied RGB facets(end+1,:) = options.facecolor; fwrite(fid, facets, 'uint16'); end % Close the file fclose(fid); %fprintf('Wrote %d facets\n',size(facets, 2)); %% Input handling subfunctions function [faces, vertices, options] = parseInputs(varargin) % Determine input type if isstruct(varargin{1}) % stlwrite('file', FVstruct, ...) if ~all(isfield(varargin{1},{'vertices','faces'})) error( 'Variable p must be a faces/vertices structure' ); end faces = varargin{1}.faces; vertices = varargin{1}.vertices; options = parseOptions(varargin{2:end}); elseif isnumeric(varargin{1}) firstNumInput = cellfun(@isnumeric,varargin); firstNumInput(find(~firstNumInput,1):end) = 0; % Only consider numerical input PRIOR to the first non-numeric numericInputCnt = nnz(firstNumInput); options = parseOptions(varargin{numericInputCnt+1:end}); switch numericInputCnt case 3 % stlwrite('file', X, Y, Z, ...) % Extract the matrix Z Z = varargin{3}; % Convert scalar XY to vectors ZsizeXY = fliplr(size(Z)); for i = 1:2 if isscalar(varargin{i}) varargin{i} = (0:ZsizeXY(i)-1) * varargin{i}; end end % Extract X and Y if isequal(size(Z), size(varargin{1}), size(varargin{2})) % X,Y,Z were all provided as matrices [X,Y] = varargin{1:2}; elseif numel(varargin{1})==ZsizeXY(1) && numel(varargin{2})==ZsizeXY(2) % Convert vector XY to meshgrid [X,Y] = meshgrid(varargin{1}, varargin{2}); else error('stlwrite:badinput', 'Unable to resolve X and Y variables'); end % Convert to faces/vertices if strcmp(options.triangulation,'delaunay') faces = delaunay(X,Y); vertices = [X(:) Y(:) Z(:)]; else if ~exist('mesh2tri','file') error('stlwrite:missing', '"mesh2tri" is required to convert X,Y,Z matrices to STL. It can be downloaded from:\n%s\n',... 'http://www.mathworks.com/matlabcentral/fileexchange/28327') end [faces, vertices] = mesh2tri(X, Y, Z, options.triangulation); end case 2 % stlwrite('file', FACES, VERTICES, ...) faces = varargin{1}; vertices = varargin{2}; otherwise error('stlwrite:badinput', 'Unable to resolve input types.'); end end if ~isempty(options.facecolor) % Handle colour preparation facecolor = uint16(options.facecolor); %Set the Valid Color bit (bit 15) c0 = bitshift(ones(size(faces,1),1,'uint16'),15); %Red color (10:15), Blue color (5:9), Green color (0:4) c0 = bitor(bitshift(bitand(2^6-1, facecolor(:,1)),10),c0); c0 = bitor(bitshift(bitand(2^11-1, facecolor(:,2)),5),c0); c0 = bitor(bitand(2^6-1, facecolor(:,3)),c0); options.facecolor = c0; else options.facecolor = 0; end function options = parseOptions(varargin) IP = inputParser; IP.addParamValue('mode', 'binary', @ischar) IP.addParamValue('title', sprintf('Created by stlwrite.m %s',datestr(now)), @ischar); IP.addParamValue('triangulation', 'delaunay', @ischar); IP.addParamValue('facecolor',[], @isnumeric) IP.addParamValue('facecolour',[], @isnumeric) IP.parse(varargin{:}); options = IP.Results; if ~isempty(options.facecolour) options.facecolor = options.facecolour; end function [F,V]=mesh2tri(X,Y,Z,tri_type) % function [F,V]=mesh2tri(X,Y,Z,tri_type) % % Available from http://www.mathworks.com/matlabcentral/fileexchange/28327 % Included here for convenience. Many thanks to Kevin Mattheus Moerman % kevinmoerman@hotmail.com % 15/07/2010 %------------------------------------------------------------------------ [J,I]=meshgrid(1:1:size(X,2)-1,1:1:size(X,1)-1); switch tri_type case 'f'%Forward slash TRI_I=[I(:),I(:)+1,I(:)+1; I(:),I(:),I(:)+1]; TRI_J=[J(:),J(:)+1,J(:); J(:),J(:)+1,J(:)+1]; F = sub2ind(size(X),TRI_I,TRI_J); case 'b'%Back slash TRI_I=[I(:),I(:)+1,I(:); I(:)+1,I(:)+1,I(:)]; TRI_J=[J(:)+1,J(:),J(:); J(:)+1,J(:),J(:)+1]; F = sub2ind(size(X),TRI_I,TRI_J); case 'x'%Cross TRI_I=[I(:)+1,I(:); I(:)+1,I(:)+1; I(:),I(:)+1; I(:),I(:)]; TRI_J=[J(:),J(:); J(:)+1,J(:); J(:)+1,J(:)+1; J(:),J(:)+1]; IND=((numel(X)+1):numel(X)+prod(size(X)-1))'; F = sub2ind(size(X),TRI_I,TRI_J); F(:,3)=repmat(IND,[4,1]); Fe_I=[I(:),I(:)+1,I(:)+1,I(:)]; Fe_J=[J(:),J(:),J(:)+1,J(:)+1]; Fe = sub2ind(size(X),Fe_I,Fe_J); Xe=mean(X(Fe),2); Ye=mean(Y(Fe),2); Ze=mean(Z(Fe),2); X=[X(:);Xe(:)]; Y=[Y(:);Ye(:)]; Z=[Z(:);Ze(:)]; end V=[X(:),Y(:),Z(:)];astra-toolbox-2.3.0/matlab/algorithms/plot_geom/astra_create_example_cone.m000066400000000000000000000144241475635207100272610ustar00rootroot00000000000000function [ proj_geom ] = create_example_cone(type) %% create_example_cone.m % create an example standard cone beam geometry % param type type of geometry to create. provided as one of the % following strings (not case sensitive): % 'normal' - standard (non vector) geometry % 'vec' - example vector geometry % 'helix' - helical trajectory vector geometry % 'deform_vec' - deformed vector geometry example, % courtesy of Van Nguyen % return proj_geom - the geometry that was created % % date 21.06.2018 % author Tim Elberfeld, Alice Presenti, Van Nguyen % imec VisionLab % University of Antwerp % last mod 07.11.2018 %% if(nargin < 1) type = 'normal'; end if strcmpi(type, 'normal') % first, give measurements in mm det_spacing = [0.035, 0.035]; detector_px = [1200, 1200]; angles = linspace2(0, 2*pi, 100); source_origin = 30; origin_det = 200; phantom_size = 5; phantom_px = 150; % voxels for the phantom vx_size = phantom_size / phantom_px; % voxel size % now express all measurements in terms of the voxel size det_spacing = det_spacing ./ vx_size; origin_det = origin_det ./ vx_size; source_origin = source_origin ./ vx_size; proj_geom = astra_create_proj_geom('cone', det_spacing(1), ... det_spacing(2), detector_px(1), detector_px(2), angles,... source_origin, origin_det); elseif strcmpi(type, 'vec') proj_geom = create_standard_vec_geom(); elseif strcmpi(type, 'deform_vec') proj_geom = create_deform_vec_geom(); elseif strcmpi(type, 'helix') detRows = 220; detCols = 220; detPitchX = 10^-3 * 11; % microns to mm detPitchY = 10^-3 * 11; % microns to mm objSrcDist = 500; % mm rotStep = 3.6; % deg numAngles = 200; zTranslation = 0.5; % mm zDist = zTranslation * numAngles; vectors = zeros(numAngles, 12); translation = -zDist + (zTranslation * (0:(numAngles-1))); minAngle = 0; % just assume we start at 0 maxAngle = (numAngles * rotStep)/180 * pi; % convert deg to rad angles = linspace(minAngle, maxAngle, 200); % source position per angle vectors(:, 1) = sin(angles) * objSrcDist; vectors(:, 2) = -cos(angles) * objSrcDist; vectors(:, 3) = translation; % detector position per angle vectors(:, 4) = 0; vectors(:, 5) = 0; vectors(:, 6) = translation; % vector from detector pixel (0,0) to (0,1) vectors(:, 7) = cos(angles); vectors(:, 8) = sin(angles); vectors(:, 9) = zeros(numAngles, 1); % vector from detector pixel (0,0) to (1, 0) vectors(:, 10) = zeros(numAngles, 1); vectors(:, 11) = zeros(numAngles, 1); vectors(:, 12) = ones(numAngles, 1); proj_geom = astra_create_proj_geom('cone_vec', detCols, detRows, vectors); else proj_geom = create_standard_vec_geom(); end function [proj_geom, z_axis] = create_standard_vec_geom() % geometry settings taken from code from Alice Presenti settings = struct; settings.detectorPixelSize = 0.0748; % detector size and number of projections settings.projectionSize = [1536 1944 21]; settings.SOD = 679.238020; %[mm] settings.SDD = 791.365618; %[mm] settings.voxelSize = 1; %[mm] settings.gamma = linspace(0,300,21)*pi/180; S0 = zeros(settings.projectionSize(3), 12); Sorig = [-settings.SOD, 0, 0,... % the ray origin vector (source) (settings.SDD-settings.SOD), 0, 0,... % detector center 0, -settings.detectorPixelSize, 0,... % detector u axis 0, 0, settings.detectorPixelSize]; % detector v axis z_axis = [0, 0, 1]; for i = 1:settings.projectionSize(3) S0(i,:) = Sorig(:); S0(i,1:3) = rotate_around3d(S0(i,1:3),... z_axis, settings.gamma(i)); S0(i,4:6) = rotate_around3d(S0(i,4:6), z_axis,... settings.gamma(i)); S0(i,7:9) = rotate_around3d(S0(i,7:9), z_axis,... settings.gamma(i)); S0(i,10:12) = rotate_around3d(S0(i,10:12),... z_axis, settings.gamma(i)); end proj_geom = astra_create_proj_geom('cone_vec', ... settings.projectionSize(2), settings.projectionSize(1), S0); end function [proj_geom, z_axis] = create_deform_vec_geom() settings = struct; settings.detectorPixelSize = 0.0748; settings.projectionSize = [1536 1944 21]; settings.SOD = 679.238020; %[mm] settings.SDD = 791.365618; %[mm] settings.voxelSize = 1; %[mm] settings.gamma = linspace(0,300,21)*pi/180; S0 = zeros(settings.projectionSize(3), 12); Sorig = [-settings.SOD, 0, 0,... % the ray origin vector (source) (settings.SDD-settings.SOD), 0, 0,... % detector center 0, -settings.detectorPixelSize, 0,... % detector u axis 0, 0, settings.detectorPixelSize]; % detector v axis z_axis = [0, 0, 1]; for i = 1:settings.projectionSize(3) S0(i,:) = Sorig(:); end S0(:, 1:3) = translate_3d(S0(:, 1:3), [100, 150, 0]); S0(:, 4:6) = translate_3d(S0(:, 4:6), [100, 150, 0]); S0 = rotate_detector(S0, [0.48,0.32,0]); S0 = magnify_proj(S0, 100); for i = 1:settings.projectionSize(3) S0(i,1:3) = rotate_around3d(S0(i,1:3), z_axis,... settings.gamma(i)); S0(i,4:6) = rotate_around3d(S0(i,4:6), z_axis,... settings.gamma(i)); S0(i,7:9) = rotate_around3d(S0(i,7:9), z_axis,... settings.gamma(i)); S0(i,10:12) = rotate_around3d(S0(i,10:12), z_axis,... settings.gamma(i)); end proj_geom = astra_create_proj_geom('cone_vec',... settings.projectionSize(2), settings.projectionSize(1), S0); end end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/astra_create_example_fanflat.m000066400000000000000000000032011475635207100277370ustar00rootroot00000000000000function [ geom ] = create_example_fanflat( type ) %% create_example_fanflat.m % brief create an example geometry of type 'fanflat' % param type type of geometry to create. provided as one of the % following strings (not case sensitive): % 'normal' - standard fanflat geometry % 'vec' - example vector geometry % return proj_geom - the geometry that was created % date 22.06.2018 % author Tim Elberfeld % imec VisionLab % University of Antwerp % last mod 07.11.2018 %% if nargin < 1 type = 'normal'; end if strcmpi(type, 'normal') geom = make_normal_geometry(); elseif strcmpi(type, 'vec') geom = astra_create_example_fanflat('normal'); geom = astra_geom_2vec(geom); else geom = make_normal_geometry(); end function [geom] = make_normal_geometry() % first, give measurements in mm det_spacing = 0.035; detector_px = 1200; angles = linspace2(0, 2*pi, 100); source_origin = 30; origin_det = 200; phantom_size = 5; phantom_px = 150; % voxels for the phantom vx_size = phantom_size / phantom_px; % voxel size % now express all measurements in terms of the voxel size det_spacing = det_spacing ./ vx_size; origin_det = origin_det ./ vx_size; source_origin = source_origin ./ vx_size; geom = astra_create_proj_geom('fanflat', det_spacing, ... detector_px, angles, source_origin, origin_det); end end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/astra_create_example_parallel3d.m000066400000000000000000000017701475635207100303600ustar00rootroot00000000000000function [ proj_geom ] = create_example_parallel3d( type ) %% create_example_parallel3d.m % brief create an example geometry of type 'parallel3d' % param type type of geometry to create. provided as one of the % following strings (not case sensitive). % no arguments will create a standard fanflat geometry. % 'vec' - example vector geometry % return proj_geom - the geometry that was created % date 22.06.2018 % author Tim Elberfeld % imec VisionLab % University of Antwerp % last mod 07.11.2018 %% if nargin < 1 type = 'nothing'; end det_spacing = [0.035, 0.035]; detector_px = [1000, 1000]; angles = linspace(0, 2*pi, 100); proj_geom = astra_create_proj_geom('parallel3d', det_spacing(1),... det_spacing(2), detector_px(1), detector_px(2), angles); if strcmp(type, 'vec') proj_geom = astra_geom_2vec(proj_geom); end end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/private/000077500000000000000000000000001475635207100233745ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/algorithms/plot_geom/private/magnify_proj.m000066400000000000000000000015441475635207100262420ustar00rootroot00000000000000 function [ magnified_vec_geom ] = magnify_proj( vec_geom, dsdd ) %% generate magnified vector geometry % param type vec_geom - example vector geometry % dsdd - deviation of SDD % return magnified_vec_geom - the geometry that was created % % date 09.07.2018 % author Van Nguyen % imec VisionLab % University of Antwerp %% magnified_vec_geom = vec_geom; vec_sd_direction = vec_geom(:,1:3) - vec_geom(:,4:6); norm_sd = sqrt(sum(vec_sd_direction.^2,2)); vec_norm_sd(:,1) = norm_sd; vec_norm_sd(:,2) = norm_sd; vec_norm_sd(:,3) = norm_sd; vec_sd_direction = vec_sd_direction ./ vec_norm_sd; magnified_vec_geom(:,4:6) = vec_geom(:,4:6) - dsdd * vec_sd_direction; clearvars -except magnified_vec_geom end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/private/rotate_around3d.m000066400000000000000000000023241475635207100266500ustar00rootroot00000000000000function [rot_vec] = rotate_around3d(vec, ax, angle) %% rotate_around.m % rotate a 3d vector around an axis by an angle % param vec: 3 x 1 vector to be rotated % param ax: 3 x 1 vector specifying the axis % param angle: scalar, angle to rotate by % return: rotated vector % % date: 21.06.2018 % author: someone at VisionLab, modified by Tim Elberfeld % imec VisionLab % University of Antwerp %% rot_vec = zeros(3, 1); rot_vec(1) = (ax(1) * ax(1) * (1-cos(angle)) + cos(angle)) * vec(1) +... (ax(1) * ax(2) * (1-cos(angle)) - ax(3) * sin(angle)) * vec(2) +... (ax(1) * ax(3) * (1-cos(angle)) + ax(2) * sin(angle)) * vec(3); rot_vec(2) = (ax(1) * ax(2) * (1-cos(angle)) + ax(3) * sin(angle)) * vec(1) +... (ax(2) * ax(2) * (1-cos(angle)) + cos(angle)) * vec(2) +... (ax(2) * ax(3) * (1-cos(angle)) - ax(1) * sin(angle)) * vec(3); rot_vec(3) = (ax(1) * ax(3) * (1-cos(angle)) - ax(2) * sin(angle)) * vec(1) +... (ax(2) * ax(3) * (1-cos(angle)) + ax(1) * sin(angle)) * vec(2) +... (ax(3) * ax(3) * (1-cos(angle)) + cos(angle)) * vec(3); end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/private/rotate_detector.m000066400000000000000000000014641475635207100267460ustar00rootroot00000000000000function [ vectors_rot ] = rotate_detector(vectors, rot_angles) %% rotate the detector part of the vectors of a vector geometry % param vectors - vectors to transform % param rot_angles - rotation euler angles % % return vectors_rot - copy of input vectors, but rotated % % date 09.07.2018 % author Van Nguyen, Tim Elberfeld % imec VisionLab % University of Antwerp % last mod 07.11.2018 %% vectors_rot = vectors; vectors_rot(:, 1: 3) = rotate_euler3d(vectors(:, 1: 3), rot_angles); vectors_rot(:, 4: 6) = rotate_euler3d(vectors(:, 4: 6), rot_angles); vectors_rot(:, 7: 9) = rotate_euler3d(vectors(:, 7: 9), rot_angles); vectors_rot(:, 10:12) = rotate_euler3d(vectors(:, 10:12), rot_angles); end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/private/rotate_euler3d.m000066400000000000000000000021371475635207100264760ustar00rootroot00000000000000function [ vectors_rot ] = rotate_euler3d(vectors, rot_angles) %% rotate some vectors by euler angles around an axis % param vectors - vectors to transform % param rot_angles - rotation euler angles % % return vectors_rot - copy of input vectors, but rotated % % date 09.07.2018 % author Van Nguyen, Tim Elberfeld % imec VisionLab % University of Antwerp % last mod 07.11.2018 %% roll = rot_angles(1); yaw = rot_angles(2); pitch = rot_angles(3); roll_mat = [1 0 0; ... 0 cos(roll) -sin(roll); ... 0 sin(roll) cos(roll)]; yaw_mat = [cos(yaw) 0 -sin(yaw); ... 0 1 0; ... sin(yaw) 0 cos(yaw)]; pitch_mat = [cos(pitch) -sin(pitch) 0; ... sin(pitch) cos(pitch) 0; ... 0 0 1]; % simulate rotation and translation of the DETECTOR rot_mat = roll_mat * yaw_mat * pitch_mat; vectors_rot = vectors; for i = 1:size(vectors, 1) vectors_rot(i, :) = (rot_mat * vectors(i, :)')'; end end astra-toolbox-2.3.0/matlab/algorithms/plot_geom/private/translate_3d.m000066400000000000000000000012731475635207100261400ustar00rootroot00000000000000function [ transl_vectors ] = translate_3d( vectors, transl_vec) %% translate some vectors by a translation vector % param vectors - the vectors to translate % param transl_vec - vector geometry translation % return translated_vec_geom - copy of input geometry with translated % vectors % % date 09.07.2018 % author Van Nguyen, Tim Elberfeld % imec VisionLab % University of Antwerp % last mod 07.11.2018 %% transl_vectors = vectors; % copy input transl = repmat(transl_vec, size(vectors, 1), 1); transl_vectors = transl_vectors + transl; end astra-toolbox-2.3.0/matlab/mex/000077500000000000000000000000001475635207100163555ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/mex/astra_mex_algorithm_c.cpp000066400000000000000000000172701475635207100234230ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ /** \file astra_mex_algorithm_c.cpp * * \brief Creates and manages algorithms (reconstruction,projection,...). */ #include #include "mexHelpFunctions.h" #include "mexInitFunctions.h" #include "astra/Globals.h" #ifdef USE_MATLAB_UNDOCUMENTED extern "C" { bool utIsInterruptPending(); } #endif #include "astra/AstraObjectManager.h" #include "astra/AstraObjectFactory.h" #include "astra/XMLNode.h" #include "astra/XMLDocument.h" using namespace std; using namespace astra; //----------------------------------------------------------------------------------------- /** id = astra_mex_algorithm('create', cfg); * * Create and configure a new algorithm object. * cfg: MATLAB struct containing the configuration parameters, see doxygen documentation for details. * id: identifier of the algorithm object as it is now stored in the astra-library. */ void astra_mex_algorithm_create(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsStruct(prhs[1])) { mexErrMsgTxt("Argument 1 not a valid MATLAB struct."); } // turn MATLAB struct to an XML-based Config object XMLConfig* cfg = structToConfig("Algorithm", prhs[1]); CAlgorithm* pAlg = CAlgorithmFactory::getSingleton().create(cfg->self.getAttribute("type")); if (!pAlg) { delete cfg; mexErrMsgTxt("Unknown algorithm type."); } // create algorithm if (!pAlg->initialize(*cfg)) { delete cfg; delete pAlg; mexErrMsgWithAstraLog("Unable to initialize algorithm."); } delete cfg; // store algorithm int iIndex = CAlgorithmManager::getSingleton().store(pAlg); // step4: set output if (1 <= nlhs) { plhs[0] = mxCreateDoubleScalar(iIndex); } } #ifdef USE_MATLAB_UNDOCUMENTED bool checkMatlabInterrupt() { return utIsInterruptPending(); } #endif //----------------------------------------------------------------------------------------- /** astra_mex_algorithm('run', id); or astra_mex_algorithm('iterate', id, iterations); * * Run or do iterations on a certain algorithm. * id: identifier of the algorithm object as stored in the astra-library. * iterations: if the algorithm is iterative, this specifies the number of iterations to perform. */ void astra_mex_algorithm_run(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: get input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } int iAid = (int)(mxGetScalar(prhs[1])); int iIterations = 0; if (3 <= nrhs) { iIterations = (int)(mxGetScalar(prhs[2])); } // step2: get algorithm object CAlgorithm* pAlg = CAlgorithmManager::getSingleton().get(iAid); if (!pAlg) { mexErrMsgTxt("Invalid algorithm ID."); } if (!pAlg->isInitialized()) { mexErrMsgTxt("Algorithm ID exists but is not initialized."); } // step3: perform actions #ifdef USE_MATLAB_UNDOCUMENTED setShouldAbortHook(&checkMatlabInterrupt); #endif pAlg->run(iIterations); } //----------------------------------------------------------------------------------------- /** astra_mex_algorithm('get_res_norm', id); * * Get the L2-norm of the residual sinogram. Not all algorithms * support this operation. */ void astra_mex_algorithm_get_res_norm(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: get input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } int iAid = (int)(mxGetScalar(prhs[1])); // step2: get algorithm object CAlgorithm* pAlg = CAlgorithmManager::getSingleton().get(iAid); if (!pAlg) { mexErrMsgTxt("Invalid algorithm ID."); } if (!pAlg->isInitialized()) { mexErrMsgTxt("Algorithm ID exists but is not initialized."); } CReconstructionAlgorithm2D* pAlg2D = dynamic_cast(pAlg); CReconstructionAlgorithm3D* pAlg3D = dynamic_cast(pAlg); float res = 0.0f; bool ok; if (pAlg2D) ok = pAlg2D->getResidualNorm(res); else if (pAlg3D) ok = pAlg3D->getResidualNorm(res); else ok = false; if (!ok) { mexErrMsgTxt("Operation not supported."); } plhs[0] = mxCreateDoubleScalar(res); } //----------------------------------------------------------------------------------------- /** astra_mex_algorithm('delete', id1, id2, ...); * * Delete one or more algorithm objects currently stored in the astra-library. * id1, id2, ... : identifiers of the algorithm objects as stored in the astra-library. */ void astra_mex_algorithm_delete(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: get algorithm ID if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } for (int i = 1; i < nrhs; i++) { int iAid = (int)(mxGetScalar(prhs[i])); CAlgorithmManager::getSingleton().remove(iAid); } } //----------------------------------------------------------------------------------------- /** astra_mex_algorithm('clear'); * * Delete all algorithm objects currently stored in the astra-library. */ void astra_mex_algorithm_clear(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { CAlgorithmManager::getSingleton().clear(); } //----------------------------------------------------------------------------------------- /** astra_mex_algorithm('info'); * * Print information about all the algorithm objects currently stored in the astra-library. */ void astra_mex_algorithm_info(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { mexPrintf("%s", astra::CAlgorithmManager::getSingleton().info().c_str()); } //----------------------------------------------------------------------------------------- static void printHelp() { mexPrintf("Please specify a mode of operation.\n"); mexPrintf("Valid modes: create, info, delete, clear, run/iterate, get_res_norm\n"); } //----------------------------------------------------------------------------------------- /** * ... = astra_mex_algorithm(mode, ...); */ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // INPUT: Mode string sMode = ""; if (1 <= nrhs) { sMode = mexToString(prhs[0]); } else { printHelp(); return; } initASTRAMex(); // SWITCH (MODE) if (sMode == "create") { astra_mex_algorithm_create(nlhs, plhs, nrhs, prhs); } else if (sMode == "info") { astra_mex_algorithm_info(nlhs, plhs, nrhs, prhs); } else if (sMode == "delete") { astra_mex_algorithm_delete(nlhs, plhs, nrhs, prhs); } else if (sMode == "clear") { astra_mex_algorithm_clear(nlhs, plhs, nrhs, prhs); } else if (sMode == "run" || sMode == "iterate") { astra_mex_algorithm_run(nlhs, plhs, nrhs, prhs); } else if (sMode == "get_res_norm") { astra_mex_algorithm_get_res_norm(nlhs, plhs, nrhs, prhs); } else { printHelp(); } return; } astra-toolbox-2.3.0/matlab/mex/astra_mex_c.cpp000066400000000000000000000174001475635207100213500ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ /** \file astra_mex_c.cpp * * \brief Contains some basic "about" functions. */ #include #include "mexHelpFunctions.h" #include "mexInitFunctions.h" #include "astra/Globals.h" #include "astra/Features.h" #include "astra/AstraObjectManager.h" #ifdef ASTRA_CUDA #include "astra/cuda/2d/astra.h" #include "astra/CompositeGeometryManager.h" #endif using namespace std; using namespace astra; //----------------------------------------------------------------------------------------- /** astra_mex('credits'); * * Print Credits */ void astra_mex_credits(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { mexPrintf("The ASTRA Toolbox has been developed at the University of Antwerp and CWI, Amsterdam by\n"); mexPrintf(" * Prof. dr. Joost Batenburg\n"); mexPrintf(" * Prof. dr. Jan Sijbers\n"); mexPrintf(" * Dr. Jeroen Bedorf\n"); mexPrintf(" * Dr. Folkert Bleichrodt\n"); mexPrintf(" * Dr. Andrei Dabravolski\n"); mexPrintf(" * Dr. Willem Jan Palenstijn\n"); mexPrintf(" * Dr. Daniel Pelt\n"); mexPrintf(" * Dr. Tom Roelandts\n"); mexPrintf(" * Dr. Alexander Skorikov\n"); mexPrintf(" * Dr. Wim van Aarle\n"); mexPrintf(" * Dr. Gert Van Gompel\n"); mexPrintf(" * Sander van der Maar, MSc.\n"); mexPrintf(" * Gert Merckx, MSc.\n"); } //----------------------------------------------------------------------------------------- /** use_cuda = astra_mex('use_cuda'); * * Is CUDA enabled? */ void astra_mex_use_cuda(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (1 <= nlhs) { plhs[0] = mxCreateDoubleScalar(astra::cudaAvailable() ? 1 : 0); } } //----------------------------------------------------------------------------------------- /** set_gpu_index = astra_mex('set_gpu_index'); * * Set active GPU */ void astra_mex_set_gpu_index(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { #ifdef ASTRA_CUDA bool usage = false; if (nrhs != 2 && nrhs != 4) { usage = true; } astra::SGPUParams params; params.memory = 0; if (!usage && nrhs >= 4) { std::string s = mexToString(prhs[2]); if (s != "memory") { usage = true; } else { params.memory = (size_t)mxGetScalar(prhs[3]); } } if (!usage && nrhs >= 2) { int n = mxGetN(prhs[1]) * mxGetM(prhs[1]); params.GPUIndices.resize(n); double* pdMatlabData = mxGetPr(prhs[1]); for (int i = 0; i < n; ++i) params.GPUIndices[i] = (int)pdMatlabData[i]; astra::CCompositeGeometryManager::setGlobalGPUParams(params); // Set first GPU if (n >= 1) { bool ret = astraCUDA::setGPUIndex((int)pdMatlabData[0]); if (!ret) mexPrintf("Failed to set GPU %d\n", (int)pdMatlabData[0]); } } if (usage) { mexPrintf("Usage: astra_mex('set_gpu_index', index/indices [, 'memory', memory])"); } #endif } //----------------------------------------------------------------------------------------- /** get_gpu_info = astra_mex('get_gpu_info'); * * Get GPU info */ void astra_mex_get_gpu_info(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { #ifdef ASTRA_CUDA int device = -1; if (nrhs >= 2 && mxIsDouble(prhs[1]) && mxGetN(prhs[1]) * mxGetM(prhs[1]) == 1 ) { device = (int)mxGetScalar(prhs[1]); } mexPrintf("%s\n", astraCUDA::getCudaDeviceString(device).c_str()); #endif } //----------------------------------------------------------------------------------------- /** has_feature = astra_mex('has_feature'); * * Check a feature flag. See include/astra/Features.h. */ void astra_mex_has_feature(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. Usage: astra_mex('has_feature', feature);"); } string sMode = mexToString(prhs[0]); bool ret = false; // NB: When adding features here, also document them centrally in // include/astra/Features.h if (sMode == "mex_link") { #ifdef USE_MATLAB_UNDOCUMENTED ret = true; #else ret = false; #endif } else { ret = astra::hasFeature(sMode); } plhs[0] = mxCreateDoubleScalar(ret ? 1 : 0); } //----------------------------------------------------------------------------------------- /** version_number = astra_mex('version'); * * Fetch the version number of the toolbox. */ void astra_mex_version(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (1 <= nlhs) { plhs[0] = mxCreateDoubleScalar(astra::getVersion()); } else { mexPrintf("ASTRA Toolbox version %s\n", astra::getVersionString()); } } //----------------------------------------------------------------------------------------- void astra_mex_info(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. Usage: astra_mex('info', index/indices);"); } for (int i = 1; i < nrhs; i++) { int iDataID = (int)(mxGetScalar(prhs[i])); CAstraObjectManagerBase *ptr; ptr = CAstraIndexManager::getSingleton().get(iDataID); if (ptr) { mexPrintf("%s\t%s\n", ptr->getType().c_str(), ptr->getInfo(iDataID).c_str()); } } } //----------------------------------------------------------------------------------------- void astra_mex_delete(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. Usage: astra_mex('delete', index/indices);"); } for (int i = 1; i < nrhs; i++) { int iDataID = (int)(mxGetScalar(prhs[i])); CAstraObjectManagerBase *ptr; ptr = CAstraIndexManager::getSingleton().get(iDataID); if (ptr) ptr->remove(iDataID); } } //----------------------------------------------------------------------------------------- static void printHelp() { mexPrintf("Please specify a mode of operation.\n"); mexPrintf(" Valid modes: version, use_cuda, credits, set_gpu_index, has_feature, info, delete\n"); } //----------------------------------------------------------------------------------------- /** * ... = astra_mex(type,...); */ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // INPUT0: Mode string sMode = ""; if (1 <= nrhs) { sMode = mexToString(prhs[0]); } else { printHelp(); return; } initASTRAMex(); // SWITCH (MODE) if (sMode == std::string("version")) { astra_mex_version(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("use_cuda")) { astra_mex_use_cuda(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("credits")) { astra_mex_credits(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("set_gpu_index")) { astra_mex_set_gpu_index(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("get_gpu_info")) { astra_mex_get_gpu_info(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("has_feature")) { astra_mex_has_feature(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("info")) { astra_mex_info(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("delete")) { astra_mex_delete(nlhs, plhs, nrhs, prhs); } else { printHelp(); } return; } astra-toolbox-2.3.0/matlab/mex/astra_mex_data2d_c.cpp000066400000000000000000000474551475635207100226040ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ /** \file astra_mex_data2d_c.cpp * * \brief Creates, manages and manipulates 2D volume and projection data objects. */ #include #include "mexHelpFunctions.h" #include "mexInitFunctions.h" #include #include "astra/Globals.h" #include "astra/AstraObjectManager.h" #include "astra/Float32ProjectionData2D.h" #include "astra/Float32VolumeData2D.h" #include "astra/ProjectionGeometry2DFactory.h" using namespace std; using namespace astra; //----------------------------------------------------------------------------------------- /** astra_mex_data2d('delete', id1, id2, ...); * * Delete one or more data objects currently stored in the astra-library. * id1, id2, ... : identifiers of the 2d data objects as stored in the astra-library. */ void astra_mex_data2d_delete(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: read input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } // step2: delete all specified data objects for (int i = 1; i < nrhs; i++) { int iDataID = (int)(mxGetScalar(prhs[i])); CData2DManager::getSingleton().remove(iDataID); } } //----------------------------------------------------------------------------------------- /** astra_mex_data2d('clear'); * * Delete all data objects currently stored in the astra-library. */ void astra_mex_data2d_clear(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { CData2DManager::getSingleton().clear(); } //----------------------------------------------------------------------------------------- /** id = astra_mex_data2d('create', datatype, geometry, data); * * Create a new data 2d object in the astra-library. * type: '-vol' for volume data, '-sino' for projection data * geom: MATLAB struct with the geometry for the data * data: Optional. Can be either a MATLAB matrix containing the data. In that case the dimensions * should match that of the geometry of the object. It can also be a single value, in which case * the entire data will be set to that value. If this isn't specified all values are set to 0. * id: identifier of the 2d data object as it is now stored in the astra-library. */ void astra_mex_data2d_create(int& nlhs, mxArray* plhs[], int& nrhs, const mxArray* prhs[]) { // step1: get datatype if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } string sDataType = mexToString(prhs[1]); CFloat32Data2D* pDataObject2D = NULL; if (nrhs >= 4 && !(mexIsScalar(prhs[3])|| mxIsDouble(prhs[3]) || mxIsLogical(prhs[3]) || mxIsSingle(prhs[3]) )) { mexErrMsgTxt("Data type must be single, double or logical."); return; } if (mxIsSparse(prhs[2])) { mexErrMsgTxt("Data may not be sparse."); return; } // SWITCH DataType if (sDataType == "-vol") { // Read geometry if (!mxIsStruct(prhs[2])) { mexErrMsgTxt("Argument 3 is not a valid MATLAB struct."); } XMLConfig* cfg = structToConfig("VolumeGeometry", prhs[2]); CVolumeGeometry2D* pGeometry = new CVolumeGeometry2D(); if (!pGeometry->initialize(*cfg)) { delete cfg; delete pGeometry; mexErrMsgWithAstraLog("Geometry class could not be initialized."); } // If data is specified, check dimensions if (nrhs >= 4 && !mexIsScalar(prhs[3])) { if (pGeometry->getGridColCount() != mxGetN(prhs[3]) || pGeometry->getGridRowCount() != mxGetM(prhs[3])) { delete cfg; delete pGeometry; mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry."); } } // Initialize data object pDataObject2D = new CFloat32VolumeData2D(*pGeometry); delete pGeometry; delete cfg; } else if (sDataType == "-sino") { // Read geometry if (!mxIsStruct(prhs[2])) { mexErrMsgTxt("Argument 3 is not a valid MATLAB struct."); } XMLConfig* cfg = structToConfig("ProjectionGeometry", prhs[2]); // FIXME: Change how the base class is created. (This is duplicated // in 'change_geometry' and Projector2D.cpp.) std::string type = cfg->self.getAttribute("type"); std::unique_ptr pGeometry = constructProjectionGeometry2D(type); if (!pGeometry) { std::string message = "'" + type + "' is not a valid 2D geometry type."; mexErrMsgTxt(message.c_str()); } if (!pGeometry->initialize(*cfg)) { delete cfg; mexErrMsgWithAstraLog("Geometry class could not be initialized."); } // If data is specified, check dimensions if (nrhs >= 4 && !mexIsScalar(prhs[3])) { if (pGeometry->getDetectorCount() != mxGetN(prhs[3]) || pGeometry->getProjectionAngleCount() != mxGetM(prhs[3])) { delete cfg; mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry."); } } // Initialize data object pDataObject2D = new CFloat32ProjectionData2D(*pGeometry); delete cfg; } else { mexErrMsgTxt("Invalid datatype. Please specify '-vol' or '-sino'."); } // Check initialization if (!pDataObject2D->isInitialized()) { delete pDataObject2D; mexErrMsgWithAstraLog("Couldn't initialize data object."); } // Store data if (nrhs == 3) { for (int i = 0; i < pDataObject2D->getSize(); ++i) { pDataObject2D->getData()[i] = 0.0f; } } // Store data if (nrhs >= 4) { // fill with scalar value if (mexIsScalar(prhs[3])) { float32 fValue = (float32)mxGetScalar(prhs[3]); for (int i = 0; i < pDataObject2D->getSize(); ++i) { pDataObject2D->getData()[i] = fValue; } } // fill with array value else { const mwSize* dims = mxGetDimensions(prhs[3]); // Check Data dimensions if (pDataObject2D->getWidth() != mxGetN(prhs[3]) || pDataObject2D->getHeight() != mxGetM(prhs[3])) { mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry."); } // logical data if (mxIsLogical(prhs[3])) { mxLogical* pbMatlabData = mxGetLogicals(prhs[3]); int i = 0; int col, row; for (col = 0; col < dims[1]; ++col) { for (row = 0; row < dims[0]; ++row) { pDataObject2D->getData2D()[row][col] = (float32)pbMatlabData[i]; ++i; } } // double data } else if (mxIsDouble(prhs[3])) { double* pdMatlabData = mxGetPr(prhs[3]); int i = 0; int col, row; for (col = 0; col < dims[1]; ++col) { for (row = 0; row < dims[0]; ++row) { pDataObject2D->getData2D()[row][col] = pdMatlabData[i]; ++i; } } // single data } else if (mxIsSingle(prhs[3])) { const float* pfMatlabData = (const float *)mxGetData(prhs[3]); int i = 0; int col, row; for (col = 0; col < dims[1]; ++col) { for (row = 0; row < dims[0]; ++row) { pDataObject2D->getData2D()[row][col] = pfMatlabData[i]; ++i; } } } else { ASTRA_ASSERT(false); } } } // step4: store data object int iIndex = CData2DManager::getSingleton().store(pDataObject2D); // step5: return data id if (nlhs >= 1) { plhs[0] = mxCreateDoubleScalar(iIndex); } } //----------------------------------------------------------------------------------------- /** astra_mex_data2d('store', id, data); * * Store data in an existing astra 2d dataobject with a MATLAB matrix or with a scalar value. * id: identifier of the 2d data object as stored in the astra-library. * data: can be either a MATLAB matrix containing the data. In that case the dimensions should match that of the geometry of the object. It can also be a single value, in which case the entire data will be set to that value. */ void astra_mex_data2d_store(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: input if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value."); } int iDataID = (int)(mxGetScalar(prhs[1])); if (!(mexIsScalar(prhs[2]) || mxIsDouble(prhs[2]) || mxIsLogical(prhs[2]) || mxIsSingle(prhs[2]))) { mexErrMsgTxt("Data type must be single, double or logical."); } if (mxIsSparse(prhs[2])) { mexErrMsgTxt("Data may not be sparse."); } // step2: get data object CFloat32Data2D* pDataObject = astra::CData2DManager::getSingleton().get(iDataID); if (!pDataObject || !pDataObject->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly."); } // step3: insert data // fill with scalar value if (mexIsScalar(prhs[2])) { float32 fValue = (float32)mxGetScalar(prhs[2]); for (int i = 0; i < pDataObject->getSize(); ++i) { pDataObject->getData()[i] = fValue; } } else { // Check Data dimensions if (pDataObject->getWidth() != mxGetN(prhs[2]) || pDataObject->getHeight() != mxGetM(prhs[2])) { mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry."); } const mwSize* dims = mxGetDimensions(prhs[2]); // logical data if (mxIsLogical(prhs[2])) { mxLogical* pbMatlabData = mxGetLogicals(prhs[2]); int i = 0; int col, row; for (col = 0; col < dims[1]; ++col) { for (row = 0; row < dims[0]; ++row) { pDataObject->getData2D()[row][col] = (float32)pbMatlabData[i]; ++i; } } // double data } else if (mxIsDouble(prhs[2])) { double* pdMatlabData = mxGetPr(prhs[2]); int i = 0; int col, row; for (col = 0; col < dims[1]; ++col) { for (row = 0; row < dims[0]; ++row) { pDataObject->getData2D()[row][col] = pdMatlabData[i]; ++i; } } // single data } else if (mxIsSingle(prhs[2])) { const float* pfMatlabData = (const float *)mxGetData(prhs[2]); int i = 0; int col, row; for (col = 0; col < dims[1]; ++col) { for (row = 0; row < dims[0]; ++row) { pDataObject->getData2D()[row][col] = pfMatlabData[i]; ++i; } } } else { ASTRA_ASSERT(false); } } } //----------------------------------------------------------------------------------------- /** geom = astra_mex_data2d('get_geometry', id); * * Fetch the geometry of a 2d data object stored in the astra-library. * id: identifier of the 2d data object as stored in the astra-library. * geom: MATLAB-struct containing information about the used geometry. */ void astra_mex_data2d_get_geometry(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // parse input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value."); } int iDataID = (int)(mxGetScalar(prhs[1])); // fetch data object CFloat32Data2D* pDataObject = astra::CData2DManager::getSingleton().get(iDataID); if (!pDataObject || !pDataObject->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly."); } // create output if (1 <= nlhs) { if (pDataObject->getType() == CFloat32Data2D::PROJECTION) { CFloat32ProjectionData2D* pDataObject2 = dynamic_cast(pDataObject); plhs[0] = configToStruct(pDataObject2->getGeometry().getConfiguration()); } else if (pDataObject->getType() == CFloat32Data2D::VOLUME) { CFloat32VolumeData2D* pDataObject2 = dynamic_cast(pDataObject); plhs[0] = configToStruct(pDataObject2->getGeometry().getConfiguration()); } } } //----------------------------------------------------------------------------------------- /** astra_mex_data2d('change_geometry', id, geom); * * Change the associated geometry of a 2d data object (volume or sinogram) * id: identifier of the 2d data object as stored in the astra-library. * geom: the new geometry struct, as created by astra_create_vol/proj_geom */ void astra_mex_data2d_change_geometry(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: check input if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value."); } // step2: get data object int iDataID = (int)(mxGetScalar(prhs[1])); CFloat32Data2D* pDataObject = astra::CData2DManager::getSingleton().get(iDataID); if (!pDataObject || !pDataObject->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly."); } CFloat32ProjectionData2D* pSinogram = dynamic_cast(pDataObject); if (pSinogram) { // Projection data // Read geometry if (!mxIsStruct(prhs[2])) { mexErrMsgTxt("Argument 3 is not a valid MATLAB struct."); } XMLConfig* cfg = structToConfig("ProjectionGeometry2D", prhs[2]); // FIXME: Change how the base class is created. (This is duplicated // in 'create' and Projector2D.cpp.) std::string type = cfg->self.getAttribute("type"); std::unique_ptr pGeometry = constructProjectionGeometry2D(type); if (!pGeometry) { delete cfg; std::string message = "'" + type + "' is not a valid 2D geometry type."; mexErrMsgTxt(message.c_str()); } if (!pGeometry->initialize(*cfg)) { delete cfg; mexErrMsgWithAstraLog("Geometry could not be initialized."); } // If data is specified, check dimensions if (pGeometry->getDetectorCount() != pSinogram->getDetectorCount() || pGeometry->getProjectionAngleCount() != pSinogram->getAngleCount()) { delete cfg; mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry."); } // If ok, change geometry pSinogram->changeGeometry(*pGeometry); delete cfg; return; } CFloat32VolumeData2D* pVolume = dynamic_cast(pDataObject); if (pVolume) { // Volume data // Read geometry if (!mxIsStruct(prhs[2])) { mexErrMsgTxt("Argument 3 is not a valid MATLAB struct."); } XMLConfig* cfg = structToConfig("VolumeGeometry2D", prhs[2]); CVolumeGeometry2D* pGeometry = new CVolumeGeometry2D(); if (!pGeometry->initialize(*cfg)) { delete cfg; delete pGeometry; mexErrMsgWithAstraLog("Geometry class could not be initialized."); } // If data is specified, check dimensions if (pGeometry->getGridColCount() != pVolume->getWidth() || pGeometry->getGridRowCount() != pVolume->getHeight()) { delete cfg; delete pGeometry; mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry."); } // If ok, change geometry pVolume->changeGeometry(*pGeometry); delete cfg; delete pGeometry; return; } // Neither sinogram nor volume object mexErrMsgTxt("Data object not found or not initialized properly."); } //----------------------------------------------------------------------------------------- /** data = astra_mex_data2d('get', id); * * Fetch data from the astra-library to a MATLAB matrix. * id: identifier of the 2d data object as stored in the astra-library. * data: MATLAB data */ void astra_mex_data2d_get(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: check input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value."); } // step2: get data object int iDataID = (int)(mxGetScalar(prhs[1])); CFloat32Data2D* pDataObject = astra::CData2DManager::getSingleton().get(iDataID); if (!pDataObject || !pDataObject->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly."); } // create output if (1 <= nlhs) { plhs[0] = mxCreateDoubleMatrix(pDataObject->getHeight(), // # rows pDataObject->getWidth(), // # cols mxREAL); // datatype 64-bits double* out = mxGetPr(plhs[0]); int i = 0; int row, col; for (col = 0; col < pDataObject->getWidth(); ++col) { for (row = 0; row < pDataObject->getHeight(); ++row) { out[i] = pDataObject->getData2D()[row][col]; ++i; } } } } //----------------------------------------------------------------------------------------- /** data = astra_mex_data2d('get_single', id); * * Fetch data from the astra-library to a MATLAB matrix. * id: identifier of the 2d data object as stored in the astra-library. * data: MATLAB data */ void astra_mex_data2d_get_single(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: check input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value."); } // step2: get data object int iDataID = (int)(mxGetScalar(prhs[1])); CFloat32Data2D* pDataObject = astra::CData2DManager::getSingleton().get(iDataID); if (!pDataObject || !pDataObject->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly."); } // create output if (1 <= nlhs) { mwSize dims[2]; dims[0] = pDataObject->getHeight(); dims[1] = pDataObject->getWidth(); plhs[0] = mxCreateNumericArray(2, dims, mxSINGLE_CLASS, mxREAL); float* out = (float *)mxGetData(plhs[0]); int i = 0; int row, col; for (col = 0; col < pDataObject->getWidth(); ++col) { for (row = 0; row < pDataObject->getHeight(); ++row) { out[i] = pDataObject->getData2D()[row][col]; ++i; } } } } //----------------------------------------------------------------------------------------- /** astra_mex_data2d('info'); * * Print information about all the 2d data objects currently stored in the astra-library. */ void astra_mex_data2d_info(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { mexPrintf("%s", astra::CData2DManager::getSingleton().info().c_str()); } //----------------------------------------------------------------------------------------- static void printHelp() { mexPrintf("Please specify a mode of operation.\n"); mexPrintf("Valid modes: get, get_single, delete, clear, set/store, create, get_geometry, change_geometry, info\n"); } //----------------------------------------------------------------------------------------- /** * ... = astra_mex_data2d(type,...); */ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // INPUT0: Mode string sMode = ""; if (1 <= nrhs) { sMode = mexToString(prhs[0]); } else { printHelp(); return; } initASTRAMex(); // SWITCH (MODE) if (sMode == std::string("get")) { astra_mex_data2d_get(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("get_single")) { astra_mex_data2d_get_single(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("delete")) { astra_mex_data2d_delete(nlhs, plhs, nrhs, prhs); } else if (sMode == "clear") { astra_mex_data2d_clear(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("store") || sMode == std::string("set")) { astra_mex_data2d_store(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("create")) { astra_mex_data2d_create(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("get_geometry")) { astra_mex_data2d_get_geometry(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("change_geometry")) { astra_mex_data2d_change_geometry(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("info")) { astra_mex_data2d_info(nlhs, plhs, nrhs, prhs); } else { printHelp(); } return; } astra-toolbox-2.3.0/matlab/mex/astra_mex_data3d_c.cpp000066400000000000000000000352011475635207100225670ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ /** \file astra_mex_data3d_c.cpp * * \brief Creates, manages and manipulates 3D volume and projection data objects. */ #include #include "mexHelpFunctions.h" #include "mexInitFunctions.h" #include "mexCopyDataHelpFunctions.h" #include "mexDataManagerHelpFunctions.h" #include #include "astra/Globals.h" #include "astra/AstraObjectManager.h" #include "astra/Data3D.h" #include "astra/ParallelProjectionGeometry3D.h" #include "astra/ParallelVecProjectionGeometry3D.h" #include "astra/ConeProjectionGeometry3D.h" #include "astra/ConeVecProjectionGeometry3D.h" using namespace std; using namespace astra; //----------------------------------------------------------------------------------------- /** * id = astra_mex_io_data('create', datatype, geometry, data); * datatype: ['-vol','-sino'] */ void astra_mex_data3d_create(int& nlhs, mxArray* plhs[], int& nrhs, const mxArray* prhs[]) { // step1: get datatype if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } const mxArray * const geometry = prhs[2]; const mxArray * const data = nrhs > 3 ? prhs[3] : NULL; if (!checkStructs(geometry)) { mexErrMsgTxt("Argument 3 is not a valid MATLAB struct."); } if (data && !checkDataType(data)) { mexErrMsgTxt("Data type must be single, double or logical."); } const string sDataType = mexToString(prhs[1]); // step2: Allocate data CData3D* pDataObject3D = allocateDataObject(sDataType, geometry, data); if (!pDataObject3D) { // Error message was already set by the function return; } // step3: Initialize data if (!data) { mxArray * emptyArray = mxCreateDoubleMatrix(0, 0, mxREAL); copyMexToCFloat32Array(emptyArray, pDataObject3D->getFloat32Memory(), pDataObject3D->getSize()); mxDestroyArray(emptyArray); } else { copyMexToCFloat32Array(data, pDataObject3D->getFloat32Memory(), pDataObject3D->getSize()); } // step4: store data object int iIndex = CData3DManager::getSingleton().store(pDataObject3D); // step5: return data id if (1 <= nlhs) { plhs[0] = mxCreateDoubleScalar(iIndex); } } //----------------------------------------------------------------------------------------- /** id = astra_mex_data3d('link', datatype, geometry, data); * * Create a new data 3d object in the astra-library. * type: * geom: MATLAB struct with the geometry for the data * data: A MATLAB matrix containing the data. * This matrix will be edited _in-place_! * id: identifier of the 3d data object as it is now stored in the astra-library. */ #ifdef USE_MATLAB_UNDOCUMENTED void astra_mex_data3d_link(int& nlhs, mxArray* plhs[], int& nrhs, const mxArray* prhs[]) { // TODO: Allow empty argument to let this function create its own mxArray // step1: get datatype if (nrhs < 4) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } const mxArray * const geometry = prhs[2]; const mxArray * const data = nrhs > 3 ? prhs[3] : NULL; const mxArray * const unshare = nrhs > 4 ? prhs[4] : NULL; const mxArray * const zIndex = nrhs > 5 ? prhs[5] : NULL; if (!checkStructs(geometry)) { mexErrMsgTxt("Argument 3 is not a valid MATLAB struct."); } if (data && !mxIsSingle(data)) { mexErrMsgTxt("Data must be single."); } string sDataType = mexToString(prhs[1]); // step2: Allocate data CData3D* pDataObject3D = allocateDataObject(sDataType, geometry, data, unshare, zIndex); if (!pDataObject3D) { // Error message was already set by the function return; } // step4: store data object int iIndex = CData3DManager::getSingleton().store(pDataObject3D); // step5: return data id if (1 <= nlhs) { plhs[0] = mxCreateDoubleScalar(iIndex); } } #endif //----------------------------------------------------------------------------------------- /** * data = astra_mex_data3d('get', id); * * Fetch data from the astra-library to a MATLAB matrix. * id: identifier of the 3d data object as stored in the astra-library. * data: MATLAB data */ void astra_mex_data3d_get(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { generic_astra_mex_data3d_get(nlhs, plhs, nrhs, prhs); } //----------------------------------------------------------------------------------------- /** * data = astra_mex_data3d('get_single', id); * * Fetch data from the astra-library to a MATLAB matrix. * id: identifier of the 3d data object as stored in the astra-library. * data: MATLAB data */ void astra_mex_data3d_get_single(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { generic_astra_mex_data3d_get(nlhs, plhs, nrhs, prhs); } //----------------------------------------------------------------------------------------- /** * astra_mex_data3d('store', id, data); * * Store MATLAB matrix data in the astra-library. * id: identifier of the 3d data object as stored in the astra-library. * data: MATLAB data */ void astra_mex_data3d_store(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: input if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!checkDataType(prhs[2])) { mexErrMsgTxt("Data type must be single or double."); } // step2: get data object CData3D* pDataObject = NULL; if (!checkID(mxGetScalar(prhs[1]), pDataObject)) { mexErrMsgTxt("Data object not found or not initialized properly.\n"); } copyMexToCFloat32Array(prhs[2], pDataObject->getFloat32Memory(), pDataObject->getSize()); } void astra_mex_data3d_dimensions(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: get input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } int iDid = (int)(mxGetScalar(prhs[1])); // step2: get data object CData3D* pData; if (!(pData = CData3DManager::getSingleton().get(iDid))) { mexErrMsgTxt("Data object not found."); } // step3: output if (1 <= nlhs) { plhs[0] = mxCreateDoubleScalar(pData->getWidth()); } if (2 <= nlhs) { plhs[1] = mxCreateDoubleScalar(pData->getHeight()); } if (3 <= nlhs) { plhs[2] = mxCreateDoubleScalar(pData->getDepth()); } } //----------------------------------------------------------------------------------------- /** geom = astra_mex_data3d('get_geometry', id); * * Fetch the geometry of a 3d data object stored in the astra-library. * id: identifier of the 3d data object as stored in the astra-library. * geom: MATLAB-struct containing information about the used geometry. */ void astra_mex_data3d_get_geometry(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // parse input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value."); } int iDataID = (int)(mxGetScalar(prhs[1])); // fetch data object CData3D* pDataObject = astra::CData3DManager::getSingleton().get(iDataID); if (!pDataObject || !pDataObject->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly."); } // create output if (1 <= nlhs) { if (pDataObject->getType() == CData3D::PROJECTION) { CFloat32ProjectionData3D* pDataObject2 = dynamic_cast(pDataObject); plhs[0] = configToStruct(pDataObject2->getGeometry().getConfiguration()); } else if (pDataObject->getType() == CData3D::VOLUME) { CFloat32VolumeData3D* pDataObject2 = dynamic_cast(pDataObject); plhs[0] = configToStruct(pDataObject2->getGeometry().getConfiguration()); } } } //----------------------------------------------------------------------------------------- /** astra_mex_data3d('change_geometry', id, geom); * * Change the geometry of a 3d data object. * id: identifier of the 3d data object as stored in the astra-library. * geom: the new geometry struct, as created by astra_create_vol/proj_geom */ void astra_mex_data3d_change_geometry(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // parse input if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } // get data object CData3D* pDataObject = NULL; if (!checkID(mxGetScalar(prhs[1]), pDataObject)) { mexErrMsgTxt("Data object not found or not initialized properly."); } const mxArray * const geometry = prhs[2]; if (!checkStructs(geometry)) { mexErrMsgTxt("Argument 3 is not a valid MATLAB struct."); } CFloat32ProjectionData3D* pProjData = dynamic_cast(pDataObject); if (pProjData) { // Projection data // Read geometry astra::XMLConfig* cfg = structToConfig("ProjectionGeometry3D", geometry); // FIXME: Change how the base class is created. (This is duplicated // in Projector3D.cpp.) std::string type = cfg->self.getAttribute("type"); astra::CProjectionGeometry3D* pGeometry = 0; if (type == "parallel3d") { pGeometry = new astra::CParallelProjectionGeometry3D(); } else if (type == "parallel3d_vec") { pGeometry = new astra::CParallelVecProjectionGeometry3D(); } else if (type == "cone") { pGeometry = new astra::CConeProjectionGeometry3D(); } else if (type == "cone_vec") { pGeometry = new astra::CConeVecProjectionGeometry3D(); } else { std::string message = "'" + type + "' is not a valid 3D geometry type."; mexErrMsgTxt(message.c_str()); } if (!pGeometry->initialize(*cfg)) { delete pGeometry; delete cfg; mexErrMsgWithAstraLog("Geometry class could not be initialized."); } delete cfg; // Check dimensions if (pGeometry->getDetectorColCount() != pProjData->getDetectorColCount() || pGeometry->getProjectionCount() != pProjData->getAngleCount() || pGeometry->getDetectorRowCount() != pProjData->getDetectorRowCount()) { delete pGeometry; mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry."); } // If ok, change geometry pProjData->changeGeometry(*pGeometry); delete pGeometry; } else { // Volume data CFloat32VolumeData3D* pVolData = dynamic_cast(pDataObject); assert(pVolData); // Read geometry astra::XMLConfig* cfg = structToConfig("VolumeGeometry3D", geometry); astra::CVolumeGeometry3D* pGeometry = new astra::CVolumeGeometry3D(); if (!pGeometry->initialize(*cfg)) { delete pGeometry; delete cfg; mexErrMsgWithAstraLog("Geometry class could not be initialized."); } delete cfg; // Check dimensions if (pGeometry->getGridColCount() != pVolData->getColCount() || pGeometry->getGridRowCount() != pVolData->getRowCount() || pGeometry->getGridSliceCount() != pVolData->getSliceCount()) { delete pGeometry; mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry."); } // If ok, change geometry pVolData->changeGeometry(*pGeometry); delete pGeometry; } } //----------------------------------------------------------------------------------------- /** * astra_mex_data3d('delete', did1, did2, ...); */ void astra_mex_data3d_delete(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: read input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } for (int i = 1; i < nrhs; i++) { int iDataID = (int)(mxGetScalar(prhs[i])); CData3DManager::getSingleton().remove(iDataID); } } //----------------------------------------------------------------------------------------- /** * astra_mex_data3d('clear'); */ void astra_mex_data3d_clear(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { CData3DManager::getSingleton().clear(); } //----------------------------------------------------------------------------------------- /** * astra_mex_data3d('info'); */ void astra_mex_data3d_info(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { mexPrintf("%s", astra::CData3DManager::getSingleton().info().c_str()); } //----------------------------------------------------------------------------------------- static void printHelp() { mexPrintf("Please specify a mode of operation.\n"); mexPrintf("Valid modes: create, get, get_single, delete, clear, info\n"); mexPrintf(" dimensions, get_geometry, change_geometry\n"); } //----------------------------------------------------------------------------------------- /** * ... = astra_mex_io_data(mode,...); */ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // INPUT: Mode string sMode = ""; if (1 <= nrhs) { sMode = mexToString(prhs[0]); } else { printHelp(); return; } initASTRAMex(); // 3D data if (sMode == std::string("create")) { astra_mex_data3d_create(nlhs, plhs, nrhs, prhs); #ifdef USE_MATLAB_UNDOCUMENTED } else if (sMode == "link") { astra_mex_data3d_link(nlhs, plhs, nrhs, prhs); #endif } else if (sMode == std::string("get")) { astra_mex_data3d_get(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("get_single")) { astra_mex_data3d_get_single(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("store") || sMode == std::string("set")) { astra_mex_data3d_store(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("delete")) { astra_mex_data3d_delete(nlhs, plhs, nrhs, prhs); } else if (sMode == "clear") { astra_mex_data3d_clear(nlhs, plhs, nrhs, prhs); } else if (sMode == "info") { astra_mex_data3d_info(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("dimensions")) { astra_mex_data3d_dimensions(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("get_geometry")) { astra_mex_data3d_get_geometry(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("change_geometry")) { astra_mex_data3d_change_geometry(nlhs, plhs, nrhs, prhs); } else { printHelp(); } return; } astra-toolbox-2.3.0/matlab/mex/astra_mex_direct_c.cpp000066400000000000000000000236261475635207100227110ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ /** \file astra_mex_direct_c.cpp * * \brief Utility functions for low-overhead FP and BP calls. */ #include #include "mexHelpFunctions.h" #include "mexCopyDataHelpFunctions.h" #include "mexDataManagerHelpFunctions.h" #include #include "astra/Globals.h" #include "astra/AstraObjectManager.h" #include "astra/CudaProjector3D.h" #include "astra/Projector3D.h" #include "astra/Data3D.h" #include "astra/CudaForwardProjectionAlgorithm3D.h" #include "astra/CudaBackProjectionAlgorithm3D.h" using namespace std; using namespace astra; class CDataMemory_simple : public astra::CDataMemory { public: CDataMemory_simple(float *ptr) { m_pfData = ptr; } ~CDataMemory_simple() { } }; #ifdef ASTRA_CUDA //----------------------------------------------------------------------------------------- /** * projection = astra_mex_direct_c('FP3D', projector_id, volume); * Both 'projection' and 'volume' are Matlab arrays. */ void astra_mex_direct_fp3d(int& nlhs, mxArray* plhs[], int& nrhs, const mxArray* prhs[]) { // TODO: Add an optional way of specifying extra options if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. Syntax: astra_mex_direct_c('FP3D', projector_id, data);"); } int iPid = (int)(mxGetScalar(prhs[1])); astra::CProjector3D* pProjector; pProjector = astra::CProjector3DManager::getSingleton().get(iPid); if (!pProjector) { mexErrMsgTxt("Projector not found."); } if (!pProjector->isInitialized()) { mexErrMsgTxt("Projector exists but is not initialized."); } bool isCuda = false; if (dynamic_cast(pProjector)) isCuda = true; if (!isCuda) { mexErrMsgTxt("Only CUDA projectors are currently supported."); } const astra::CVolumeGeometry3D &pVolGeom = pProjector->getVolumeGeometry(); const astra::CProjectionGeometry3D &pProjGeom = pProjector->getProjectionGeometry(); const mxArray* const data = prhs[2]; if (!checkDataType(data)) { mexErrMsgTxt("Data type must be single or double."); } if (!checkDataSize(data, &pVolGeom)) { mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry."); } // Allocate input data astra::CFloat32VolumeData3D* pInput; if (mxIsSingle(data)) { astra::CDataStorage* m = new CDataMemory_simple((float *)mxGetData(data)); pInput = new astra::CFloat32VolumeData3D(pVolGeom, m); } else { size_t dataSize = pVolGeom.getGridColCount(); dataSize *= pVolGeom.getGridRowCount(); dataSize *= pVolGeom.getGridSliceCount(); CDataStorage* pStorage = new CDataMemory(dataSize); pInput = new astra::CFloat32VolumeData3D(pVolGeom, pStorage); copyMexToCFloat32Array(data, pInput->getFloat32Memory(), dataSize); } // Allocate output data // If the input is single, we also allocate single output. // Otherwise, double. astra::CFloat32ProjectionData3D* pOutput; mxArray *pOutputMx; if (mxIsSingle(data)) { mwSize dims[3]; dims[0] = pProjGeom.getDetectorColCount(); dims[1] = pProjGeom.getProjectionCount(); dims[2] = pProjGeom.getDetectorRowCount(); // Allocate uninitialized mxArray of size dims. // (It will be zeroed by CudaForwardProjectionAlgorithm3D) const mwSize zero_dims[2] = {0, 0}; pOutputMx = mxCreateNumericArray(2, zero_dims, mxSINGLE_CLASS, mxREAL); mxSetDimensions(pOutputMx, dims, 3); const mwSize num_elems = mxGetNumberOfElements(pOutputMx); const mwSize elem_size = mxGetElementSize(pOutputMx); mxSetData(pOutputMx, mxMalloc(elem_size * num_elems)); astra::CDataStorage* m = new CDataMemory_simple((float *)mxGetData(pOutputMx)); pOutput = new astra::CFloat32ProjectionData3D(pProjGeom, m); } else { size_t dataSize = pProjGeom.getDetectorColCount(); dataSize *= pProjGeom.getProjectionCount(); dataSize *= pProjGeom.getDetectorRowCount(); CDataStorage* pStorage = new CDataMemory(dataSize); pOutput = new astra::CFloat32ProjectionData3D(pProjGeom, pStorage); } // Perform FP astra::CCudaForwardProjectionAlgorithm3D* pAlg; pAlg = new astra::CCudaForwardProjectionAlgorithm3D(); pAlg->initialize(pProjector, pOutput, pInput); if (!pAlg->isInitialized()) { // TODO: Delete pOutputMx? delete pAlg; delete pInput; delete pOutput; mexErrMsgWithAstraLog("Error initializing algorithm."); } pAlg->run(); delete pAlg; if (mxIsSingle(data)) { } else { pOutputMx = createEquivMexArray(pOutput); copyCFloat32ArrayToMex(pOutput->getFloat32Memory(), pOutputMx); } plhs[0] = pOutputMx; delete pOutput; delete pInput; } //----------------------------------------------------------------------------------------- /** * projection = astra_mex_direct_c('BP3D', projector_id, volume); * Both 'projection' and 'volume' are Matlab arrays. */ void astra_mex_direct_bp3d(int& nlhs, mxArray* plhs[], int& nrhs, const mxArray* prhs[]) { // TODO: Add an optional way of specifying extra options if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. Syntax: astra_mex_direct_c('BP3D', projector_id, data);"); } int iPid = (int)(mxGetScalar(prhs[1])); astra::CProjector3D* pProjector; pProjector = astra::CProjector3DManager::getSingleton().get(iPid); if (!pProjector) { mexErrMsgTxt("Projector not found."); } if (!pProjector->isInitialized()) { mexErrMsgTxt("Projector exists but is not initialized."); } bool isCuda = false; if (dynamic_cast(pProjector)) isCuda = true; if (!isCuda) { mexErrMsgTxt("Only CUDA projectors are currently supported."); } const astra::CVolumeGeometry3D &pVolGeom = pProjector->getVolumeGeometry(); const astra::CProjectionGeometry3D &pProjGeom = pProjector->getProjectionGeometry(); const mxArray* const data = prhs[2]; if (!checkDataType(data)) { mexErrMsgTxt("Data type must be single or double."); } if (!checkDataSize(data, &pProjGeom)) { mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry."); } // Allocate input data astra::CFloat32ProjectionData3D* pInput; if (mxIsSingle(data)) { astra::CDataStorage* m = new CDataMemory_simple((float *)mxGetData(data)); pInput = new astra::CFloat32ProjectionData3D(pProjGeom, m); } else { size_t dataSize = pProjGeom.getDetectorColCount(); dataSize *= pProjGeom.getProjectionCount(); dataSize *= pProjGeom.getDetectorRowCount(); CDataStorage* pStorage = new CDataMemory(dataSize); pInput = new astra::CFloat32ProjectionData3D(pProjGeom, pStorage); copyMexToCFloat32Array(data, pInput->getFloat32Memory(), dataSize); } // Allocate output data // If the input is single, we also allocate single output. // Otherwise, double. astra::CFloat32VolumeData3D* pOutput; mxArray *pOutputMx; if (mxIsSingle(data)) { mwSize dims[3]; dims[0] = pVolGeom.getGridColCount(); dims[1] = pVolGeom.getGridRowCount(); dims[2] = pVolGeom.getGridSliceCount(); // Allocate uninitialized mxArray of size dims. // (It will be zeroed by CudaBackProjectionAlgorithm3D) const mwSize zero_dims[2] = {0, 0}; pOutputMx = mxCreateNumericArray(2, zero_dims, mxSINGLE_CLASS, mxREAL); mxSetDimensions(pOutputMx, dims, 3); const mwSize num_elems = mxGetNumberOfElements(pOutputMx); const mwSize elem_size = mxGetElementSize(pOutputMx); mxSetData(pOutputMx, mxMalloc(elem_size * num_elems)); astra::CDataStorage* m = new CDataMemory_simple((float *)mxGetData(pOutputMx)); pOutput = new astra::CFloat32VolumeData3D(pVolGeom, m); } else { size_t dataSize = pVolGeom.getGridColCount(); dataSize *= pVolGeom.getGridRowCount(); dataSize *= pVolGeom.getGridSliceCount(); CDataStorage* pStorage = new CDataMemory(dataSize); pOutput = new astra::CFloat32VolumeData3D(pVolGeom, pStorage); } // Perform BP astra::CCudaBackProjectionAlgorithm3D* pAlg; pAlg = new astra::CCudaBackProjectionAlgorithm3D(); pAlg->initialize(pProjector, pInput, pOutput); if (!pAlg->isInitialized()) { // TODO: Delete pOutputMx? delete pAlg; delete pInput; delete pOutput; mexErrMsgWithAstraLog("Error initializing algorithm."); } pAlg->run(); delete pAlg; if (mxIsSingle(data)) { } else { pOutputMx = createEquivMexArray(pOutput); copyCFloat32ArrayToMex(pOutput->getFloat32Memory(), pOutputMx); } plhs[0] = pOutputMx; delete pOutput; delete pInput; } #endif //----------------------------------------------------------------------------------------- static void printHelp() { mexPrintf("Please specify a mode of operation.\n"); mexPrintf("Valid modes: FP3D, BP3D\n"); } //----------------------------------------------------------------------------------------- /** * ... = astra_mex_direct_c(mode,...); */ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // INPUT: Mode string sMode; if (1 <= nrhs) { sMode = mexToString(prhs[0]); } else { printHelp(); return; } #ifndef ASTRA_CUDA mexErrMsgTxt("Only CUDA projectors are currently supported."); #else // 3D data if (sMode == "FP3D") { astra_mex_direct_fp3d(nlhs, plhs, nrhs, prhs); } else if (sMode == "BP3D") { astra_mex_direct_bp3d(nlhs, plhs, nrhs, prhs); } else { printHelp(); } #endif return; } astra-toolbox-2.3.0/matlab/mex/astra_mex_log_c.cpp000066400000000000000000000225111475635207100222100ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ /** \file astra_mex_log_c.cpp * * \brief Manages astra logging */ #include #include "mexHelpFunctions.h" #include "mexInitFunctions.h" #include "astra/Logging.h" using namespace std; using namespace astra; //----------------------------------------------------------------------------------------- /** astra_mex_log('debug', file, line, message); * * Log a debug message. * file: Originating file name * line: Originating line number * message: Log message. */ void astra_mex_log_debug(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 4) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } string filename = mexToString(prhs[1]); int linenumber = (int)mxGetScalar(prhs[2]); string message = mexToString(prhs[3]); astra::CLogger::debug(filename.c_str(),linenumber,"%s",message.c_str()); } //----------------------------------------------------------------------------------------- /** astra_mex_log('info', file, line, message); * * Log an info message. * file: Originating file name * line: Originating line number * message: Log message. */ void astra_mex_log_info(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 4) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } string filename = mexToString(prhs[1]); int linenumber = (int)mxGetScalar(prhs[2]); string message = mexToString(prhs[3]); astra::CLogger::info(filename.c_str(),linenumber,"%s",message.c_str()); } //----------------------------------------------------------------------------------------- /** astra_mex_log('warn', file, line, message); * * Log a warning message. * file: Originating file name * line: Originating line number * message: Log message. */ void astra_mex_log_warn(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 4) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } string filename = mexToString(prhs[1]); int linenumber = (int)mxGetScalar(prhs[2]); string message = mexToString(prhs[3]); astra::CLogger::warn(filename.c_str(),linenumber,"%s",message.c_str()); } //----------------------------------------------------------------------------------------- /** astra_mex_log('error', file, line, message); * * Log an error message. * file: Originating file name * line: Originating line number * message: Log message. */ void astra_mex_log_error(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 4) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); return; } string filename = mexToString(prhs[1]); int linenumber = (int)mxGetScalar(prhs[2]); string message = mexToString(prhs[3]); astra::CLogger::error(filename.c_str(),linenumber,"%s",message.c_str()); } //----------------------------------------------------------------------------------------- /** astra_mex_log('enable', type); * * Enable logging. * type: which output to enable ('all', 'file', 'screen') */ void astra_mex_log_enable(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } string sType = mexToString(prhs[1]); if(sType == "all"){ astra::CLogger::enable(); }else if(sType == "file"){ astra::CLogger::enableFile(); }else if(sType == "screen"){ astra::CLogger::enableScreen(); } else { mexErrMsgTxt("Specify which output to enable ('all', 'file', or 'screen')."); } } //----------------------------------------------------------------------------------------- /** astra_mex_log('disable', type); * * Disable logging. * type: which output to disable ('all', 'file', 'screen') */ void astra_mex_log_disable(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } string sType = mexToString(prhs[1]); if(sType == "all"){ astra::CLogger::disable(); }else if(sType == "file"){ astra::CLogger::disableFile(); }else if(sType == "screen"){ astra::CLogger::disableScreen(); } else { mexErrMsgTxt("Specify which output to disable ('all', 'file', or 'screen')."); } } //----------------------------------------------------------------------------------------- /** astra_mex_log('format', type, fmt); * * Enable logging. * type: which output to format ('file', 'screen') * fmt: format string * Here are the substitutions you may use: * %f: Source file name generating the log call. * %n: Source line number where the log call was made. * %m: The message text sent to the logger (after printf formatting). * %d: The current date, formatted using the logger's date format. * %t: The current time, formatted using the logger's time format. * %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR"). * %%: A literal percent sign. * The default format string is "%d %t %f(%n): %l: %m\n". */ void astra_mex_log_format(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } string sType = mexToString(prhs[1]); string sFormat = mexToString(prhs[2]); if (!sFormat.empty()) { char lastChar = *sFormat.rbegin(); if (lastChar!='\n'){ sFormat += '\n'; } }else{ sFormat += '\n'; } if(sType == "file"){ astra::CLogger::setFormatFile(sFormat.c_str()); }else if(sType == "screen"){ astra::CLogger::setFormatScreen(sFormat.c_str()); } else { mexErrMsgTxt("Specify which output to format ('file' or 'screen')."); } } //----------------------------------------------------------------------------------------- /** astra_mex_log('output', type, output, level); * * Set output file / output screen. * type: which output to set ('file', 'screen') * output: which output file / screen to use: * 'file': filename * 'screen': 'stdout' or 'stderr' * level: logging level to use ('debug', 'info', 'warn', or 'error') */ void astra_mex_log_output(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 4) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } string sType = mexToString(prhs[1]); string sOutput = mexToString(prhs[2]); string sLevel = mexToString(prhs[3]); log_level eLevel; if(sLevel == "debug"){ eLevel = LOG_DEBUG; }else if(sLevel == "info"){ eLevel = LOG_INFO; }else if(sLevel == "warn"){ eLevel = LOG_WARN; }else if(sLevel == "error"){ eLevel = LOG_ERROR; }else{ mexErrMsgTxt("Specify which log level to use ('debug', 'info', 'warn', or 'error')."); } if(sType == "file"){ astra::CLogger::setOutputFile(sOutput.c_str(),eLevel); }else if(sType == "screen"){ int fd; if(sOutput == "stdout"){ fd=1; }else if(sOutput == "stderr"){ fd=2; }else{ mexErrMsgTxt("Specify which screen to output to ('stdout' or 'stderr')."); } astra::CLogger::setOutputScreen(fd,eLevel); } else { mexErrMsgTxt("Specify which output to set ('file' or 'screen')."); } } //----------------------------------------------------------------------------------------- static void printHelp() { mexPrintf("Please specify a mode of operation.\n"); mexPrintf("Valid modes: debug, info, warn, error, enable, disable, format, output\n"); } //----------------------------------------------------------------------------------------- /** * ... = astra_mex_log(mode, ...); */ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // INPUT: Mode string sMode = ""; if (1 <= nrhs) { sMode = mexToString(prhs[0]); } else { printHelp(); return; } initASTRAMex(); // SWITCH (MODE) if (sMode == "debug") { astra_mex_log_debug(nlhs, plhs, nrhs, prhs); }else if (sMode == "info") { astra_mex_log_info(nlhs, plhs, nrhs, prhs); }else if (sMode == "warn") { astra_mex_log_warn(nlhs, plhs, nrhs, prhs); }else if (sMode == "error") { astra_mex_log_error(nlhs, plhs, nrhs, prhs); }else if (sMode == "enable") { astra_mex_log_enable(nlhs, plhs, nrhs, prhs); }else if (sMode == "disable") { astra_mex_log_disable(nlhs, plhs, nrhs, prhs); }else if (sMode == "format") { astra_mex_log_format(nlhs, plhs, nrhs, prhs); }else if (sMode == "output") { astra_mex_log_output(nlhs, plhs, nrhs, prhs); } else { printHelp(); } return; } astra-toolbox-2.3.0/matlab/mex/astra_mex_matrix_c.cpp000066400000000000000000000305431475635207100227370ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ /** \file astra_mex_matrix_c.cpp * * \brief Create sparse (projection) matrices in the ASTRA workspace */ #include #include "mexHelpFunctions.h" #include "mexInitFunctions.h" #include #include "astra/Globals.h" #include "astra/AstraObjectManager.h" #include "astra/SparseMatrix.h" using namespace std; using namespace astra; //----------------------------------------------------------------------------------------- /** astra_mex_matrix('delete', id1, id2, ...); * * Delete one or more data objects currently stored in the astra-library. * id1, id2, ... : identifiers of the 2d data objects as stored in the astra-library. */ void astra_mex_matrix_delete(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: read input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } // step2: delete all specified data objects for (int i = 1; i < nrhs; i++) { int iDataID = (int)(mxGetScalar(prhs[i])); CMatrixManager::getSingleton().remove(iDataID); } } //----------------------------------------------------------------------------------------- /** astra_mex_matrix('clear'); * * Delete all data objects currently stored in the astra-library. */ void astra_mex_matrix_clear(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { CMatrixManager::getSingleton().clear(); } static bool matlab_to_astra(const mxArray* _rhs, CSparseMatrix* _pMatrix) { // Check input if (!mxIsSparse (_rhs)) { mexErrMsgTxt("Argument is not a valid MATLAB sparse matrix."); } if (!_pMatrix->isInitialized()) { mexErrMsgTxt("Couldn't initialize data object."); } unsigned int iHeight = mxGetM(_rhs); unsigned int iWidth = mxGetN(_rhs); unsigned long lSize = mxGetNzmax(_rhs); if (_pMatrix->m_lSize < lSize || _pMatrix->m_iHeight < iHeight) { // TODO: support resizing? mexErrMsgTxt("Matrix too large to store in this object."); } // Transpose matrix, as matlab stores a matrix column-by-column // but we want it row-by-row. // 1. Compute sizes of rows. We store these in _pMatrix->m_plRowStarts. // 2. Fill data structure // Complexity: O( #rows + #entries ) for (unsigned int i = 0; i <= iHeight; ++i) _pMatrix->m_plRowStarts[i] = 0; mwIndex *colStarts = mxGetJc(_rhs); mwIndex *rowIndices = mxGetIr(_rhs); double *floatValues = 0; mxLogical *boolValues = 0; bool bLogical = mxIsLogical(_rhs); if (bLogical) boolValues = mxGetLogicals(_rhs); else floatValues = mxGetPr(_rhs); for (mwIndex i = 0; i < colStarts[iWidth]; ++i) { unsigned int iRow = rowIndices[i]; assert(iRow < iHeight); _pMatrix->m_plRowStarts[iRow+1]++; } // Now _pMatrix->m_plRowStarts[i+1] is the number of entries in row i for (unsigned int i = 1; i <= iHeight; ++i) _pMatrix->m_plRowStarts[i] += _pMatrix->m_plRowStarts[i-1]; // Now _pMatrix->m_plRowStarts[i+1] is the number of entries in rows <= i, // so the intended start of row i+1 int iCol = 0; for (mwIndex i = 0; i < colStarts[iWidth]; ++i) { while (i >= colStarts[iCol+1]) iCol++; unsigned int iRow = rowIndices[i]; assert(iRow < iHeight); float32 fVal; if (bLogical) fVal = (float32)boolValues[i]; else fVal = (float32)floatValues[i]; unsigned long lIndex = _pMatrix->m_plRowStarts[iRow]++; _pMatrix->m_pfValues[lIndex] = fVal; _pMatrix->m_piColIndices[lIndex] = iCol; } // Now _pMatrix->m_plRowStarts[i] is the start of row i+1 for (unsigned int i = iHeight; i > 0; --i) _pMatrix->m_plRowStarts[i] = _pMatrix->m_plRowStarts[i-1]; _pMatrix->m_plRowStarts[0] = 0; #if 0 // Debugging: dump matrix for (unsigned int i = 0; i < iHeight; ++i) { printf("Row %d: %ld-%ld\n", i, _pMatrix->m_plRowStarts[i], _pMatrix->m_plRowStarts[i+1]); for (unsigned long j = _pMatrix->m_plRowStarts[i]; j < _pMatrix->m_plRowStarts[i+1]; ++j) { printf("(%d,%d) = %f\n", i, _pMatrix->m_piColIndices[j], _pMatrix->m_pfValues[j]); } } #endif return true; } static bool astra_to_matlab(const CSparseMatrix* _pMatrix, mxArray*& _lhs) { if (!_pMatrix->isInitialized()) { mexErrMsgTxt("Uninitialized data object."); } unsigned int iHeight = _pMatrix->m_iHeight; unsigned int iWidth = _pMatrix->m_iWidth; unsigned long lSize = _pMatrix->m_lSize; _lhs = mxCreateSparse(iHeight, iWidth, lSize, mxREAL); if (!mxIsSparse (_lhs)) { mexErrMsgTxt("Couldn't initialize matlab sparse matrix."); } mwIndex *colStarts = mxGetJc(_lhs); mwIndex *rowIndices = mxGetIr(_lhs); double *floatValues = mxGetPr(_lhs); for (unsigned int i = 0; i <= iWidth; ++i) colStarts[i] = 0; for (unsigned int i = 0; i < _pMatrix->m_plRowStarts[iHeight]; ++i) { unsigned int iCol = _pMatrix->m_piColIndices[i]; assert(iCol < iWidth); colStarts[iCol+1]++; } // Now _pMatrix->m_plRowStarts[i+1] is the number of entries in row i for (unsigned int i = 1; i <= iWidth; ++i) colStarts[i] += colStarts[i-1]; // Now _pMatrix->m_plRowStarts[i+1] is the number of entries in rows <= i, // so the intended start of row i+1 unsigned int iRow = 0; for (unsigned int i = 0; i < _pMatrix->m_plRowStarts[iHeight]; ++i) { while (i >= _pMatrix->m_plRowStarts[iRow+1]) iRow++; unsigned int iCol = _pMatrix->m_piColIndices[i]; assert(iCol < iWidth); double fVal = _pMatrix->m_pfValues[i]; unsigned long lIndex = colStarts[iCol]++; floatValues[lIndex] = fVal; rowIndices[lIndex] = iRow; } // Now _pMatrix->m_plRowStarts[i] is the start of row i+1 for (unsigned int i = iWidth; i > 0; --i) colStarts[i] = colStarts[i-1]; colStarts[0] = 0; return true; } //----------------------------------------------------------------------------------------- /** id = astra_mex_matrix('create', data); * * Create a new matrix object in the astra-library. * data: a sparse MATLAB matrix containing the data. * id: identifier of the matrix object as it is now stored in the astra-library. */ void astra_mex_matrix_create(int& nlhs, mxArray* plhs[], int& nrhs, const mxArray* prhs[]) { // step1: get datatype if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsSparse (prhs[1])) { mexErrMsgTxt("Argument is not a valid MATLAB sparse matrix."); } unsigned int iHeight = mxGetM(prhs[1]); unsigned int iWidth = mxGetN(prhs[1]); unsigned long lSize = mxGetNzmax(prhs[1]); CSparseMatrix* pMatrix = new CSparseMatrix(iHeight, iWidth, lSize); // Check initialization if (!pMatrix->isInitialized()) { delete pMatrix; mexErrMsgTxt("Couldn't initialize data object."); } bool bResult = matlab_to_astra(prhs[1], pMatrix); if (!bResult) { delete pMatrix; mexErrMsgWithAstraLog("Failed to create data object."); } // store data object int iIndex = CMatrixManager::getSingleton().store(pMatrix); // return data id if (1 <= nlhs) { plhs[0] = mxCreateDoubleScalar(iIndex); } } //----------------------------------------------------------------------------------------- /** astra_mex_matrix('store', id, data); * * Store a sparse MATLAB matrix in an existing astra matrix dataobject. * id: identifier of the 2d data object as stored in the astra-library. * data: a sparse MATLAB matrix. */ void astra_mex_matrix_store(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: input if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value."); } int iDataID = (int)(mxGetScalar(prhs[1])); // step2: get data object CSparseMatrix* pMatrix = astra::CMatrixManager::getSingleton().get(iDataID); if (!pMatrix || !pMatrix->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly.\n"); return; } bool bResult = matlab_to_astra(prhs[2], pMatrix); if (!bResult) { mexErrMsgWithAstraLog("Failed to store matrix."); } } //----------------------------------------------------------------------------------------- /** geom = astra_mex_matrix('get_size', id); * * Fetch the dimensions and size of a matrix stored in the astra-library. * id: identifier of the 2d data object as stored in the astra-library. * geom: a 1x2 matrix containing [rows, columns] */ void astra_mex_matrix_get_size(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value."); } int iDataID = (int)(mxGetScalar(prhs[1])); // step2: get data object CSparseMatrix* pMatrix = astra::CMatrixManager::getSingleton().get(iDataID); if (!pMatrix || !pMatrix->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly."); } // create output // TODO } //----------------------------------------------------------------------------------------- /** data = astra_mex_matrix('get', id); * * Fetch data from the astra-library to a MATLAB matrix. * id: identifier of the matrix data object as stored in the astra-library. * data: MATLAB */ void astra_mex_matrix_get(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: check input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value."); } int iDataID = (int)(mxGetScalar(prhs[1])); // step2: get data object CSparseMatrix* pMatrix = astra::CMatrixManager::getSingleton().get(iDataID); if (!pMatrix || !pMatrix->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly."); } // create output if (1 <= nlhs) { bool bResult = astra_to_matlab(pMatrix, plhs[0]); if (!bResult) { mexErrMsgWithAstraLog("Failed to get matrix."); } } } //----------------------------------------------------------------------------------------- /** astra_mex_matrix('info'); * * Print information about all the matrix objects currently stored in the astra-library. */ void astra_mex_matrix_info(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { mexPrintf("%s", astra::CMatrixManager::getSingleton().info().c_str()); } //----------------------------------------------------------------------------------------- static void printHelp() { mexPrintf("Please specify a mode of operation.\n"); mexPrintf("Valid modes: get, delete, clear, store, create, get_size, info\n"); } //----------------------------------------------------------------------------------------- /** * ... = astra_mex_matrix(type,...); */ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // INPUT0: Mode string sMode = ""; if (1 <= nrhs) { sMode = mexToString(prhs[0]); } else { printHelp(); return; } initASTRAMex(); // SWITCH (MODE) if (sMode == std::string("get")) { astra_mex_matrix_get(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("delete")) { astra_mex_matrix_delete(nlhs, plhs, nrhs, prhs); } else if (sMode == "clear") { astra_mex_matrix_clear(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("store")) { astra_mex_matrix_store(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("create")) { astra_mex_matrix_create(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("get_size")) { astra_mex_matrix_get_size(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("info")) { astra_mex_matrix_info(nlhs, plhs, nrhs, prhs); } else { printHelp(); } return; } astra-toolbox-2.3.0/matlab/mex/astra_mex_plugin_c.cpp000066400000000000000000000135731475635207100227350ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ /** \file astra_mex_plugin_c.cpp * * \brief Manages Python plugins. */ #include #include "mexHelpFunctions.h" #include "mexInitFunctions.h" #include "astra/PluginAlgorithmFactory.h" #include using namespace std; using namespace astra; static void fixLapackLoading() { // When running in Matlab, we need to force numpy // to use its internal lapack library instead of // Matlab's MKL library to avoid errors. To do this, // we set Python's dlopen flags to RTLD_NOW|RTLD_DEEPBIND // and import 'numpy.linalg.lapack_lite' here. We reset // Python's dlopen flags afterwards. PyObject *sys = PyImport_ImportModule("sys"); if (sys != NULL) { PyObject *curFlags = PyObject_CallMethod(sys, "getdlopenflags", NULL); if (curFlags != NULL) { PyObject *retVal = PyObject_CallMethod(sys, "setdlopenflags", "i", 10); // RTLD_NOW|RTLD_DEEPBIND if (retVal != NULL) { PyObject *lapack = PyImport_ImportModule("numpy.linalg.lapack_lite"); if (lapack != NULL) { Py_DECREF(lapack); } PyObject *retVal2 = PyObject_CallMethod(sys, "setdlopenflags", "O",curFlags); if (retVal2 != NULL) { Py_DECREF(retVal2); } Py_DECREF(retVal); } Py_DECREF(curFlags); } Py_DECREF(sys); } } //----------------------------------------------------------------------------------------- /** astra_mex_plugin('init'); * * Initialize plugin support by initializing python and importing astra */ void astra_mex_plugin_init() { if(!Py_IsInitialized()){ Py_Initialize(); PyEval_InitThreads(); } #ifndef _MSC_VER fixLapackLoading(); #endif // Importing astra may be overkill, since we only need to initialize // PythonPluginAlgorithmFactory from astra.plugin_c. PyObject *mod = PyImport_ImportModule("astra"); Py_XDECREF(mod); } //----------------------------------------------------------------------------------------- /** astra_mex_plugin('get_registered'); * * Print registered plugins. */ void astra_mex_plugin_get_registered(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { astra::CPluginAlgorithmFactory *fact = astra::CPluginAlgorithmFactory::getFactory(); if (!fact) { mexPrintf("Plugin support not initialized."); return; } std::map mp = fact->getRegisteredMap(); for(std::map::iterator it=mp.begin();it!=mp.end();it++){ mexPrintf("%s: %s\n",it->first.c_str(), it->second.c_str()); } } //----------------------------------------------------------------------------------------- /** astra_mex_plugin('register', class_name); * * Register plugin. */ void astra_mex_plugin_register(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { astra::CPluginAlgorithmFactory *fact = astra::CPluginAlgorithmFactory::getFactory(); if (!fact) { mexPrintf("Plugin support not initialized."); return; } if (2 <= nrhs) { string class_name = mexToString(prhs[1]); fact->registerPlugin(class_name); }else{ mexPrintf("astra_mex_plugin('register', class_name);\n"); } } //----------------------------------------------------------------------------------------- /** astra_mex_plugin('get_help', name); * * Get help about plugin. */ void astra_mex_plugin_get_help(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { astra::CPluginAlgorithmFactory *fact = astra::CPluginAlgorithmFactory::getFactory(); if (!fact) { mexPrintf("Plugin support not initialized."); return; } if (2 <= nrhs) { string name = mexToString(prhs[1]); mexPrintf((fact->getHelp(name)+"\n").c_str()); }else{ mexPrintf("astra_mex_plugin('get_help', name);\n"); } } //----------------------------------------------------------------------------------------- static void printHelp() { mexPrintf("Please specify a mode of operation.\n"); mexPrintf(" Valid modes: register, get_registered, get_help\n"); } //----------------------------------------------------------------------------------------- /** * ... = astra_mex(type,...); */ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // INPUT0: Mode string sMode = ""; if (1 <= nrhs) { sMode = mexToString(prhs[0]); } else { printHelp(); return; } initASTRAMex(); // SWITCH (MODE) if (sMode == "init") { astra_mex_plugin_init(); } else if (sMode == std::string("get_registered")) { astra_mex_plugin_get_registered(nlhs, plhs, nrhs, prhs); }else if (sMode == std::string("get_help")) { astra_mex_plugin_get_help(nlhs, plhs, nrhs, prhs); }else if (sMode == std::string("register")) { astra_mex_plugin_register(nlhs, plhs, nrhs, prhs); } else { printHelp(); } return; } astra-toolbox-2.3.0/matlab/mex/astra_mex_projector3d_c.cpp000066400000000000000000000173601475635207100236730ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ /** \file astra_mex_projector3d_c.cpp * * \brief Create and manage 3d projectors in the ASTRA workspace */ #include #include "mexHelpFunctions.h" #include "mexInitFunctions.h" #include "astra/Globals.h" #include "astra/Projector3D.h" #include "astra/AstraObjectManager.h" #include "astra/AstraObjectFactory.h" #include "astra/ProjectionGeometry3D.h" #include "astra/VolumeGeometry3D.h" #include #include using namespace std; using namespace astra; //----------------------------------------------------------------------------------------- /** * [pid] = astra_mex_projector('create', cfgstruct); */ void astra_mex_projector3d_create(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsStruct(prhs[1])) { mexErrMsgTxt("Argument 1 not a valid MATLAB struct."); } // turn MATLAB struct to an XML-based Config object XMLConfig* cfg = structToConfig("Projector3D", prhs[1]); // create algorithm CProjector3D* pProj = CProjector3DFactory::getSingleton().create(cfg->self.getAttribute("type")); if (pProj == NULL) { delete cfg; mexErrMsgTxt("Unknown Projector3D type."); } // create algorithm if (!pProj->initialize(*cfg)) { delete cfg; delete pProj; mexErrMsgWithAstraLog("Unable to initialize Projector3D."); } delete cfg; // store projector int iIndex = CProjector3DManager::getSingleton().store(pProj); // step4: set output if (1 <= nlhs) { plhs[0] = mxCreateDoubleScalar(iIndex); } } //----------------------------------------------------------------------------------------- /** * astra_mex_projector3d('destroy', pid1, pid2, ...); */ void astra_mex_projector3d_destroy(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: read input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } for (int i = 1; i < nrhs; i++) { int iPid = (int)(mxGetScalar(prhs[i])); CProjector3DManager::getSingleton().remove(iPid); } } //----------------------------------------------------------------------------------------- /** * astra_mex_projector3d('clear'); */ void astra_mex_projector3d_clear(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { CProjector3DManager::getSingleton().clear(); } //----------------------------------------------------------------------------------------- /** * [proj_geom] = astra_mex_projector3d('get_projection_geometry', pid); */ void astra_mex_projector3d_get_projection_geometry(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: read input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } int iPid = (int)(mxGetScalar(prhs[1])); // step2: get projector CProjector3D* pProjector = CProjector3DManager::getSingleton().get(iPid); if (!pProjector || !pProjector->isInitialized()) { mexErrMsgTxt("Projector could not be found or is not initialized."); } // step3: get projection_geometry and turn it into a MATLAB struct if (1 <= nlhs) { Config *cfg = pProjector->getProjectionGeometry().getConfiguration(); plhs[0] = configToStruct(cfg); delete cfg; } } //----------------------------------------------------------------------------------------- /** * [recon_geom] = astra_mex_projector3d('get_volume_geometry', pid); */ void astra_mex_projector3d_get_volume_geometry(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: read input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } int iPid = (int)(mxGetScalar(prhs[1])); // step2: get projector CProjector3D* pProjector = CProjector3DManager::getSingleton().get(iPid); if (!pProjector || !pProjector->isInitialized()) { mexErrMsgTxt("Projector could not be found or is not initialized."); } // step3: get projection_geometry and turn it into a MATLAB struct if (1 <= nlhs) { Config *cfg = pProjector->getVolumeGeometry().getConfiguration(); plhs[0] = configToStruct(cfg); delete cfg; } } //----------------------------------------------------------------------------------------- /** result = astra_mex_projector3d('is_cuda', id); * * Return is the specified projector is a cuda projector. * id: identifier of the projector object as stored in the astra-library. */ void astra_mex_projector3d_is_cuda(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: get input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } int iPid = (int)(mxGetScalar(prhs[1])); // step2: get projector CProjector3D* pProjector = CProjector3DManager::getSingleton().get(iPid); if (!pProjector || !pProjector->isInitialized()) { mexErrMsgTxt("Projector could not be found or is not initialized."); } #ifdef ASTRA_CUDA CCudaProjector3D* pCP = dynamic_cast(pProjector); plhs[0] = mxCreateLogicalScalar(pCP ? 1 : 0); #else plhs[0] = mxCreateLogicalScalar(0); #endif } //----------------------------------------------------------------------------------------- /** * astra_mex_projector3d('help'); */ void astra_mex_projector3d_help(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { cout << "astra_mex_projector3d help:" <. ----------------------------------------------------------------------- */ /** \file astra_mex_projector_c.cpp * * \brief Create and manage 2d projectors in the ASTRA workspace */ #include "astra/Globals.h" #include #include "mexHelpFunctions.h" #include "mexInitFunctions.h" #include "astra/AstraObjectManager.h" #include "astra/Projector2D.h" #include "astra/AstraObjectFactory.h" #include "astra/Float32VolumeData2D.h" #include "astra/ProjectionGeometry2D.h" #include "astra/ParallelProjectionGeometry2D.h" #include "astra/VolumeGeometry2D.h" #include #include using namespace std; using namespace astra; //----------------------------------------------------------------------------------------- /** id = astra_mex_projector('create', cfg); * * Create and configure a new projector object. * cfg: MATLAB struct containing the configuration parameters, see doxygen documentation for details. * id: identifier of the projector object as it is now stored in the astra-library. */ void astra_mex_projector_create(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { int iIndex = 0; if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } if (!mxIsStruct(prhs[1])) { mexErrMsgTxt("Argument 1 not a valid MATLAB struct."); } // turn MATLAB struct to an XML-based Config object XMLConfig* cfg = structToConfig("Projector2D", prhs[1]); // create algorithm CProjector2D* pProj = CProjector2DFactory::getSingleton().create(cfg->self.getAttribute("type")); if (pProj == NULL) { delete cfg; mexErrMsgTxt("Unknown Projector2D type."); } // create algorithm if (!pProj->initialize(*cfg)) { delete cfg; delete pProj; mexErrMsgWithAstraLog("Unable to initialize Projector2D."); } delete cfg; // store projector iIndex = CProjector2DManager::getSingleton().store(pProj); // step4: set output if (1 <= nlhs) { plhs[0] = mxCreateDoubleScalar(iIndex); } } //----------------------------------------------------------------------------------------- /** astra_mex_projector('delete', id1, id2, ...); * * Delete one or more projector objects currently stored in the astra-library. * id1, id2, ... : identifiers of the projector objects as stored in the astra-library. */ void astra_mex_projector_delete(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: read input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } for (int i = 1; i < nrhs; i++) { int iPid = (int)(mxGetScalar(prhs[i])); CProjector2DManager::getSingleton().remove(iPid); } } //----------------------------------------------------------------------------------------- /** astra_mex_projector('clear'); * * Delete all projector objects currently stored in the astra-library. */ void astra_mex_projector_clear(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { CProjector2DManager::getSingleton().clear(); } //----------------------------------------------------------------------------------------- /** astra_mex_projector('info'); * * Print information about all the projector objects currently stored in the astra-library. */ void astra_mex_projector_info(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { mexPrintf("%s", astra::CProjector2DManager::getSingleton().info().c_str()); } //----------------------------------------------------------------------------------------- /** proj_geom = astra_mex_projector('projection_geometry', id); * * Fetch the projection geometry of a certain projector. * id: identifier of the projector object as stored in the astra-library. * proj_geom: MATLAB struct containing all information about the projection geometry */ void astra_mex_projector_projection_geometry(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: read input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } int iPid = (int)(mxGetScalar(prhs[1])); // step2: get projector CProjector2D* pProjector = CProjector2DManager::getSingleton().get(iPid); if (!pProjector || !pProjector->isInitialized()) { mexErrMsgTxt("Projector could not be found or is not initialized."); } // step3: get projection_geometry and turn it into a MATLAB struct if (1 <= nlhs) { Config *cfg = pProjector->getProjectionGeometry().getConfiguration(); plhs[0] = configToStruct(cfg); delete cfg; } } //----------------------------------------------------------------------------------------- /** vol_geom = astra_mex_projector('volume_geometry', id); * * Fetch the volume geometry of a certain projector. * id: identifier of the projector object as stored in the astra-library. * vol_geom: MATLAB struct containing all information about the volume geometry */ void astra_mex_projector_volume_geometry(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: read input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } int iPid = (int)(mxGetScalar(prhs[1])); // step2: get projector CProjector2D* pProjector = CProjector2DManager::getSingleton().get(iPid); if (!pProjector || !pProjector->isInitialized()) { mexErrMsgTxt("Projector could not be found or is not initialized."); } // step3: get projection_geometry and turn it into a MATLAB struct if (1 <= nlhs) { Config *cfg = pProjector->getVolumeGeometry().getConfiguration(); plhs[0] = configToStruct(cfg); delete cfg; } } //----------------------------------------------------------------------------------------- /** weights = astra_mex_projector('weights_single_ray', id, projection_index, detector_index); * * Calculate the nonzero weights of a certain projection ray. * id: identifier of the projector object as stored in the astra-library. * projection_index: index of the projection angle * detector_index: index of the detector * weights: list of computed weights [pixel_index, weight] */ void astra_mex_projector_weights_single_ray(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: get input if (nrhs < 4) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } int iPid = (int)(mxGetScalar(prhs[1])); int iProjectionIndex = (int)(mxGetScalar(prhs[2])); int iDetectorIndex = (int)(mxGetScalar(prhs[3])); // step2: get projector CProjector2D* pProjector = CProjector2DManager::getSingleton().get(iPid); if (!pProjector || !pProjector->isInitialized()) { mexErrMsgTxt("Projector could not be found or is not initialized."); } // step3: create output vars int iStoredPixelCount; int iMaxPixelCount = pProjector->getProjectionWeightsCount(iProjectionIndex); SPixelWeight* pPixelsWeights = new SPixelWeight[iMaxPixelCount]; // step4: perform operation pProjector->computeSingleRayWeights(iProjectionIndex, iDetectorIndex, pPixelsWeights, iMaxPixelCount, iStoredPixelCount); // step5: return output if (1 <= nlhs) { mwSize dims[2]; dims[0] = iStoredPixelCount; dims[1] = 2; plhs[0] = mxCreateNumericArray(2, dims, mxDOUBLE_CLASS, mxREAL); double* out = mxGetPr(plhs[0]); for (int col = 0; col < iStoredPixelCount; col++) { out[col] = pPixelsWeights[col].m_iIndex; out[iStoredPixelCount+col] = pPixelsWeights[col].m_fWeight; } } // garbage collection delete[] pPixelsWeights; } //----------------------------------------------------------------------------------------- /** weights = astra_mex_projector('weights_projection', id, projection_index); * * Calculate the nonzero weights of all rays in a certain projection. * id: identifier of the projector object as stored in the astra-library. * projection_index: index of the projection angle * weights: sparse matrix containing the rows of the projection matric belonging to the requested projection angle. */ void astra_mex_projector_weights_projection(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: get input if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } int iPid = (int)(mxGetScalar(prhs[1])); int iProjectionIndex = (int)(mxGetScalar(prhs[2])); // step2: get projector CProjector2D* pProjector = CProjector2DManager::getSingleton().get(iPid); if (!pProjector || !pProjector->isInitialized()) { mexErrMsgTxt("Projector could not be found or is not initialized."); } // step3: create output vars SPixelWeight* pPixelsWheights = new SPixelWeight[pProjector->getProjectionWeightsCount(iProjectionIndex)]; int* piRayStoredPixelCount = new int[pProjector->getProjectionGeometry().getDetectorCount()]; // step4: perform operation pProjector->computeProjectionRayWeights(iProjectionIndex, pPixelsWheights, piRayStoredPixelCount); // step5: return output if (1 <= nlhs) { // get basic values int iMatrixSize = pProjector->getVolumeGeometry().getWindowLengthX() * pProjector->getVolumeGeometry().getWindowLengthY(); int iDetectorCount = pProjector->getProjectionGeometry().getDetectorCount(); int iTotalStoredPixelCount = 0; for (int i = 0; i < iDetectorCount; i++) { iTotalStoredPixelCount += piRayStoredPixelCount[i]; } // create matlab sparse matrix plhs[0] = mxCreateSparse(iMatrixSize, // number of rows (#pixels) iDetectorCount, // number of columns (#detectors) iTotalStoredPixelCount, // number of non-zero elements mxREAL); // element type double* values = mxGetPr(plhs[0]); mwIndex* rows = mxGetIr(plhs[0]); mwIndex* cols = mxGetJc(plhs[0]); int currentBase = 0; int currentIndex = 0; for (int i = 0; i < iDetectorCount; i++) { for (int j = 0; j < piRayStoredPixelCount[i]; j++) { values[currentIndex + j] = pPixelsWheights[currentBase + j].m_fWeight; rows[currentIndex + j] = pPixelsWheights[currentBase + j].m_iIndex; } currentBase += pProjector->getProjectionWeightsCount(iProjectionIndex) / pProjector->getProjectionGeometry().getDetectorCount(); currentIndex += piRayStoredPixelCount[i]; } cols[0] = piRayStoredPixelCount[0]; for (int j = 1; j < iDetectorCount; j++) { cols[j] = cols[j-1] + piRayStoredPixelCount[j]; } cols[iDetectorCount] = iTotalStoredPixelCount; } delete[] pPixelsWheights; delete[] piRayStoredPixelCount; } //----------------------------------------------------------------------------------------- /** matrix_id = astra_mex_projector('matrix', id); * * Create an explicit projection matrix for this projector. * It returns an ID usable with astra_mex_matrix(). * id: identifier of the projector object as stored in the astra-library. */ void astra_mex_projector_matrix(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: get input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } int iPid = (int)(mxGetScalar(prhs[1])); // step2: get projector CProjector2D* pProjector = CProjector2DManager::getSingleton().get(iPid); if (!pProjector || !pProjector->isInitialized()) { mexErrMsgTxt("Projector could not be found or is not initialized."); } CSparseMatrix* pMatrix = pProjector->getMatrix(); if (!pMatrix || !pMatrix->isInitialized()) { delete pMatrix; mexErrMsgWithAstraLog("Couldn't initialize data object."); } // store data object int iIndex = CMatrixManager::getSingleton().store(pMatrix); // return data id if (1 <= nlhs) { plhs[0] = mxCreateDoubleScalar(iIndex); } } //----------------------------------------------------------------------------------------- /** result = astra_mex_projector('is_cuda', id); * * Return is the specified projector is a cuda projector. * id: identifier of the projector object as stored in the astra-library. */ void astra_mex_projector_is_cuda(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: get input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } int iPid = (int)(mxGetScalar(prhs[1])); // step2: get projector CProjector2D* pProjector = CProjector2DManager::getSingleton().get(iPid); if (!pProjector || !pProjector->isInitialized()) { mexErrMsgTxt("Projector could not be found or is not initialized."); } #ifdef ASTRA_CUDA CCudaProjector2D* pCP = dynamic_cast(pProjector); plhs[0] = mxCreateLogicalScalar(pCP ? 1 : 0); #else plhs[0] = mxCreateLogicalScalar(0); #endif } //----------------------------------------------------------------------------------------- static void printHelp() { mexPrintf("Please specify a mode of operation.\n"); mexPrintf("Valid modes: create, delete, clear, info, projection_geometry,\n"); mexPrintf(" volume_geometry, weights_single_ray, weights_projection\n"); mexPrintf(" matrix, is_cuda\n"); } //----------------------------------------------------------------------------------------- /** * ... = astra_mex_projector(mode, ...); */ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // INPUT: Mode string sMode = ""; if (1 <= nrhs) { sMode = mexToString(prhs[0]); } else { printHelp(); return; } initASTRAMex(); // SWITCH (MODE) if (sMode == "create") { astra_mex_projector_create(nlhs, plhs, nrhs, prhs); } else if (sMode == "delete") { astra_mex_projector_delete(nlhs, plhs, nrhs, prhs); } else if (sMode == "clear") { astra_mex_projector_clear(nlhs, plhs, nrhs, prhs); } else if (sMode == "info") { astra_mex_projector_info(nlhs, plhs, nrhs, prhs); } else if (sMode == "projection_geometry") { astra_mex_projector_projection_geometry(nlhs, plhs, nrhs, prhs); } else if (sMode == "volume_geometry") { astra_mex_projector_volume_geometry(nlhs, plhs, nrhs, prhs); } else if (sMode == "weights_single_ray") { astra_mex_projector_weights_single_ray(nlhs, plhs, nrhs, prhs); } else if (sMode == "weights_projection") { astra_mex_projector_weights_projection(nlhs, plhs, nrhs, prhs); } else if (sMode == "matrix") { astra_mex_projector_matrix(nlhs, plhs, nrhs, prhs); } else if (sMode == "is_cuda") { astra_mex_projector_is_cuda(nlhs, plhs, nrhs, prhs); } else { printHelp(); } return; } astra-toolbox-2.3.0/matlab/mex/mexCopyDataHelpFunctions.cpp000066400000000000000000000226341475635207100240100ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "mexCopyDataHelpFunctions.h" #include "mexHelpFunctions.h" #define ROUND_DOWN(x, s) ((x) & ~((s)-1)) #ifdef _OPENMP #include #endif #if defined(__SSE2__) # include # define STORE_32F_64F_CORE8(in, out, count, base) \ {\ const __m128 inV0 = *((const __m128 *) &in[count + 0 + base]);\ const __m128 inV1 = *((const __m128 *) &in[count + 4 + base]);\ \ *((__m128d *)&out[count + 0 + base]) = _mm_cvtps_pd(inV0);\ *((__m128d *)&out[count + 2 + base]) = _mm_cvtps_pd(_mm_movehl_ps(inV0, inV0));\ \ *((__m128d *)&out[count + 4 + base]) = _mm_cvtps_pd(inV1);\ *((__m128d *)&out[count + 6 + base]) = _mm_cvtps_pd(_mm_movehl_ps(inV1, inV1));\ } # define STORE_64F_32F_CORE8(in, out, count, base) \ {\ const __m128d inV0 = *((const __m128d *) &in[count + 0 + base]);\ const __m128d inV1 = *((const __m128d *) &in[count + 2 + base]);\ \ const __m128d inV2 = *((const __m128d *) &in[count + 4 + base]);\ const __m128d inV3 = *((const __m128d *) &in[count + 6 + base]);\ \ *((__m128 *)&out[count + 0 + base]) = _mm_movelh_ps(_mm_cvtpd_ps(inV0), _mm_cvtpd_ps(inV1));\ *((__m128 *)&out[count + 4 + base]) = _mm_movelh_ps(_mm_cvtpd_ps(inV2), _mm_cvtpd_ps(inV3));\ } # define STORE_32F_32F_CORE8(in, out, count, base) \ {\ *((__m128 *)&out[count + 0 + base]) = *((const __m128 *)&in[count + 0 + base]);\ *((__m128 *)&out[count + 4 + base]) = *((const __m128 *)&in[count + 4 + base]);\ } # define STORE_CONST_32F_CORE8(val, out, count, base) \ {\ *((__m128 *)&out[count + 0 + base]) = val;\ *((__m128 *)&out[count + 4 + base]) = val;\ } #else # define STORE_32F_64F_CORE8(in, out, count, base) \ {\ out[count + 0 + base] = (double)in[count + 0 + base];\ out[count + 1 + base] = (double)in[count + 1 + base];\ out[count + 2 + base] = (double)in[count + 2 + base];\ out[count + 3 + base] = (double)in[count + 3 + base];\ out[count + 4 + base] = (double)in[count + 4 + base];\ out[count + 5 + base] = (double)in[count + 5 + base];\ out[count + 6 + base] = (double)in[count + 6 + base];\ out[count + 7 + base] = (double)in[count + 7 + base];\ } # define STORE_64F_32F_CORE8(in, out, count, base) \ {\ out[count + 0 + base] = (float)in[count + 0 + base];\ out[count + 1 + base] = (float)in[count + 1 + base];\ out[count + 2 + base] = (float)in[count + 2 + base];\ out[count + 3 + base] = (float)in[count + 3 + base];\ out[count + 4 + base] = (float)in[count + 4 + base];\ out[count + 5 + base] = (float)in[count + 5 + base];\ out[count + 6 + base] = (float)in[count + 6 + base];\ out[count + 7 + base] = (float)in[count + 7 + base];\ } # define STORE_32F_32F_CORE8(in, out, count, base) \ {\ out[count + 0 + base] = in[count + 0 + base];\ out[count + 1 + base] = in[count + 1 + base];\ out[count + 2 + base] = in[count + 2 + base];\ out[count + 3 + base] = in[count + 3 + base];\ out[count + 4 + base] = in[count + 4 + base];\ out[count + 5 + base] = in[count + 5 + base];\ out[count + 6 + base] = in[count + 6 + base];\ out[count + 7 + base] = in[count + 7 + base];\ } #endif #define STORE_8F_32F_CORE8(in, out, count, base) \ {\ out[count + 0 + base] = (float)in[count + 0 + base];\ out[count + 1 + base] = (float)in[count + 1 + base];\ out[count + 2 + base] = (float)in[count + 2 + base];\ out[count + 3 + base] = (float)in[count + 3 + base];\ out[count + 4 + base] = (float)in[count + 4 + base];\ out[count + 5 + base] = (float)in[count + 5 + base];\ out[count + 6 + base] = (float)in[count + 6 + base];\ out[count + 7 + base] = (float)in[count + 7 + base];\ } const char * warnDataTypeNotSupported = "Data type not supported: nothing was copied"; void _copyMexToCFloat32Array(const mxArray * const inArray, astra::float32 * const out) { const long long tot_size = mxGetNumberOfElements(inArray); const long long block = 32; const long long totRoundedPixels = ROUND_DOWN(tot_size, block); // Array of doubles if (mxIsDouble(inArray)) { const double * const pdMatlabData = mxGetPr(inArray); #pragma omp for nowait for (long long count = 0; count < totRoundedPixels; count += block) { STORE_64F_32F_CORE8(pdMatlabData, out, count, 0); STORE_64F_32F_CORE8(pdMatlabData, out, count, 8); STORE_64F_32F_CORE8(pdMatlabData, out, count, 16); STORE_64F_32F_CORE8(pdMatlabData, out, count, 24); } #pragma omp for nowait for (long long count = totRoundedPixels; count < tot_size; count++) { out[count] = pdMatlabData[count]; } } // Array of floats else if (mxIsSingle(inArray)) { const float * const pfMatlabData = (const float *)mxGetData(inArray); #pragma omp for nowait for (long long count = 0; count < totRoundedPixels; count += block) { STORE_32F_32F_CORE8(pfMatlabData, out, count, 0); STORE_32F_32F_CORE8(pfMatlabData, out, count, 8); STORE_32F_32F_CORE8(pfMatlabData, out, count, 16); STORE_32F_32F_CORE8(pfMatlabData, out, count, 24); } #pragma omp for nowait for (long long count = totRoundedPixels; count < tot_size; count++) { out[count] = pfMatlabData[count]; } } // Array of logicals else if (mxIsLogical(inArray)) { const mxLogical * const pfMatlabData = (const mxLogical *)mxGetLogicals(inArray); #pragma omp for nowait for (long long count = 0; count < totRoundedPixels; count += block) { STORE_8F_32F_CORE8(pfMatlabData, out, count, 0); STORE_8F_32F_CORE8(pfMatlabData, out, count, 8); STORE_8F_32F_CORE8(pfMatlabData, out, count, 16); STORE_8F_32F_CORE8(pfMatlabData, out, count, 24); } #pragma omp for nowait for (long long count = totRoundedPixels; count < tot_size; count++) { out[count] = pfMatlabData[count]; } } else { #pragma omp single nowait mexWarnMsgIdAndTxt("ASTRA_MEX:wrong_datatype", warnDataTypeNotSupported); } } void _copyCFloat32ArrayToMex(const astra::float32 * const in, mxArray * const outArray) { const long long tot_size = mxGetNumberOfElements(outArray); const long long block = 32; const long long tot_rounded_size = ROUND_DOWN(tot_size, block); if (mxIsDouble(outArray)) { double * const pdMatlabData = mxGetPr(outArray); #pragma omp for nowait for (long long count = 0; count < tot_rounded_size; count += block) { STORE_32F_64F_CORE8(in, pdMatlabData, count, 0); STORE_32F_64F_CORE8(in, pdMatlabData, count, 8); STORE_32F_64F_CORE8(in, pdMatlabData, count, 16); STORE_32F_64F_CORE8(in, pdMatlabData, count, 24); } #pragma omp for nowait for (long long count = tot_rounded_size; count < tot_size; count++) { pdMatlabData[count] = in[count]; } } else if (mxIsSingle(outArray)) { float * const pfMatlabData = (float *) mxGetData(outArray); #pragma omp for nowait for (long long count = 0; count < tot_rounded_size; count += block) { STORE_32F_32F_CORE8(in, pfMatlabData, count, 0); STORE_32F_32F_CORE8(in, pfMatlabData, count, 8); STORE_32F_32F_CORE8(in, pfMatlabData, count, 16); STORE_32F_32F_CORE8(in, pfMatlabData, count, 24); } #pragma omp for nowait for (long long count = tot_rounded_size; count < tot_size; count++) { pfMatlabData[count] = in[count]; } } else { #pragma omp single nowait mexWarnMsgIdAndTxt("ASTRA_MEX:wrong_datatype", warnDataTypeNotSupported); } } void _initializeCFloat32Array(const astra::float32 & val, astra::float32 * const out, const size_t & tot_size) { #ifdef __SSE2__ const long long block = 32; const long long tot_rounded_size = ROUND_DOWN(tot_size, block); const __m128 vecVal = _mm_set1_ps(val); { #pragma omp for nowait for (long long count = 0; count < tot_rounded_size; count += block) { STORE_CONST_32F_CORE8(vecVal, out, count, 0); STORE_CONST_32F_CORE8(vecVal, out, count, 8); STORE_CONST_32F_CORE8(vecVal, out, count, 16); STORE_CONST_32F_CORE8(vecVal, out, count, 24); } #else const long long tot_rounded_size = 0; { #endif #pragma omp for nowait for (long long count = tot_rounded_size; count < (long long)tot_size; count++) { out[count] = val; } } } void copyMexToCFloat32Array(const mxArray * const in, astra::float32 * const out, const size_t &tot_size) { #pragma omp parallel { // fill with scalar value if (mexIsScalar(in) || mxIsEmpty(in)) { astra::float32 fValue = 0.f; if (!mxIsEmpty(in)) { fValue = (astra::float32)mxGetScalar(in); } _initializeCFloat32Array(fValue, out, tot_size); } // fill with array value else { _copyMexToCFloat32Array(in, out); } } } void copyCFloat32ArrayToMex(const float * const in, mxArray * const outArray) { #pragma omp parallel { _copyCFloat32ArrayToMex(in, outArray); } } astra-toolbox-2.3.0/matlab/mex/mexCopyDataHelpFunctions.h000066400000000000000000000031211475635207100234430ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef MEXCOPYDATAHELPFUNCTIONS_H_ #define MEXCOPYDATAHELPFUNCTIONS_H_ #include #include "astra/Globals.h" #include void copyMexToCFloat32Array(const mxArray * const, astra::float32 * const, const size_t &); void copyCFloat32ArrayToMex(const astra::float32 * const, mxArray * const); template mxArray * createEquivMexArray(const AType * const pDataObj) { mwSize dims[3]; dims[0] = pDataObj->getWidth(); dims[1] = pDataObj->getHeight(); dims[2] = pDataObj->getDepth(); return mxCreateNumericArray(3, dims, MType, mxREAL); } #endif /* MEXCOPYDATAHELPFUNCTIONS_H_ */ astra-toolbox-2.3.0/matlab/mex/mexDataManagerHelpFunctions.cpp000066400000000000000000000220641475635207100244450ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "mexDataManagerHelpFunctions.h" #include "mexHelpFunctions.h" #include "astra/ProjectionGeometry3DFactory.h" #ifdef USE_MATLAB_UNDOCUMENTED extern "C" { mxArray *mxCreateSharedDataCopy(const mxArray *pr); int mxUnshareArray(mxArray *pr, int level); mxArray *mxUnreference(mxArray *pr); #if 0 // Unsupported in Matlab R2014b and later bool mxIsSharedArray(const mxArray *pr); #endif } class CDataStorageMatlab : public astra::CDataMemory { public: // offset allows linking the data object to a sub-volume (in the z direction) // offset is measured in floats. CDataStorageMatlab(const mxArray* _pArray, bool bUnshare, size_t iOffset) { // Convert from slice to offset mwSize dims[3]; get3DMatrixDims(_pArray, dims); iOffset *= dims[0]; iOffset *= dims[1]; //fprintf(stderr, "Passed:\narray: %p\tdata: %p\n", (void*)_pArray, (void*)mxGetData(_pArray)); // First unshare the input array, so that we may modify it. if (bUnshare) { #if 0 // Unsupported in Matlab R2014b and later if (mxIsSharedArray(_pArray)) { fprintf(stderr, "Performance note: unsharing shared array in link\n"); } #endif mxUnshareArray(const_cast(_pArray), 0); //fprintf(stderr, "Unshared:\narray: %p\tdata: %p\n", (void*)_pArray, (void*)mxGetData(_pArray)); } // Then create a (persistent) copy so the data won't be deleted // or changed. m_pLink = mxCreateSharedDataCopy(_pArray); //fprintf(stderr, "SharedDataCopy:\narray: %p\tdata: %p\n", (void*)m_pLink, (void*)mxGetData(m_pLink)); mexMakeArrayPersistent(m_pLink); this->m_pfData = (float *)mxGetData(_pArray); this->m_pfData += iOffset; } virtual ~CDataStorageMatlab() { // destroy the shared array //fprintf(stderr, "Destroy:\narray: %p\tdata: %p\n", (void*)m_pLink, (void*)mxGetData(m_pLink)); mxDestroyArray(m_pLink); } private: mxArray* m_pLink; }; #endif //----------------------------------------------------------------------------------------- bool checkID(const astra::int32 & id, astra::CData3D *& pDataObj) { pDataObj = astra::CData3DManager::getSingleton().get(id); return (pDataObj && pDataObj->isFloat32Memory() && pDataObj->isInitialized()); } //----------------------------------------------------------------------------------------- bool checkDataType(const mxArray * const in) { return (mexIsScalar(in) || mxIsDouble(in) || mxIsSingle(in) || mxIsLogical(in)); } //----------------------------------------------------------------------------------------- bool checkStructs(const mxArray * const in) { return mxIsStruct(in); } //----------------------------------------------------------------------------------------- bool checkDataSize(const mxArray * const mArray, const astra::CProjectionGeometry3D * const geom) { mwSize dims[3]; get3DMatrixDims(mArray, dims); return (geom->getDetectorColCount() == dims[0] && geom->getProjectionCount() == dims[1] && geom->getDetectorRowCount() == dims[2]); } //----------------------------------------------------------------------------------------- bool checkDataSize(const mxArray * const mArray, const astra::CVolumeGeometry3D * const geom) { mwSize dims[3]; get3DMatrixDims(mArray, dims); return (geom->getGridColCount() == dims[0] && geom->getGridRowCount() == dims[1] && geom->getGridSliceCount() == dims[2]); } //----------------------------------------------------------------------------------------- bool checkDataSize(const mxArray * const mArray, const astra::CProjectionGeometry3D * const geom, const mwIndex & zOffset) { mwSize dims[3]; get3DMatrixDims(mArray, dims); return (geom->getDetectorColCount() == dims[0] && geom->getProjectionCount() == dims[1] && (zOffset + geom->getDetectorRowCount()) <= dims[2]); } //----------------------------------------------------------------------------------------- bool checkDataSize(const mxArray * const mArray, const astra::CVolumeGeometry3D * const geom, const mwIndex & zOffset) { mwSize dims[3]; get3DMatrixDims(mArray, dims); return (geom->getGridColCount() == dims[0] && geom->getGridRowCount() == dims[1] && (zOffset + geom->getGridSliceCount()) <= dims[2]); } //----------------------------------------------------------------------------------------- astra::CData3D * allocateDataObject(const std::string & sDataType, const mxArray * const geometry, const mxArray * const data, const mxArray * const unshare, const mxArray * const zIndex) { astra::CData3D* pDataObject3D = NULL; bool bUnshare = true; if (unshare) { if (!mexIsScalar(unshare)) { mexErrMsgTxt("Argument 5 (read-only) must be scalar."); } // unshare the array if we're not linking read-only bUnshare = !(bool)mxGetScalar(unshare); } mwIndex iZ = 0; if (zIndex) { if (!mexIsScalar(zIndex)) { mexErrMsgTxt("Argument 6 (Z) must be scalar."); } iZ = (mwSignedIndex)mxGetScalar(zIndex); } // SWITCH DataType if (sDataType == "-vol") { // Read geometry astra::XMLConfig* cfg = structToConfig("VolumeGeometry3D", geometry); astra::CVolumeGeometry3D* pGeometry = new astra::CVolumeGeometry3D(); if (!pGeometry->initialize(*cfg)) { delete pGeometry; delete cfg; mexErrMsgWithAstraLog("Geometry class could not be initialized."); } delete cfg; // If data is specified, check dimensions if (data && !mexIsScalar(data)) { if (! (zIndex ? checkDataSize(data, pGeometry, iZ) : checkDataSize(data, pGeometry)) ) { delete pGeometry; mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry."); } } // Initialize data object size_t dataSize = pGeometry->getGridColCount(); dataSize *= pGeometry->getGridRowCount(); dataSize *= pGeometry->getGridSliceCount(); #ifdef USE_MATLAB_UNDOCUMENTED if (unshare) { astra::CDataStorage* pHandle = new CDataStorageMatlab(data, bUnshare, iZ); // Initialize data object pDataObject3D = new astra::CFloat32VolumeData3D(*pGeometry, pHandle); } else { astra::CDataStorage* pStorage = new astra::CDataMemory(dataSize); pDataObject3D = new astra::CFloat32VolumeData3D(*pGeometry, pStorage); } #else astra::CDataStorage* pStorage = new astra::CDataMemory(dataSize); pDataObject3D = new astra::CFloat32VolumeData3D(*pGeometry, pStorage); #endif delete pGeometry; } else if (sDataType == "-sino" || sDataType == "-proj3d" || sDataType == "-sinocone") { // Read geometry astra::XMLConfig* cfg = structToConfig("ProjectionGeometry3D", geometry); std::string type = cfg->self.getAttribute("type"); std::unique_ptr pGeometry = astra::constructProjectionGeometry3D(type); if (!pGeometry) { delete cfg; std::string message = "'" + type + "' is not a valid 3D geometry type."; mexErrMsgTxt(message.c_str()); } if (!pGeometry->initialize(*cfg)) { delete cfg; mexErrMsgWithAstraLog("Geometry class could not be initialized."); } delete cfg; // If data is specified, check dimensions if (data && !mexIsScalar(data)) { if (! (zIndex ? checkDataSize(data, pGeometry.get(), iZ) : checkDataSize(data, pGeometry.get())) ) { mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry."); } } // Initialize data object size_t dataSize = pGeometry->getDetectorColCount(); dataSize *= pGeometry->getProjectionCount(); dataSize *= pGeometry->getDetectorRowCount(); #ifdef USE_MATLAB_UNDOCUMENTED if (unshare) { astra::CDataStorage* pHandle = new CDataStorageMatlab(data, bUnshare, iZ); // Initialize data object pDataObject3D = new astra::CFloat32ProjectionData3D(*pGeometry, pHandle); } else { astra::CDataStorage* pStorage = new astra::CDataMemory(dataSize); pDataObject3D = new astra::CFloat32ProjectionData3D(*pGeometry, pStorage); } #else astra::CDataStorage* pStorage = new astra::CDataMemory(dataSize); pDataObject3D = new astra::CFloat32ProjectionData3D(*pGeometry, pStorage); #endif } else { mexErrMsgTxt("Invalid datatype. Please specify '-vol' or '-proj3d'."); } // Check initialization if (!pDataObject3D->isInitialized()) { delete pDataObject3D; mexErrMsgTxt("Couldn't initialize data object."); } return pDataObject3D; } astra-toolbox-2.3.0/matlab/mex/mexDataManagerHelpFunctions.h000066400000000000000000000053511475635207100241120ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef MEXDATAMANAGERHELPFUNCTIONS_H_ #define MEXDATAMANAGERHELPFUNCTIONS_H_ #include #include "astra/Globals.h" #include "astra/AstraObjectManager.h" #include "astra/Data3D.h" #include "astra/ProjectionGeometry3D.h" #include "astra/VolumeGeometry3D.h" #include "mexCopyDataHelpFunctions.h" bool checkID(const astra::int32 &, astra::CData3D *&); bool checkDataType(const mxArray * const); bool checkStructs(const mxArray * const); bool checkDataSize(const mxArray * const, const astra::CProjectionGeometry3D * const); bool checkDataSize(const mxArray * const, const astra::CVolumeGeometry3D * const); bool checkDataSize(const mxArray * const, const astra::CProjectionGeometry3D * const, const mwIndex & zOffset); bool checkDataSize(const mxArray * const, const astra::CVolumeGeometry3D * const, const mwIndex & zOffset); astra::CData3D * allocateDataObject(const std::string & sDataType, const mxArray * const geometry, const mxArray * const data, const mxArray * const unshare = NULL, const mxArray * const zOffset = NULL); //----------------------------------------------------------------------------------------- template void generic_astra_mex_data3d_get(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list."); } // step2: get data object/s astra::CData3D* pDataObject = NULL; if (!checkID(mxGetScalar(prhs[1]), pDataObject)) { mexErrMsgTxt("Data object not found or not initialized properly."); } // create output if (1 <= nlhs) { plhs[0] = createEquivMexArray(pDataObject); copyCFloat32ArrayToMex(pDataObject->getFloat32Memory(), plhs[0]); } } #endif /* MEXDATAMANAGERHELPFUNCTIONS_H_ */ astra-toolbox-2.3.0/matlab/mex/mexHelpFunctions.cpp000066400000000000000000000253641475635207100223660ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ /** \file mexHelpFunctions.cpp * * \brief Contains some functions for interfacing matlab with c data structures */ #include "mexHelpFunctions.h" #include "astra/Utilities.h" #include "astra/Logging.h" using namespace std; using namespace astra; void mexErrMsgWithAstraLog(string message) { string last_err_msg = CLogger::getLastErrMsg(); if (!last_err_msg.empty()) { message.append(" "); message.append(last_err_msg); } mexErrMsgTxt(message.c_str()); } //----------------------------------------------------------------------------------------- // get string from matlab string mexToString(const mxArray* pInput) { // is string? if (mxIsChar(pInput)) { mwSize iLength = mxGetNumberOfElements(pInput) + 1; char* buf = new char[iLength]; mxGetString(pInput, buf, iLength); std::string res = std::string(buf); delete[] buf; return res; } // is scalar? if (mxIsNumeric(pInput) && mxGetM(pInput)*mxGetN(pInput) == 1) { return StringUtil::doubleToString(mxGetScalar(pInput)); } return ""; } //----------------------------------------------------------------------------------------- // return true if the argument is a scalar bool mexIsScalar(const mxArray* pInput) { return (mxIsNumeric(pInput) && mxGetM(pInput)*mxGetN(pInput) == 1); } //----------------------------------------------------------------------------------------- void get3DMatrixDims(const mxArray* x, mwSize *dims) { const mwSize* mdims = mxGetDimensions(x); mwSize dimCount = mxGetNumberOfDimensions(x); if (dimCount == 1) { dims[0] = mdims[0]; dims[1] = 1; dims[2] = 1; } else if (dimCount == 2) { dims[0] = mdims[0]; dims[1] = mdims[1]; dims[2] = 1; } else if (dimCount == 3) { dims[0] = mdims[0]; dims[1] = mdims[1]; dims[2] = mdims[2]; } else { dims[0] = 0; dims[1] = 0; dims[2] = 0; } } //----------------------------------------------------------------------------------------- // turn an std vector object to an mxArray mxArray* vectorToMxArray(std::vector mInput) { mxArray* res = mxCreateDoubleMatrix(1, mInput.size(), mxREAL); double* pdData = mxGetPr(res); for (unsigned int i = 0; i < mInput.size(); i++) { pdData[i] = mInput[i]; } return res; } //----------------------------------------------------------------------------------------- // turn a vector> object to an mxArray mxArray* vector2DToMxArray(std::vector > mInput) { unsigned int sizex = mInput.size(); if (sizex == 0) return mxCreateString("empty"); unsigned int sizey = mInput[0].size(); mxArray* res = mxCreateDoubleMatrix(sizex, sizey, mxREAL); double* pdData = mxGetPr(res); for (unsigned int i = 0; i < sizex; i++) { for (unsigned int j = 0; j < sizey && j < mInput[i].size(); j++) { pdData[j*sizex+i] = mInput[i][j]; } } return res; } //----------------------------------------------------------------------------------------- // turn a MATLAB struct into a Config object XMLConfig* structToConfig(string rootname, const mxArray* pStruct) { if (!mxIsStruct(pStruct)) { mexErrMsgTxt("Input must be a struct."); } // create the document XMLConfig* cfg = new XMLConfig(rootname); // read the struct bool ret = structToXMLNode(cfg->self, pStruct); if (!ret) { delete cfg; mexErrMsgTxt("Error parsing struct."); } return cfg; } //----------------------------------------------------------------------------------------- bool structToXMLNode(XMLNode node, const mxArray* pStruct) { // loop all fields int nfields = mxGetNumberOfFields(pStruct); for (int i = 0; i < nfields; i++) { // field and fieldname std::string sFieldName = std::string(mxGetFieldNameByNumber(pStruct, i)); const mxArray* pField = mxGetFieldByNumber(pStruct, 0, i); // string if (mxIsChar(pField)) { string sValue = mexToString(pField); if (sFieldName == "type") { node.addAttribute("type", sValue); } else { node.addChildNode(sFieldName, sValue); } } // scalar else if (mxIsNumeric(pField) && mxGetM(pField)*mxGetN(pField) == 1) { string sValue = mexToString(pField); node.addChildNode(sFieldName, sValue); } // numerical array else if (mxIsNumeric(pField) && mxGetM(pField)*mxGetN(pField) > 1) { if (!mxIsDouble(pField)) { mexErrMsgTxt("Numeric input must be double."); } XMLNode listbase = node.addChildNode(sFieldName); double* pdValues = mxGetPr(pField); listbase.setContent(pdValues, mxGetN(pField), mxGetM(pField), true); } // not castable to a single string else if (mxIsStruct(pField)) { if (sFieldName == "options" || sFieldName == "option" || sFieldName == "Options" || sFieldName == "Option") { bool ret = optionsToXMLNode(node, pField); if (!ret) return false; } else { XMLNode newNode = node.addChildNode(sFieldName); bool ret = structToXMLNode(newNode, pField); if (!ret) return false; } } } return true; } //----------------------------------------------------------------------------------------- // Options struct to xml node bool optionsToXMLNode(XMLNode node, const mxArray* pOptionStruct) { // loop all fields int nfields = mxGetNumberOfFields(pOptionStruct); for (int i = 0; i < nfields; i++) { std::string sFieldName = std::string(mxGetFieldNameByNumber(pOptionStruct, i)); const mxArray* pField = mxGetFieldByNumber(pOptionStruct, 0, i); if (node.hasOption(sFieldName)) { mexErrMsgTxt("Duplicate option."); } // string or scalar if (mxIsChar(pField) || mexIsScalar(pField)) { string sValue = mexToString(pField); node.addOption(sFieldName, sValue); } // numerical array else if (mxIsNumeric(pField) && mxGetM(pField)*mxGetN(pField) > 1) { if (!mxIsDouble(pField)) { mexErrMsgTxt("Numeric input must be double."); } XMLNode listbase = node.addChildNode("Option"); listbase.addAttribute("key", sFieldName); double* pdValues = mxGetPr(pField); listbase.setContent(pdValues, mxGetN(pField), mxGetM(pField), true); } else { mexErrMsgTxt("Unsupported option type."); } } return true; } //----------------------------------------------------------------------------------------- // turn a matlab struct into a c++ map std::map parseStruct(const mxArray* pInput) { std::map res; // check type if (!mxIsStruct(pInput)) { mexErrMsgTxt("Input must be a struct."); } // get field names int nfields = mxGetNumberOfFields(pInput); for (int i = 0; i < nfields; i++) { std::string sFieldName = std::string(mxGetFieldNameByNumber(pInput, i)); res[sFieldName] = mxGetFieldByNumber(pInput,0,i); } return res; } //----------------------------------------------------------------------------------------- // turn a Config object into a MATLAB struct mxArray* configToStruct(astra::Config* cfg) { astra::XMLConfig *xmlcfg = dynamic_cast(cfg); if (!xmlcfg) return 0; return XMLNodeToStruct(xmlcfg->self); } //----------------------------------------------------------------------------------------- mxArray* XMLNodeToStruct(astra::XMLNode node) { std::map mList; std::map mOptions; // type_attribute if (node.hasAttribute("type")) { mList["type"] = mxCreateString(node.getAttribute("type").c_str()); } list nodes = node.getNodes(); for (list::iterator it = nodes.begin(); it != nodes.end(); it++) { XMLNode subnode = (*it); // option if (subnode.getName() == "Option") { if(subnode.hasAttribute("value")){ mOptions[subnode.getAttribute("key")] = stringToMxArray(subnode.getAttribute("value")); }else{ mOptions[subnode.getAttribute("key")] = stringToMxArray(subnode.getContent()); } } // regular content else { mList[subnode.getName()] = stringToMxArray(subnode.getContent()); } } if (mOptions.size() > 0) mList["options"] = buildStruct(mOptions); return buildStruct(mList); } //----------------------------------------------------------------------------------------- mxArray* stringToMxArray(std::string input) { // matrix if (input.find(';') != std::string::npos) { // split rows std::vector row_strings; std::vector col_strings; StringUtil::splitString(row_strings, input, ";"); StringUtil::splitString(col_strings, row_strings[0], ","); // get dimensions size_t rows = row_strings.size(); size_t cols = col_strings.size(); // init matrix mxArray* pMatrix = mxCreateDoubleMatrix(rows, cols, mxREAL); double* out = mxGetPr(pMatrix); // loop elements for (size_t row = 0; row < rows; row++) { StringUtil::splitString(col_strings, row_strings[row], ","); // check size for (size_t col = 0; col < col_strings.size(); col++) { out[col*rows + row] = StringUtil::stringToFloat(col_strings[col]); } } return pMatrix; } // vector if (input.find(',') != std::string::npos) { // split std::vector items; StringUtil::splitString(items, input, ","); // init matrix mxArray* pVector = mxCreateDoubleMatrix(1, items.size(), mxREAL); double* out = mxGetPr(pVector); // loop elements for (size_t i = 0; i < items.size(); i++) { out[i] = StringUtil::stringToFloat(items[i]); } return pVector; } try { // number return mxCreateDoubleScalar(StringUtil::stringToDouble(input)); } catch (const StringUtil::bad_cast &) { // string return mxCreateString(input.c_str()); } } //----------------------------------------------------------------------------------------- // turn a c++ map into a matlab struct mxArray* buildStruct(std::map mInput) { mwSize dims[2] = {1, 1}; mxArray* res = mxCreateStructArray(2,dims,0,0); for (std::map::iterator it = mInput.begin(); it != mInput.end(); it++) { mxAddField(res, (*it).first.c_str()); mxSetField(res, 0, (*it).first.c_str(), (*it).second); } return res; } astra-toolbox-2.3.0/matlab/mex/mexHelpFunctions.h000066400000000000000000000044031475635207100220220ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_ASTRA_MEX_HELPFUNCTIONS #define _INC_ASTRA_MEX_HELPFUNCTIONS #define USE_MATLAB_UNDOCUMENTED #include #include #include #include #include #include #include #include "astra/Globals.h" #include "astra/Utilities.h" #include "astra/Config.h" #include "astra/XMLConfig.h" #include "astra/XMLDocument.h" #include "astra/XMLNode.h" // utility functions void mexErrMsgWithAstraLog(std::string message); std::string mexToString(const mxArray* pInput); bool mexIsScalar(const mxArray* pInput); void get3DMatrixDims(const mxArray* x, mwSize *dims); // convert float vector into a MATLAB object mxArray* vectorToMxArray(std::vector mInput); // turn a MATLAB struct into a Config object astra::XMLConfig* structToConfig(std::string rootname, const mxArray* pStruct); bool structToXMLNode(astra::XMLNode node, const mxArray* pStruct); bool optionsToXMLNode(astra::XMLNode node, const mxArray* pOptionStruct); std::map parseStruct(const mxArray* pInput); // turn a Config object into a MATLAB struct mxArray* configToStruct(astra::Config* cfg); mxArray* XMLNodeToStruct(astra::XMLNode xml); mxArray* stringToMxArray(std::string input); mxArray* buildStruct(std::map mInput); #endif astra-toolbox-2.3.0/matlab/mex/mexInitFunctions.cpp000066400000000000000000000013631475635207100223720ustar00rootroot00000000000000#include #include "astra/Logging.h" bool mexIsInitialized=false; /** * Callback to print log message to Matlab window. * */ void logCallBack(const char *msg, size_t len){ mexPrintf("%s",msg); } /** * Initialize mex functions. * */ void initASTRAMex(){ if(mexIsInitialized) return; astra::running_in_matlab=true; if(!astra::CLogger::setCallbackScreen(&logCallBack)){ mexErrMsgTxt("Error initializing mex functions."); } mexIsInitialized=true; // If we have support for plugins, initialize them. // (NB: Call this after setting mexIsInitialized, to avoid recursively // calling initASTRAMex) mexEvalString("if exist('astra_mex_plugin_c') == 3; astra_mex_plugin_c('init'); end"); } astra-toolbox-2.3.0/matlab/mex/mexInitFunctions.h000066400000000000000000000001501475635207100220300ustar00rootroot00000000000000#ifndef _INC_ASTRA_MEX_INITFUNCTIONS #define _INC_ASTRA_MEX_INITFUNCTIONS void initASTRAMex(); #endif astra-toolbox-2.3.0/matlab/mex/octave_support.cpp000066400000000000000000000022721475635207100221410ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include extern "C" { bool utIsInterruptPending() { return octave_signal_caught; } mxArray *mxCreateSharedDataCopy(const mxArray *) { return 0; } bool mxUnshareArray(mxArray *, bool) { return false; } } astra-toolbox-2.3.0/matlab/tools/000077500000000000000000000000001475635207100167245ustar00rootroot00000000000000astra-toolbox-2.3.0/matlab/tools/ROIselectfull.m000066400000000000000000000005751475635207100216250ustar00rootroot00000000000000function V_out = ROIselectfull(input, ROI) s1 = size(input,1); s2 = size(input,2); [x y] = meshgrid(-(s2-1)/2:(s2-1)/2,(s1-1)/2:-1:-(s1-1)/2); A = Afstand(x,y,0,0); V_out = zeros(size(input)); for slice = 1:size(input,3); V = input(:,:,slice); V(A > ROI/2) = 0; V_out(:,:,slice) = V; end end function A = Afstand(x1,y1,x2,y2) A = sqrt((x1-x2).^2+(y1-y2).^2); end astra-toolbox-2.3.0/matlab/tools/astra_add_noise_to_sino.m000066400000000000000000000031751475635207100237610ustar00rootroot00000000000000function sinogram_out = astra_add_noise_to_sino(sinogram_in,I0) %-------------------------------------------------------------------------- % sinogram_out = astra_add_noise_to_sino(sinogram_in,I0) % % Add poisson noise to a sinogram. % % sinogram_in: input sinogram, can be either MATLAB-data or an % astra-identifier. In the latter case, this operation is inplace and the % result will also be stored in this data object. % I0: background intensity, used to set noise level, lower equals more % noise % sinogram_out: output sinogram in MATLAB-data. %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- if numel(sinogram_in) == 1 sinogramRaw = astra_mex_data2d('get', sinogram_in); else sinogramRaw = sinogram_in; end % scale to [0,1] max_sinogramRaw = max(sinogramRaw(:)); sinogramRawScaled = sinogramRaw ./ max_sinogramRaw; % to detector count sinogramCT = I0 * exp(-sinogramRawScaled); % add poison noise sinogramCT_A = sinogramCT * 1e-12; sinogramCT_B = double(imnoise(sinogramCT_A, 'poisson')); sinogramCT_C = sinogramCT_B * 1e12; % to density sinogramCT_D = sinogramCT_C / I0; sinogram_out = -max_sinogramRaw * log(sinogramCT_D); if numel(sinogram_in) == 1 astra_mex_data2d('store', sinogram_in, sinogram_out); end astra-toolbox-2.3.0/matlab/tools/astra_clear.m000066400000000000000000000015121475635207100213610ustar00rootroot00000000000000%-------------------------------------------------------------------------- % Clears and frees memory of all objects (data, projectors, algorithms) % currently in the astra-library. %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- astra_mex_data2d('clear'); astra_mex_data3d('clear'); astra_mex_algorithm('clear'); astra_mex_projector('clear'); astra_mex_projector3d('clear'); astra_mex_matrix('clear'); astra-toolbox-2.3.0/matlab/tools/astra_create_backprojection.m000066400000000000000000000034741475635207100246240ustar00rootroot00000000000000function [vol_id, vol] = astra_create_backprojection(data, proj_id) %-------------------------------------------------------------------------- % [vol_id, vol] = astra_create_backprojection(data, proj_id) % % Create a CPU based back projection. % % data: input sinogram, can be either MATLAB-data or an astra-identifier. % proj_id: identifier of the projector as it is stored in the astra-library % vol_id: identifier of the volume data object as it is now stored in the astra-library. % vol: MATLAB data version of the volume %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- % get projection geometry proj_geom = astra_mex_projector('projection_geometry', proj_id); vol_geom = astra_mex_projector('volume_geometry', proj_id); % store sinogram if (numel(data) > 1) sino_id = astra_mex_data2d('create','-sino', proj_geom, data); else sino_id = data; end % store volume vol_id = astra_mex_data2d('create','-vol', vol_geom, 0); if astra_mex_projector('is_cuda', proj_id) cfg = astra_struct('BP_CUDA'); else cfg = astra_struct('BP'); end cfg.ProjectorId = proj_id; cfg.ProjectionDataId = sino_id; cfg.ReconstructionDataId = vol_id; % create backprojection alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('iterate', alg_id); astra_mex_algorithm('delete', alg_id); if (numel(data) > 1) astra_mex_data2d('delete', sino_id); end if nargout >= 2 vol = astra_mex_data2d('get',vol_id); end astra-toolbox-2.3.0/matlab/tools/astra_create_backprojection3d_cuda.m000066400000000000000000000032311475635207100260360ustar00rootroot00000000000000function [vol_id, vol] = astra_create_backprojection3d_cuda(data, proj_geom, vol_geom) %-------------------------------------------------------------------------- % [vol_id, vol] = astra_create_backprojection3d_cuda(data, proj_geom, vol_geom) % % Create a GPU based backprojection. % % data: input projection data, can be either MATLAB-data or an astra-identifier. % proj_geom: MATLAB struct containing the projection geometry. % vol_geom: MATLAB struct containing the volume geometry. % vol_id: identifier of the volume data object as it is now stored in % the astra-library. % vol: MATLAB data version of the volume. %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- % store projection data if (numel(data) > 1) sino_id = astra_mex_data3d('create','-proj3d', proj_geom, data); else sino_id = data; end % store volume vol_id = astra_mex_data3d('create','-vol', vol_geom, 0); % create sinogram cfg = astra_struct('BP3D_CUDA'); cfg.ProjectionDataId = sino_id; cfg.ReconstructionDataId = vol_id; alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('iterate', alg_id); astra_mex_algorithm('delete', alg_id); if (numel(data) > 1) astra_mex_data3d('delete', sino_id); end if nargout >= 2 vol = astra_mex_data3d('get',vol_id); end astra-toolbox-2.3.0/matlab/tools/astra_create_backprojection_cuda.m000066400000000000000000000032511475635207100256110ustar00rootroot00000000000000function backProj = astra_create_backprojection_cuda(sinogramData, proj_geom, vol_geom) %-------------------------------------------------------------------------- % backProj = astra_create_backprojection_cuda(sinogramData, proj_geom, vol_geom) % % Creates a CUDA-based simple backprojection % % sinogramData: 2D matrix with projections stored row-based % theta: projection angles, length should be equal to the number of rows in % sinogramData % reconstructionSize: vector with length 2 with the row and column count of % the reconstruction image % backProj: 2D back projection from sinogram data %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- recon_id = astra_mex_data2d('create', '-vol', vol_geom, 0); sinogram_id = astra_mex_data2d('create', '-sino', proj_geom, sinogramData); cfg = astra_struct('BP_CUDA'); cfg.ProjectionDataId = sinogram_id; cfg.ReconstructionDataId = recon_id; alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('run', alg_id); backProj = astra_mex_data2d('get', recon_id); astra_mex_data2d('delete', sinogram_id); astra_mex_data2d('delete', recon_id); astra_mex_algorithm('delete', alg_id); end astra-toolbox-2.3.0/matlab/tools/astra_create_fbp_reconstruction.m000066400000000000000000000014471475635207100255350ustar00rootroot00000000000000function [FBP_id, FBP] = astra_create_fbp_reconstruction(sinogram, proj_id) proj_geom = astra_mex_projector('projection_geometry', proj_id); vol_geom = astra_mex_projector('volume_geometry', proj_id); if numel(sinogram) == 1 sinogram_id = sinogram; else sinogram_id = astra_mex_data2d('create', '-sino', proj_geom, sinogram); end FBP_id = astra_mex_data2d('create','-vol',vol_geom, 0); cfg = astra_struct('FBP_CUDA'); cfg.ProjectionDataId = sinogram_id; cfg.ReconstructionDataId = FBP_id; cfg.FilterType = 'Ram-Lak'; cfg.ProjectorId = proj_id; cfg.Options.GPUindex = 0; alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('run', alg_id); astra_mex_algorithm('delete', alg_id); if numel(sinogram) ~= 1 astra_mex_data2d('delete', sinogram_id); end FBP = astra_mex_data2d('get', FBP_id); astra-toolbox-2.3.0/matlab/tools/astra_create_proj_geom.m000066400000000000000000000227321475635207100236060ustar00rootroot00000000000000function proj_geom = astra_create_proj_geom(type, varargin) %-------------------------------------------------------------------------- % proj_geom = astra_create_proj_geom('parallel', det_width, det_count, angles) % % Create a 2D parallel beam geometry. See the API for more information. % det_width: distance between two adjacent detectors % det_count: number of detectors in a single projection % angles: projection angles in radians, should be between -pi/4 and 7pi/4 % proj_geom: MATLAB struct containing all information of the geometry %-------------------------------------------------------------------------- % proj_geom = astra_create_proj_geom('parallel3d', det_spacing_x, det_spacing_y, det_row_count, det_col_count, angles) % % Create a 3D parallel beam geometry. See the API for more information. % det_spacing_x: distance between two horizontally adjacent detectors % det_spacing_y: distance between two vertically adjacent detectors % det_row_count: number of detector rows in a single projection % det_col_count: number of detector columns in a single projection % angles: projection angles in radians, should be between -pi/4 and 7pi/4 % proj_geom: MATLAB struct containing all information of the geometry %-------------------------------------------------------------------------- % proj_geom = astra_create_proj_geom('fanflat', det_width, det_count, angles, source_origin, origin_det) % % Create a 2D flat fan beam geometry. See the API for more information. % det_width: distance between two adjacent detectors % det_count: number of detectors in a single projection % angles: projection angles in radians, should be between -pi/4 and 7pi/4 % source_origin: distance between the source and the center of rotation % origin_det: distance between the center of rotation and the detector array % proj_geom: MATLAB struct containing all information of the geometry %-------------------------------------------------------------------------- % proj_geom = astra_create_proj_geom('fanflat_vec', det_count, vectors) % % Create a 2D flat fan beam geometry specified by 2D vectors. % See the API for more information. % det_count: number of detectors in a single projection % vectors: a matrix containing the actual geometry. Each row corresponds % to a single projection, and consists of: % ( srcX, srcY, dX, dY, uX, uY ) % src: the ray source % d : the center of the detector % u : the vector from detector pixel 0 to 1 % proj_geom: MATLAB struct containing all information of the geometry %-------------------------------------------------------------------------- % proj_geom = astra_create_proj_geom('cone', det_spacing_x, det_spacing_y, det_row_count, det_col_count, angles, source_origin, origin_det) % % Create a 3D cone beam geometry. See the API for more information. % det_spacing_x: distance between two horizontally adjacent detectors % det_spacing_y: distance between two vertically adjacent detectors % det_row_count: number of detector rows in a single projection % det_col_count: number of detector columns in a single projection % angles: projection angles in radians, should be between -pi/4 and 7pi/4 % source_origin: distance between the source and the center of rotation % origin_det: distance between the center of rotation and the detector array % proj_geom: MATLAB struct containing all information of the geometry %-------------------------------------------------------------------------- % proj_geom = astra_create_proj_geom('cone_vec', det_row_count, det_col_count, vectors) % % Create a 3D cone beam geometry specified by 3D vectors. % See the API for more information. % det_row_count: number of detector rows in a single projection % det_col_count: number of detector columns in a single projection % vectors: a matrix containing the actual geometry. Each row corresponds % to a single projection, and consists of: % ( srcX, srcY, srcZ, dX, dY, dZ, uX, uY, uZ, vX, vY, vZ ) % src: the ray source % d : the center of the detector % u : the vector from detector pixel (0,0) to (0,1) % v : the vector from detector pixel (0,0) to (1,0) % proj_geom: MATLAB struct containing all information of the geometry %-------------------------------------------------------------------------- % proj_geom = astra_create_proj_geom('parallel3d_vec', det_row_count, det_col_count, vectors) % % Create a 3D parallel beam geometry specified by 3D vectors. % See the API for more information. % det_row_count: number of detector rows in a single projection % det_col_count: number of detector columns in a single projection % vectors: a matrix containing the actual geometry. Each row corresponds % to a single projection, and consists of: % ( rayX, rayY, rayZ, dX, dY, dZ, uX, uY, uZ, vX, vY, vZ ) % ray: the ray direction % d : the center of the detector % u : the vector from detector pixel (0,0) to (0,1) % v : the vector from detector pixel (0,0) to (1,0) % proj_geom: MATLAB struct containing all information of the geometry %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- if strcmp(type,'parallel') if numel(varargin) < 3 error('Not enough input arguments. Usage: astra_create_proj_geom(parallel, detector_spacing, det_count, angles);'); end proj_geom = struct( ... 'type', 'parallel', ... 'DetectorWidth', varargin{1}, ... 'DetectorCount', varargin{2}, ... 'ProjectionAngles', varargin{3} ... ); elseif strcmp(type,'parallel_vec') if numel(varargin) < 2 error('Not enough input arguments. Usage: astra_create_proj_geom(parallel_vec, det_count, V);') end if size(varargin{2}, 2) ~= 6 error('V should be a Nx6 matrix, with N the number of projections.') end proj_geom = struct( ... 'type', 'parallel_vec', ... 'DetectorCount', varargin{1}, ... 'Vectors', varargin{2} ... ); elseif strcmp(type,'fanflat') if numel(varargin) < 5 error('Not enough input arguments. Usage: astra_create_proj_geom(fanflat, det_width, det_count, angles, source_origin, source_det);'); end proj_geom = struct( ... 'type', 'fanflat', ... 'DetectorWidth', varargin{1}, ... 'DetectorCount', varargin{2}, ... 'ProjectionAngles', varargin{3}, ... 'DistanceOriginSource', varargin{4}, ... 'DistanceOriginDetector', varargin{5} ... ); elseif strcmp(type,'fanflat_vec') if numel(varargin) < 2 error('Not enough input arguments. Usage: astra_create_proj_geom(fanflat_vec, det_count, V).') end if size(varargin{2}, 2) ~= 6 error('V should be a Nx6 matrix, with N the number of projections') end proj_geom = struct( ... 'type', 'fanflat_vec', ... 'DetectorCount', varargin{1}, ... 'Vectors', varargin{2} ... ); elseif strcmp(type,'parallel3d') if numel(varargin) < 5 error('Not enough input arguments: astra_create_proj_geom(parallel3d, detector_spacing_x, detector_spacing_y, det_row_count, det_col_count, angles);'); end proj_geom = struct( ... 'type', 'parallel3d', ... 'DetectorSpacingX', varargin{1}, ... 'DetectorSpacingY', varargin{2}, ... 'DetectorRowCount', varargin{3}, ... 'DetectorColCount', varargin{4}, ... 'ProjectionAngles', varargin{5} ... ); elseif strcmp(type,'cone') if numel(varargin) < 7 error('Not enough input arguments. Usage: astra_create_proj_geom(cone, detector_spacing_x, detector_spacing_y, det_row_count, det_col_count, angles, source_origin, source_det);'); end proj_geom = struct( ... 'type', 'cone', ... 'DetectorSpacingX', varargin{1}, ... 'DetectorSpacingY', varargin{2}, ... 'DetectorRowCount', varargin{3}, ... 'DetectorColCount', varargin{4}, ... 'ProjectionAngles', varargin{5}, ... 'DistanceOriginSource', varargin{6}, ... 'DistanceOriginDetector',varargin{7} ... ); elseif strcmp(type,'cone_vec') if numel(varargin) < 3 error('Not enough input arguments. Usage: astra_create_proj_geom(cone_vec, det_row_count, det_col_count, V);') end if size(varargin{3}, 2) ~= 12 error('V should be a Nx12 matrix, with N the number of projections') end proj_geom = struct( ... 'type', 'cone_vec', ... 'DetectorRowCount', varargin{1}, ... 'DetectorColCount', varargin{2}, ... 'Vectors', varargin{3} ... ); elseif strcmp(type,'parallel3d_vec') if numel(varargin) < 3 error('Not enough input arguments. Usage: astra_create_proj_geom(parallel3d_vec, det_row_count, det_col_count, V);') end if size(varargin{3}, 2) ~= 12 error('V should be a Nx12 matrix, with N the number of projections.') end proj_geom = struct( ... 'type', 'parallel3d_vec', ... 'DetectorRowCount', varargin{1}, ... 'DetectorColCount', varargin{2}, ... 'Vectors', varargin{3} ... ); elseif strcmp(type,'sparse_matrix') if numel(varargin) < 3 error('Not enough input arguments. Usage: astra_create_proj_geom(sparse_matrix, det_width, det_count, angles, matrix_id);') end proj_geom = struct( ... 'type', 'sparse_matrix', ... 'DetectorWidth', varargin{1}, ... 'DetectorCount', varargin{2}, ... 'ProjectionAngles', varargin{3}, ... 'MatrixID', varargin{4} ... ); else error(['Unknown projection geometry type: ' type '.']); end astra-toolbox-2.3.0/matlab/tools/astra_create_projector.m000066400000000000000000000036541475635207100236360ustar00rootroot00000000000000function proj_id = astra_create_projector(type, proj_geom, vol_geom, options) %-------------------------------------------------------------------------- % proj_id = astra_create_projector(type, proj_geom, vol_geom, options) % % Create a new projector object based on projection and volume geometry. % Used when the default values of each projector are sufficient. % % type: type of the projector. 'blob', 'line', 'linear' 'strip', ... See API for more information. % proj_geom: MATLAB struct containing the projection geometry. % vol_geom: MATLAB struct containing the volume geometry. % options: Optional MATLAB struct containing projector options (like: 'GPUindex', 'DetectorSuperSampling', and 'VoxelSuperSampling') % proj_id: identifier of the projector as it is now stored in the astra-library. %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ------------------------------------------------------------------------- cfg_proj = astra_struct(type); cfg_proj.ProjectionGeometry = proj_geom; cfg_proj.VolumeGeometry = vol_geom; if strcmp(type,'blob') % Blob options blob_size = 2; blob_sample_rate = 0.01; blob_values = kaiserBessel(2, 10.4, blob_size, 0:blob_sample_rate:blob_size); cfg_proj.Kernel.KernelSize = blob_size; cfg_proj.Kernel.SampleRate = blob_sample_rate; cfg_proj.Kernel.SampleCount = length(blob_values); cfg_proj.Kernel.KernelValues = blob_values; end if exist('options', 'var') cfg_proj.options = options; end if strcmp(type,'cuda3d') proj_id = astra_mex_projector3d('create', cfg_proj); else proj_id = astra_mex_projector('create', cfg_proj); end astra-toolbox-2.3.0/matlab/tools/astra_create_reconstruction.m000066400000000000000000000057431475635207100247110ustar00rootroot00000000000000function [recon_id, recon] = astra_create_reconstruction(rec_type, proj_id, sinogram, iterations, use_mask, mask, use_minc, minc, use_maxc, maxc) %-------------------------------------------------------------------------- % [recon_id, recon] = astra_create_reconstruction(rec_type, proj_id, sinogram, iterations, use_mask, mask, use_minc, minc, use_maxc, maxc) % % Create a CPU based iterative reconstruction. % % rec_type: reconstruction type, 'ART', 'SART' 'SIRT' or 'CGLS', not all options are adjustable % proj_id: identifier of the projector as it is stored in the astra-library % sinogram: sinogram data OR sinogram identifier % iterations: number of iterations to perform % use_mask: use a reconstrucionmask? 'yes' or 'no' % mask: mask data OR mask identifier. % use_minc: use a minimum constraint? 'yes' or 'no' % minc: minimum constraint value % use_maxc: use a maximum constraint? 'yes' or 'no' % maxc: maximum constraint value % recon_id: identifier of the reconstruction data object as it is now stored in the astra-library % recon: MATLAB data version of the reconstruction %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- if nargin <= 4 use_mask = 'no'; mask = []; use_minc = 'no'; minc = 0; use_maxc = 'no'; maxc = 255; end if nargin <= 6 use_minc = 'no'; minc = 0; use_maxc = 'no'; maxc = 255; end if numel(sinogram) == 1 sinogram_id = sinogram; else proj_geom = astra_mex_projector('projection_geometry', proj_id); sinogram_id = astra_mex_data2d('create', '-sino', proj_geom, sinogram); end % create reconstruction object vol_geom = astra_mex_projector('volume_geometry', proj_id); recon_id = astra_mex_data2d('create', '-vol', vol_geom, 0); % configure cfg = astra_struct(rec_type); cfg.ProjectorId = proj_id; cfg.ProjectionDataId = sinogram_id; cfg.ReconstructionDataId = recon_id; if strcmp(use_mask,'yes') if numel(mask) == 1 mask_id = mask; else mask_id = astra_mex_data2d('create', '-vol', vol_geom, mask); end cfg.options.ReconstructionMaskId = mask_id; end cfg.options.UseMinConstraint = use_minc; cfg.options.MinConstraintValue = minc; cfg.options.UseMaxConstraint = use_maxc; cfg.options.MaxConstraintValue = maxc; cfg.options.ProjectionOrder = 'random'; alg_id = astra_mex_algorithm('create', cfg); % iterate astra_mex_algorithm('iterate', alg_id, iterations); % return object recon = astra_mex_data2d('get', recon_id); % garbage collection astra_mex_algorithm('delete', alg_id); if numel(sinogram) ~= 1 astra_mex_data2d('delete', sinogram_id); end if strcmp(use_mask,'yes') if numel(mask) ~= 1 astra_mex_data2d('delete', mask_id); end end astra-toolbox-2.3.0/matlab/tools/astra_create_reconstruction_cuda.m000066400000000000000000000053501475635207100256770ustar00rootroot00000000000000function [recon_id, recon] = astra_create_reconstruction_cuda(rec_type, proj_geom, vol_geom, sinogram, iterations, use_mask, mask, use_minc, minc, use_maxc, maxc) %-------------------------------------------------------------------------- % [recon_id, recon] = astra_create_reconstruction_cuda(rec_type, proj_geom, vol_geom, sinogram, iterations, use_mask, mask, use_minc, minc, use_maxc, maxc) % % Create a GPU based iterative reconstruction. % % rec_type: reconstruction type, defaults to 'SIRT_CUDA' if empty % proj_geom: projection geometry struct % vol_geom: volume geometry struct % sinogram: sinogram data OR sinogram identifier % iterations: number of iterations to perform % use_mask: use a reconstrucionmask? 'yes' or 'no' % mask: mask data OR mask identifier. % use_minc: use a minimum constraint? 'yes' or 'no' % minc: minimum constraint value % use_maxc: use a maximum constraint? 'yes' or 'no' % maxc: maximum constraint value % recon_id: identifier of the reconstruction data object as it is now stored in the astra-library % recon: MATLAB data version of the reconstruction %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- if numel(sinogram) == 1 sinogram_id = sinogram; else sinogram_id = astra_mex_data2d('create', '-sino', proj_geom, sinogram); end % create reconstruction object recon_id = astra_mex_data2d('create', '-vol', vol_geom, 0); if strcmp(rec_type,'') rec_type = 'SIRT_CUDA'; end % configure cfg = astra_struct(rec_type); cfg.ProjectionGeometry = proj_geom; cfg.ReconstructionGeometry = vol_geom; cfg.ProjectionDataId = sinogram_id; cfg.ReconstructionDataId = recon_id; if strcmp(use_mask,'yes') if numel(mask) == 1 mask_id = mask; else mask_id = astra_mex_data2d('create', '-vol', vol_geom, mask); end cfg.options.ReconstructionMaskId = mask_id; end cfg.options.UseMinConstraint = use_minc; cfg.options.MinConstraintValue = minc; cfg.options.UseMaxConstraint = use_maxc; cfg.options.MaxConstraintValue = maxc; alg_id = astra_mex_algorithm('create', cfg); % iterate astra_mex_algorithm('iterate', alg_id, iterations); % return object recon = astra_mex_data2d('get', recon_id); % garbage collection astra_mex_algorithm('delete', alg_id); if numel(sinogram) ~= 1 astra_mex_data2d('delete', sinogram_id); end if strcmp(use_mask,'yes') if numel(mask) ~= 1 astra_mex_data2d('delete', mask_id); end end astra-toolbox-2.3.0/matlab/tools/astra_create_sino.m000066400000000000000000000034551475635207100225760ustar00rootroot00000000000000function [sino_id, sino] = astra_create_sino(data, proj_id) %-------------------------------------------------------------------------- % [sino_id, sino] = astra_create_sino(data, proj_id) % % Create a CPU based forward projection. % % data: input volume, can be either MATLAB-data or an astra-identifier. % proj_id: identifier of the projector as it is stored in the astra-library % sino_id: identifier of the sinogram data object as it is now stored in the astra-library. % sino: MATLAB data version of the sinogram %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- % get projection geometry proj_geom = astra_mex_projector('projection_geometry', proj_id); vol_geom = astra_mex_projector('volume_geometry', proj_id); % store volume if (numel(data) > 1) volume_id = astra_mex_data2d('create','-vol', vol_geom, data); else volume_id = data; end % store sino sino_id = astra_mex_data2d('create','-sino', proj_geom, 0); if astra_mex_projector('is_cuda', proj_id) cfg = astra_struct('FP_CUDA'); else cfg = astra_struct('FP'); end cfg.ProjectorId = proj_id; cfg.ProjectionDataId = sino_id; cfg.VolumeDataId = volume_id; % create sinogram alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('iterate', alg_id); astra_mex_algorithm('delete', alg_id); if (numel(data) > 1) astra_mex_data2d('delete', volume_id); end if nargout >= 2 sino = astra_mex_data2d('get',sino_id); end astra-toolbox-2.3.0/matlab/tools/astra_create_sino3d_cuda.m000066400000000000000000000034051475635207100240140ustar00rootroot00000000000000function [sino_id, sino] = astra_create_sino3d_cuda(data, proj_geom, vol_geom) %-------------------------------------------------------------------------- % [sino_id, sino] = astra_create_sino3d_cuda(data, proj_geom, vol_geom) % % Create a GPU based forward projection. % % data: input volume, can be either MATLAB-data or an astra-identifier. % proj_geom: MATLAB struct containing the projection geometry. % vol_geom: MATLAB struct containing the volume geometry. % sino_id: identifier of the sinogram data object as it is now stored in % the astra-library. % sino: MATLAB data version of the sinogram. %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- % store volume if (numel(data) > 1) if (strcmp(class(data),'single')) % read-only link volume_id = astra_mex_data3d('link','-vol', vol_geom, data, 1); else volume_id = astra_mex_data3d('create','-vol', vol_geom, data); end else volume_id = data; end % store sino sino_id = astra_mex_data3d('create','-sino', proj_geom, 0); % create sinogram cfg = astra_struct('FP3D_CUDA'); cfg.ProjectionDataId = sino_id; cfg.VolumeDataId = volume_id; alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('iterate', alg_id); astra_mex_algorithm('delete', alg_id); if (numel(data) > 1) astra_mex_data3d('delete', volume_id); end if nargout >= 2 sino = astra_mex_data3d('get',sino_id); end astra-toolbox-2.3.0/matlab/tools/astra_create_sino_cuda.m000066400000000000000000000033731475635207100235710ustar00rootroot00000000000000function [sino_id, sino] = astra_create_sino_cuda(data, proj_geom, vol_geom, gpu_index) %-------------------------------------------------------------------------- % [sino_id, sino] = astra_create_sino_cuda(data, proj_geom, vol_geom, gpu_index) % % Create a GPU based forward projection. % % data: input volume, can be either MATLAB-data or an astra-identifier. % proj_geom: MATLAB struct containing the projection geometry. % vol_geom: MATLAB struct containing the volume geometry. % gpu_index: the index of the GPU to use (optional). % sino_id: identifier of the sinogram data object as it is now stored in % the astra-library. % sino: MATLAB data version of the sinogram. %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- % store volume if (numel(data) > 1) volume_id = astra_mex_data2d('create','-vol', vol_geom, data); else volume_id = data; end % store sino sino_id = astra_mex_data2d('create','-sino', proj_geom, 0); % create sinogram cfg = astra_struct('FP_CUDA'); cfg.ProjectionDataId = sino_id; cfg.VolumeDataId = volume_id; if nargin > 3 cfg.option.GPUindex = gpu_index; end alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('iterate', alg_id); astra_mex_algorithm('delete', alg_id); if (numel(data) > 1) astra_mex_data2d('delete', volume_id); end if nargout >= 2 sino = astra_mex_data2d('get',sino_id); end astra-toolbox-2.3.0/matlab/tools/astra_create_sino_gpu.m000066400000000000000000000033711475635207100234460ustar00rootroot00000000000000function [sino_id, sino] = astra_create_sino_gpu(data, proj_geom, vol_geom, gpu_index) %-------------------------------------------------------------------------- % [sino_id, sino] = astra_create_sino_gpu(data, proj_geom, vol_geom, gpu_index) % % Create a GPU based forward projection. % % data: input volume, can be either MATLAB-data or an astra-identifier. % proj_geom: MATLAB struct containing the projection geometry. % vol_geom: MATLAB struct containing the volume geometry. % gpu_index: the index of the GPU to use (optional). % sino_id: identifier of the sinogram data object as it is now stored in % the astra-library. % sino: MATLAB data version of the sinogram. %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- % store volume if (numel(data) > 1) volume_id = astra_mex_data2d('create','-vol', vol_geom, data); else volume_id = data; end % store sino sino_id = astra_mex_data2d('create','-sino', proj_geom, 0); % create sinogram cfg = astra_struct('FP_CUDA'); cfg.ProjectionDataId = sino_id; cfg.VolumeDataId = volume_id; if nargin > 3 cfg.option.GPUindex = gpu_index; end alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('iterate', alg_id); astra_mex_algorithm('delete', alg_id); if (numel(data) > 1) astra_mex_data2d('delete', volume_id); end if nargout >= 2 sino = astra_mex_data2d('get',sino_id); end astra-toolbox-2.3.0/matlab/tools/astra_create_sino_sampling.m000066400000000000000000000034661475635207100244720ustar00rootroot00000000000000function [sino_id, sino] = astra_create_sino_sampling(data, proj_geom, vol_geom, gpu_index, sampling) %-------------------------------------------------------------------------- % [sino_id, sino] = astra_create_sino_cuda(data, proj_geom, vol_geom, gpu_index) % % Create a GPU based forward projection. % % data: input volume, can be either MATLAB-data or an astra-identifier. % proj_geom: MATLAB struct containing the projection geometry. % vol_geom: MATLAB struct containing the volume geometry. % gpu_index: the index of the GPU to use (optional). % sino_id: identifier of the sinogram data object as it is now stored in % the astra-library. % sino: MATLAB data version of the sinogram. %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- % store volume if (numel(data) > 1) volume_id = astra_mex_data2d('create','-vol', vol_geom, data); else volume_id = data; end % store sino sino_id = astra_mex_data2d('create','-sino', proj_geom, 0); % create sinogram cfg = astra_struct('FP_CUDA'); cfg.ProjectionDataId = sino_id; cfg.VolumeDataId = volume_id; cfg.option.DetectorSuperSampling = sampling; if nargin > 3 cfg.option.GPUindex = gpu_index; end alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('iterate', alg_id); astra_mex_algorithm('delete', alg_id); if (numel(data) > 1) astra_mex_data2d('delete', volume_id); end if nargout >= 2 sino = astra_mex_data2d('get',sino_id); end astra-toolbox-2.3.0/matlab/tools/astra_create_vol_geom.m000066400000000000000000000101011475635207100234170ustar00rootroot00000000000000function vol_geom = astra_create_vol_geom(varargin) %-------------------------------------------------------------------------- % vol_geom = astra_create_vol_geom([row_count col_count]); % vol_geom = astra_create_vol_geom(row_count, col_count); % vol_geom = astra_create_vol_geom(row_count, col_count, min_x, max_x, min_y, max_y); % % Create a 2D volume geometry. See the API for more information. % row_count: number of rows. % col_count: number of columns. % min_x: minimum value on the x-axis. % max_x: maximum value on the x-axis. % min_y: minimum value on the y-axis. % max_y: maximum value on the y-axis. % vol_geom: MATLAB struct containing all information of the geometry. %-------------------------------------------------------------------------- % vol_geom = astra_create_vol_geom(row_count, col_count, slice_count); % vol_geom = astra_create_vol_geom(row_count, col_count, slice_count, min_x, max_x, min_y, max_y, min_z, max_z); % % Create a 3D volume geometry. See the API for more information. % row_count: number of rows. % col_count: number of columns. % slice_count: number of slices. % vol_geom: MATLAB struct containing all information of the geometry. %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- % astra_create_vol_geom([row_and_col_count ]) if numel(varargin) == 1 && numel(varargin{1}) == 1 vol_geom = struct(); vol_geom.GridRowCount = varargin{1}(1); vol_geom.GridColCount = varargin{1}(1); % astra_create_vol_geom([row_count col_count]) elseif numel(varargin) == 1 && numel(varargin{1}) == 2 vol_geom = struct(); vol_geom.GridRowCount = varargin{1}(1); vol_geom.GridColCount = varargin{1}(2); % astra_create_vol_geom([row_count col_count slice_count]) elseif numel(varargin) == 1 && numel(varargin{1}) == 3 vol_geom = struct(); vol_geom.GridRowCount = varargin{1}(1); vol_geom.GridColCount = varargin{1}(2); vol_geom.GridSliceCount = varargin{1}(3); % astra_create_vol_geom(row_count, col_count) elseif numel(varargin) == 2 vol_geom = struct(); vol_geom.GridRowCount = varargin{1}; vol_geom.GridColCount = varargin{2}; % astra_create_vol_geom(row_count, col_count, min_x, max_x, min_y, max_y) elseif numel(varargin) == 6 vol_geom = struct(); vol_geom.GridRowCount = varargin{1}; vol_geom.GridColCount = varargin{2}; vol_geom.option.WindowMinX = varargin{3}; vol_geom.option.WindowMaxX = varargin{4}; vol_geom.option.WindowMinY = varargin{5}; vol_geom.option.WindowMaxY = varargin{6}; % astra_create_vol_geom(row_count, col_count, slice_count) elseif numel(varargin) == 3 vol_geom = struct(); vol_geom.GridRowCount = varargin{1}; vol_geom.GridColCount = varargin{2}; vol_geom.GridSliceCount = varargin{3}; % astra_create_vol_geom(row_count, col_count, slice_count, min_x, max_x, min_y, max_y, min_z, max_z) elseif numel(varargin) == 9 vol_geom = struct(); vol_geom.GridRowCount = varargin{1}; vol_geom.GridColCount = varargin{2}; vol_geom.GridSliceCount = varargin{3}; vol_geom.option.WindowMinX = varargin{4}; vol_geom.option.WindowMaxX = varargin{5}; vol_geom.option.WindowMinY = varargin{6}; vol_geom.option.WindowMaxY = varargin{7}; vol_geom.option.WindowMinZ = varargin{8}; vol_geom.option.WindowMaxZ = varargin{9}; end % set the window options, if not set already. if ~isfield(vol_geom, 'option') || ~isfield(vol_geom.option, 'WindowMinX') vol_geom.option.WindowMinX = -vol_geom.GridColCount / 2; vol_geom.option.WindowMaxX = vol_geom.GridColCount / 2; vol_geom.option.WindowMinY = -vol_geom.GridRowCount / 2; vol_geom.option.WindowMaxY = vol_geom.GridRowCount / 2; if isfield(vol_geom, 'GridSliceCount') vol_geom.option.WindowMinZ = -vol_geom.GridSliceCount / 2; vol_geom.option.WindowMaxZ = vol_geom.GridSliceCount / 2; end end astra-toolbox-2.3.0/matlab/tools/astra_data_gui.fig000066400000000000000000000132621475635207100223660ustar00rootroot00000000000000MATLAB 5.0 MAT-file, Platform: PCWIN64, Created on: Wed Mar 07 12:30:55 2012 'IMŸxœìYOOGCùS4ªD{ˆ¸´€D+Ó ¥JÔ²à@ ‚‰TÉ­3^í)ëÝÕ̬çD=qì±Ç^*‘oÐC=¤R=äÐJUU¡Š"T©Ç\2³;³Þ]ì5Nh$ž´<¿Ùyoçýfæ½7àò œò' <ê—r"ð ‹¾å|úzš“ê—#òýuþ°º¤‰ 4‹†'ØÄ²aQ W°Q$Èä­ÔF:††ÛcŒ?ƒÆ8R-Æ1 ¹ q)—pÙ!¨Yà ? ÉvA•Š&ôÞê å÷„”3üÏ®‰Ú‚Üã`kÆ2,ìá6T¡í7ÜÆº «ÈoX7*#²æ¹nÖ8ŒkuË®ó† d:Ë0`6¤ï68Õ"YÌäTxÛ²(fØ2ý†üÐ×s²‰ÁÞ.m#èÏÉâ=Lq˜ÕÁ.EädÐï±dÛÖ¡0ãµga4“˜ œ½†AxY¯@u†íÜÎHÄŽud2\E¾!ag œ½®ú@x]MòçªK'‹Q.ì¥N·§µ°çwzíé`Ñã?Jþ“ä¿Hþ«äÏ$ÿMò?$ÿSò¿$?”üoÉ$?–üDòÿgü$2Σˆ‡?•ßç¥KüÃ㊋¿ÂAá¢pR¸)® g…ûyé_þG"oˆJá¬|v„ó™WnäVWV¶³¹ È X˜ú`j‹§6Ë„F®†© üÐÍÉ9Hù"OÍù²ƒ?äu•ÿÝt›ïö…¾Û¬R©#½dH/ Dщ^*¤—¦e¢NòýX!‡Ý>Ÿ¿[àì:!8n‘×§€"µnSšÇ§$ŸÔ¢~µ²ÄqHú¶>ñÙÜÑóíí§ÿÌ?|¢u3K  k¿®qúïsmóøÉÝã'«Z¹Äé0«éÓµÂtm®£ñœ†s»s ϳuh¨ïE¿ÛÊàêàâ0ï|0*åwù³º»~keÓ[ƒ¯º{|ë5°{þèg_­çïùÃk_\CùŠ÷–Á26K–ìkñCÁEY¯d HUñNB•<ÔuDý¾ZÂF£ÈWzu1ÅfÙ@Lž hêâ4ãE‚aé{ü½§'œ'Ó=™´Ã_Q+ü'“ñðySÊYÐ8' BEÌÔwl‡V s¡|F ÖWI.% Wo¿¯¡7ÞOÑÿ~_wø ?«î¶#–%³*E6w„ù7¥TÜÇ©D̩䘺//Wk´>m…×Õ^BÎÜÈm«L`uJ'4·±”½³´œÛþ(=?_è,¾'Bv ×÷Nô4Ð]}£ÖÁL—àŽn”o_[dÚP÷ðÚo3¾/"x ™–ÞKk3•ÍÂWHgs¨Æ§EÄÂÙpXœ™fX¾I^í›é¹&9ÞQpemvtO Oªõªå¨=ÜEA;JïÛ€ÞD¤_‚[ÚÈlîùдó „ö‚÷Æ£‘q¨ÿe(ûBžô~þžáùVy±›sØÏO¥´Š{îz_så;ëÚ§¯Ü||åùb'ëõ4¼âÖ?¢î¾Œ/—ñåË^BŽ_T^í0À\Æ—ÖñEýöxëCÝo½Òè¶®•óÛ}5ŵ6ö^v½z ]sëÕg1ïyT<»šëÕ¸õi´þ|ÿÿìY½oÓ@?§mú!1P$@ZÒJRhQ¤^T1ô#")"¹8nbêØ‘})íÆH·Ž]*1²ö(ˆµ ü Ü‹}ÍÅ58q]@ÂO:_|ï—wïÞ—ÎyRE"]b³gcˆ~ç÷‡—Øqf „ÃË5b™*¦ÕÆÙëkã$¼:YtÏQÝ‹ê^Ñe/à»­{–V)ZÌó• _T÷¼ë¯9Aë^‹^¥ñÛã݃ãÝØþá!~eïä[+Þk®äëŽw¿úGö>ÇJœb—·ù(?ýßù©ä²ðò¯Ø]%¨(?yç§ÔÎO⹅ѱ÷éù û Ç=Á ³/ÞÇó­¾x<ê‹=ûb1Îüúâã$IŠêKT_ÎÕÿÒmõ¿(¼úÂ×íáÚçklõ»×{Ÿ ü Úáƒï]ÝûüÊ^½Þ)•Fù%Ê/%—½€_¢þ?¿ ¢«==_aŸaá¸ç ¸áö¯wí{Ýã’ç·:Û¿öÚŸþÉ~4Hýª¶êÆ“ ‡W¯bûžv¢ãþ¦×|˜BÁêU¶éßøÞ1ˆ:í <øw]ÕC‘çzðûà]|\Ü‹âý>.ìïy+ø'ÿÿìYÏoÔFžÝ„” ZUJ/(ª$RT%HH”TÚ ‹P+eIhê!í2»öz§8˧›ÇrË‘ªBâΉþ=Ð ‚;ø ˆ*qà„˜g{”ñ¬YÛËØ‘¬ñóÎ{ûæ›yß{cŸü÷ÊþÃ+ÓøÉchgðß•å•åÿ«'B‹âš×Qq£¨‰å’rA›w0>ÞxBoq³ËAïE¯œñG„Þd,CoŸ_'656‰…b?ÎgøñEÂHæ]ÞØ&Ý¡èK?dÿC†½¯4{ ”3fûß´pÎËc›2¿‰ ¼ŽÅÏ¡ÝÁç1èß+†w%–ï‹ëšC¹m“rÛÔE‹¡¶×µi‹pÊœK„xT³©ëRGºÎ| C”G¦m¶¸i¬5½òH «uˆc™—[Z³”^õÇÿ8Jâr«C<"¬y~~;“šëÄrh;ž8Ê·?jv@¨KÓ>÷1â%ËŸ¬¸Kè!Ön‡ÿ»Žúïc•/`ψ+â«*þïéí»Oo?¯†òêØëA¥‚ut»²I»¿¡§÷õÇñJ®Èm1‡{ÌVì Îß…øý|¼ØþšŠå{¨—.’ÖMà.Ǩ1›y¨Fl»)žÉßk’ â¦ÀßMKÍ3 #>j×|ÓSMè4£3Ñ0ù ßºëû¾,¤R©êí)ü;•/Œ¿Q¯åzÀ<· ·IÓc,&DßtÅD¸X );œŠ@–ø´'dÖ†`VC ¯Çi^§5¼@®]ØZ÷˜å‘í™ËÔ6ý­úÊæêÊÅ­ŸÎ..-5óÕ z±•‚{=ëM—‹á(÷Áœ‚‹Èô»n¼!ÛTnÍß™wÓwI+Âk/ÿ_4¼@öÛß,â¹N”»Ì±,†Ø¿ó‚®=Ò€Û†й٨ΰ2ŽfztÄ@襵ùy48¿‡9»Û,1…¿lª©wGÑ;¥+ KõÚÚÈ_£ž}Ø8@øÀžZWMj~Lhë òttû¼¬ü&çž'žUU8}yßUøþÃ7ÕNÚü ÒÚÝ×¹êp½ž6 ÊGü2â—_5¼@.Ì/2Ïæ$˜¿¤ó‹œóûð˧s>¶†¢/ý=ΰ÷áëÞ aÝ람ÿAÅëÞ:é"µÕ©“{ë`/qRÖëb±ÆA¿:ù0ëb5n²êbXˆ›è\·_Õû0® æ³Î(Ÿ %ŸÍR>Sy;Í?Kà ä¼ùÌ· àƒw0+çQ^KÏkr­óä5ÕÞ¸ØI{¥âzeq'ÿozû¯?¡­àGá{îŽ~ý‡¯Á÷_…<ÓQì–Rìê<“•'Pr=AÞ©Øô×÷ý¢ø±ß#>ý¼ù´£áò{ði¡“ˆOÓùô:àSÕ¿aÔûªixŸÒp¹gß1ßaØÓ{œaÿß–Ãó@·`¼JÞ»zÏEëùì×~µŠñnøÝåEU~GnÍî4gw^~ß‘Óò3ø²´4}é‡ìq†½"qñÿÿìYÍoE;Nè©‚B$*$äC!‰!§T¥‚‚7uqH›ˆ¤= Hf¼;¶‡nvV³³¡áÔcˉc8DêÿC¹¸p‡þ¥¸€óvwðxv›]»nR ?e5~ëy/oó>×S¡SòšB1M&|I»€N'÷ö­«Ö9¹Þ-Çrp¿œ#?‰*h&á?—×-ŠõhƒTPæ)~SpêuzßoŠ=—hûW|ߥ6‘ëX`´…µÍ’À> õì«dØwF³o&áí.æØ„==šž,œ*NUy|+éüšõóჃÿÕúè‚eÏï¶æw¯ƒÞZŽ}}öM Kµ*"Wé“« Aî »;¯é„‡ÕŸ]ê$@ƒ¾+9vLø¶,_¼²C­Ÿäè›3ôRÁ˜¼ÒV(ó:œ…~Æó Eñqob0¼gþ;”ŽI[Tô…Ü2¼¥CRÃ¥¾ßM©p“´I\"ßYo}!Wí–ÜÖèb¯CVm­»NÆÆÑÆ[žžiCð«´rRõÂᨘ2ôR{Ľ|ñ’gÏ ù‚µÛC範¬§Qþz\Ù«Ö‡ßÏÊ¿YË´ÇÔ«HéUø„œ¹ZèhO£þs>¤6óg®¦gøüðq\?Ï W?¢t~¸†í;»<§Á\ÆQ»nKÞSß7T2H(]oûëkƒ,¢ˆéV@¸®â8ëïQçnú}Yr¥R)’Û×òïL¾°¿;`¾VçϹƒ…‹[œ±$!Ä—"GŠ÷•¬ði‡^”Y›2³:y3N³ðzËÀ øÆÛœu8Þ©®R—Û7V¶ÖV®mz±¶¼ÜJã—¥·Ô§·„¶3p/"g%rÕûBå .²Òïù‰C¶©rÍ/¿øØŽñÚϱÏ6ð>h¿]³ºqíZ"»òXé¿‹2]sÜ„ÍNHæÛQ=iÆõ¤©¢i~)%¹$·Ãªt..FÿoØ<ƒ=æíí°PÅvÞ9(Òõ(9Ÿ×Œ}%©éFc}øó(åÍÒ=}z5mØ¡z5¥ø7âËÚw¯&k‘¸ÖíÕóù0õ,Æåë—¨žýSï¶.X¿{ðg¡~Ü쫉CÅ8ÏŒóŒcàüyFU݉fœg²óŒêÅŸ'Ï|ƒó¯¼þß:ßgÍ5}þ1b=ÈXóæ“W ½À'óÉsöÃõ¨¾?Â÷Iæ°lö§fÿjÎÈf|œómV¼^§œØšAÞWKxM‡rtómž=Ç9ß^zhÂúÊÿáœñlZ?Ü|òèæ“×#´æž²EÇçl¿ôª‚’Ê äsæ.( ¤cP×áÎ3ð‰Mq\²_†ùt­jÅﯦ†‹Ç{(æ<:èû^ékáI½ÿ÷wÿïþ®cà|Ñþ.ÉoÍô«Ôb-Þ¸¿ËîïÎië0ù?žÿ.'ùÿ¯z\Þ´ž>‚ðw¡yÐôÛÏP±¼sõã <ÇeñOÿá”×·êÿ¿,?)<Ž»®OÏ<øýW#’GÆš‡ï ¯ãúxeÈú¿/º>ŽëaLãzØÃ±H=üÿÿíUOoÓ0w·µb A‘8€¨´VôÐÄe)E‡ý©è@ UŠœÔMÍR;r\ ßøißfG¸ïÂ7@Ü‘8à×ÚÉ¢…•]@<éÉù%~¿<ÿüì‡Øk.öâû]Th_yIù9åKhjE Ê=‚•5Žû·Nu°ã½$¾¬“W„É–¸†c)° n0¢ÕUO2·G…Û!¡šH9k 0 ȆÏVëÇ¢ë*FÃ[«Mòsrò[Nå3ÎÆC>Šõ{àiœÀcÌæ1q¶>—Só Ši«µÓ|MyÄ&ËtÕ2{!q¥¾ÇßJ*’æ6ü€¯N¿,Xß.éøÚ_)CŸ%‹´©(ÿü ìŠóþàèÝÁÑ÷õ{wÁ®;ß·¿nÿXÏÓ«€~ͳ¬žžë|œœ¸ ) Ü£ÜIÉÙL§·ÏBÎÿ‹êO+êB|†CÚÛÅở“W1•`¨ß7gRcžNYu=¢>gRðÐâ±óÊÚ[§e”Ôûf¥éLâK§Ó¹¬q¤ü)£Òœ1°Cû{·yLá<Ü‘‚² ™ß‘ãXñÍ( ©!䑺 Þ¾™ýŽnçSºöX`_'<û‹ O9ƒÇ^7Ì,Χä;Ä2Äžà\N¿Ç$R I.ÁLRF̶¦ï‘cçv{Ö×>~»ñG}íú7ûÚøŒâÑ ãO ãƒxœíX;NÃ@¯?‰“ر¡á(é‘P(" è,+%"r,BÜ€#pJJŽÀh¸G`7^Ç‘ˆÒPìH£·ïyf÷yý)6$¢î‘§P¦OU¸š[2?â UÍÉ:(ÊÅM¿h鯳ÒÕ5K×LŽÏÎëy¾¢fžX×[ktVµŠ2Oæ‹ì¢L‹"+“D÷¿B³^Å™Uÿno¾ïgûg½ÐšÐã±Ì™ÌBt¥ÙÕõ:Òü‘®ïóéÝ|™'³4¿ZdÉò¼¹-ÒiFí!Z4¾Ö–ŒÛŒ;Œ»Œ{ŒwçÏÓg¼ÇxŸñãã!ãà ûTï[L€¨Û€¨;€¨»€¨{€¨wQï¢î¢ÞD½ˆúõõõ! êÃÿx/o´ù?€ßH]?†ú€Õ{¬^ñ£jøÉçªßáíö?ä~Œãßø7þãßø7þãßøÿ»ÿ—ýÿ¶Ñöõöí‚þ¶ó—K¨šãù îÿ>ç/*¾>9Qñastra-toolbox-2.3.0/matlab/tools/astra_data_gui.m000066400000000000000000000361651475635207100220640ustar00rootroot00000000000000function varargout = astra_data_gui(varargin) % ASTRA_DATA_GUI M-file for ASTRA_DATA_GUI.fig % ASTRA_DATA_GUI, by itself, creates a new ASTRA_DATA_GUI or raises the existing % singleton*. % % H = ASTRA_DATA_GUI returns the handle to a new ASTRA_DATA_GUI or the handle to % the existing singleton*. % % ASTRA_DATA_GUI('CALLBACK',hObject,eventData,handles,...) calls the local % function named CALLBACK in ASTRA_DATA_GUI.M with the given input arguments. % % ASTRA_DATA_GUI('Property','Value',...) creates a new ASTRA_DATA_GUI or raises the % existing singleton*. Starting from the left, property value pairs are % applied to the GUI before ASTRA_DATA_GUI_OpeningFcn gets called. An % unrecognized property name or invalid value makes property application % stop. All inputs are passed to ASTRA_DATA_GUI_OpeningFcn via varargin. % % *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one % instance to run (singleton)". % % See also: GUIDE, GUIDATA, GUIHANDLES % Edit the above text to modify the response to help ASTRA_DATA_GUI % Last Modified by GUIDE v2.5 05-Mar-2012 14:34:03 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; gui_State = struct('gui_Name', mfilename, ... 'gui_Singleton', gui_Singleton, ... 'gui_OpeningFcn', @astra_data_gui_OpeningFcn, ... 'gui_OutputFcn', @astra_data_gui_OutputFcn, ... 'gui_LayoutFcn', [] , ... 'gui_Callback', []); if nargin && ischar(varargin{1}) gui_State.gui_Callback = str2func(varargin{1}); end if nargout [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); else gui_mainfcn(gui_State, varargin{:}); end % End initialization code - DO NOT EDIT % --- Executes just before astra_data_gui is made visible. function astra_data_gui_OpeningFcn(hObject, eventdata, handles, varargin) % This function has no output args, see OutputFcn. % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % varargin command line arguments to astra_data_gui (see VARARGIN) % Choose default command line output for astra_data_gui handles.output = hObject; handles.data = []; % Update handles structure guidata(hObject, handles); % UIWAIT makes astra_data_gui wait for user response (see UIRESUME) % uiwait(handles.figure1); % --- Outputs from this function are returned to the command line. function varargout = astra_data_gui_OutputFcn(hObject, eventdata, handles) % varargout cell array for returning output args (see VARARGOUT); % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Get default command line output from handles structure varargout{1} = handles.output; % Use this function to display a figure using the gui from any m-file % example: % Handle = astra_data_gui(); % astra_data_gui('loadVolume',guihandles(Handle),'rand(30,30,30)',15); function loadVolume(handles,name,figure_number) set(handles.txt_var, 'String', name); set(handles.figure_number, 'String', num2str(figure_number)); btn_load_Callback(handles.txt_var, [], handles); function txt_var_Callback(hObject, eventdata, handles) %#ok<*DEFNU> % hObject handle to txt_var (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: get(hObject,'String') returns contents of txt_var as text % str2double(get(hObject,'String')) returns contents of txt_var as a double % --- Executes during object creation, after setting all properties. function txt_var_CreateFcn(hObject, eventdata, handles) % hObject handle to txt_var (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes on button press in btn_load. function btn_load_Callback(hObject, eventdata, handles) % hObject handle to btn_load (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) s = get(handles.txt_var, 'String'); data = evalin('base', s); handles.data = data; guidata(hObject, handles); % Set Default Stuff set(handles.sld_slice, 'Min',1); set(handles.sld_slice, 'Max', size(data,3)); set(handles.sld_slice, 'SliderStep', [1/(size(data,3)-2) 1/(size(data,3)-2)]); set(handles.sld_slice, 'Value', size(data,3)/2); sliderValue = floor(get(handles.sld_slice, 'Value')); set(handles.txt_slice, 'String', num2str(sliderValue)); set(handles.txt_min, 'String', num2str(1)); set(handles.txt_max, 'String', num2str(size(data,3))); set(handles.sld_magnification, 'Min',1); set(handles.sld_magnification, 'Max', 400); set(handles.sld_magnification, 'SliderStep', [1/(400-2) 1/(400-2)]); set(handles.sld_magnification, 'Value', 100); sliderValue3 = floor(get(handles.sld_magnification, 'Value')); set(handles.txt_mag, 'String', num2str(sliderValue3)); figure_number = floor(str2double(get(handles.figure_number, 'String'))); if(isnan(figure_number) || figure_number < 1) set(handles.figure_number, 'String', num2str(10)); end showimage(handles); % --- SHOW IMAGE function showimage(handles) sliderValue = floor(get(handles.sld_slice, 'Value')); magnification = floor(get(handles.sld_magnification, 'Value')); figure_number = floor(str2double(get(handles.figure_number, 'String'))); image_matrix = handles.data; if get(handles.btn_x, 'Value') == 1 figure(figure_number), imshow(sliceExtractor((image_matrix(:,:,:)), 'y', sliderValue),[],'InitialMagnification', magnification); ylabel('y') xlabel('z') set(gcf,'Name','ASTRA DATA GUI') elseif get(handles.btn_y, 'Value') == 1 figure(figure_number), imshow(sliceExtractor((image_matrix(:,:,:)), 'x', sliderValue),[],'InitialMagnification', magnification); ylabel('x') xlabel('z') set(gcf,'Name','ASTRA DATA GUI') else figure(figure_number), imshow(sliceExtractor((image_matrix(:,:,:)), 'z', sliderValue),[],'InitialMagnification', magnification); ylabel('x') xlabel('y') set(gcf,'Name','ASTRA DATA GUI') end % --- Executes on slider movement. function sld_slice_Callback(hObject, eventdata, handles) % hObject handle to sld_slice (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) sliderValue = floor(get(handles.sld_slice, 'Value')); set(handles.txt_slice, 'String', num2str(sliderValue)); showimage(handles); % --- Executes during object creation, after setting all properties. function sld_slice_CreateFcn(hObject, eventdata, handles) % hObject handle to sld_slice (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: slider controls usually have a light gray background. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end % --- Executes on button press in pushbutton2. function pushbutton2_Callback(hObject, eventdata, handles) % hObject handle to pushbutton2 (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % --- Executes on button press in pushbutton3. function pushbutton3_Callback(hObject, eventdata, handles) % hObject handle to pushbutton3 (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % --- Executes on button press in pushbutton4. function pushbutton4_Callback(hObject, eventdata, handles) % hObject handle to pushbutton4 (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) function txt_slice_Callback(hObject, eventdata, handles) % hObject handle to txt_slice (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) slice = str2double(get(handles.txt_slice, 'String')); max = str2num(get(handles.txt_max,'String')); min = str2num(get(handles.txt_min,'String')); if(slice > max) set(handles.txt_slice, 'String', num2str(max)); set(handles.sld_slice, 'Value', max); elseif(slice < min) set(handles.txt_slice, 'String', num2str(min)); set(handles.sld_slice, 'Value', min); else set(handles.sld_slice, 'Value', slice); end showimage(handles); % --- Executes during object creation, after setting all properties. function txt_slice_CreateFcn(hObject, eventdata, handles) % hObject handle to txt_slice (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes on slider movement. function sld_magnification_Callback(hObject, eventdata, handles) % hObject handle to sld_slice2 (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: get(hObject,'Value') returns position of slider % get(hObject,'Min') and get(hObject,'Max') to determine range of slider sliderValue3 = floor(get(handles.sld_magnification, 'Value')); set(handles.txt_mag, 'String', num2str(sliderValue3)); if(~isempty(handles.data)) showimage(handles); end % --- Executes during object creation, after setting all properties. function sld_magnification_CreateFcn(hObject, eventdata, handles) % hObject handle to sld_slice2 (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: slider controls usually have a light gray background. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end function txt_mag_Callback(hObject, eventdata, handles) % hObject handle to txt_slice2 (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) magnification = str2double(get(handles.txt_mag, 'String')); if(magnification > 400) set(handles.txt_mag, 'String', num2str(400)); set(handles.sld_magnification, 'Value', 400); elseif(magnification < 1) set(handles.txt_mag, 'String', num2str(1)); set(handles.sld_magnification, 'Value', 1); else set(handles.sld_magnification, 'Value', magnification); end if(~isempty(handles.data)) showimage(handles); end % --- Executes during object creation, after setting all properties. function txt_mag_CreateFcn(hObject, eventdata, handles) % hObject handle to txt_slice2 (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes on slider movement. function figure_number_Callback(hObject, eventdata, handles) % hObject handle to sld_slice2 (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: get(hObject,'Value') returns position of slider % get(hObject,'Min') and get(hObject,'Max') to determine range of slider number = floor(str2double(get(handles.figure_number, 'String'))); if(number < 1) set(handles.figure_number, 'String', num2str(1)); else set(handles.figure_number, 'String', num2str(number)); end if(~isempty(handles.data)) showimage(handles); end % --- Executes during object creation, after setting all properties. function figure_number_CreateFcn(hObject, eventdata, handles) % hObject handle to sld_slice2 (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: slider controls usually have a light gray background. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end % --- Executes when selected object is changed in btn_dir. function btn_dir_SelectionChangeFcn(hObject, eventdata, handles) % hObject handle to the selected object in btn_dir % eventdata structure with the following fields (see UIBUTTONGROUP) % EventName: string 'SelectionChanged' (read only) % OldValue: handle of the previously selected object or empty if none was selected % NewValue: handle of the currently selected object % handles structure with handles and user data (see GUIDATA) data = handles.data; if(hObject == handles.btn_x) set(handles.btn_x, 'Value', 1); set(handles.btn_y, 'Value', 0); set(handles.btn_z, 'Value', 0); elseif(hObject == handles.btn_y) set(handles.btn_x, 'Value', 0); set(handles.btn_y, 'Value', 1); set(handles.btn_z, 'Value', 0); elseif(hObject == handles.btn_z) set(handles.btn_x, 'Value', 0); set(handles.btn_y, 'Value', 0); set(handles.btn_z, 'Value', 1); end if get(handles.btn_x, 'Value') == 1 set(handles.sld_slice, 'Min',1); set(handles.sld_slice, 'Max', size(data,1)); set(handles.sld_slice, 'SliderStep', [1/(size(data,1)-2) 1/(size(data,1)-2)]); set(handles.sld_slice, 'Value', size(data,1)/2); sliderValue = get(handles.sld_slice, 'Value'); set(handles.txt_slice, 'String', num2str(sliderValue)); set(handles.txt_min, 'String', num2str(1)); set(handles.txt_max, 'String', num2str(size(data,1))); elseif get(handles.btn_y, 'Value') == 1 set(handles.sld_slice, 'Min',1); set(handles.sld_slice, 'Max', size(data,2)); set(handles.sld_slice, 'SliderStep', [1/(size(data,2)-2) 1/(size(data,2)-2)]); set(handles.sld_slice, 'Value', size(data,2)/2); sliderValue = get(handles.sld_slice, 'Value'); set(handles.txt_slice, 'String', num2str(sliderValue)); set(handles.txt_min, 'String', num2str(1)); set(handles.txt_max, 'String', num2str(size(data,2))); else set(handles.sld_slice, 'Min',1); set(handles.sld_slice, 'Max', size(data,3)); set(handles.sld_slice, 'SliderStep', [1/(size(data,3)-2) 1/(size(data,3)-2)]); set(handles.sld_slice, 'Value', size(data,3)/2); sliderValue = get(handles.sld_slice, 'Value'); set(handles.txt_slice, 'String', num2str(sliderValue)); set(handles.txt_min, 'String', num2str(1)); set(handles.txt_max, 'String', num2str(size(data,3))); end showimage(handles); astra-toolbox-2.3.0/matlab/tools/astra_data_op.m000066400000000000000000000004601475635207100217030ustar00rootroot00000000000000function astra_data_op(op, data, scalar, gpu_core) cfg = astra_struct('DataOperation_CUDA'); cfg.Operation = op; cfg.Scalar = scalar; cfg.DataId = data; cfg.option.GPUindex = gpu_core; alg_id = astra_mex_algorithm('create',cfg); astra_mex_algorithm('run',alg_id); astra_mex_algorithm('delete',alg_id); astra-toolbox-2.3.0/matlab/tools/astra_data_op_mask.m000066400000000000000000000005251475635207100227200ustar00rootroot00000000000000function astra_data_op_mask(op, data, scalar, mask, gpu_core) cfg = astra_struct('DataOperation_CUDA'); cfg.Operation = op; cfg.Scalar = scalar; cfg.DataId = data; cfg.option.GPUindex = gpu_core; cfg.option.MaskId = mask; alg_id = astra_mex_algorithm('create',cfg); astra_mex_algorithm('run',alg_id); astra_mex_algorithm('delete',alg_id); astra-toolbox-2.3.0/matlab/tools/astra_downsample_sinogram.m000066400000000000000000000027541475635207100243540ustar00rootroot00000000000000function [sinogram_new, proj_geom_new] = astra_downsample_sinogram(sinogram, proj_geom, factor) %------------------------------------------------------------------------ % [sinogram_new, proj_geom_new] = astra_downsample_sinogram(sinogram, proj_geom, factor) % % Downsample the sinogram with some factor and adjust projection geometry % accordingly % % sinogram: MATLAB data version of the sinogram. % proj_geom: MATLAB struct containing the projection geometry. % factor: the factor by which the number of detectors is divided. % sinogram_new: MATLAB data version of the resampled sinogram. % proj_geom_new: MATLAB struct containing the new projection geometry. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ if mod(size(sinogram,2),factor) ~= 0 disp('size of the sinogram must be a divisor of the factor'); end sinogram_new = zeros(size(sinogram,1),size(sinogram,2)/factor); for i = 1:size(sinogram,2)/factor sinogram_new(:,i) = sum(sinogram(:,(factor*(i-1)+1):factor*i),2); end proj_geom_new = proj_geom; proj_geom_new.DetectorCount = proj_geom_new.DetectorCount / factor; astra-toolbox-2.3.0/matlab/tools/astra_geom_2vec.m000066400000000000000000000106641475635207100221510ustar00rootroot00000000000000function proj_geom_vec = astra_geom_2vec(proj_geom) %-------------------------------------------------------------------------- % proj_geom_vec = astra_geom_2vec(proj_geom) % % Convert a conventional projection geometry to a corresponding vector-base % projection geometry % % proj_geom: input projection geometry (parallel, fanflat, parallel3d, cone) % proj_geom_vec: output vector-base projection geometry %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- % PARALLEL 2D if strcmp(proj_geom.type,'parallel') vectors = zeros(numel(proj_geom.ProjectionAngles), 6); for i = 1:numel(proj_geom.ProjectionAngles) % ray direction vectors(i,1) = sin(proj_geom.ProjectionAngles(i)); vectors(i,2) = -cos(proj_geom.ProjectionAngles(i)); % center of detector vectors(i,3) = 0; vectors(i,4) = 0; % vector from detector pixel 0 to 1 vectors(i,5) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorWidth; vectors(i,6) = sin(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorWidth; end proj_geom_vec = astra_create_proj_geom('parallel_vec', proj_geom.DetectorCount, vectors); % FANFLAT elseif strcmp(proj_geom.type,'fanflat') vectors = zeros(numel(proj_geom.ProjectionAngles), 6); for i = 1:numel(proj_geom.ProjectionAngles) % source vectors(i,1) = sin(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginSource; vectors(i,2) = -cos(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginSource; % center of detector vectors(i,3) = -sin(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginDetector; vectors(i,4) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginDetector; % vector from detector pixel 0 to 1 vectors(i,5) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorWidth; vectors(i,6) = sin(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorWidth; end proj_geom_vec = astra_create_proj_geom('fanflat_vec', proj_geom.DetectorCount, vectors); % CONE elseif strcmp(proj_geom.type,'cone') vectors = zeros(numel(proj_geom.ProjectionAngles), 12); for i = 1:numel(proj_geom.ProjectionAngles) % source vectors(i,1) = sin(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginSource; vectors(i,2) = -cos(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginSource; vectors(i,3) = 0; % center of detector vectors(i,4) = -sin(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginDetector; vectors(i,5) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginDetector; vectors(i,6) = 0; % vector from detector pixel (0,0) to (0,1) vectors(i,7) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorSpacingX; vectors(i,8) = sin(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorSpacingX; vectors(i,9) = 0; % vector from detector pixel (0,0) to (1,0) vectors(i,10) = 0; vectors(i,11) = 0; vectors(i,12) = proj_geom.DetectorSpacingY; end proj_geom_vec = astra_create_proj_geom('cone_vec', proj_geom.DetectorRowCount, proj_geom.DetectorColCount, vectors); % PARALLEL 3D elseif strcmp(proj_geom.type,'parallel3d') vectors = zeros(numel(proj_geom.ProjectionAngles), 12); for i = 1:numel(proj_geom.ProjectionAngles) % ray direction vectors(i,1) = sin(proj_geom.ProjectionAngles(i)); vectors(i,2) = -cos(proj_geom.ProjectionAngles(i)); vectors(i,3) = 0; % center of detector vectors(i,4) = 0; vectors(i,5) = 0; vectors(i,6) = 0; % vector from detector pixel (0,0) to (0,1) vectors(i,7) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorSpacingX; vectors(i,8) = sin(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorSpacingX; vectors(i,9) = 0; % vector from detector pixel (0,0) to (1,0) vectors(i,10) = 0; vectors(i,11) = 0; vectors(i,12) = proj_geom.DetectorSpacingY; end proj_geom_vec = astra_create_proj_geom('parallel3d_vec', proj_geom.DetectorRowCount, proj_geom.DetectorColCount, vectors); elseif ismember(proj_geom.type, {'parallel_vec', 'fanflat_vec', 'parallel3d_vec', 'cone_vec'}) proj_geom_vec = proj_geom; else error(['No suitable vector geometry found for type: ' proj_geom.type '.']) end end astra-toolbox-2.3.0/matlab/tools/astra_geom_postalignment.m000066400000000000000000000036301475635207100241710ustar00rootroot00000000000000function proj_geom = astra_geom_postalignment(proj_geom, factor) %-------------------------------------------------------------------------- % proj_geom = astra_geom_postalignment(proj_geom, factorU) % proj_geom = astra_geom_postalignment(proj_geom, [factorU factorV]) % % Apply a postalignment to a projection geometry. Can be used to model the % rotation axis offset. % % For 2D geometries, the argument factor is a single float specifying the % distance to shift the detector (measured in detector pixels). % % For 3D geometries, factor is a pair of floats specifying the horizontal % resp. vertical distances to shift the detector. If only a single float is % specified, this is treated as an horizontal shift. % % proj_geom: input projection geometry % factor: number of pixels to shift the detector % proj_geom: output %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- proj_geom = astra_geom_2vec(proj_geom); if strcmp(proj_geom.type,'fanflat_vec') || strcmp(proj_geom.type,'parallel_vec') proj_geom.Vectors(:,3:4) = proj_geom.Vectors(:,3:4) + factor(1) * proj_geom.Vectors(:,5:6); elseif strcmp(proj_geom.type,'cone_vec') || strcmp(proj_geom.type,'parallel3d_vec') if numel(factor) == 1 proj_geom.Vectors(:,4:6) = proj_geom.Vectors(:,4:6) + factor * proj_geom.Vectors(:,7:9); elseif numel(factor) > 1 proj_geom.Vectors(:,4:6) = proj_geom.Vectors(:,4:6) + factor(1) * proj_geom.Vectors(:,7:9) + factor(2) * proj_geom.Vectors(:,10:12); end else error(['Projection geometry of type ' proj_geom.type ' is not suited for postalignment correction.']) end end astra-toolbox-2.3.0/matlab/tools/astra_geom_size.m000066400000000000000000000030061475635207100222540ustar00rootroot00000000000000function s = astra_geom_size(geom, dim) %-------------------------------------------------------------------------- % s = astra_geom_size(geom, dim) % % Get the size of a volume or projection geometry. % % geom: volume or projection geometry % dim (optional): which dimension % s: output %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- if isfield(geom, 'GridSliceCount') % 3D Volume geometry? s = [ geom.GridColCount, geom.GridRowCount, geom.GridSliceCount ]; elseif isfield(geom, 'GridColCount') % 2D Volume geometry? s = [ geom.GridRowCount, geom.GridColCount ]; elseif strcmp(geom.type,'parallel') || strcmp(geom.type,'fanflat') s = [numel(geom.ProjectionAngles), geom.DetectorCount]; elseif strcmp(geom.type,'parallel3d') || strcmp(geom.type,'cone') s = [geom.DetectorColCount, numel(geom.ProjectionAngles), geom.DetectorRowCount]; elseif strcmp(geom.type,'fanflat_vec') || strcmp(geom.type,'parallel_vec') s = [size(geom.Vectors,1), geom.DetectorCount]; elseif strcmp(geom.type,'parallel3d_vec') || strcmp(geom.type,'cone_vec') s = [geom.DetectorColCount, size(geom.Vectors,1), geom.DetectorRowCount]; end if nargin == 2 s = s(dim); end end astra-toolbox-2.3.0/matlab/tools/astra_geom_superresolution.m000066400000000000000000000013141475635207100245640ustar00rootroot00000000000000function proj_geom = astra_geom_superresolution(proj_geom, factor) if strcmp(proj_geom.type,'parallel') proj_geom.DetectorWidth = proj_geom.DetectorWidth/factor; proj_geom.DetectorCount = proj_geom.DetectorCount * factor; elseif strcmp(proj_geom.type,'fanflat') proj_geom.DetectorWidth = proj_geom.DetectorWidth/factor; proj_geom.DetectorCount = proj_geom.DetectorCount * factor; elseif strcmp(proj_geom.type,'fanflat_vec') proj_geom.Vectors(:,5:6) = proj_geom.Vectors(:,5:6) / factor; % DetectorSize proj_geom.DetectorCount = proj_geom.DetectorCount * factor; else error(['Projection geometry of type ' proj_geom.type ' is not suited for super-resolution (or not implemented).']) end astra-toolbox-2.3.0/matlab/tools/astra_geom_visualize.m000066400000000000000000000161011475635207100233150ustar00rootroot00000000000000function astra_geom_visualize(proj_geom, vol_geom) if strcmp(proj_geom.type,'parallel') || strcmp(proj_geom.type,'fanflat') ||strcmp(proj_geom.type,'parallel3d') || strcmp(proj_geom.type,'cone') proj_geom = astra_geom_2vec(proj_geom); end % open window f = figure('Visible','off'); hold on % display projection 1 displayProjection(1); % label txt = uicontrol('Style','text', 'Position',[10 10 70 20], 'String','Projection'); % slider anglecount = size(proj_geom.Vectors,1); sld = uicontrol('Style', 'slider', ... 'Min', 1, 'Max', anglecount, 'SliderStep', [1 1]/anglecount, 'Value', 1, ... 'Position', [80 10 200 20], ... 'Callback', @updateProjection); f.Visible = 'on'; function updateProjection(source, callbackdata) displayProjection(floor(source.Value)); end function displayProjection(a) colours = get(gca,'ColorOrder'); % set title title(['projection ' num2str(a)]); if strcmp(proj_geom.type,'parallel_vec') v = proj_geom.Vectors; d = proj_geom.DetectorCount; if ~isfield(vol_geom, 'option') minx = -vol_geom.GridRowCount/2; miny = -vol_geom.GridColCount/2; minz = -vol_geom.GridSliceCount/2; maxx = vol_geom.GridRowCount/2; else minx = vol_geom.option.WindowMinX; miny = vol_geom.option.WindowMinY; maxx = vol_geom.option.WindowMaxX; maxy = vol_geom.option.WindowMaxY; end % axis cla axis([minx maxx miny maxy]*2.25) axis square % volume plot([minx minx maxx maxx minx], [miny maxy maxy miny miny], 'LineWidth', 1, 'Color', colours(1,:)) % ray s = maxx - minx; plot([0 v(a,1)]*s*0.33, [0 v(a,2)]*s*0.33, 'LineWidth', 2, 'Color', colours(3,:)) % detector s2 = s*0.75; plot([-d/2 d/2]*v(a,5) + v(a,3) + s2*v(a,1), [-d/2 d/2]*v(a,6) + v(a,4) + s2*v(a,2), 'LineWidth', 2, 'Color', colours(5,:)) elseif strcmp(proj_geom.type,'fanflat_vec') v = proj_geom.Vectors; d = proj_geom.DetectorCount; if ~isfield(vol_geom, 'option') minx = -vol_geom.GridRowCount/2; miny = -vol_geom.GridColCount/2; minz = -vol_geom.GridSliceCount/2; maxx = vol_geom.GridRowCount/2; else minx = vol_geom.option.WindowMinX; miny = vol_geom.option.WindowMinY; maxx = vol_geom.option.WindowMaxX; maxy = vol_geom.option.WindowMaxY; end % axis cla axis([minx maxx miny maxy]*2.25) axis square % volume plot([minx minx maxx maxx minx], [miny maxy maxy miny miny], 'LineWidth', 1, 'Color', colours(1,:)) % detector D1 = v(a,3:4) - d/2*v(a,5:6); D2 = v(a,3:4) + d/2*v(a,5:6); plot([D1(1) D2(1)], [D1(2) D2(2)], 'LineWidth', 2, 'Color', colours(5,:)) % beam plot([v(a,1) D1(1)], [v(a,2) D1(2)], 'LineWidth', 1, 'Color', colours(3,:)) plot([v(a,1) D2(1)], [v(a,2) D2(2)], 'LineWidth', 1, 'Color', colours(3,:)) elseif strcmp(proj_geom.type,'parallel3d_vec') v = proj_geom.Vectors; d1 = proj_geom.DetectorRowCount; d2 = proj_geom.DetectorColCount; if ~isfield(vol_geom, 'option') minx = -vol_geom.GridRowCount/2; miny = -vol_geom.GridColCount/2; minz = -vol_geom.GridSliceCount/2; maxx = vol_geom.GridRowCount/2; maxy = vol_geom.GridColCount/2; maxz = vol_geom.GridSliceCount/2; else minx = vol_geom.option.WindowMinX; miny = vol_geom.option.WindowMinY; minz = vol_geom.option.WindowMinZ; maxx = vol_geom.option.WindowMaxX; maxy = vol_geom.option.WindowMaxY; maxz = vol_geom.option.WindowMaxZ; end % axis windowminx = min(v(:,4)); windowminy = min(v(:,5)); windowminz = max(v(:,6)); windowmaxx = max(v(:,4)); windowmaxy = max(v(:,5)); windowmaxz = max(v(:,6)); cla axis([minx maxx miny maxy minz maxz]*5.10) % volume plot3([minx minx maxx maxx minx], [miny maxy maxy miny miny], [minz minz minz minz minz], 'LineWidth', 1, 'Color', colours(1,:)) plot3([minx minx maxx maxx minx], [miny maxy maxy miny miny], [maxz maxz maxz maxz maxz], 'LineWidth', 1, 'Color', colours(1,:)) plot3([minx minx], [miny miny], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:)) plot3([maxx maxx], [miny miny], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:)) plot3([minx minx], [maxy maxy], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:)) plot3([maxx maxx], [maxy maxy], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:)) % detector D1 = v(a,4:6) - d1/2*v(a,7:9) - d2/2*v(a,10:12); D2 = v(a,4:6) + d1/2*v(a,7:9) - d2/2*v(a,10:12); D3 = v(a,4:6) + d1/2*v(a,7:9) + d2/2*v(a,10:12); D4 = v(a,4:6) - d1/2*v(a,7:9) + d2/2*v(a,10:12); plot3([D1(1) D2(1) D3(1) D4(1) D1(1)], [D1(2) D2(2) D3(2) D4(2) D1(2)], [D1(3) D2(3) D3(3) D4(3) D1(3)], 'LineWidth', 2, 'Color', colours(5,:)) % ray s = maxx - minx; plot3([0 v(a,1)]*s*0.30, [0 v(a,2)]*s*0.30, [0 v(a,3)]*s*0.30, 'LineWidth', 2, 'Color', colours(3,:)) elseif strcmp(proj_geom.type,'cone_vec') v = proj_geom.Vectors; d1 = proj_geom.DetectorRowCount; d2 = proj_geom.DetectorColCount; if ~isfield(vol_geom, 'option') minx = -vol_geom.GridRowCount/2; miny = -vol_geom.GridColCount/2; minz = -vol_geom.GridSliceCount/2; maxx = vol_geom.GridRowCount/2; maxy = vol_geom.GridColCount/2; maxz = vol_geom.GridSliceCount/2; else minx = vol_geom.option.WindowMinX; miny = vol_geom.option.WindowMinY; minz = vol_geom.option.WindowMinZ; maxx = vol_geom.option.WindowMaxX; maxy = vol_geom.option.WindowMaxY; maxz = vol_geom.option.WindowMaxZ; end % axis windowminx = min(v(:,4)); windowminy = min(v(:,5)); windowminz = max(v(:,6)); windowmaxx = max(v(:,4)); windowmaxy = max(v(:,5)); windowmaxz = max(v(:,6)); cla axis([minx maxx miny maxy minz maxz]*5.10) % volume plot3([minx minx maxx maxx minx], [miny maxy maxy miny miny], [minz minz minz minz minz], 'LineWidth', 1, 'Color', colours(1,:)) plot3([minx minx maxx maxx minx], [miny maxy maxy miny miny], [maxz maxz maxz maxz maxz], 'LineWidth', 1, 'Color', colours(1,:)) plot3([minx minx], [miny miny], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:)) plot3([maxx maxx], [miny miny], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:)) plot3([minx minx], [maxy maxy], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:)) plot3([maxx maxx], [maxy maxy], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:)) % detector D1 = v(a,4:6) - d1/2*v(a,7:9) - d2/2*v(a,10:12); D2 = v(a,4:6) + d1/2*v(a,7:9) - d2/2*v(a,10:12); D3 = v(a,4:6) + d1/2*v(a,7:9) + d2/2*v(a,10:12); D4 = v(a,4:6) - d1/2*v(a,7:9) + d2/2*v(a,10:12); plot3([D1(1) D2(1) D3(1) D4(1) D1(1)], [D1(2) D2(2) D3(2) D4(2) D1(2)], [D1(3) D2(3) D3(3) D4(3) D1(3)], 'LineWidth', 2, 'Color', colours(5,:)) % beam plot3([v(a,1) D1(1)], [v(a,2) D1(2)], [v(a,3) D1(3)], 'LineWidth', 1, 'Color', colours(3,:)) plot3([v(a,1) D2(1)], [v(a,2) D2(2)], [v(a,3) D2(3)], 'LineWidth', 1, 'Color', colours(3,:)) plot3([v(a,1) D3(1)], [v(a,2) D3(2)], [v(a,3) D3(3)], 'LineWidth', 1, 'Color', colours(3,:)) plot3([v(a,1) D4(1)], [v(a,2) D4(2)], [v(a,3) D4(3)], 'LineWidth', 1, 'Color', colours(3,:)) else error(['Invalid projection geometry type: ' proj_geom.type '.']) end end end astra-toolbox-2.3.0/matlab/tools/astra_get_gpu_info.m000066400000000000000000000013251475635207100227420ustar00rootroot00000000000000function astra_set_gpu_index(index) %-------------------------------------------------------------------------- % Set the index of the GPU to use %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- if nargin < 1 astra_mex('get_gpu_info'); else astra_mex('get_gpu_info', index); end astra-toolbox-2.3.0/matlab/tools/astra_imshow.m000066400000000000000000000002331475635207100216000ustar00rootroot00000000000000function V = astra_imshow(data, range) if numel(data) == 1 data = astra_mex_data2d('get', data); end imshow(data,range); if nargout >= 1 V = data; end astra-toolbox-2.3.0/matlab/tools/astra_mex.m000066400000000000000000000015671475635207100210760ustar00rootroot00000000000000function [varargout] = astra_mex(varargin) %------------------------------------------------------------------------ % Reference page in Help browser % astra_mex. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ if nargout == 0 astra_mex_c(varargin{:}); if exist('ans','var') varargout{1} = ans; end else varargout = cell(1,nargout); [varargout{:}] = astra_mex_c(varargin{:}); end astra-toolbox-2.3.0/matlab/tools/astra_mex_algorithm.m000066400000000000000000000016511475635207100231360ustar00rootroot00000000000000function [varargout] = astra_mex_algorithm(varargin) %------------------------------------------------------------------------ % Reference page in Help browser % astra_mex_algorithm. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ if nargout == 0 astra_mex_algorithm_c(varargin{:}); if exist('ans','var') varargout{1} = ans; end else varargout = cell(1,nargout); [varargout{:}] = astra_mex_algorithm_c(varargin{:}); end astra-toolbox-2.3.0/matlab/tools/astra_mex_data2d.m000066400000000000000000000016321475635207100223060ustar00rootroot00000000000000function [varargout] = astra_mex_data2d(varargin) %------------------------------------------------------------------------ % Reference page in Help browser % astra_mex_data2d. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ if nargout == 0 astra_mex_data2d_c(varargin{:}); if exist('ans','var') varargout{1} = ans; end else varargout = cell(1,nargout); [varargout{:}] = astra_mex_data2d_c(varargin{:}); end astra-toolbox-2.3.0/matlab/tools/astra_mex_data3d.m000066400000000000000000000016321475635207100223070ustar00rootroot00000000000000function [varargout] = astra_mex_data3d(varargin) %------------------------------------------------------------------------ % Reference page in Help browser % astra_mex_data3d. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ if nargout == 0 astra_mex_data3d_c(varargin{:}); if exist('ans','var') varargout{1} = ans; end else varargout = cell(1,nargout); [varargout{:}] = astra_mex_data3d_c(varargin{:}); end astra-toolbox-2.3.0/matlab/tools/astra_mex_direct.m000066400000000000000000000016321475635207100224210ustar00rootroot00000000000000function [varargout] = astra_mex_direct(varargin) %------------------------------------------------------------------------ % Reference page in Help browser % astra_mex_data3d. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ if nargout == 0 astra_mex_direct_c(varargin{:}); if exist('ans','var') varargout{1} = ans; end else varargout = cell(1,nargout); [varargout{:}] = astra_mex_direct_c(varargin{:}); end astra-toolbox-2.3.0/matlab/tools/astra_mex_log.m000066400000000000000000000024061475635207100217300ustar00rootroot00000000000000function [varargout] = astra_mex_log(varargin) %------------------------------------------------------------------------ % Reference page in Help browser % astra_mex_log. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ if size(varargin,2)==2 && (strcmp(varargin{1},'debug') || strcmp(varargin{1},'info') || strcmp(varargin{1},'warn') || strcmp(varargin{1},'error')) d = dbstack(1); if size(d,1)==0 astra_mex_log_c(varargin{1},'Unknown',0,varargin{2}) else astra_mex_log_c(varargin{1},d(1).file,d(1).line,varargin{2}) end else if nargout == 0 astra_mex_log_c(varargin{:}); if exist('ans','var') varargout{1} = ans; end else varargout = cell(1,nargout); [varargout{:}] = astra_mex_log_c(varargin{:}); end end astra-toolbox-2.3.0/matlab/tools/astra_mex_matrix.m000066400000000000000000000016321475635207100224530ustar00rootroot00000000000000function [varargout] = astra_mex_matrix(varargin) %------------------------------------------------------------------------ % Reference page in Help browser % astra_mex_matrix. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ if nargout == 0 astra_mex_matrix_c(varargin{:}); if exist('ans','var') varargout{1} = ans; end else varargout = cell(1,nargout); [varargout{:}] = astra_mex_matrix_c(varargin{:}); end astra-toolbox-2.3.0/matlab/tools/astra_mex_plugin.m000066400000000000000000000016321475635207100224450ustar00rootroot00000000000000function [varargout] = astra_mex_plugin(varargin) %------------------------------------------------------------------------ % Reference page in Help browser % astra_mex_plugin. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ if nargout == 0 astra_mex_plugin_c(varargin{:}); if exist('ans','var') varargout{1} = ans; end else varargout = cell(1,nargout); [varargout{:}] = astra_mex_plugin_c(varargin{:}); end astra-toolbox-2.3.0/matlab/tools/astra_mex_projector.m000066400000000000000000000016511475635207100231570ustar00rootroot00000000000000function [varargout] = astra_mex_projector(varargin) %------------------------------------------------------------------------ % Reference page in Help browser % astra_mex_projector. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ if nargout == 0 astra_mex_projector_c(varargin{:}); if exist('ans','var') varargout{1} = ans; end else varargout = cell(1,nargout); [varargout{:}] = astra_mex_projector_c(varargin{:}); end astra-toolbox-2.3.0/matlab/tools/astra_mex_projector3d.m000066400000000000000000000016631475635207100234110ustar00rootroot00000000000000function [varargout] = astra_mex_projector3d(varargin) %------------------------------------------------------------------------ % Reference page in Help browser % astra_mex_projector3d. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ if nargout == 0 astra_mex_projector3d_c(varargin{:}); if exist('ans','var') varargout{1} = ans; end else varargout = cell(1,nargout); [varargout{:}] = astra_mex_projector3d_c(varargin{:}); end astra-toolbox-2.3.0/matlab/tools/astra_plot_geom.m000066400000000000000000000105501475635207100222620ustar00rootroot00000000000000function [] = astra_plot_geom(geometry, varargin) %-------------------------------------------------------------------------- % [] = astra_plot_geometry(geometry, varargin) % % plot an astra geometry % % geometry: any astra geometry, either volume geometry, projection % geometry or an *.stl file (powered by stlRead). % varargin: supports a variable number of (ordered and unordered) % arguments. % % the remaining arguments depend on the input: % if 'geometry' is % - a volume geometry % vx_size voxel size in unit of preference. Must % be same unit that was used to scale the % projection geometry. % and as unorderd string-value-pairs % Magnification magnification factor for the phantom. % For small phantoms it might be % necessary to scale the render up as % otherwise it won't show up in the plot. % Default = 1 % LineWidth line width for the box wireframe. % Default = 2 % Color color of the wireframe. Default = 'r' % % - a projection geometry (as unordered string-value-pairs) % RotationAxis if specified, will change the drawn % rotation axis to provided axis. % Must be 3-vector. Default value is % [NaN, NaN, NaN], (meaning do not draw). % RotationAxisOffset if specified, will translate the drawn % rotation axis by the provided vector. % Default = [0, 0, 0] % VectorIdx index of the vector to visualize if % vector geometry type. Default = 1 % Color Color for all markers and lines if not % otherwise specified % DetectorMarker marker for the detector locations. % Default = '.' % DetectorMarkerColor color specifier for the detector marker. % Default = 'k' % DetectorLineColor color for the lines drawing the % detector outline % DetectorLineWidth line width of detector rectangle % SourceMarker marker for the source locations % SourceMarkerColor color specifier for the source marker % SourceDistance (only for parallel3d and parallel3d_vec) % distance of source to origin % OpticalAxisColor Color for drawing the optical axis % % - a path to an *.stl file % magn - magnification factor for vertices in % CAD file. Default value = 1 % %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- if exist('astra_create_example_cone') ~= 2 error('Please add astra/algorithms/plot_geom to your path to use this function') end if is_vol_geom(geometry) draw.draw_vol_geom(geometry, varargin{:}); elseif is_proj_geom(geometry) draw.draw_proj_geom(geometry, varargin{:}); elseif ischar(geometry) % assume 'geometry' is a path to a CAD file draw.draw_cad_phantom(geometry, varargin{:}); end % ---- helper functions ---- function [ res ] = is_vol_geom(geom) res = false; if sum(isfield(geom, {'GridRowCount', 'GridColCount'})) == 2 res = true; end end function [ res ] = is_proj_geom(geom) res = false; if isfield(geom, 'type') res = true; end end end astra-toolbox-2.3.0/matlab/tools/astra_projector_handle.m000066400000000000000000000015421475635207100236200ustar00rootroot00000000000000classdef astra_projector_handle < handle %ASTRA_PROJECTOR_HANDLE Handle class around an astra_mex_projector id % Automatically deletes the projector when deleted. %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ properties id end methods function obj = astra_projector_handle(proj_id) obj.id = proj_id; end function delete(obj) astra_mex_projector('delete', obj.id); end end end astra-toolbox-2.3.0/matlab/tools/astra_set_directory.m000066400000000000000000000015041475635207100231530ustar00rootroot00000000000000function in = astra_set_directory(in) %------------------------------------------------------------------------ % in = astra_set_directory(in) % % Creates the directories present in the input path if they do not exist % already % % in: input path. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ a = find(in == '/' | in == '\'); for i = 1:numel(a) if ~isdir(in(1:a(i))) mkdir(in(1:a(i))); end end astra-toolbox-2.3.0/matlab/tools/astra_set_gpu_index.m000066400000000000000000000012351475635207100231320ustar00rootroot00000000000000function astra_set_gpu_index(index) %-------------------------------------------------------------------------- % Set the index of the GPU to use %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- astra_mex('set_gpu_index', index); astra-toolbox-2.3.0/matlab/tools/astra_struct.m000066400000000000000000000022041475635207100216160ustar00rootroot00000000000000function res = astra_struct(type) %------------------------------------------------------------------------ % res = astra_struct(type) % % Create an ASTRA struct % % type: type of the struct to be generated. % res: the generated matlab struct. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ res = struct(); res.options = struct(); if nargin >= 1 % For backward compatibility, transparently accept SIRT_CUDA2 % for SIRT_CUDA, and FP_CUDA2 for FP_CUDA. if strcmp(type, 'SIRT_CUDA2') type = 'SIRT_CUDA'; warning('SIRT_CUDA2 has been deprecated. Use SIRT_CUDA instead.'); end if strcmp(type, 'FP_CUDA2') type = 'FP_CUDA'; warning('FP_CUDA2 has been deprecated. Use FP_CUDA instead.'); end res.type = type; end astra-toolbox-2.3.0/matlab/tools/astra_test.m000066400000000000000000000013471475635207100212600ustar00rootroot00000000000000%-------------------------------------------------------------------------- % Perform a basic test of ASTRA functionality. %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- astra_mex('version'); if astra_mex('use_cuda') astra_test_CUDA; else fprintf('No GPU support available') astra_test_noCUDA; end astra-toolbox-2.3.0/matlab/tools/astra_test_CUDA.m000066400000000000000000000027321475635207100220530ustar00rootroot00000000000000%-------------------------------------------------------------------------- % Perform a basic test of ASTRA CPU and CUDA functionality. %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- %% fprintf('Getting GPU info...') astra_get_gpu_info() %% astra_test_noCUDA() %% fprintf('Testing basic CUDA 2D functionality...') vg = astra_create_vol_geom(2, 32); pg = astra_create_proj_geom('parallel', 1, 32, [0]); proj_id = astra_create_projector('cuda', pg, vg); vol = rand(2, 32); [sino_id, sino] = astra_create_sino(vol, proj_id); astra_mex_data2d('delete', sino_id); astra_mex_projector('delete', proj_id); err = max(abs(sino - sum(vol))); if err < 1e-6 disp('Ok') else disp('Error') end %% fprintf('Testing basic CUDA 3D functionality...') vg = astra_create_vol_geom(2, 32, 32); pg = astra_create_proj_geom('parallel3d', 1, 1, 32, 32, [0]); vol = rand(32, 2, 32); [sino_id, sino] = astra_create_sino3d_cuda(vol, pg, vg); astra_mex_data3d('delete', sino_id); err = max(max(abs(sino - sum(vol,2)))); if err < 1e-6 disp('Ok') else disp('Error') end astra-toolbox-2.3.0/matlab/tools/astra_test_noCUDA.m000066400000000000000000000020141475635207100224010ustar00rootroot00000000000000%-------------------------------------------------------------------------- % Perform a basic test of ASTRA CPU functionality. %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- fprintf('Testing basic CPU 2D functionality...') vg = astra_create_vol_geom(2, 32); pg = astra_create_proj_geom('parallel', 1, 32, [0]); proj_id = astra_create_projector('line', pg, vg); vol = rand(2, 32); [sino_id, sino] = astra_create_sino(vol, proj_id); astra_mex_data2d('delete', sino_id); astra_mex_projector('delete', proj_id); err = max(abs(sino - sum(vol))); if err < 1e-6 disp('Ok') else disp('Error') end astra-toolbox-2.3.0/matlab/tools/compute_rnmp.m000066400000000000000000000012201475635207100216050ustar00rootroot00000000000000function [rnmp, nmp] = compute_rnmp(phantom, S) phantom = double(phantom == max(phantom(:))); S = double(S == max(S(:))); %u1 = sort(unique(phantom)); %u2 = sort(unique(S)); %for i = 1:numel(u1) % phantom_(phantom == u1(i)) = i; % S_(S == u2(i)) = i; %end %phantom = phantom_; %S = S_; if numel(size(phantom)) == 2 S = imresize(S, size(phantom), 'nearest'); elseif numel(size(phantom)) == 3 S2 = zeros(size(phantom)); for slice = 1:size(phantom,3) S2(:,:,slice) = imresize(S(:,:,slice), [size(phantom,1) size(phantom,2)], 'nearest'); end S = S2; end nmp = sum(abs(phantom(:) ~= S(:))); rnmp = nmp / sum(phantom(:)); end astra-toolbox-2.3.0/matlab/tools/createOrderART.m000066400000000000000000000036641475635207100217210ustar00rootroot00000000000000function rayOrder = createOrderART(proj_geom, mode) %------------------------------------------------------------------------ % rayOrder = createOrderART(proj_geom, mode) % % Creates an array defining the order in which ART will iterate over the % projections and projection rays % % proj_geom: MATLAB struct containing the projection geometry. % mode: string defining the wanted ray order, can be either 'sequential', % 'randomray' or 'randomproj'. % rayOrder: array of two columns of length angle_count * det_count, with % the first column being the index of the projection and the second column % the index of the ray. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ angle_count = length(proj_geom.projection_angles); det_count = proj_geom.detector_count; % create order rayOrder = zeros(angle_count * det_count, 2); if strcmp(mode,'sequential') == 1 index = 1; for i = 1:angle_count for j = 1:det_count rayOrder(index,1) = i; rayOrder(index,2) = j; index = index + 1; end end elseif strcmp(mode,'randomray') == 1 index = 1; for i = 1:angle_count for j = 1:det_count rayOrder(index,1) = i; rayOrder(index,2) = j; index = index + 1; end end r = randperm(angle_count * det_count); rayOrder(:,1) = rayOrder(r,1); rayOrder(:,2) = rayOrder(r,2); elseif strcmp(mode,'randomproj') == 1 index = 1; r = randperm(angle_count); for i = 1:angle_count for j = 1:det_count rayOrder(index,1) = r(i); rayOrder(index,2) = j; index = index + 1; end end else disp('mode not known'); end astra-toolbox-2.3.0/matlab/tools/downsample_sinogram.m000066400000000000000000000004151475635207100231520ustar00rootroot00000000000000function sinogram_out = downsample_sinogram(sinogram, ds) if ds == 1 sinogram_out = sinogram; return; end sinogram_out = sinogram(:,1:ds:end,:); for i = 2:ds sinogram_out = sinogram_out + sinogram(:,i:ds:end,:); end sinogram_out = sinogram_out / (ds*ds); astra-toolbox-2.3.0/matlab/tools/imreadgs.m000066400000000000000000000015141475635207100206760ustar00rootroot00000000000000function Im = imreadgs(filename) %------------------------------------------------------------------------ % Im = imreadgs(filename) % % Reads an image and transforms it into a grayscale image consisting of % doubles. % % filename: name of the image file. % Im: a grayscale image in double. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ Im = double(imread(filename)); if size(Im,3) > 1 Im = Im(:,:,1); end astra-toolbox-2.3.0/matlab/tools/imresize3D.m000066400000000000000000000016031475635207100211200ustar00rootroot00000000000000function out = imresize3D(in, s_out, method) %------------------------------------------------------------------------ % out = imresize3D(in, s_out, method) % % Resizes a 3-component image % % in: input image. % s_out: 2 element array with the wanted image size, [rows columns]. % out: the resized 3-component image. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ out = zeros(s_out); for i = 1:size(in,3) out(:,:,i) = imresize(in(:,:,i), s_out(1:2), method); end astra-toolbox-2.3.0/matlab/tools/imscale.m000066400000000000000000000014611475635207100205210ustar00rootroot00000000000000function out = imscale(in) %------------------------------------------------------------------------ % out = imscale(in) % % Rescales the image values between zero and one. % % in: input image. % out: scaled output image. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ mi = min(in(:)); ma = max(in(:)); if (ma-mi) == 0 out = zeros(size(in)); else out = (in - mi) / (ma - mi); end astra-toolbox-2.3.0/matlab/tools/imwritesc.m000066400000000000000000000013621475635207100211120ustar00rootroot00000000000000function imwritesc(in, filename) %------------------------------------------------------------------------ % imwritesc(in, filename) % % Rescale between zero and one and write image % % in: input image. % filename: name of output image file. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ imwrite(imscale(in),filename); astra-toolbox-2.3.0/matlab/tools/kaiserBessel.m000066400000000000000000000017121475635207100215170ustar00rootroot00000000000000function res = kaiserBessel(m,alpha,a,r) %------------------------------------------------------------------------ % res = kaiserBessel(m,alpha,a,r) % % Calculates the Kaiser windowing function % % a: length of the sequence. % m: order. % alpha: determines shape of window. % r: input values for which to compute window value. % res: the window values. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ sq = sqrt(1 - (r./a).^2); res1 = 1 ./ besseli(m, alpha); res2 = sq .^ m; res3 = besseli(m, alpha .* sq); res = res1 .* res2 .* res3; astra-toolbox-2.3.0/matlab/tools/linspace2.m000066400000000000000000000014431475635207100207640ustar00rootroot00000000000000function out = linspace2(a,b,c) %------------------------------------------------------------------------ % out = linspace2(a,b,c) % % Generates linearly spaced vectors % % a: lower limit. % b: upper limit (exclusive). % c: number of elements. % out: linearly spaced vector. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ out = linspace(a,b,c+1); out = out(1:end-1); astra-toolbox-2.3.0/matlab/tools/opTomo.m000066400000000000000000000222651475635207100203660ustar00rootroot00000000000000%OPTOMO Wrapper for ASTRA tomography projector % % OP = OPTOMO(TYPE, PROJ_GEOM, VOL_GEOM) generates a Spot operator OP for % the ASTRA forward and backprojection operations. The string TYPE % determines the model used for the projections. Possible choices are: % TYPE: * using the CPU % 'line' - use a line kernel % 'linear' - use a Joseph kernel % 'strip' - use the strip kernel % * using the GPU % 'cuda' - use a Joseph kernel, on the GPU, currently using % 'cuda' is the only option in 3D. % The PROJ_GEOM and VOL_GEOM structures are projection and volume % geometries as used in the ASTRA toolbox. % % OP = OPTOMO(TYPE, PROJ_GEOM, VOL_GEOM, GPU_INDEX) also specify the % index of the GPU that should be used, if multiple GPUs are present in % the host system. By default GPU_INDEX is 0. % % Note: this code depends on the Matlab toolbox % "Spot - A Linear-Operator Toolbox" which can be downloaded from % http://www.cs.ubc.ca/labs/scl/spot/ %-------------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %-------------------------------------------------------------------------- classdef opTomo < opSpot %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Properties %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% properties ( Access = private ) % multiplication function funHandle % ASTRA identifiers sino_id vol_id fp_alg_id bp_alg_id proj_id % ASTRA IDs handle astra_handle end % properties properties ( SetAccess = private, GetAccess = public ) proj_size vol_size proj_geom vol_geom end % properties %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Methods - public %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % opTomo - constructor %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function op = opTomo(type, proj_geom, vol_geom, gpu_index) if nargin < 4 || isempty(gpu_index), gpu_index = 0; end proj_size = astra_geom_size(proj_geom); vol_size = astra_geom_size(vol_geom); % construct operator op = op@opSpot('opTomo', prod(proj_size), prod(vol_size)); % determine the dimension is2D = ~isfield(vol_geom, 'GridSliceCount'); gpuEnabled = strcmpi(type, 'cuda'); if is2D % create a projector proj_id = astra_create_projector(type, proj_geom, vol_geom); % create a function handle op.funHandle = @opTomo_intrnl2D; % Initialize ASTRA data objects. % projection data sino_id = astra_mex_data2d('create', '-sino', proj_geom, 0); % image data vol_id = astra_mex_data2d('create', '-vol', vol_geom, 0); % Setup forward and back projection algorithms. if gpuEnabled fp_alg = 'FP_CUDA'; bp_alg = 'BP_CUDA'; else fp_alg = 'FP'; bp_alg = 'BP'; end % configuration for ASTRA fp algorithm cfg_fp = astra_struct(fp_alg); cfg_fp.ProjectorId = proj_id; cfg_fp.ProjectionDataId = sino_id; cfg_fp.VolumeDataId = vol_id; % configuration for ASTRA bp algorithm cfg_bp = astra_struct(bp_alg); cfg_bp.ProjectionDataId = sino_id; cfg_bp.ProjectorId = proj_id; cfg_bp.ReconstructionDataId = vol_id; % set GPU index if gpuEnabled cfg_fp.option.GPUindex = gpu_index; cfg_bp.option.GPUindex = gpu_index; end fp_alg_id = astra_mex_algorithm('create', cfg_fp); bp_alg_id = astra_mex_algorithm('create', cfg_bp); % Create handle to ASTRA objects, so they will be deleted % if opTomo is deleted. op.astra_handle = opTomo_helper_handle([sino_id, ... vol_id, proj_id, fp_alg_id, bp_alg_id]); op.fp_alg_id = fp_alg_id; op.bp_alg_id = bp_alg_id; op.sino_id = sino_id; op.vol_id = vol_id; else % 3D % only gpu/cuda code for 3D if ~gpuEnabled error(['Only type ' 39 'cuda' 39 ' is supported ' ... 'for 3D geometries.']) end % setup projector cfg = astra_struct('cuda3d'); cfg.ProjectionGeometry = proj_geom; cfg.VolumeGeometry = vol_geom; cfg.option.GPUindex = gpu_index; % create projector op.proj_id = astra_mex_projector3d('create', cfg); % create handle to ASTRA object, for cleaning up op.astra_handle = opTomo_helper_handle(op.proj_id); % create a function handle op.funHandle = @opTomo_intrnl3D; end % pass object properties op.proj_size = proj_size; op.vol_size = vol_size; op.cflag = false; op.sweepflag = false; op.proj_geom = proj_geom; op.vol_geom = vol_geom; end % opTomo - constructor end % methods - public %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Methods - protected %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% methods( Access = protected ) % multiplication function y = multiply(op,x,mode) % ASTRA cannot handle sparse vectors if issparse(x) x = full(x); end if isa(x, 'double') isdouble = true; x = single(x); else isdouble = false; end % the multiplication y = op.funHandle(op, x, mode); % make sure output is column vector y = y(:); if isdouble y = double(y); end end % multiply end % methods - protected %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Methods - private %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% methods( Access = private ) % 2D projection code function y = opTomo_intrnl2D(op,x,mode) if mode == 1 % x is passed as a vector, reshape it into an image. x = reshape(x, op.vol_size); % Matlab data copied to ASTRA data astra_mex_data2d('store', op.vol_id, x); % forward projection astra_mex_algorithm('iterate', op.fp_alg_id); % retrieve Matlab array y = astra_mex_data2d('get_single', op.sino_id); else % x is passed as a vector, reshape it into a sinogram. x = reshape(x, op.proj_size); % Matlab data copied to ASTRA data astra_mex_data2d('store', op.sino_id, x); % backprojection astra_mex_algorithm('iterate', op.bp_alg_id); % retrieve Matlab array y = astra_mex_data2d('get_single', op.vol_id); end end % opTomo_intrnl2D % 3D projection code function y = opTomo_intrnl3D(op,x,mode) if mode == 1 % x is passed as a vector, reshape it into an image x = reshape(x, op.vol_size); % forward projection y = astra_mex_direct('FP3D', op.proj_id, x); else % x is passed as a vector, reshape it into projection data x = reshape(x, op.proj_size); y = astra_mex_direct('BP3D', op.proj_id, x); end end % opTomo_intrnl3D end % methods - private end % classdef astra-toolbox-2.3.0/matlab/tools/opTomo_helper_handle.m000066400000000000000000000016011475635207100232270ustar00rootroot00000000000000classdef opTomo_helper_handle < handle %ASTRA.OPTOMO_HELPER_HANDLE Handle class around an astra identifier % Automatically deletes the data when object is deleted. % Multiple id's can be passed as an array as input to % the constructor. properties id end methods function obj = opTomo_helper_handle(id) obj.id = id; end function delete(obj) for i = 1:numel(obj.id) % delete any kind of object astra_mex_data2d('delete', obj.id(i)); astra_mex_data3d('delete', obj.id(i)); astra_mex_algorithm('delete', obj.id(i)); astra_mex_matrix('delete', obj.id(i)); astra_mex_projector('delete', obj.id(i)); astra_mex_projector3d('delete', obj.id(i)) end end end end astra-toolbox-2.3.0/matlab/tools/overlayImage.m000066400000000000000000000017761475635207100215410ustar00rootroot00000000000000function im = overlayImage(reconstruction, ground_truth) %------------------------------------------------------------------------ % im = overlayImage(reconstruction, ground_truth) % % Produces an overlay image of two images, useful for image comparison. % % reconstruction: first input image matrix. % ground_truth: second input image matrix. % im: output 3-component image, third channel is 0. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ im(:,:,1) = reconstruction ./ max(reconstruction(:)); im(:,:,2) = ground_truth ./ max(ground_truth(:)); im(:,:,3) = zeros(size(reconstruction)); astra-toolbox-2.3.0/matlab/tools/rebin_fan2par.m000066400000000000000000000064111475635207100216140ustar00rootroot00000000000000function F = rebin_fan2par(RadonData, BetaDeg, D, thetaDeg) %------------------------------------------------------------------------ % F = rebin_fan2par(RadonData, BetaDeg, D, thetaDeg) % % Deze functie zet fan beam data om naar parallelle data, door interpolatie % (fast resorting algorithm, zie Kak en Slaney) % Radondata zoals altijd: eerste coord gamma , de rijen % tweede coord beta, de kolommen, beide hoeken in % radialen % PixPProj: aantal pixels per projectie (voor skyscan data typisch 1000) % BetaDeg: vector met alle draaihoeken in graden % D: afstand bron - rotatiecentrum in pixels, dus afstand % bron-rotatiecentrum(um) gedeeld door image pixel size (um). % thetaDeg: vector met gewenste sinogramwaarden voor theta in graden % de range van thetaDeg moet steeds kleiner zijn dan die van betadeg % D,gamma,beta, theta zoals gebruikt in Kak & Slaney %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ NpixPProj = size(RadonData,1); % aantal pixels per projectie %if mod(size(Radondata,1),2)==0 % NpixPProjNew=NpixPProj+1; %else NpixPProjNew = NpixPProj; %end %% FAN-BEAM RAYS % flip sinogram, why? RadonData = flipdim(RadonData,2); % matlab gebruikt tegengestelde draairichting (denkik) als skyscan, of er is een of andere flipdim geweest die gecorrigeerd moet worden)) % DetPixPos: distance of each detector to the ray through the origin (theta) DetPixPos = -(NpixPProj-1)/2:(NpixPProj-1)/2; % posities detectorpixels % GammaStralen: alpha's? (result in radians!!) GammaStralen = atan(DetPixPos/D); % alle met de detectorpixelposities overeenkomstige gammahoeken % put beta (theta) and gamma (alpha) for each ray in 2D matrices [beta gamma] = meshgrid(BetaDeg,GammaStralen); % t: minimal distance between each ray and the ray through the origin t = D*sin(gamma); % t-waarden overeenkomstig met de verschillende gamma's theta = gamma*180/pi + beta; % theta-waarden in graden overeenkomstig met verschillende gamma en beta waarden %% PARALLEL BEAM RAYS % DetPixPos: distance of each detector to the ray through the origin (theta) DetPixPos = -(NpixPProjNew-1)/2:(NpixPProjNew-1)/2; % posities detectorpixels % GammaStralen: alpha's? (result in radians!!) GammaStralenNew = atan(DetPixPos/D); % alle met de detectorpixelposities overeenkomstige gammahoeken % put beta (theta) and gamma (alpha) for each ray in 2D matrices [~, gamma] = meshgrid(BetaDeg,GammaStralenNew); % t: minimal distance between each ray and the ray through the origin tnew = D * sin(gamma); % t-waarden overeenkomstig met de verschillende gamma's % calculate new t step = (max(tnew)-min(tnew)) / (NpixPProjNew-1); t_para = min(tnew):step:max(tnew); [thetaNewCoord tNewCoord] = meshgrid(thetaDeg, t_para); %% Interpolate Interpolant = TriScatteredInterp(theta(:), t(:), RadonData(:),'nearest'); F = Interpolant(thetaNewCoord,tNewCoord); astra-toolbox-2.3.0/matlab/tools/sliceExtractor.m000066400000000000000000000021141475635207100220730ustar00rootroot00000000000000function slice = sliceExtractor(data, dir, slicenr) %------------------------------------------------------------------------ % slice = sliceExtractor(data, dir, slicenr) % % Outputs a specified slice from a three dimensional matrix/volume % % data: the 3D volume. % dir: the direction in which the volume is sliced. % slicenr: the index of the slice to retrieve. % slice: 2D image matrix containing the slice. %------------------------------------------------------------------------ %------------------------------------------------------------------------ % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ %------------------------------------------------------------------------ slicenr = round(slicenr); if strcmp(dir,'z') slice = squeeze(data(:,:,slicenr)); end if strcmp(dir,'x') slice = squeeze(data(:,slicenr,:)); end if strcmp(dir,'y') slice = squeeze(data(slicenr,:,:)); end astra-toolbox-2.3.0/python/000077500000000000000000000000001475635207100156455ustar00rootroot00000000000000astra-toolbox-2.3.0/python/COPYING000066400000000000000000001045131475635207100167040ustar00rootroot00000000000000 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 . astra-toolbox-2.3.0/python/README.md000066400000000000000000000034031475635207100171240ustar00rootroot00000000000000**The ASTRA Toolbox** provides high-performance GPU primitives for 2D and 3D tomography. It supports 2D parallel and fan beam, and 3D parallel and cone beam geometries. All of them have highly flexible source/detector positioning. A large number of 2D and 3D algorithms are available, including FBP, SIRT, SART, CGLS. The basic forward and backward projection operations are GPU-accelerated, and directly callable from Python to enable building new algorithms. ## References If you use the ASTRA Toolbox for your research, we would appreciate it if you would refer to the following papers: *W. van Aarle, W. J. Palenstijn, J. Cant, E. Janssens, F. Bleichrodt, A. Dabravolski, J. De Beenhouwer, K. J. Batenburg, and J. Sijbers, “Fast and Flexible X-ray Tomography Using the ASTRA Toolboxâ€, Optics Express, 24(22), 25129-25147, (2016), https://dx.doi.org/10.1364/OE.24.025129* *W. van Aarle, W. J. Palenstijn, J. De Beenhouwer, T. Altantzis, S. Bals, K. J. Batenburg, and J. Sijbers, “The ASTRA Toolbox: A platform for advanced algorithm development in electron tomographyâ€, Ultramicroscopy, 157, 35–47, (2015), https://dx.doi.org/10.1016/j.ultramic.2015.05.002* Additionally, if you use parallel beam GPU code, we would appreciate it if you would refer to the following paper: *W. J. Palenstijn, K J. Batenburg, and J. Sijbers, "Performance improvements for iterative electron tomography reconstruction using graphics processing units (GPUs)", Journal of Structural Biology, vol. 176, issue 2, pp. 250-253, 2011, https://dx.doi.org/10.1016/j.jsb.2011.07.017* ## License The ASTRA Toolbox is open source under the GPLv3 license. ## Copyright 2010-2024, imec Vision Lab, University of Antwerp 2014-2024, CWI, Amsterdam https://visielab.uantwerpen.be/ and https://www.cwi.nl/ astra-toolbox-2.3.0/python/astra/000077500000000000000000000000001475635207100167575ustar00rootroot00000000000000astra-toolbox-2.3.0/python/astra/CFloat32CustomPython.h000066400000000000000000000014471475635207100230500ustar00rootroot00000000000000class GILHelper { public: GILHelper() { state = PyGILState_Ensure(); } ~GILHelper() { PyGILState_Release(state); } private: PyGILState_STATE state; }; class CFloat32CustomPython : public astra::CFloat32CustomMemory { public: CFloat32CustomPython(PyArrayObject * arrIn) { GILHelper gil; // hold GIL during this function arr = arrIn; // Set pointer to numpy data pointer m_fPtr = (float *)PyArray_DATA(arr); // Increase reference count since ASTRA has a reference Py_INCREF(arr); } virtual ~CFloat32CustomPython() { GILHelper gil; // hold GIL during this function // Decrease reference count since ASTRA object is destroyed Py_DECREF(arr); } private: PyArrayObject* arr; }; astra-toolbox-2.3.0/python/astra/PyAlgorithmFactory.pxd000066400000000000000000000026011475635207100232620ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from libcpp.string cimport string from libcpp cimport bool from .PyIncludes cimport * cdef extern from "astra/AstraObjectFactory.h" namespace "astra": cdef cppclass CAlgorithmFactory: CAlgorithmFactory() CAlgorithm *create(string) cdef extern from "astra/AstraObjectFactory.h" namespace "astra::CAlgorithmFactory": cdef CAlgorithmFactory* getSingletonPtr() astra-toolbox-2.3.0/python/astra/PyAlgorithmManager.pxd000066400000000000000000000027021475635207100232270ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # -------------------------------------------------------------------- from libcpp.string cimport string from libcpp cimport bool from .PyIncludes cimport * cdef extern from "astra/AstraObjectManager.h" namespace "astra": cdef cppclass CAlgorithmManager: int store(CAlgorithm *) CAlgorithm * get(int) void remove(int) void clear() string info() cdef extern from "astra/AstraObjectManager.h" namespace "astra::CAlgorithmManager": cdef CAlgorithmManager* getSingletonPtr() astra-toolbox-2.3.0/python/astra/PyData2DManager.pxd000066400000000000000000000026571475635207100223510ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from libcpp.string cimport string from .PyIncludes cimport * cdef extern from "astra/AstraObjectManager.h" namespace "astra": cdef cppclass CData2DManager: string info() void clear() void remove(int i) int store(CFloat32Data2D *) CFloat32Data2D * get(int i) cdef extern from "astra/AstraObjectManager.h" namespace "astra::CData2DManager": cdef CData2DManager* getSingletonPtr() astra-toolbox-2.3.0/python/astra/PyData3DManager.pxd000066400000000000000000000026411475635207100223430ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from libcpp.string cimport string from .PyIncludes cimport * cdef extern from "astra/AstraObjectManager.h" namespace "astra": cdef cppclass CData3DManager: string info() void clear() void remove(int i) int store(CData3D *) CData3D * get(int i) cdef extern from "astra/AstraObjectManager.h" namespace "astra::CData3DManager": cdef CData3DManager* getSingletonPtr() astra-toolbox-2.3.0/python/astra/PyIncludes.pxd000066400000000000000000000235041475635207100215570ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from libcpp cimport bool from libcpp.string cimport string from libcpp.memory cimport unique_ptr from .PyXMLDocument cimport XMLNode include "config.pxi" cdef extern from "astra/Globals.h" namespace "astra": ctypedef float float32 ctypedef double float64 ctypedef unsigned short int uint16 ctypedef signed short int sint16 ctypedef unsigned char uchar8 ctypedef signed char schar8 ctypedef int int32 ctypedef short int int16 cdef extern from "astra/Config.h" namespace "astra": cdef cppclass Config: pass cdef extern from "astra/XMLConfig.h" namespace "astra": cdef cppclass XMLConfig(Config): XMLConfig(const string& rootname) XMLNode self cdef extern from "astra/VolumeGeometry2D.h" namespace "astra": cdef cppclass CVolumeGeometry2D: bool initialize(Config) int getGridColCount() int getGridRowCount() int getGridTotCount() float32 getWindowLengthX() float32 getWindowLengthY() float32 getWindowArea() float32 getPixelLengthX() float32 getPixelLengthY() float32 getPixelArea() float32 getWindowMinX() float32 getWindowMinY() float32 getWindowMaxX() float32 getWindowMaxY() Config* getConfiguration() bool isEqual(const CVolumeGeometry2D&) cdef extern from "astra/Float32Data2D.h" namespace "astra": cdef cppclass CCustomMemory[T]: pass cdef cppclass CFloat32CustomMemory: pass cdef extern from "astra/Float32VolumeData2D.h" namespace "astra": cdef cppclass CFloat32VolumeData2D: CFloat32VolumeData2D(const CVolumeGeometry2D&) CFloat32VolumeData2D(const CVolumeGeometry2D&, CFloat32CustomMemory*) CVolumeGeometry2D& getGeometry() int getWidth() int getHeight() void changeGeometry(const CVolumeGeometry2D&) Config* getConfiguration() cdef extern from "astra/ProjectionGeometry2D.h" namespace "astra": cdef cppclass CProjectionGeometry2D: CProjectionGeometry2D() bool initialize(Config) int getDetectorCount() int getProjectionAngleCount() bool isOfType(string) float32 getProjectionAngle(int) float32 getDetectorWidth() Config* getConfiguration() bool isEqual(const CProjectionGeometry2D&) cdef extern from "astra/ProjectionGeometry2DFactory.h" namespace "astra": cdef unique_ptr[CProjectionGeometry2D] constructProjectionGeometry2D(const string&type) cdef extern from "astra/Float32Data2D.h" namespace "astra::CFloat32Data2D": cdef enum TWOEDataType "astra::CFloat32Data2D::EDataType": TWOPROJECTION "astra::CFloat32Data2D::PROJECTION" TWOVOLUME "astra::CFloat32Data2D::VOLUME" cdef extern from "astra/Data3D.h" namespace "astra::CData3D": cdef enum THREEEDataType "astra::CData3D::EDataType": THREEPROJECTION "astra::CData3D::PROJECTION" THREEVOLUME "astra::CData3D::VOLUME" cdef extern from "astra/Float32Data2D.h" namespace "astra": cdef cppclass CFloat32Data2D: bool isInitialized() size_t getSize() float32 *getData() float32 **getData2D() int getWidth() int getHeight() TWOEDataType getType() cdef extern from "astra/Float32ProjectionData2D.h" namespace "astra": cdef cppclass CFloat32ProjectionData2D: CFloat32ProjectionData2D(const CProjectionGeometry2D&) CFloat32ProjectionData2D(const CProjectionGeometry2D&, CFloat32CustomMemory*) CProjectionGeometry2D& getGeometry() void changeGeometry(const CProjectionGeometry2D&) int getDetectorCount() int getAngleCount() cdef extern from "astra/Algorithm.h" namespace "astra": cdef cppclass CAlgorithm: bool initialize(Config) bool run(int) nogil bool isInitialized() cdef extern from "astra/ReconstructionAlgorithm2D.h" namespace "astra": cdef cppclass CReconstructionAlgorithm2D: bool getResidualNorm(float32&) cdef extern from "astra/ReconstructionAlgorithm3D.h" namespace "astra": cdef cppclass CReconstructionAlgorithm3D: bool getResidualNorm(float32&) cdef extern from "astra/Projector2D.h" namespace "astra": cdef cppclass CProjector2D: bool isInitialized() bool initialize(Config) const CProjectionGeometry2D& getProjectionGeometry() const CVolumeGeometry2D& getVolumeGeometry() CSparseMatrix* getMatrix() cdef extern from "astra/Projector3D.h" namespace "astra": cdef cppclass CProjector3D: bool isInitialized() bool initialize(Config) const CProjectionGeometry3D& getProjectionGeometry() const CVolumeGeometry3D& getVolumeGeometry() IF HAVE_CUDA==True: cdef extern from "astra/CudaProjector3D.h" namespace "astra": cdef cppclass CCudaProjector3D cdef extern from "astra/CudaProjector2D.h" namespace "astra": cdef cppclass CCudaProjector2D cdef extern from "astra/Data3D.h" namespace "astraCUDA3d": cdef cppclass MemHandle3D: pass cdef extern from "astra/Data3D.h" namespace "astraCUDA3d": cdef MemHandle3D wrapHandle(float *D_ptr, unsigned int x, unsigned int y, unsigned int z, unsigned int pitch) cdef extern from "astra/SparseMatrix.h" namespace "astra": cdef cppclass CSparseMatrix: CSparseMatrix(unsigned int,unsigned int,unsigned long) unsigned int m_iWidth unsigned int m_iHeight unsigned long m_lSize bool isInitialized() float32* m_pfValues unsigned int* m_piColIndices unsigned long* m_plRowStarts cdef extern from "astra/Data3D.h" namespace "astra": cdef cppclass CData3D: bool isInitialized() size_t getSize() int getWidth() int getHeight() int getDepth() bool isFloat32Memory() float32* getFloat32Memory() CDataStorage *getStorage() THREEEDataType getType() #cdef extern from "astra/Data3D.h" namespace "astra": # cdef cppclass CFloat32Data3DMemory(CFloat32Data3D): # CFloat32Data3DMemory() # void updateStatistics() # float32 *getData() # THREEEDataType getType() cdef extern from "astra/VolumeGeometry3D.h" namespace "astra": cdef cppclass CVolumeGeometry3D: CVolumeGeometry3D() bool initialize(Config) Config * getConfiguration() int getGridColCount() int getGridRowCount() int getGridSliceCount() cdef extern from "astra/ProjectionGeometry3D.h" namespace "astra": cdef cppclass CProjectionGeometry3D: CProjectionGeometry3D() bool initialize(Config) Config * getConfiguration() int getProjectionCount() int getDetectorColCount() int getDetectorRowCount() void getProjectedBBox(double, double, double, double, double, double, double&, double&, double&, double&) void projectPoint(double, double, double, int, double&, double&) cdef extern from "astra/ProjectionGeometry3DFactory.h" namespace "astra": cdef unique_ptr[CProjectionGeometry3D] constructProjectionGeometry3D(const string&type) cdef extern from "astra/Data3D.h" namespace "astra": cdef cppclass CDataStorage: pass cdef cppclass CDataMemory[T](CDataStorage): CDataMemory(size_t) CDataMemory(CCustomMemory[T]*) pass cdef extern from "astra/Data3D.h" namespace "astra": cdef cppclass CFloat32VolumeData3D(CData3D): CFloat32VolumeData3D(unique_ptr[CVolumeGeometry3D]&&, CDataStorage*) CFloat32VolumeData3D(const CVolumeGeometry3D &, CDataStorage*) const CVolumeGeometry3D& getGeometry() void changeGeometry(const CVolumeGeometry3D&) void changeGeometry(unique_ptr[CVolumeGeometry3D] &&) int getRowCount() int getColCount() int getSliceCount() cdef CFloat32VolumeData3D* createCFloat32VolumeData3DMemory(unique_ptr[CVolumeGeometry3D]&&) cdef CFloat32VolumeData3D* createCFloat32VolumeData3DMemory(const CVolumeGeometry3D &) cdef extern from "astra/Data3D.h" namespace "astra": cdef cppclass CFloat32ProjectionData3D(CData3D): CFloat32ProjectionData3D(unique_ptr[CProjectionGeometry3D]&&, CDataStorage*) CFloat32ProjectionData3D(const CProjectionGeometry3D &, CDataStorage*) const CProjectionGeometry3D& getGeometry() void changeGeometry(const CProjectionGeometry3D&) void changeGeometry(unique_ptr[CProjectionGeometry3D] &&) int getDetectorRowCount() int getDetectorColCount() int getAngleCount() cdef CFloat32ProjectionData3D* createCFloat32ProjectionData3DMemory(unique_ptr[CProjectionGeometry3D]&&) cdef CFloat32ProjectionData3D* createCFloat32ProjectionData3DMemory(const CProjectionGeometry3D &) IF HAVE_CUDA==True: cdef extern from "astra/Data3D.h" namespace "astra": cdef cppclass CDataGPU(CDataStorage): CDataGPU(MemHandle3D) astra-toolbox-2.3.0/python/astra/PyIndexManager.pxd000066400000000000000000000027211475635207100223510ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from libcpp.string cimport string from .PyIncludes cimport * cdef extern from "astra/AstraObjectManager.h" namespace "astra": cdef cppclass CAstraObjectManagerBase: string getInfo(int) void remove(int) string getType() cdef cppclass CAstraIndexManager: CAstraObjectManagerBase* get(int) cdef extern from "astra/AstraObjectManager.h" namespace "astra::CAstraIndexManager": cdef CAstraIndexManager* getSingletonPtr() astra-toolbox-2.3.0/python/astra/PyMatrixManager.pxd000066400000000000000000000026551475635207100225540ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from libcpp.string cimport string from .PyIncludes cimport * cdef extern from "astra/AstraObjectManager.h" namespace "astra": cdef cppclass CMatrixManager: string info() void clear() void remove(int i) int store(CSparseMatrix *) CSparseMatrix * get(int i) cdef extern from "astra/AstraObjectManager.h" namespace "astra::CMatrixManager": cdef CMatrixManager* getSingletonPtr() astra-toolbox-2.3.0/python/astra/PyProjector2DFactory.pxd000066400000000000000000000025551475635207100235010ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from libcpp.string cimport string from libcpp cimport bool from .PyIncludes cimport * cdef extern from "astra/AstraObjectFactory.h" namespace "astra": cdef cppclass CProjector2DFactory: CProjector2D *create(string) cdef extern from "astra/AstraObjectFactory.h" namespace "astra::CProjector2DFactory": cdef CProjector2DFactory* getSingletonPtr() astra-toolbox-2.3.0/python/astra/PyProjector2DManager.pxd000066400000000000000000000026721475635207100234440ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from libcpp.string cimport string from .PyIncludes cimport * cdef extern from "astra/AstraObjectManager.h" namespace "astra": cdef cppclass CProjector2DManager: string info() void clear() void remove(int i) int store(CProjector2D *) CProjector2D * get(int i) cdef extern from "astra/AstraObjectManager.h" namespace "astra::CProjector2DManager": cdef CProjector2DManager* getSingletonPtr() astra-toolbox-2.3.0/python/astra/PyProjector3DFactory.pxd000066400000000000000000000025551475635207100235020ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from libcpp.string cimport string from libcpp cimport bool from .PyIncludes cimport * cdef extern from "astra/AstraObjectFactory.h" namespace "astra": cdef cppclass CProjector3DFactory: CProjector3D *create(string) cdef extern from "astra/AstraObjectFactory.h" namespace "astra::CProjector3DFactory": cdef CProjector3DFactory* getSingletonPtr() astra-toolbox-2.3.0/python/astra/PyProjector3DManager.pxd000066400000000000000000000026721475635207100234450ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from libcpp.string cimport string from .PyIncludes cimport * cdef extern from "astra/AstraObjectManager.h" namespace "astra": cdef cppclass CProjector3DManager: string info() void clear() void remove(int i) int store(CProjector3D *) CProjector3D * get(int i) cdef extern from "astra/AstraObjectManager.h" namespace "astra::CProjector3DManager": cdef CProjector3DManager* getSingletonPtr() astra-toolbox-2.3.0/python/astra/PyXMLDocument.pxd000066400000000000000000000045721475635207100221540ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # distutils: language = c++ # distutils: libraries = astra from libcpp.string cimport string from libcpp cimport bool from libcpp.list cimport list from libcpp.vector cimport vector cdef extern from "astra/Globals.h" namespace "astra": ctypedef float float32 ctypedef double float64 ctypedef unsigned short int uint16 ctypedef signed short int sint16 ctypedef unsigned char uchar8 ctypedef signed char schar8 ctypedef int int32 ctypedef short int int16 cdef extern from "astra/XMLNode.h" namespace "astra": cdef cppclass XMLNode: string getName() XMLNode addChildNode(string name) XMLNode addChildNode(string, string) void addAttribute(string, string) void addAttribute(string, float32) void addOption(string, string) bool hasOption(string) string getAttribute(string) list[XMLNode] getNodes() vector[float32] getContentNumericalArray() void setContent(double*, int, int, bool) void setContent(double*, int) string getContent() bool hasAttribute(string) cdef extern from "astra/XMLDocument.h" namespace "astra": cdef cppclass XMLDocument: void saveToFile(string sFilename) XMLNode getRootNode() cdef extern from "astra/XMLDocument.h" namespace "astra::XMLDocument": cdef XMLDocument *createDocument(string rootname) astra-toolbox-2.3.0/python/astra/__init__.py000066400000000000000000000035741475635207100211010ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from . import matlab as m from .creators import astra_dict,create_vol_geom, create_proj_geom, create_backprojection, create_sino, create_reconstruction, create_projector,create_sino3d_gpu, create_backprojection3d_gpu from .functions import data_op, add_noise_to_sino, clear, move_vol_geom, geom_size, geom_2vec, geom_postalignment from .extrautils import clipCircle from .astra import set_gpu_index, get_gpu_info, use_cuda, has_feature from . import data2d from . import astra from . import data3d from . import algorithm from . import projector from . import projector3d from . import matrix from . import plugin from . import plugins from . import log from .optomo import OpTomo from .tests import test, test_noCUDA, test_CUDA __version__ = '2.3.0' import os if 'ASTRA_GPU_INDEX' in os.environ: L = [ int(x) for x in os.environ['ASTRA_GPU_INDEX'].split(',') ] set_gpu_index(L) astra-toolbox-2.3.0/python/astra/algorithm.py000066400000000000000000000045311475635207100213220ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from . import algorithm_c as a def create(config): """Create algorithm object. :param config: Algorithm options. :type config: :class:`dict` :returns: :class:`int` -- the ID of the constructed object. """ return a.create(config) def run(i, iterations=1): """Run an algorithm. :param i: ID of object. :type i: :class:`int` :param iterations: Number of iterations to run. :type iterations: :class:`int` """ return a.run(i,iterations) def get_res_norm(i): """Get residual norm of algorithm. :param i: ID of object. :type i: :class:`int` :returns: :class:`float` -- The residual norm. """ return a.get_res_norm(i) def delete(ids): """Delete an algorithm object. :param ids: ID or list of ID's to delete. :type ids: :class:`int` or :class:`list` """ return a.delete(ids) def get_plugin_object(i): """Return the Python object instance of a plugin algorithm. :param i: ID of object corresponding to a plugin algorithm. :type i: :class:`int` :returns: The Python object instance of the plugin algorithm. """ return a.get_plugin_object(i) def clear(): """Clear all algorithm objects.""" return a.clear() def info(): """Print info on algorithm objects in memory.""" return a.info() astra-toolbox-2.3.0/python/astra/algorithm_c.pyx000066400000000000000000000100671475635207100220150ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # # distutils: language = c++ # distutils: libraries = astra from __future__ import print_function from .PyIncludes cimport * from . cimport PyAlgorithmManager from .PyAlgorithmManager cimport CAlgorithmManager from . cimport PyAlgorithmFactory from .PyAlgorithmFactory cimport CAlgorithmFactory from . cimport PyXMLDocument from .PyXMLDocument cimport XMLDocument from . cimport utils from .utils import wrap_from_bytes from .log import AstraError cdef CAlgorithmManager * manAlg = PyAlgorithmManager.getSingletonPtr() cdef extern from *: CReconstructionAlgorithm2D * dynamic_cast_recAlg2D "dynamic_cast" (CAlgorithm * ) CReconstructionAlgorithm3D * dynamic_cast_recAlg3D "dynamic_cast" (CAlgorithm * ) cdef extern from "src/PythonPluginAlgorithm.h" namespace "astra": cdef cppclass CPluginAlgorithm: object getInstance() cdef extern from *: CPluginAlgorithm * dynamic_cast_PluginAlg "dynamic_cast" (CAlgorithm * ) def create(config): cdef XMLConfig * cfg = utils.dictToConfig(b'Algorithm', config) cdef CAlgorithm * alg alg = PyAlgorithmFactory.getSingletonPtr().create(cfg.self.getAttribute(b'type')) if alg == NULL: del cfg raise AstraError("Unknown algorithm type") if not alg.initialize(cfg[0]): del cfg del alg raise AstraError("Unable to initialize algorithm", append_log=True) del cfg return manAlg.store(alg) cdef CAlgorithm * getAlg(i) except NULL: cdef CAlgorithm * alg = manAlg.get(i) if alg == NULL: raise AstraError("Unknown algorithm type") if not alg.isInitialized(): raise AstraError("Algorithm not initialized") return alg def run(i, iterations=0): cdef CAlgorithm * alg = getAlg(i) cdef int its = iterations cdef bool ret = True with nogil: ret = alg.run(its) if not ret: raise AstraError("Algorithm failed", append_log=True) def get_res_norm(i): cdef CReconstructionAlgorithm2D * pAlg2D cdef CReconstructionAlgorithm3D * pAlg3D cdef CAlgorithm * alg = getAlg(i) cdef float32 res = 0.0 pAlg2D = dynamic_cast_recAlg2D(alg) pAlg3D = dynamic_cast_recAlg3D(alg) if pAlg2D != NULL: if not pAlg2D.getResidualNorm(res): raise AstraError("Operation not supported") elif pAlg3D != NULL: if not pAlg3D.getResidualNorm(res): raise AstraError("Operation not supported") else: raise AstraError("Operation not supported") return res def delete(ids): try: for i in ids: manAlg.remove(i) except TypeError: manAlg.remove(ids) def get_plugin_object(algorithm_id): cdef CAlgorithm *alg cdef CPluginAlgorithm *pluginAlg alg = getAlg(algorithm_id) pluginAlg = dynamic_cast_PluginAlg(alg) if not pluginAlg: raise AstraError("Not a plugin algorithm") return pluginAlg.getInstance() def clear(): manAlg.clear() def info(): print(wrap_from_bytes(manAlg.info())) astra-toolbox-2.3.0/python/astra/astra.py000066400000000000000000000047021475635207100204460ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from . import astra_c as a def credits(): """Print credits of the ASTRA Toolbox.""" return a.credits() def use_cuda(): """Test if CUDA is enabled. :returns: :class:`bool` -- ``True`` if CUDA is enabled. """ return a.use_cuda() def set_gpu_index(idx, memory=0): """Set default GPU index to use. :param idx: GPU index :type idx: :class:`int` """ a.set_gpu_index(idx, memory) def get_gpu_info(idx=-1): """Get GPU info. :param idx: GPU index, or -1 for current device :type idx: :class:`int` :returns: :class:`str` -- GPU info """ return a.get_gpu_info(idx) def has_feature(feature): """Check a feature flag. These are used to check if certain functionality has been enabled at compile time, if new functionality is present, or if a backward-incompatible change is present. See include/astra/Features.h for a list. :param feature: The name of the feature :type feature: :class:`str` :returns: :class:`bool` -- The presence of the feature """ return a.has_feature(feature) def delete(ids): """Delete an astra object. :param ids: ID or list of ID's to delete. :type ids: :class:`int` or :class:`list` """ return a.delete(ids) def info(ids): """Print info about an astra object. :param ids: ID or list of ID's to show. :type ids: :class:`int` or :class:`list` """ return a.info(ids) astra-toolbox-2.3.0/python/astra/astra_c.pyx000066400000000000000000000103431475635207100211360ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # # distutils: language = c++ # distutils: libraries = astra from __future__ import print_function include "config.pxi" from .utils import wrap_from_bytes, wrap_to_bytes from .log import AstraError from libcpp.string cimport string from libcpp.vector cimport vector from libcpp cimport bool from . cimport PyIndexManager from .PyIndexManager cimport CAstraObjectManagerBase cdef extern from "astra/Globals.h" namespace "astra": bool cudaEnabled() bool cudaAvailable() cdef extern from "astra/Features.h" namespace "astra": bool hasFeature(string) IF HAVE_CUDA==True: cdef extern from "astra/cuda/2d/astra.h" namespace "astraCUDA": bool setGPUIndex(int) string getCudaDeviceString(int) ELSE: def setGPUIndex(): pass def getCudaDeviceString(idx): pass cdef extern from "astra/CompositeGeometryManager.h" namespace "astra": cdef cppclass SGPUParams: vector[int] GPUIndices size_t memory cdef extern from "astra/CompositeGeometryManager.h" namespace "astra::CCompositeGeometryManager": void setGlobalGPUParams(SGPUParams&) def credits(): print("""The ASTRA Toolbox has been developed at the University of Antwerp and CWI, Amsterdam by * Prof. dr. Joost Batenburg * Prof. dr. Jan Sijbers * Dr. Jeroen Bedorf * Dr. Folkert Bleichrodt * Dr. Andrei Dabravolski * Dr. Willem Jan Palenstijn * Dr. Daniel Pelt * Dr. Tom Roelandts * Dr. Alexander Skorikov * Dr. Wim van Aarle * Dr. Gert Van Gompel * Sander van der Maar, MSc. * Gert Merckx, MSc.""") def use_cuda(): return cudaAvailable() IF HAVE_CUDA==True: def set_gpu_index(idx, memory=0): try: import collections.abc as abc except: import collections as abc cdef SGPUParams params if (not isinstance(idx, abc.Iterable)) or isinstance(idx, (str, bytes)): idx = (idx,) if memory != 0 and memory < 1024*1024: raise AstraError("Setting GPU memory lower than 1MB is not supported") params.memory = memory params.GPUIndices = idx setGlobalGPUParams(params) ret = setGPUIndex(params.GPUIndices[0]) if not ret: print("Failed to set GPU " + str(params.GPUIndices[0])) def get_gpu_info(idx=-1): return wrap_from_bytes(getCudaDeviceString(idx)) ELSE: def set_gpu_index(idx, memory=0): raise AstraError("CUDA support is not enabled in ASTRA") def get_gpu_info(idx=-1): raise AstraError("CUDA support is not enabled in ASTRA") def delete(ids): try: import collections.abc as abc except: import collections as abc cdef CAstraObjectManagerBase* ptr if (not isinstance(ids, abc.Iterable)) or isinstance(ids, (str, bytes)): ids = (ids,) for i in ids: ptr = PyIndexManager.getSingletonPtr().get(i) if ptr: ptr.remove(i) def info(ids): try: import collections.abc as abc except: import collections as abc cdef CAstraObjectManagerBase* ptr if (not isinstance(ids, abc.Iterable)) or isinstance(ids, (str, bytes)): ids = (ids,) for i in ids: ptr = PyIndexManager.getSingletonPtr().get(i) if ptr: s = ptr.getType() + b"\t" + ptr.getInfo(i) print(wrap_from_bytes(s)) def has_feature(feature): return hasFeature(wrap_to_bytes(feature)) astra-toolbox-2.3.0/python/astra/creators.py000066400000000000000000000563601475635207100211650ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from __future__ import print_function import numpy as np import math from . import data2d from . import data3d from . import projector from . import projector3d from . import algorithm from .log import AstraError def astra_dict(intype): """Creates a dict to use with the ASTRA Toolbox. :param intype: Type of the ASTRA object. :type intype: :class:`string` :returns: :class:`dict` -- An ASTRA dict of type ``intype``. """ if intype == 'SIRT_CUDA2': intype = 'SIRT_CUDA' print('SIRT_CUDA2 has been deprecated. Use SIRT_CUDA instead.') elif intype == 'FP_CUDA2': intype = 'FP_CUDA' print('FP_CUDA2 has been deprecated. Use FP_CUDA instead.') return {'type': intype} def create_vol_geom(*varargin): """Create a volume geometry structure. This method can be called in a number of ways: ``create_vol_geom(n_rows_and_cols)``: :returns: A 2D volume geometry of size :math:`N \\times N`. ``create_vol_geom((n_rows, n_cols))``: :returns: A 2D volume geometry of size :math:`n_rows \\times n_cols`. ``create_vol_geom(n_rows, n_cols)``: :returns: A 2D volume geometry of size :math:`n_rows \\times n_cols`. ``create_vol_geom(n_rows, n_cols, minx, maxx, miny, maxy)``: :returns: A 2D volume geometry of size :math:`n_rows \\times n_cols`, windowed as :math:`minx \\leq x \\leq maxx` and :math:`miny \\leq y \\leq maxy`. Note that rows are oriented along the Y axis, and columns along the X axis. ``create_vol_geom((n_rows, n_cols, n_slices))``: :returns: A 3D volume geometry of size :math:`n_rows \\times n_cols \\times n_slices`. ``create_vol_geom(n_rows, n_cols, n_slices)``: :returns: A 3D volume geometry of size :math:`n_rows \\times n_cols \\times n_slices`. ``create_vol_geom(n_rows, n_cols, n_slices, minx, maxx, miny, maxy, minz, maxz)``: :returns: A 3D volume geometry of size :math:`n_rows \\times n_cols \\times n_slices`, windowed as :math:`minx \\leq x \\leq maxx`, :math:`miny \\leq y \\leq maxy`, and :math:`minz \\leq z \\leq maxz`. Note that rows are oriented along the Y axis, columns along the X axis, and slices along the Z axis. """ vol_geom = {'option': {}} # astra_create_vol_geom(row_count) if len(varargin) == 1 and isinstance(varargin[0], int) == 1: vol_geom['GridRowCount'] = varargin[0] vol_geom['GridColCount'] = varargin[0] # astra_create_vol_geom([row_count col_count]) elif len(varargin) == 1 and len(varargin[0]) == 2: vol_geom['GridRowCount'] = varargin[0][0] vol_geom['GridColCount'] = varargin[0][1] # astra_create_vol_geom([row_count col_count slice_count]) elif len(varargin) == 1 and len(varargin[0]) == 3: vol_geom['GridRowCount'] = varargin[0][0] vol_geom['GridColCount'] = varargin[0][1] vol_geom['GridSliceCount'] = varargin[0][2] # astra_create_vol_geom(row_count, col_count) elif len(varargin) == 2: vol_geom['GridRowCount'] = varargin[0] vol_geom['GridColCount'] = varargin[1] # astra_create_vol_geom(row_count, col_count, min_x, max_x, min_y, max_y) elif len(varargin) == 6: vol_geom['GridRowCount'] = varargin[0] vol_geom['GridColCount'] = varargin[1] vol_geom['option']['WindowMinX'] = varargin[2] vol_geom['option']['WindowMaxX'] = varargin[3] vol_geom['option']['WindowMinY'] = varargin[4] vol_geom['option']['WindowMaxY'] = varargin[5] # astra_create_vol_geom(row_count, col_count, slice_count) elif len(varargin) == 3: vol_geom['GridRowCount'] = varargin[0] vol_geom['GridColCount'] = varargin[1] vol_geom['GridSliceCount'] = varargin[2] # astra_create_vol_geom(row_count, col_count, slice_count, min_x, max_x, min_y, max_y, min_z, max_z) elif len(varargin) == 9: vol_geom['GridRowCount'] = varargin[0] vol_geom['GridColCount'] = varargin[1] vol_geom['GridSliceCount'] = varargin[2] vol_geom['option']['WindowMinX'] = varargin[3] vol_geom['option']['WindowMaxX'] = varargin[4] vol_geom['option']['WindowMinY'] = varargin[5] vol_geom['option']['WindowMaxY'] = varargin[6] vol_geom['option']['WindowMinZ'] = varargin[7] vol_geom['option']['WindowMaxZ'] = varargin[8] # set the window options, if not set already. if not 'WindowMinX' in vol_geom['option']: vol_geom['option']['WindowMinX'] = -vol_geom['GridColCount'] / 2. vol_geom['option']['WindowMaxX'] = vol_geom['GridColCount'] / 2. vol_geom['option']['WindowMinY'] = -vol_geom['GridRowCount'] / 2. vol_geom['option']['WindowMaxY'] = vol_geom['GridRowCount'] / 2. if 'GridSliceCount' in vol_geom: vol_geom['option']['WindowMinZ'] = -vol_geom['GridSliceCount'] / 2. vol_geom['option']['WindowMaxZ'] = vol_geom['GridSliceCount'] / 2. return vol_geom def create_proj_geom(intype, *args): """Create a projection geometry. This method can be called in a number of ways: ``create_proj_geom('parallel', detector_spacing, det_count, angles)``: :param detector_spacing: Distance between two adjacent detector pixels. :type detector_spacing: :class:`float` :param det_count: Number of detector pixels. :type det_count: :class:`int` :param angles: Array of angles in radians. :type angles: :class:`numpy.ndarray` :returns: A parallel projection geometry. ``create_proj_geom('parallel_vec', det_count, V)``: :param det_count: Number of detector pixels. :type det_count: :class:`int` :param V: Vector array. :type V: :class:`numpy.ndarray` :returns: A parallel-beam projection geometry. ``create_proj_geom('fanflat', det_width, det_count, angles, source_origin, origin_det)``: :param det_width: Size of a detector pixel. :type det_width: :class:`float` :param det_count: Number of detector pixels. :type det_count: :class:`int` :param angles: Array of angles in radians. :type angles: :class:`numpy.ndarray` :param source_origin: Position of the source. :param origin_det: Position of the detector :returns: A fan-beam projection geometry. ``create_proj_geom('fanflat_vec', det_count, V)``: :param det_count: Number of detector pixels. :type det_count: :class:`int` :param V: Vector array. :type V: :class:`numpy.ndarray` :returns: A fan-beam projection geometry. ``create_proj_geom('parallel3d', detector_spacing_x, detector_spacing_y, det_row_count, det_col_count, angles)``: :param detector_spacing_*: Distance between two adjacent detector pixels. :type detector_spacing_*: :class:`float` :param det_row_count: Number of detector pixel rows. :type det_row_count: :class:`int` :param det_col_count: Number of detector pixel columns. :type det_col_count: :class:`int` :param angles: Array of angles in radians. :type angles: :class:`numpy.ndarray` :returns: A parallel projection geometry. ``create_proj_geom('cone', detector_spacing_x, detector_spacing_y, det_row_count, det_col_count, angles, source_origin, origin_det)``: :param detector_spacing_*: Distance between two adjacent detector pixels. :type detector_spacing_*: :class:`float` :param det_row_count: Number of detector pixel rows. :type det_row_count: :class:`int` :param det_col_count: Number of detector pixel columns. :type det_col_count: :class:`int` :param angles: Array of angles in radians. :type angles: :class:`numpy.ndarray` :param source_origin: Distance between point source and origin. :type source_origin: :class:`float` :param origin_det: Distance between the detector and origin. :type origin_det: :class:`float` :returns: A cone-beam projection geometry. ``create_proj_geom('cone_vec', det_row_count, det_col_count, V)``: :param det_row_count: Number of detector pixel rows. :type det_row_count: :class:`int` :param det_col_count: Number of detector pixel columns. :type det_col_count: :class:`int` :param V: Vector array. :type V: :class:`numpy.ndarray` :returns: A cone-beam projection geometry. ``create_proj_geom('parallel3d_vec', det_row_count, det_col_count, V)``: :param det_row_count: Number of detector pixel rows. :type det_row_count: :class:`int` :param det_col_count: Number of detector pixel columns. :type det_col_count: :class:`int` :param V: Vector array. :type V: :class:`numpy.ndarray` :returns: A parallel projection geometry. ``create_proj_geom('sparse_matrix', det_width, det_count, angles, matrix_id)``: :param det_width: Size of a detector pixel. :type det_width: :class:`float` :param det_count: Number of detector pixels. :type det_count: :class:`int` :param angles: Array of angles in radians. :type angles: :class:`numpy.ndarray` :param matrix_id: ID of the sparse matrix. :type matrix_id: :class:`int` :returns: A projection geometry based on a sparse matrix. """ if intype == 'parallel': if len(args) < 3: raise AstraError( 'Not enough variables. Usage: astra.create_proj_geom(parallel, detector_spacing, det_count, angles)') return {'type': 'parallel', 'DetectorWidth': args[0], 'DetectorCount': args[1], 'ProjectionAngles': args[2]} elif intype == 'parallel_vec': if len(args) < 2: raise AstraError('Not enough variables. Usage: astra.create_proj_geom(parallel_vec, det_count, V)') if not args[1].shape[1] == 6: raise AstraError('V should be a Nx6 matrix, with N the number of projections') return {'type':'parallel_vec', 'DetectorCount':args[0], 'Vectors':args[1]} elif intype == 'fanflat': if len(args) < 5: raise AstraError('Not enough variables. Usage: astra.create_proj_geom(fanflat, det_width, det_count, angles, source_origin, origin_det)') return {'type': 'fanflat', 'DetectorWidth': args[0], 'DetectorCount': args[1], 'ProjectionAngles': args[2], 'DistanceOriginSource': args[3], 'DistanceOriginDetector': args[4]} elif intype == 'fanflat_vec': if len(args) < 2: raise AstraError('Not enough variables. Usage: astra.create_proj_geom(fanflat_vec, det_count, V)') if not args[1].shape[1] == 6: raise AstraError('V should be a Nx6 matrix, with N the number of projections') return {'type':'fanflat_vec', 'DetectorCount':args[0], 'Vectors':args[1]} elif intype == 'parallel3d': if len(args) < 5: raise AstraError('Not enough variables. Usage: astra.reate_proj_geom(parallel3d, detector_spacing_x, detector_spacing_y, det_row_count, det_col_count, angles)') return {'type':'parallel3d', 'DetectorSpacingX':args[0], 'DetectorSpacingY':args[1], 'DetectorRowCount':args[2], 'DetectorColCount':args[3],'ProjectionAngles':args[4]} elif intype == 'cone': if len(args) < 7: raise AstraError('Not enough variables. Usage: astra.create_proj_geom(cone, detector_spacing_x, detector_spacing_y, det_row_count, det_col_count, angles, source_origin, origin_det)') return {'type': 'cone','DetectorSpacingX':args[0], 'DetectorSpacingY':args[1], 'DetectorRowCount':args[2],'DetectorColCount':args[3],'ProjectionAngles':args[4],'DistanceOriginSource': args[5],'DistanceOriginDetector':args[6]} elif intype == 'cone_vec': if len(args) < 3: raise AstraError('Not enough variables. Usage: astra.create_proj_geom(cone_vec, det_row_count, det_col_count, V)') if not args[2].shape[1] == 12: raise AstraError('V should be a Nx12 matrix, with N the number of projections') return {'type': 'cone_vec','DetectorRowCount':args[0],'DetectorColCount':args[1],'Vectors':args[2]} elif intype == 'parallel3d_vec': if len(args) < 3: raise AstraError('Not enough variables. Usage: astra.create_proj_geom(parallel3d_vec, det_row_count, det_col_count, V)') if not args[2].shape[1] == 12: raise AstraError('V should be a Nx12 matrix, with N the number of projections') return {'type': 'parallel3d_vec','DetectorRowCount':args[0],'DetectorColCount':args[1],'Vectors':args[2]} elif intype == 'sparse_matrix': if len(args) < 4: raise AstraError( 'Not enough variables. Usage: astra.create_proj_geom(sparse_matrix, det_width, det_count, angles, matrix_id)') return {'type': 'sparse_matrix', 'DetectorWidth': args[0], 'DetectorCount': args[1], 'ProjectionAngles': args[2], 'MatrixID': args[3]} else: raise AstraError('Unknown projection geometry type: ' + intype) def create_backprojection(data, proj_id, returnData=True): """Create a backprojection of a sinogram (2D). :param data: Sinogram data or ID. :type data: :class:`numpy.ndarray` or :class:`int` :param proj_id: ID of the projector to use. :type proj_id: :class:`int` :param returnData: If False, only return the ID of the backprojection. :type returnData: :class:`bool` :returns: :class:`int` or (:class:`int`, :class:`numpy.ndarray`) -- If ``returnData=False``, returns the ID of the backprojection. Otherwise, returns a tuple containing the ID of the backprojection and the backprojection itself, in that order. """ proj_geom = projector.projection_geometry(proj_id) vol_geom = projector.volume_geometry(proj_id) if isinstance(data, np.ndarray): sino_id = data2d.create('-sino', proj_geom, data) else: sino_id = data vol_id = data2d.create('-vol', vol_geom, 0) if projector.is_cuda(proj_id): algString = 'BP_CUDA' else: algString = 'BP' cfg = astra_dict(algString) cfg['ProjectorId'] = proj_id cfg['ProjectionDataId'] = sino_id cfg['ReconstructionDataId'] = vol_id alg_id = algorithm.create(cfg) algorithm.run(alg_id) algorithm.delete(alg_id) if isinstance(data, np.ndarray): data2d.delete(sino_id) if returnData: return vol_id, data2d.get(vol_id) else: return vol_id def create_backprojection3d_gpu(data, proj_geom, vol_geom, returnData=True): """Create a backprojection of a sinogram (3D) using CUDA. :param data: Sinogram data or ID. :type data: :class:`numpy.ndarray` or :class:`int` :param proj_geom: Projection geometry. :type proj_geom: :class:`dict` :param vol_geom: Volume geometry. :type vol_geom: :class:`dict` :param returnData: If False, only return the ID of the backprojection. :type returnData: :class:`bool` :returns: :class:`int` or (:class:`int`, :class:`numpy.ndarray`) -- If ``returnData=False``, returns the ID of the backprojection. Otherwise, returns a tuple containing the ID of the backprojection and the backprojection itself, in that order. """ if isinstance(data, np.ndarray): sino_id = data3d.create('-sino', proj_geom, data) else: sino_id = data vol_id = data3d.create('-vol', vol_geom, 0) cfg = astra_dict('BP3D_CUDA') cfg['ProjectionDataId'] = sino_id cfg['ReconstructionDataId'] = vol_id alg_id = algorithm.create(cfg) algorithm.run(alg_id) algorithm.delete(alg_id) if isinstance(data, np.ndarray): data3d.delete(sino_id) if returnData: return vol_id, data3d.get(vol_id) else: return vol_id def create_sino(data, proj_id, returnData=True, gpuIndex=None): """Create a forward projection of an image (2D). :param data: Image data or ID. :type data: :class:`numpy.ndarray` or :class:`int` :param proj_id: ID of the projector to use. :type proj_id: :class:`int` :param returnData: If False, only return the ID of the forward projection. :type returnData: :class:`bool` :param gpuIndex: Optional GPU index. :type gpuIndex: :class:`int` :returns: :class:`int` or (:class:`int`, :class:`numpy.ndarray`) If ``returnData=False``, returns the ID of the forward projection. Otherwise, returns a tuple containing the ID of the forward projection and the forward projection itself, in that order. """ proj_geom = projector.projection_geometry(proj_id) vol_geom = projector.volume_geometry(proj_id) if isinstance(data, np.ndarray): volume_id = data2d.create('-vol', vol_geom, data) else: volume_id = data sino_id = data2d.create('-sino', proj_geom, 0) if projector.is_cuda(proj_id): algString = 'FP_CUDA' else: algString = 'FP' cfg = astra_dict(algString) cfg['ProjectorId'] = proj_id if gpuIndex is not None: cfg['option'] = {'GPUindex': gpuIndex} cfg['ProjectionDataId'] = sino_id cfg['VolumeDataId'] = volume_id alg_id = algorithm.create(cfg) algorithm.run(alg_id) algorithm.delete(alg_id) if isinstance(data, np.ndarray): data2d.delete(volume_id) if returnData: return sino_id, data2d.get(sino_id) else: return sino_id def create_sino3d_gpu(data, proj_geom, vol_geom, returnData=True, gpuIndex=None): """Create a forward projection of an image (3D). :param data: Image data or ID. :type data: :class:`numpy.ndarray` or :class:`int` :param proj_geom: Projection geometry. :type proj_geom: :class:`dict` :param vol_geom: Volume geometry. :type vol_geom: :class:`dict` :param returnData: If False, only return the ID of the forward projection. :type returnData: :class:`bool` :param gpuIndex: Optional GPU index. :type gpuIndex: :class:`int` :returns: :class:`int` or (:class:`int`, :class:`numpy.ndarray`) -- If ``returnData=False``, returns the ID of the forward projection. Otherwise, returns a tuple containing the ID of the forward projection and the forward projection itself, in that order. """ if isinstance(data, np.ndarray): volume_id = data3d.create('-vol', vol_geom, data) else: volume_id = data sino_id = data3d.create('-sino', proj_geom, 0) algString = 'FP3D_CUDA' cfg = astra_dict(algString) if not gpuIndex==None: cfg['option']={'GPUindex':gpuIndex} cfg['ProjectionDataId'] = sino_id cfg['VolumeDataId'] = volume_id alg_id = algorithm.create(cfg) algorithm.run(alg_id) algorithm.delete(alg_id) if isinstance(data, np.ndarray): data3d.delete(volume_id) if returnData: return sino_id, data3d.get(sino_id) else: return sino_id def create_reconstruction(rec_type, proj_id, sinogram, iterations=1, use_mask='no', mask=np.array([]), use_minc='no', minc=0, use_maxc='no', maxc=255, returnData=True, filterType=None, filterData=None): """Create a reconstruction of a sinogram (2D). :param rec_type: Name of the reconstruction algorithm. :type rec_type: :class:`string` :param proj_id: ID of the projector to use. :type proj_id: :class:`int` :param sinogram: Sinogram data or ID. :type sinogram: :class:`numpy.ndarray` or :class:`int` :param iterations: Number of iterations to run. :type iterations: :class:`int` :param use_mask: Whether to use a mask. :type use_mask: ``'yes'`` or ``'no'`` :param mask: Mask data or ID :type mask: :class:`numpy.ndarray` or :class:`int` :param use_minc: Whether to force a minimum value on the reconstruction pixels. :type use_minc: ``'yes'`` or ``'no'`` :param minc: Minimum value to use. :type minc: :class:`float` :param use_maxc: Whether to force a maximum value on the reconstruction pixels. :type use_maxc: ``'yes'`` or ``'no'`` :param maxc: Maximum value to use. :type maxc: :class:`float` :param returnData: If False, only return the ID of the reconstruction. :type returnData: :class:`bool` :param filterType: Which type of filter to use for filter-based methods. :type filterType: :class:`string` :param filterData: Optional filter data for filter-based methods. :type filterData: :class:`numpy.ndarray` :returns: :class:`int` or (:class:`int`, :class:`numpy.ndarray`) -- If ``returnData=False``, returns the ID of the reconstruction. Otherwise, returns a tuple containing the ID of the reconstruction and reconstruction itself, in that order. """ proj_geom = projector.projection_geometry(proj_id) if isinstance(sinogram, np.ndarray): sino_id = data2d.create('-sino', proj_geom, sinogram) else: sino_id = sinogram vol_geom = projector.volume_geometry(proj_id) recon_id = data2d.create('-vol', vol_geom, 0) cfg = astra_dict(rec_type) cfg['ProjectorId'] = proj_id cfg['ProjectionDataId'] = sino_id cfg['ReconstructionDataId'] = recon_id cfg['options'] = {} if use_mask == 'yes': if isinstance(mask, np.ndarray): mask_id = data2d.create('-vol', vol_geom, mask) else: mask_id = mask cfg['options']['ReconstructionMaskId'] = mask_id if not filterType == None: cfg['FilterType'] = filterType if not filterData == None: if isinstance(filterData, np.ndarray): nexpow = int( pow(2, math.ceil(math.log(2 * proj_geom['DetectorCount'], 2)))) filtSize = nexpow / 2 + 1 filt_proj_geom = create_proj_geom( 'parallel', 1.0, filtSize, proj_geom['ProjectionAngles']) filt_id = data2d.create('-sino', filt_proj_geom, filterData) else: filt_id = filterData cfg['FilterSinogramId'] = filt_id cfg['options']['UseMinConstraint'] = use_minc cfg['options']['MinConstraintValue'] = minc cfg['options']['UseMaxConstraint'] = use_maxc cfg['options']['MaxConstraintValue'] = maxc cfg['options']['ProjectionOrder'] = 'random' alg_id = algorithm.create(cfg) algorithm.run(alg_id, iterations) algorithm.delete(alg_id) if isinstance(sinogram, np.ndarray): data2d.delete(sino_id) if use_mask == 'yes' and isinstance(mask, np.ndarray): data2d.delete(mask_id) if not filterData == None: if isinstance(filterData, np.ndarray): data2d.delete(filt_id) if returnData: return recon_id, data2d.get(recon_id) else: return recon_id def create_projector(proj_type, proj_geom, vol_geom, options=None): """Create a 2D or 3D projector. :param proj_type: Projector type, such as ``'line'``, ``'linear'``, ... :type proj_type: :class:`string` :param proj_geom: Projection geometry. :type proj_geom: :class:`dict` :param vol_geom: Volume geometry. :type vol_geom: :class:`dict` :param options: Projector options structure defining ``'VoxelSuperSampling'``, ``'DetectorSuperSampling'``. :type options: :class:`dict` :returns: :class:`int` -- The ID of the projector. """ if proj_type == 'blob': raise NotImplementedError('Blob type not yet implemented') cfg = astra_dict(proj_type) cfg['ProjectionGeometry'] = proj_geom cfg['VolumeGeometry'] = vol_geom if options is not None: cfg['options'] = options types3d = ['cuda3d'] if proj_type in types3d: return projector3d.create(cfg) else: return projector.create(cfg) astra-toolbox-2.3.0/python/astra/data2d.py000066400000000000000000000104021475635207100204650ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from . import data2d_c as d from .pythonutils import checkArrayForLink def clear(): """Clear all 2D data objects.""" return d.clear() def delete(ids): """Delete a 2D object. :param ids: ID or list of ID's to delete. :type ids: :class:`int` or :class:`list` """ return d.delete(ids) def create(datatype, geometry, data=None): """Create a 2D object. :param datatype: Data object type, '-vol' or '-sino'. :type datatype: :class:`string` :param geometry: Volume or projection geometry. :type geometry: :class:`dict` :param data: Data to fill the constructed object with, either a scalar or array. :type data: :class:`float` or :class:`numpy.ndarray` :returns: :class:`int` -- the ID of the constructed object. """ return d.create(datatype,geometry,data) def link(datatype, geometry, data): """Link a 2D numpy array with the toolbox. :param datatype: Data object type, '-vol' or '-sino'. :type datatype: :class:`string` :param geometry: Volume or projection geometry. :type geometry: :class:`dict` :param data: Numpy array to link :type data: :class:`numpy.ndarray` :returns: :class:`int` -- the ID of the constructed object. """ checkArrayForLink(data) return d.create(datatype,geometry,data,True) def store(i, data): """Fill existing 2D object with data. :param i: ID of object to fill. :type i: :class:`int` :param data: Data to fill the object with, either a scalar or array. :type data: :class:`float` or :class:`numpy.ndarray` """ return d.store(i, data) def get_geometry(i): """Get the geometry of a 2D object. :param i: ID of object. :type i: :class:`int` :returns: :class:`dict` -- The geometry of object with ID ``i``. """ return d.get_geometry(i) def change_geometry(i, geom): """Change the geometry of a 2D object. :param i: ID of object. :type i: :class:`int` :param geom: new geometry. :type geom: :class:`dict` """ return d.change_geometry(i, geom) def get(i): """Get a 2D object. :param i: ID of object to get. :type i: :class:`int` :returns: :class:`numpy.ndarray` -- The object data. """ return d.get(i) def get_shared(i): """Get a 2D object with memory shared between the ASTRA toolbox and numpy array. :param i: ID of object to get. :type i: :class:`int` :returns: :class:`numpy.ndarray` -- The object data. """ return d.get_shared(i) def get_single(i): """Get a 2D object in single precision. :param i: ID of object to get. :type i: :class:`int` :returns: :class:`numpy.ndarray` -- The object data. """ return d.get_single(i) def shepp_logan(geometry, modified=True, returnData=True): """Create a 2D data object with a Shepp-Logan phantom. :param geometry: Volume geometry :param modified: If False, generate the original Shepp-Logan phantom :param returnData: If False, only return the ID of the new data object :returns: :class:`int` or (:class:`int`, :class`numpy.ndarray`) """ i = create('-vol', geometry) d.shepp_logan(i, modified) if returnData: return i, get(i) else: return i def info(): """Print info on 2D objects in memory.""" return d.info() astra-toolbox-2.3.0/python/astra/data2d_c.pyx000066400000000000000000000252601475635207100211670ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # # distutils: language = c++ # distutils: libraries = astra from __future__ import print_function cimport cython from cython cimport view from . cimport PyData2DManager from .PyData2DManager cimport CData2DManager from . cimport PyProjector2DManager from .PyProjector2DManager cimport CProjector2DManager from . cimport PyXMLDocument from .PyXMLDocument cimport XMLDocument import numpy as np cimport numpy as np np.import_array() from .PyIncludes cimport * from . cimport utils from .utils import wrap_from_bytes from .log import AstraError from .pythonutils import geom_size import operator cdef CData2DManager * man2d = PyData2DManager.getSingletonPtr() cdef CProjector2DManager * manProj = PyProjector2DManager.getSingletonPtr() cdef extern from "CFloat32CustomPython.h": cdef cppclass CFloat32CustomPython: CFloat32CustomPython(np.ndarray arrIn) cdef extern from "astra/SheppLogan.h" namespace "astra": cdef void generateSheppLogan(CFloat32VolumeData2D*, bool) def clear(): man2d.clear() def delete(ids): try: for i in ids: man2d.remove(i) except TypeError: man2d.remove(ids) def create(datatype, geometry, data=None, link=False): cdef XMLConfig *cfg cdef CVolumeGeometry2D * pGeometry cdef unique_ptr[CProjectionGeometry2D] ppGeometry cdef CFloat32Data2D * pDataObject2D cdef CFloat32CustomMemory * pCustom if link: geom_shape = geom_size(geometry) if data.shape != geom_shape: raise ValueError("The dimensions of the data {} do not match those " "specified in the geometry {}".format(data.shape, geom_shape)) if datatype == '-vol': cfg = utils.dictToConfig(b'VolumeGeometry', geometry) pGeometry = new CVolumeGeometry2D() if not pGeometry.initialize(cfg[0]): del cfg del pGeometry raise AstraError('Geometry class could not be initialized', append_log=True) if link: pCustom = new CFloat32CustomPython(data) pDataObject2D = new CFloat32VolumeData2D(cython.operator.dereference(pGeometry), pCustom) else: pDataObject2D = new CFloat32VolumeData2D(cython.operator.dereference(pGeometry)) del cfg del pGeometry elif datatype == '-sino': cfg = utils.dictToConfig(b'ProjectionGeometry', geometry) tpe = cfg.self.getAttribute(b'type') ppGeometry = constructProjectionGeometry2D(tpe) if not ppGeometry: raise ValueError("'{}' is not a valid 2D geometry type".format(tpe)) if not ppGeometry.get().initialize(cfg[0]): del cfg raise AstraError('Geometry class could not be initialized', append_log=True) if link: pCustom = new CFloat32CustomPython(data) pDataObject2D = new CFloat32ProjectionData2D(cython.operator.dereference(ppGeometry), pCustom) else: pDataObject2D = new CFloat32ProjectionData2D(cython.operator.dereference(ppGeometry)) del cfg else: raise ValueError("Invalid datatype. Please specify '-vol' or '-sino'") if not pDataObject2D.isInitialized(): del pDataObject2D raise AstraError("Couldn't initialize data object", append_log=True) if not link: fillDataObject(pDataObject2D, data) return man2d.store(pDataObject2D) cdef fillDataObject(CFloat32Data2D * obj, data): if data is None: fillDataObjectScalar(obj, 0) else: if isinstance(data, np.ndarray): obj_shape = (obj.getHeight(), obj.getWidth()) if data.shape != obj_shape: raise ValueError( "The dimensions of the data {} do not match those specified " "in the geometry {}".format(data.shape, obj_shape)) fillDataObjectArray(obj, np.ascontiguousarray(data,dtype=np.float32)) else: fillDataObjectScalar(obj, np.float32(data)) cdef fillDataObjectScalar(CFloat32Data2D * obj, float s): cdef size_t i for i in range(obj.getSize()): obj.getData()[i] = s @cython.boundscheck(False) @cython.wraparound(False) cdef fillDataObjectArray(CFloat32Data2D * obj, float [:,::1] data): cdef float [:,::1] cView = obj.getData2D()[0] cView[:] = data cdef CFloat32Data2D * getObject(i) except NULL: cdef CFloat32Data2D * pDataObject = man2d.get(i) if pDataObject == NULL: raise AstraError("Data object not found") if not pDataObject.isInitialized(): raise AstraError("Data object not initialized properly") return pDataObject def store(i, data): cdef CFloat32Data2D * pDataObject = getObject(i) fillDataObject(pDataObject, data) def get_geometry(i): cdef CFloat32Data2D * pDataObject = getObject(i) cdef CFloat32ProjectionData2D * pDataObject2 cdef CFloat32VolumeData2D * pDataObject3 if pDataObject.getType() == TWOPROJECTION: pDataObject2 = pDataObject geom = utils.configToDict(pDataObject2.getGeometry().getConfiguration()) elif pDataObject.getType() == TWOVOLUME: pDataObject3 = pDataObject geom = utils.configToDict(pDataObject3.getGeometry().getConfiguration()) else: raise AstraError("Not a known data object") return geom cdef CProjector2D * getProjector(i) except NULL: cdef CProjector2D * proj = manProj.get(i) if proj == NULL: raise AstraError("Projector not found") if not proj.isInitialized(): raise AstraError("Projector not initialized") return proj def check_compatible(i, proj_id): cdef CProjector2D * proj = getProjector(proj_id) cdef CFloat32Data2D * pDataObject = getObject(i) cdef CFloat32ProjectionData2D * pDataObject2 cdef CFloat32VolumeData2D * pDataObject3 if pDataObject.getType() == TWOPROJECTION: pDataObject2 = pDataObject return pDataObject2.getGeometry().isEqual(proj.getProjectionGeometry()) elif pDataObject.getType() == TWOVOLUME: pDataObject3 = pDataObject return pDataObject3.getGeometry().isEqual(proj.getVolumeGeometry()) else: raise AstraError("Not a known data object type") def change_geometry(i, geom): cdef XMLConfig *cfg cdef CVolumeGeometry2D * pGeometry cdef unique_ptr[CProjectionGeometry2D] ppGeometry cdef CFloat32Data2D * pDataObject = getObject(i) cdef CFloat32ProjectionData2D * pDataObject2 cdef CFloat32VolumeData2D * pDataObject3 if pDataObject.getType() == TWOPROJECTION: pDataObject2 = pDataObject cfg = utils.dictToConfig(b'ProjectionGeometry', geom) tpe = cfg.self.getAttribute(b'type') ppGeometry = constructProjectionGeometry2D(tpe) if not ppGeometry: raise ValueError("'{}' is not a valid 2D geometry type".format(tpe)) if not ppGeometry.get().initialize(cfg[0]): del cfg AstraError('Geometry class could not be initialized', append_log=True) geom_shape = (ppGeometry.get().getProjectionAngleCount(), ppGeometry.get().getDetectorCount()) obj_shape = (pDataObject2.getAngleCount(), pDataObject2.getDetectorCount()) if geom_shape != obj_shape: del cfg raise ValueError("The dimensions of the data {} do not match those " "specified in the geometry {}".format(obj_shape, geom_shape)) pDataObject2.changeGeometry(cython.operator.dereference(ppGeometry)) del cfg elif pDataObject.getType() == TWOVOLUME: pDataObject3 = pDataObject cfg = utils.dictToConfig(b'VolumeGeometry', geom) pGeometry = new CVolumeGeometry2D() if not pGeometry.initialize(cfg[0]): del cfg del pGeometry raise AstraError('Geometry class could not be initialized', append_log=True) geom_shape = (pGeometry.getGridRowCount(), pGeometry.getGridColCount()) obj_shape = (pDataObject3.getHeight(), pDataObject3.getWidth()) if geom_shape != obj_shape: del cfg del pGeometry raise ValueError("The dimensions of the data {} do not match those " "specified in the geometry {}".format(obj_shape, geom_shape)) pDataObject3.changeGeometry(cython.operator.dereference(pGeometry)) del cfg del pGeometry else: raise AstraError("Not a known data object") @cython.boundscheck(False) @cython.wraparound(False) def get(i): cdef CFloat32Data2D * pDataObject = getObject(i) outArr = np.empty((pDataObject.getHeight(), pDataObject.getWidth()),dtype=np.float32,order='C') cdef float [:,::1] mView = outArr cdef float [:,::1] cView = pDataObject.getData2D()[0] mView[:] = cView return outArr def get_shared(i): cdef CFloat32Data2D * pDataObject = getObject(i) cdef np.npy_intp shape[2] shape[0] = pDataObject.getHeight() shape[1] = pDataObject.getWidth() return np.PyArray_SimpleNewFromData(2,shape,np.NPY_FLOAT32,pDataObject.getData2D()[0]) def get_single(i): raise NotImplementedError("Not yet implemented") def shepp_logan(i, modified=True): cdef CFloat32Data2D * pDataObject = getObject(i) cdef CFloat32VolumeData2D * pVolumeDataObject = getObject(i) generateSheppLogan(pVolumeDataObject, modified); def info(): print(wrap_from_bytes(man2d.info())) astra-toolbox-2.3.0/python/astra/data3d.py000066400000000000000000000112371475635207100204750ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from . import data3d_c as d def create(datatype,geometry,data=None): """Create a 3D object. :param datatype: Data object type, '-vol' or '-sino'. :type datatype: :class:`string` :param geometry: Volume or projection geometry. :type geometry: :class:`dict` :param data: Data to fill the constructed object with, either a scalar or array. :type data: :class:`float` or :class:`numpy.ndarray` :returns: :class:`int` -- the ID of the constructed object. """ return d.create(datatype,geometry,data) def link(datatype, geometry, data): """Link a 3D array with ASTRA from another library supporting dlpack, such as numpy or pytorch. :param datatype: Data object type, '-vol' or '-sino'. :type datatype: :class:`string` :param geometry: Volume or projection geometry. :type geometry: :class:`dict` :param data: Array to link. Needs to implement the dlpack protocol. :type data: :class:`object` :returns: :class:`int` -- the ID assigned to the linked object. """ return d.create(datatype,geometry,data,True) def get(i): """Get a 3D object. :param i: ID of object to get. Linked GPU arrays are not supported. :type i: :class:`int` :returns: :class:`numpy.ndarray` -- The object data. """ return d.get(i) def get_shared(i): """Get a 3D object with memory shared between the ASTRA toolbox and numpy array. :param i: ID of object to get. Linked GPU arrays are not supported. :type i: :class:`int` :returns: :class:`numpy.ndarray` -- The object data. """ return d.get_shared(i) def get_single(i): """Get a 3D object in single precision. :param i: ID of object to get. Linked GPU arrays are not supported. :type i: :class:`int` :returns: :class:`numpy.ndarray` -- The object data. """ return d.get_single(i) def store(i,data): """Fill existing 3D object with data. :param i: ID of object to fill. Linked GPU arrays are not supported. :type i: :class:`int` :param data: Data to fill the object with, either a scalar or array. :type data: :class:`float` or :class:`numpy.ndarray` """ return d.store(i,data) def get_geometry(i): """Get the geometry of a 3D object. :param i: ID of object. :type i: :class:`int` :returns: :class:`dict` -- The geometry of object with ID ``i``. """ return d.get_geometry(i) def change_geometry(i, geometry): """Change the geometry of a 3D object. :param i: ID of object. :type i: :class:`int` :param geometry: Volume or projection geometry. :type geometry: :class:`dict` """ return d.change_geometry(i, geometry) def shepp_logan(geometry, modified=True, returnData=True): """Create a 3D data object with a Shepp-Logan phantom. :param geometry: Volume geometry :param modified: If False, generate the original Shepp-Logan phantom :param returnData: If False, only return the ID of the new data object :returns: :class:`int` or (:class:`int`, :class`numpy.ndarray`) """ i = create('-vol', geometry) d.shepp_logan(i, modified) if returnData: return i, get(i) else: return i def dimensions(i): """Get dimensions of a 3D object. :param i: ID of object. :type i: :class:`int` :returns: :class:`tuple` -- dimensions of object with ID ``i``. """ return d.dimensions(i) def delete(ids): """Delete a 3D object. :param ids: ID or list of ID's to delete. :type ids: :class:`int` or :class:`list` """ return d.delete(ids) def clear(): """Clear all 3D data objects.""" return d.clear() def info(): """Print info on 3D objects in memory.""" return d.info() astra-toolbox-2.3.0/python/astra/data3d_c.pyx000066400000000000000000000203511475635207100211640ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # # distutils: language = c++ # distutils: libraries = astra from __future__ import print_function cimport cython from libcpp.utility cimport move from . cimport PyData3DManager from .PyData3DManager cimport CData3DManager from .PyIncludes cimport * import numpy as np cimport numpy as np np.import_array() from . cimport PyXMLDocument from .PyXMLDocument cimport XMLDocument from . cimport utils from .utils import wrap_from_bytes from .utils cimport linkVolFromGeometry, linkProjFromGeometry, createProjectionGeometry3D, createVolumeGeometry3D from .log import AstraError from .pythonutils import geom_size, GPULink import operator include "config.pxi" cdef extern from "astra/SheppLogan.h" namespace "astra": cdef void generateSheppLogan3D(CFloat32VolumeData3D*, bool) cdef CData3DManager * man3d = PyData3DManager.getSingletonPtr() def create(datatype,geometry,data=None, link=False): cdef unique_ptr[CVolumeGeometry3D] pGeometry cdef unique_ptr[CProjectionGeometry3D] ppGeometry cdef CData3D * pDataObject3D if datatype == '-vol': pGeometry = createVolumeGeometry3D(geometry) if link: pDataObject3D = linkVolFromGeometry(cython.operator.dereference(pGeometry), data) else: pDataObject3D = createCFloat32VolumeData3DMemory(move(pGeometry)) elif datatype == '-sino' or datatype == '-proj3d' or datatype == '-sinocone': ppGeometry = createProjectionGeometry3D(geometry) if link: pDataObject3D = linkProjFromGeometry(cython.operator.dereference(ppGeometry), data) else: pDataObject3D = createCFloat32ProjectionData3DMemory(move(ppGeometry)) else: raise ValueError("Invalid datatype. Please specify '-vol' or '-proj3d'") if not pDataObject3D.isInitialized(): del pDataObject3D raise AstraError("Couldn't initialize data object", append_log=True) if not link: fillDataObject(pDataObject3D, data) return man3d.store(pDataObject3D) def get_geometry(i): cdef CData3D * pDataObject = getObject(i) cdef CFloat32ProjectionData3D * pDataObject2 cdef CFloat32VolumeData3D * pDataObject3 if pDataObject.getType() == THREEPROJECTION: pDataObject2 = pDataObject geom = utils.configToDict(pDataObject2.getGeometry().getConfiguration()) elif pDataObject.getType() == THREEVOLUME: pDataObject3 = pDataObject geom = utils.configToDict(pDataObject3.getGeometry().getConfiguration()) else: raise AstraError("Not a known data object") return geom def change_geometry(i, geom): cdef CData3D * pDataObject = getObject(i) cdef CFloat32ProjectionData3D * pDataObject2 cdef CFloat32VolumeData3D * pDataObject3 if pDataObject.getType() == THREEPROJECTION: pDataObject2 = pDataObject ppGeometry = createProjectionGeometry3D(geom) geom_shape = (ppGeometry.get().getDetectorRowCount(), ppGeometry.get().getProjectionCount(), ppGeometry.get().getDetectorColCount()) obj_shape = (pDataObject2.getDetectorRowCount(), pDataObject2.getAngleCount(), pDataObject2.getDetectorColCount()) if geom_shape != obj_shape: raise ValueError("The dimensions of the data {} do not match those " "specified in the geometry {}".format(obj_shape, geom_shape)) pDataObject2.changeGeometry(move(ppGeometry)) elif pDataObject.getType() == THREEVOLUME: pDataObject3 = pDataObject pGeometry = createVolumeGeometry3D(geom) geom_shape = (pGeometry.get().getGridSliceCount(), pGeometry.get().getGridRowCount(), pGeometry.get().getGridColCount()) obj_shape = (pDataObject3.getSliceCount(), pDataObject3.getRowCount(), pDataObject3.getColCount()) if geom_shape != obj_shape: raise ValueError("The dimensions of the data {} do not match those " "specified in the geometry {}".format(obj_shape, geom_shape)) pDataObject3.changeGeometry(move(pGeometry)) else: raise AstraError("Not a known data object") cdef fillDataObject(CData3D * obj, data): if not obj.isFloat32Memory(): raise ValueError("Data object is not float32/memory") if data is None: fillDataObjectScalar(obj, 0) else: if isinstance(data, np.ndarray): obj_shape = (obj.getDepth(), obj.getHeight(), obj.getWidth()) if data.shape != obj_shape: raise ValueError("The dimensions of the data {} do not match those " "specified in the geometry {}".format(data.shape, obj_shape)) fillDataObjectArray(obj, np.ascontiguousarray(data,dtype=np.float32)) else: fillDataObjectScalar(obj, np.float32(data)) cdef fillDataObjectScalar(CData3D * obj, float s): cdef size_t i for i in range(obj.getSize()): obj.getFloat32Memory()[i] = s @cython.boundscheck(False) @cython.wraparound(False) cdef fillDataObjectArray(CData3D* obj, const float [:,:,::1] data): cdef const float [:,:,::1] cView = obj.getFloat32Memory() cView[:] = data cdef CData3D * getObject(i) except NULL: cdef CData3D * pDataObject = man3d.get(i) if pDataObject == NULL: raise AstraError("Data object not found") if not pDataObject.isInitialized(): raise AstraError("Data object not initialized properly") return pDataObject @cython.boundscheck(False) @cython.wraparound(False) def get(i): cdef CData3D * pDataObject = getObject(i) if not pDataObject.isFloat32Memory(): raise ValueError("Data object is not float32/memory") outArr = np.empty((pDataObject.getDepth(),pDataObject.getHeight(), pDataObject.getWidth()),dtype=np.float32,order='C') cdef float [:,:,::1] mView = outArr cdef float [:,:,::1] cView = pDataObject.getFloat32Memory() mView[:] = cView return outArr def get_shared(i): cdef CData3D * pDataObject = getObject(i) if not pDataObject.isFloat32Memory(): raise ValueError("Data object is not float32/memory") cdef np.npy_intp shape[3] shape[0] = pDataObject.getDepth() shape[1] = pDataObject.getHeight() shape[2] = pDataObject.getWidth() return np.PyArray_SimpleNewFromData(3,shape,np.NPY_FLOAT32,pDataObject.getFloat32Memory()) def get_single(i): raise NotImplementedError("Not yet implemented") def store(i,data): cdef CData3D * pDataObject = getObject(i) fillDataObject(pDataObject, data) def dimensions(i): cdef CData3D * pDataObject = getObject(i) return (pDataObject.getDepth(),pDataObject.getHeight(),pDataObject.getWidth()) def shepp_logan(i, modified=True): cdef CData3D * pDataObject = getObject(i) cdef CFloat32VolumeData3D * pVolumeDataObject = pDataObject generateSheppLogan3D(pVolumeDataObject, modified); def delete(ids): try: for i in ids: man3d.remove(i) except TypeError: man3d.remove(ids) def clear(): man3d.clear() def info(): print(wrap_from_bytes(man3d.info())) astra-toolbox-2.3.0/python/astra/experimental.pyx000066400000000000000000000226401475635207100222220ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # # distutils: language = c++ # distutils: libraries = astra include "config.pxi" from . cimport utils from .utils import wrap_from_bytes from .utils cimport createProjectionGeometry3D from .log import AstraError IF HAVE_CUDA==True: from .PyIncludes cimport * from libcpp.vector cimport vector cdef extern from "astra/Filters.h" namespace "astra": cdef enum E_FBPFILTER: FILTER_ERROR FILTER_NONE FILTER_RAMLAK cdef cppclass SFilterConfig: SFilterConfig() E_FBPFILTER m_eType cdef extern from "astra/CompositeGeometryManager.h" namespace "astra::CCompositeGeometryManager": cdef enum EJobMode: MODE_ADD MODE_SET cdef extern from "astra/CompositeGeometryManager.h" namespace "astra": cdef cppclass CCompositeGeometryManager: bool doFP(CProjector3D *, vector[CFloat32VolumeData3D *], vector[CFloat32ProjectionData3D *], EJobMode) nogil bool doBP(CProjector3D *, vector[CFloat32VolumeData3D *], vector[CFloat32ProjectionData3D *], EJobMode) nogil bool doFDK(CProjector3D *, CFloat32VolumeData3D *, CFloat32ProjectionData3D *, bool, SFilterConfig &, EJobMode) nogil cdef extern from *: CFloat32VolumeData3D * dynamic_cast_vol_mem "dynamic_cast" (CData3D * ) CFloat32ProjectionData3D * dynamic_cast_proj_mem "dynamic_cast" (CData3D * ) from . cimport PyProjector3DManager from .PyProjector3DManager cimport CProjector3DManager from . cimport PyData3DManager from .PyData3DManager cimport CData3DManager cdef CProjector3DManager * manProj = PyProjector3DManager.getSingletonPtr() cdef CData3DManager * man3d = PyData3DManager.getSingletonPtr() def do_composite(projector_id, vol_ids, proj_ids, mode, t): if mode != MODE_ADD and mode != MODE_SET: raise AstraError("Internal error: wrong composite mode") cdef EJobMode eMode = mode; cdef vector[CFloat32VolumeData3D *] vol cdef CFloat32VolumeData3D * pVolObject cdef CFloat32ProjectionData3D * pProjObject for v in vol_ids: pVolObject = dynamic_cast_vol_mem(man3d.get(v)) if pVolObject == NULL: raise AstraError("Data object not found") if not pVolObject.isInitialized(): raise AstraError("Data object not initialized properly") vol.push_back(pVolObject) cdef vector[CFloat32ProjectionData3D *] proj for v in proj_ids: pProjObject = dynamic_cast_proj_mem(man3d.get(v)) if pProjObject == NULL: raise AstraError("Data object not found") if not pProjObject.isInitialized(): raise AstraError("Data object not initialized properly") proj.push_back(pProjObject) cdef CCompositeGeometryManager m cdef CProjector3D * projector = manProj.get(projector_id) # may be NULL cdef bool ret = True if t == "FP": with nogil: ret = m.doFP(projector, vol, proj, eMode) if not ret: raise AstraError("Failed to perform FP", append_log=True) elif t == "BP": with nogil: ret = m.doBP(projector, vol, proj, eMode) if not ret: raise AstraError("Failed to perform BP", append_log=True) else: raise AstraError("Internal error: wrong composite op type") def do_composite_FP(projector_id, vol_ids, proj_ids): do_composite(projector_id, vol_ids, proj_ids, MODE_SET, "FP") def do_composite_BP(projector_id, vol_ids, proj_ids): do_composite(projector_id, vol_ids, proj_ids, MODE_SET, "BP") def accumulate_FP(projector_id, vol_id, proj_id): do_composite(projector_id, [vol_id], [proj_id], MODE_ADD, "FP") def accumulate_BP(projector_id, vol_id, proj_id): do_composite(projector_id, [vol_id], [proj_id], MODE_ADD, "BP") def accumulate_FDK(projector_id, vol_id, proj_id): cdef CFloat32VolumeData3D * pVolObject cdef CFloat32ProjectionData3D * pProjObject pVolObject = dynamic_cast_vol_mem(man3d.get(vol_id)) if pVolObject == NULL: raise AstraError("Data object not found") if not pVolObject.isInitialized(): raise AstraError("Data object not initialized properly") pProjObject = dynamic_cast_proj_mem(man3d.get(proj_id)) if pProjObject == NULL: raise AstraError("Data object not found") if not pProjObject.isInitialized(): raise AstraError("Data object not initialized properly") cdef CCompositeGeometryManager m cdef CProjector3D * projector = manProj.get(projector_id) # may be NULL cdef SFilterConfig filterConfig filterConfig.m_eType = FILTER_RAMLAK cdef bool ret = True with nogil: ret = m.doFDK(projector, pVolObject, pProjObject, False, filterConfig, MODE_ADD) if not ret: raise AstraError("Failed to perform FDK", append_log=True) from . cimport utils from .utils cimport linkVolFromGeometry, linkProjFromGeometry def direct_FPBP3D(projector_id, vol, proj, mode, t): if mode != MODE_ADD and mode != MODE_SET: raise AstraError("Internal error: wrong composite mode") cdef EJobMode eMode = mode cdef CProjector3D * projector = manProj.get(projector_id) if projector == NULL: raise AstraError("Projector not found") cdef CFloat32VolumeData3D * pVol = linkVolFromGeometry(projector.getVolumeGeometry(), vol) cdef CFloat32ProjectionData3D * pProj = linkProjFromGeometry(projector.getProjectionGeometry(), proj) cdef vector[CFloat32VolumeData3D *] vols cdef vector[CFloat32ProjectionData3D *] projs vols.push_back(pVol) projs.push_back(pProj) cdef CCompositeGeometryManager m cdef bool ret = True try: if t == "FP": with nogil: ret = m.doFP(projector, vols, projs, eMode) if not ret: AstraError("Failed to perform FP", append_log=True) elif t == "BP": with nogil: ret = m.doBP(projector, vols, projs, eMode) if not ret: AstraError("Failed to perform BP", append_log=True) else: AstraError("Internal error: wrong op type") finally: del pVol del pProj def direct_FP3D(projector_id, vol, proj): """Perform a 3D forward projection with pre-allocated input/output. :param projector_id: A 3D projector object handle :type datatype: :class:`int` :param vol: The input data, as either a numpy array, or a GPULink object :type datatype: :class:`numpy.ndarray` or :class:`astra.data3d.GPULink` :param proj: The pre-allocated output data, either numpy array or GPULink :type datatype: :class:`numpy.ndarray` or :class:`astra.data3d.GPULink` """ direct_FPBP3D(projector_id, vol, proj, MODE_SET, "FP") def direct_BP3D(projector_id, vol, proj): """Perform a 3D back projection with pre-allocated input/output. :param projector_id: A 3D projector object handle :type datatype: :class:`int` :param vol: The pre-allocated output data, as either a numpy array, or a GPULink object :type datatype: :class:`numpy.ndarray` or :class:`astra.data3d.GPULink` :param proj: The input data, either numpy array or GPULink :type datatype: :class:`numpy.ndarray` or :class:`astra.data3d.GPULink` """ direct_FPBP3D(projector_id, vol, proj, MODE_SET, "BP") def getProjectedBBox(geometry, minx, maxx, miny, maxy, minz, maxz): cdef unique_ptr[CProjectionGeometry3D] ppGeometry cdef double minu=0., maxu=0., minv=0., maxv=0. ppGeometry = createProjectionGeometry3D(geometry) ppGeometry.get().getProjectedBBox(minx, maxx, miny, maxy, minz, maxz, minu, maxu, minv, maxv) return (minv, maxv) def projectPoint(geometry, x, y, z, angle): cdef unique_ptr[CProjectionGeometry3D] ppGeometry cdef double u=0., v=0. ppGeometry = createProjectionGeometry3D(geometry) ppGeometry.get().projectPoint(x, y, z, angle, u, v) return (u, v) astra-toolbox-2.3.0/python/astra/extrautils.pyx000066400000000000000000000026461475635207100217350ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # distutils: language = c++ def clipCircle(img): cdef int i,j cdef double x2,y2,mid,bnd cdef long sz,sz2 sz = img.shape[0] sz2 = sz*sz bnd = sz2/4. mid = (sz-1.)/2. nDel=0 for i in range(sz): for j in range(sz): x2 = (i-mid)*(i-mid) y2 = (j-mid)*(j-mid) if x2+y2>bnd: img[i,j]=0 nDel=nDel+1 return nDel astra-toolbox-2.3.0/python/astra/functions.py000066400000000000000000000250601475635207100213440ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- """Additional functions for PyAstraToolbox. .. moduleauthor:: Daniel M. Pelt """ from copy import deepcopy from . import creators as ac import numpy as np from . import data2d from . import data3d from . import projector from . import algorithm from . import pythonutils from .log import AstraError def clear(): """Clears all used memory of the ASTRA Toolbox. .. note:: This is irreversible. """ data2d.clear() data3d.clear() projector.clear() algorithm.clear() def data_op(op, data, scalar, gpu_core, mask=None): """Perform data operation on data. :param op: Operation to perform. :param data: Data to perform operation on. :param scalar: Scalar argument to data operation. :param gpu_core: GPU core to perform operation on. :param mask: Optional mask. """ cfg = ac.astra_dict('DataOperation_CUDA') cfg['Operation'] = op cfg['Scalar'] = scalar cfg['DataId'] = data if not mask == None: cfg['MaskId'] = mask cfg['option']['GPUindex'] = gpu_core alg_id = algorithm.create(cfg) algorithm.run(alg_id) algorithm.delete(alg_id) def add_noise_to_sino(sinogram_in, I0, seed=None): """Adds Poisson noise to a sinogram. :param sinogram_in: Sinogram to add noise to. :type sinogram_in: :class:`numpy.ndarray` :param I0: Background intensity. Lower values lead to higher noise. :type I0: :class:`float` :returns: :class:`numpy.ndarray` -- the sinogram with added noise. """ if not seed==None: curstate = np.random.get_state() np.random.seed(seed) if isinstance(sinogram_in, np.ndarray): sinogramRaw = sinogram_in else: sinogramRaw = data2d.get(sinogram_in) max_sinogramRaw = sinogramRaw.max() sinogramRawScaled = sinogramRaw / max_sinogramRaw # to detector count sinogramCT = I0 * np.exp(-sinogramRawScaled) # add poison noise sinogramCT_C = np.zeros_like(sinogramCT) for i in range(sinogramCT_C.shape[0]): for j in range(sinogramCT_C.shape[1]): sinogramCT_C[i, j] = np.random.poisson(sinogramCT[i, j]) # to density sinogramCT_D = sinogramCT_C / I0 sinogram_out = -max_sinogramRaw * np.log(sinogramCT_D) if not isinstance(sinogram_in, np.ndarray): data2d.store(sinogram_in, sinogram_out) if not seed==None: np.random.set_state(curstate) return sinogram_out def move_vol_geom(geom, pos, is_relative=False): """Moves center of volume geometry to new position. :param geom: Input volume geometry :type geom: :class:`dict` :param pos: Tuple (x,y[,z]) for new position, with the center of the image at (0,0[,0]) :type pos: :class:`tuple` :param is_relative: Whether new position is relative to the old position :type is_relative: :class:`bool` :returns: :class:`dict` -- Volume geometry with the new center """ ret_geom = geom.copy() ret_geom['option'] = geom['option'].copy() if not is_relative: ret_geom['option']['WindowMinX'] = -geom['GridColCount']/2. ret_geom['option']['WindowMaxX'] = geom['GridColCount']/2. ret_geom['option']['WindowMinY'] = -geom['GridRowCount']/2. ret_geom['option']['WindowMaxY'] = geom['GridRowCount']/2. if len(pos)>2: ret_geom['option']['WindowMinZ'] = -geom['GridSliceCount']/2. ret_geom['option']['WindowMaxZ'] = geom['GridSliceCount']/2. ret_geom['option']['WindowMinX'] += pos[0] ret_geom['option']['WindowMaxX'] += pos[0] ret_geom['option']['WindowMinY'] += pos[1] ret_geom['option']['WindowMaxY'] += pos[1] if len(pos)>2: ret_geom['option']['WindowMinZ'] += pos[2] ret_geom['option']['WindowMaxZ'] += pos[2] return ret_geom def geom_size(geom, dim=None): """Returns the size of a volume or sinogram, based on the projection or volume geometry. :param geom: Geometry to calculate size from :type geometry: :class:`dict` :param dim: Optional axis index to return :type dim: :class:`int` """ return pythonutils.geom_size(geom,dim) def geom_2vec(proj_geom): """Returns a vector-based projection geometry from a basic projection geometry. :param proj_geom: Projection geometry to convert :type proj_geom: :class:`dict` """ if proj_geom['type'] == 'parallel': angles = proj_geom['ProjectionAngles'] vectors = np.zeros((len(angles), 6)) for i in range(len(angles)): # source vectors[i, 0] = np.sin(angles[i]) vectors[i, 1] = -np.cos(angles[i]) # center of detector vectors[i, 2] = 0 vectors[i, 3] = 0 # vector from detector pixel 0 to 1 vectors[i, 4] = np.cos(angles[i]) * proj_geom['DetectorWidth'] vectors[i, 5] = np.sin(angles[i]) * proj_geom['DetectorWidth'] proj_geom_out = ac.create_proj_geom( 'parallel_vec', proj_geom['DetectorCount'], vectors) elif proj_geom['type'] == 'fanflat': angles = proj_geom['ProjectionAngles'] vectors = np.zeros((len(angles), 6)) for i in range(len(angles)): # source vectors[i, 0] = np.sin(angles[i]) * proj_geom['DistanceOriginSource'] vectors[i, 1] = -np.cos(angles[i]) * proj_geom['DistanceOriginSource'] # center of detector vectors[i, 2] = -np.sin(angles[i]) * proj_geom['DistanceOriginDetector'] vectors[i, 3] = np.cos(angles[i]) * proj_geom['DistanceOriginDetector'] # vector from detector pixel 0 to 1 vectors[i, 4] = np.cos(angles[i]) * proj_geom['DetectorWidth'] vectors[i, 5] = np.sin(angles[i]) * proj_geom['DetectorWidth'] proj_geom_out = ac.create_proj_geom( 'fanflat_vec', proj_geom['DetectorCount'], vectors) elif proj_geom['type'] == 'cone': angles = proj_geom['ProjectionAngles'] vectors = np.zeros((len(angles), 12)) for i in range(len(angles)): # source vectors[i, 0] = np.sin(angles[i]) * proj_geom['DistanceOriginSource'] vectors[i, 1] = -np.cos(angles[i]) * proj_geom['DistanceOriginSource'] vectors[i, 2] = 0 # center of detector vectors[i, 3] = -np.sin(angles[i]) * proj_geom['DistanceOriginDetector'] vectors[i, 4] = np.cos(angles[i]) * proj_geom['DistanceOriginDetector'] vectors[i, 5] = 0 # vector from detector pixel (0,0) to (0,1) vectors[i, 6] = np.cos(angles[i]) * proj_geom['DetectorSpacingX'] vectors[i, 7] = np.sin(angles[i]) * proj_geom['DetectorSpacingX'] vectors[i, 8] = 0 # vector from detector pixel (0,0) to (1,0) vectors[i, 9] = 0 vectors[i, 10] = 0 vectors[i, 11] = proj_geom['DetectorSpacingY'] proj_geom_out = ac.create_proj_geom( 'cone_vec', proj_geom['DetectorRowCount'], proj_geom['DetectorColCount'], vectors) # PARALLEL elif proj_geom['type'] == 'parallel3d': angles = proj_geom['ProjectionAngles'] vectors = np.zeros((len(angles), 12)) for i in range(len(angles)): # ray direction vectors[i, 0] = np.sin(angles[i]) vectors[i, 1] = -np.cos(angles[i]) vectors[i, 2] = 0 # center of detector vectors[i, 3] = 0 vectors[i, 4] = 0 vectors[i, 5] = 0 # vector from detector pixel (0,0) to (0,1) vectors[i, 6] = np.cos(angles[i]) * proj_geom['DetectorSpacingX'] vectors[i, 7] = np.sin(angles[i]) * proj_geom['DetectorSpacingX'] vectors[i, 8] = 0 # vector from detector pixel (0,0) to (1,0) vectors[i, 9] = 0 vectors[i, 10] = 0 vectors[i, 11] = proj_geom['DetectorSpacingY'] proj_geom_out = ac.create_proj_geom( 'parallel3d_vec', proj_geom['DetectorRowCount'], proj_geom['DetectorColCount'], vectors) elif proj_geom['type'] in ['parallel_vec', 'fanflat_vec', 'parallel3d_vec', 'cone_vec']: return deepcopy(proj_geom) else: raise AstraError('No suitable vector geometry found for type: ' + proj_geom['type']) return proj_geom_out def geom_postalignment(proj_geom, factor): """Apply a postalignment to a projection geometry. Can be used to model the rotation axis offset. For 2D geometries, the argument factor is a single float specifying the distance to shift the detector (measured in detector pixels). For 3D geometries, factor can be a pair of floats specifying the horizontal resp. vertical distances to shift the detector. If only a single float is specified, this is treated as a horizontal shift. :param proj_geom: input projection geometry :type proj_geom: :class:`dict` :param factor: number of pixels to shift the detector :type factor: :class:`float` """ proj_geom = geom_2vec(proj_geom) if proj_geom['type'] == 'parallel_vec' or proj_geom['type'] == 'fanflat_vec': V = proj_geom['Vectors'] V[:,2:4] = V[:,2:4] + factor * V[:,4:6] elif proj_geom['type'] == 'parallel3d_vec' or proj_geom['type'] == 'cone_vec': if isinstance(factor, (float, int)): factor = factor, 0 V = proj_geom['Vectors'] V[:,3:6] = V[:,3:6] + factor[0] * V[:,6:9] if len(factor) > 1: # Accommodate factor = (number,) semantics V[:,3:6] = V[:,3:6] + factor[1] * V[:,9:12] else: raise AstraError(proj_geom['type'] + 'geometry is not suitable for postalignment') return proj_geom astra-toolbox-2.3.0/python/astra/log.py000066400000000000000000000123701475635207100201150ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from . import log_c as l import inspect def debug(message): """Log a debug message. :param message: Message to log. :type message: :class:`string` """ prev_f = inspect.getframeinfo(inspect.currentframe().f_back) l.log_debug(prev_f.filename,prev_f.lineno,message) def info(message): """Log an info message. :param message: Message to log. :type message: :class:`string` """ prev_f = inspect.getframeinfo(inspect.currentframe().f_back) l.log_info(prev_f.filename,prev_f.lineno,message) def warn(message): """Log a warning message. :param message: Message to log. :type message: :class:`string` """ prev_f = inspect.getframeinfo(inspect.currentframe().f_back) l.log_warn(prev_f.filename,prev_f.lineno,message) def error(message): """Log an error message. :param message: Message to log. :type message: :class:`string` """ prev_f = inspect.getframeinfo(inspect.currentframe().f_back) l.log_error(prev_f.filename,prev_f.lineno,message) def enable(): """Enable logging to screen and file.""" l.log_enable() def enableScreen(): """Enable logging to screen.""" l.log_enableScreen() def enableFile(): """Enable logging to file (note that a file has to be set).""" l.log_enableFile() def disable(): """Disable all logging.""" l.log_disable() def disableScreen(): """Disable logging to screen.""" l.log_disableScreen() def disableFile(): """Disable logging to file.""" l.log_disableFile() def setFormatFile(fmt): """Set the format string for log messages. Here are the substitutions you may use: %f: Source file name generating the log call. %n: Source line number where the log call was made. %m: The message text sent to the logger (after printf formatting). %d: The current date, formatted using the logger's date format. %t: The current time, formatted using the logger's time format. %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR"). %%: A literal percent sign. The default format string is "%d %t %f(%n): %l: %m\n". :param fmt: Format to use, end with "\n". :type fmt: :class:`string` """ l.log_setFormatFile(fmt) def setFormatScreen(fmt): """Set the format string for log messages. Here are the substitutions you may use: %f: Source file name generating the log call. %n: Source line number where the log call was made. %m: The message text sent to the logger (after printf formatting). %d: The current date, formatted using the logger's date format. %t: The current time, formatted using the logger's time format. %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR"). %%: A literal percent sign. The default format string is "%d %t %f(%n): %l: %m\n". :param fmt: Format to use, end with "\n". :type fmt: :class:`string` """ l.log_setFormatScreen(fmt) STDOUT=1 STDERR=2 DEBUG=0 INFO=1 WARN=2 ERROR=3 def setOutputScreen(fd, level): """Set which screen to output to, and which level to use. :param fd: File descriptor of output screen (STDOUT or STDERR). :type fd: :class:`int` :param level: Logging level to use (DEBUG, INFO, WARN, or ERROR). :type level: :class:`int` """ l.log_setOutputScreen(fd, level) def setOutputFile(filename, level): """Set which file to output to, and which level to use. :param filename: File name of output file. :type filename: :class:`string` :param level: Logging level to use (DEBUG, INFO, WARN, or ERROR). :type level: :class:`int` """ l.log_setOutputFile(filename, level) class AstraError(Exception): def __init__(self, message='', append_log=False): """Error in Astra Toolbox operation. :param message: Error message, defaults to empty string. :type message: str, optional :param append_log: Append last error message recorded by the Astra shared library to the Python error message, defaults to False. :type append_log: bool, optional """ if append_log: last_err_msg = l.log_getLastErrMsg() if last_err_msg: message += ': ' + last_err_msg super(AstraError, self).__init__(message) astra-toolbox-2.3.0/python/astra/log_c.pyx000066400000000000000000000063461475635207100206150ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # # distutils: language = c++ # distutils: libraries = astra from libcpp.string cimport string cdef extern from "astra/Logging.h" namespace "astra": cdef enum log_level: LOG_DEBUG LOG_INFO LOG_WARN LOG_ERROR cdef extern from "astra/Logging.h" namespace "astra::CLogger": void debug(const char *sfile, int sline, const char *fmt, ...) void info(const char *sfile, int sline, const char *fmt, ...) void warn(const char *sfile, int sline, const char *fmt, ...) void error(const char *sfile, int sline, const char *fmt, ...) void setOutputScreen(int fd, log_level m_eLevel) void setOutputFile(const char *filename, log_level m_eLevel) void enable() void enableScreen() void enableFile() void disable() void disableScreen() void disableFile() void setFormatFile(const char *fmt) void setFormatScreen(const char *fmt) string getLastErrMsg() def log_debug(sfile, sline, message): sfile = sfile.encode('ascii') message = message.encode('ascii') debug(sfile,sline,"%s",message) def log_info(sfile, sline, message): sfile = sfile.encode('ascii') message = message.encode('ascii') info(sfile,sline,"%s",message) def log_warn(sfile, sline, message): sfile = sfile.encode('ascii') message = message.encode('ascii') warn(sfile,sline,"%s",message) def log_error(sfile, sline, message): sfile = sfile.encode('ascii') message = message.encode('ascii') error(sfile,sline,"%s",message) def log_enable(): enable() def log_enableScreen(): enableScreen() def log_enableFile(): enableFile() def log_disable(): disable() def log_disableScreen(): disableScreen() def log_disableFile(): disableFile() def log_setFormatFile(fmt): fmt = fmt.encode('ascii') setFormatFile(fmt) def log_setFormatScreen(fmt): fmt = fmt.encode('ascii') setFormatScreen(fmt) enumList = [LOG_DEBUG,LOG_INFO,LOG_WARN,LOG_ERROR] def log_setOutputScreen(fd, level): setOutputScreen(fd, enumList[level]) def log_setOutputFile(filename, level): filename = filename.encode('ascii') setOutputFile(filename, enumList[level]) def log_getLastErrMsg(): return getLastErrMsg().decode('UTF-8') astra-toolbox-2.3.0/python/astra/matlab.py000066400000000000000000000061171475635207100205760ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- """This module implements a MATLAB-like interface to the ASTRA Toolbox. Note that all functions are called with a :class:`string` as the first argument, specifying the operation to perform. This un-pythonic way is used to make transitioning from MATLAB code to Python code easier, as the MATLAB interface uses the same type of method calling. After an initial ``import astra``, these functions can be accessed in the ``astra.m`` module. """ from . import astra_c from . import data2d_c from . import data3d_c from . import projector_c from . import algorithm_c from . import matrix_c def astra(command, *args): """MATLAB-like interface to the :mod:`astra.astra` module For example: ``astra.m.astra('use_cuda')`` -- Check if CUDA is enabled. """ return getattr(astra_c, command)(*args) def data2d(command, *args): """MATLAB-like interface to the :mod:`astra.data2d` module For example: ``astra.m.data2d('create',type,geometry,data)`` -- Create a 2D object. """ return getattr(data2d_c, command)(*args) def data3d(command, *args): """MATLAB-like interface to the :mod:`astra.data3d` module For example: ``astra.m.data3d('get',i)`` -- Get 3D object data. """ return getattr(data3d_c, command)(*args) def projector(command, *args): """MATLAB-like interface to the :mod:`astra.projector` module For example: ``astra.m.projector('volume_geometry',i)`` -- Get volume geometry. """ return getattr(projector_c, command)(*args) def matrix(command, *args): """MATLAB-like interface to the :mod:`astra.matrix` module For example: ``astra.m.matrix('delete',i)`` -- Delete a matrix. """ return getattr(matrix_c, command)(*args) def algorithm(command, *args): """MATLAB-like interface to the :mod:`astra.algorithm` module For example: ``astra.m.algorithm('run',i,1000)`` -- Run an algorithm with 1000 iterations. """ if command == 'iterate': command = 'run' return getattr(algorithm_c, command)(*args) astra-toolbox-2.3.0/python/astra/matrix.py000066400000000000000000000044601475635207100206410ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from . import matrix_c as m def delete(ids): """Delete a matrix object. :param ids: ID or list of ID's to delete. :type ids: :class:`int` or :class:`list` """ return m.delete(ids) def clear(): """Clear all matrix objects.""" return m.clear() def create(data): """Create matrix object with data. :param data: Data to fill the created object with. :type data: :class:`scipy.sparse.csr_matrix` :returns: :class:`int` -- the ID of the constructed object. """ return m.create(data) def store(i,data): """Fill existing matrix object with data. :param i: ID of object to fill. :type i: :class:`int` :param data: Data to fill the object with. :type data: :class:`scipy.sparse.csr_matrix` """ return m.store(i,data) def get_size(i): """Get matrix dimensions. :param i: ID of object. :type i: :class:`int` :returns: :class:`tuple` -- matrix dimensions. """ return m.get_size(i) def get(i): """Get a matrix object. :param i: ID of object to get. :type i: :class:`int` :returns: :class:`scipy.sparse.csr_matrix` -- The object data. """ return m.get(i) def info(): """Print info on matrix objects in memory.""" return m.info() astra-toolbox-2.3.0/python/astra/matrix_c.pyx000066400000000000000000000074131475635207100213340ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # # distutils: language = c++ # distutils: libraries = astra from __future__ import print_function import numpy as np import scipy.sparse as ss from libcpp cimport bool from . cimport PyMatrixManager from .PyMatrixManager cimport CMatrixManager from .PyIncludes cimport * from .utils import wrap_from_bytes from .log import AstraError cdef CMatrixManager * manM = PyMatrixManager.getSingletonPtr() def delete(ids): try: for i in ids: manM.remove(i) except TypeError: manM.remove(ids) def clear(): manM.clear() cdef int csr_matrix_to_astra(data,CSparseMatrix *mat) except -1: if isinstance(data,ss.csr_matrix): csrD = data else: csrD = data.tocsr() if not mat.isInitialized(): raise AstraError("Provided matrix object was not initialized") if csrD.nnz > mat.m_lSize or csrD.shape[0] > mat.m_iHeight: raise AstraError("Matrix too large to store in this object") for i in range(len(csrD.indptr)): mat.m_plRowStarts[i] = csrD.indptr[i] for i in range(csrD.nnz): mat.m_piColIndices[i] = csrD.indices[i] mat.m_pfValues[i] = csrD.data[i] cdef astra_to_csr_matrix(CSparseMatrix *mat): indptr = np.zeros(mat.m_iHeight+1,dtype=int) indices = np.zeros(mat.m_plRowStarts[mat.m_iHeight],dtype=int) data = np.zeros(mat.m_plRowStarts[mat.m_iHeight]) for i in range(mat.m_iHeight+1): indptr[i] = mat.m_plRowStarts[i] for i in range(mat.m_plRowStarts[mat.m_iHeight]): indices[i] = mat.m_piColIndices[i] data[i] = mat.m_pfValues[i] return ss.csr_matrix((data,indices,indptr),shape=(mat.m_iHeight,mat.m_iWidth)) def create(data): cdef CSparseMatrix* pMatrix pMatrix = new CSparseMatrix(data.shape[0], data.shape[1], data.nnz) if not pMatrix.isInitialized(): del pMatrix raise AstraError("Couldn't initialize data object", append_log=True) try: csr_matrix_to_astra(data,pMatrix) except: del pMatrix raise AstraError("Failed to create data object") return manM.store(pMatrix) cdef CSparseMatrix * getObject(i) except NULL: cdef CSparseMatrix * pDataObject = manM.get(i) if pDataObject == NULL: raise AstraError("Data object not found") if not pDataObject.isInitialized(): raise AstraError("Data object not initialized properly") return pDataObject def store(i,data): cdef CSparseMatrix * pDataObject = getObject(i) csr_matrix_to_astra(data,pDataObject) def get_size(i): cdef CSparseMatrix * pDataObject = getObject(i) return (pDataObject.m_iHeight,pDataObject.m_iWidth) def get(i): cdef CSparseMatrix * pDataObject = getObject(i) return astra_to_csr_matrix(pDataObject) def info(): print(wrap_from_bytes(manM.info())) astra-toolbox-2.3.0/python/astra/optomo.py000066400000000000000000000204501475635207100206470ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from . import data2d from . import data3d from . import projector from . import projector3d from . import creators from . import algorithm from . import functions import numpy as np from functools import reduce import operator import scipy.sparse.linalg class OpTomo(scipy.sparse.linalg.LinearOperator): """Object that imitates a projection matrix with a given projector. This object can do forward projection by using the ``*`` operator:: W = astra.OpTomo(proj_id) fp = W*image bp = W.T*sinogram It can also be used in minimization methods of the :mod:`scipy.sparse.linalg` module:: W = astra.OpTomo(proj_id) output = scipy.sparse.linalg.lsqr(W,sinogram) :param proj_id: ID to a projector. :type proj_id: :class:`int` """ def __init__(self,proj_id): self.dtype = np.float32 try: self.vg = projector.volume_geometry(proj_id) self.pg = projector.projection_geometry(proj_id) self.data_mod = data2d self.appendString = "" if projector.is_cuda(proj_id): self.appendString += "_CUDA" except Exception: self.vg = projector3d.volume_geometry(proj_id) self.pg = projector3d.projection_geometry(proj_id) self.data_mod = data3d self.appendString = "3D" if projector3d.is_cuda(proj_id): self.appendString += "_CUDA" self.vshape = functions.geom_size(self.vg) self.vsize = reduce(operator.mul,self.vshape) self.sshape = functions.geom_size(self.pg) self.ssize = reduce(operator.mul,self.sshape) self.shape = (self.ssize, self.vsize) self.proj_id = proj_id self.transposeOpTomo = OpTomoTranspose(self) try: self.T = self.transposeOpTomo except AttributeError: # Scipy >= 0.16 defines self.T using self._transpose() pass def _transpose(self): return self.transposeOpTomo # real operator _adjoint = _transpose def __checkArray(self, arr, shp): if len(arr.shape)==1: arr = arr.reshape(shp) if arr.dtype != np.float32: arr = arr.astype(np.float32) if arr.flags['C_CONTIGUOUS']==False: arr = np.ascontiguousarray(arr) return arr def _matvec(self,v): """Implements the forward operator. :param v: Volume to forward project. :type v: :class:`numpy.ndarray` """ return self.FP(v, out=None).ravel() def rmatvec(self,s): """Implements the transpose operator. :param s: The projection data. :type s: :class:`numpy.ndarray` """ return self.BP(s, out=None).ravel() def __mul__(self,v): """Provides easy forward operator by *. :param v: Volume to forward project. :type v: :class:`numpy.ndarray` """ # Catch the case of a forward projection of a 2D/3D image if isinstance(v, np.ndarray) and v.shape==self.vshape: return self._matvec(v) return scipy.sparse.linalg.LinearOperator.__mul__(self, v) def reconstruct(self, method, s, iterations=1, extraOptions = None): """Reconstruct an object. :param method: Method to use for reconstruction. :type method: :class:`string` :param s: The projection data. :type s: :class:`numpy.ndarray` :param iterations: Number of iterations to use. :type iterations: :class:`int` :param extraOptions: Extra options to use during reconstruction (i.e. for cfg['option']). :type extraOptions: :class:`dict` """ if extraOptions is None: extraOptions={} s = self.__checkArray(s, self.sshape) sid = self.data_mod.link('-sino',self.pg,s) v = np.zeros(self.vshape,dtype=np.float32) vid = self.data_mod.link('-vol',self.vg,v) cfg = creators.astra_dict(method) cfg['ProjectionDataId'] = sid cfg['ReconstructionDataId'] = vid cfg['ProjectorId'] = self.proj_id cfg['option'] = extraOptions alg_id = algorithm.create(cfg) algorithm.run(alg_id,iterations) algorithm.delete(alg_id) self.data_mod.delete([vid,sid]) return v def FP(self,v,out=None): """Perform forward projection. Output must have the right 2D/3D shape. Input may also be flattened. Output must also be contiguous and float32. This isn't required for the input, but it is more efficient if it is. :param v: Volume to forward project. :type v: :class:`numpy.ndarray` :param out: Array to store result in. :type out: :class:`numpy.ndarray` """ v = self.__checkArray(v, self.vshape) vid = self.data_mod.link('-vol',self.vg,v) if out is None: out = np.zeros(self.sshape,dtype=np.float32) sid = self.data_mod.link('-sino',self.pg,out) cfg = creators.astra_dict('FP'+self.appendString) cfg['ProjectionDataId'] = sid cfg['VolumeDataId'] = vid cfg['ProjectorId'] = self.proj_id fp_id = algorithm.create(cfg) algorithm.run(fp_id) algorithm.delete(fp_id) self.data_mod.delete([vid,sid]) return out def BP(self,s,out=None): """Perform backprojection. Output must have the right 2D/3D shape. Input may also be flattened. Output must also be contiguous and float32. This isn't required for the input, but it is more efficient if it is. :param : The projection data. :type s: :class:`numpy.ndarray` :param out: Array to store result in. :type out: :class:`numpy.ndarray` """ s = self.__checkArray(s, self.sshape) sid = self.data_mod.link('-sino',self.pg,s) if out is None: out = np.zeros(self.vshape,dtype=np.float32) vid = self.data_mod.link('-vol',self.vg,out) cfg = creators.astra_dict('BP'+self.appendString) cfg['ProjectionDataId'] = sid cfg['ReconstructionDataId'] = vid cfg['ProjectorId'] = self.proj_id bp_id = algorithm.create(cfg) algorithm.run(bp_id) algorithm.delete(bp_id) self.data_mod.delete([vid,sid]) return out class OpTomoTranspose(scipy.sparse.linalg.LinearOperator): """This object provides the transpose operation (``.T``) of the OpTomo object. Do not use directly, since it can be accessed as member ``.T`` of an :class:`OpTomo` object. """ def __init__(self,parent): self.parent = parent self.dtype = np.float32 self.shape = (parent.shape[1], parent.shape[0]) try: self.T = self.parent except AttributeError: # Scipy >= 0.16 defines self.T using self._transpose() pass def _matvec(self, s): return self.parent.rmatvec(s) def rmatvec(self, v): return self.parent.matvec(v) def _transpose(self): return self.parent # real operator _adjoint = _transpose def __mul__(self,s): # Catch the case of a backprojection of 2D/3D data if isinstance(s, np.ndarray) and s.shape==self.parent.sshape: return self._matvec(s) return scipy.sparse.linalg.LinearOperator.__mul__(self, s) astra-toolbox-2.3.0/python/astra/plugin.py000066400000000000000000000104011475635207100206230ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from . import plugin_c as p from . import log from . import data2d from . import data2d_c from . import data3d from . import projector from .log import AstraError import inspect import traceback class base(object): def astra_init(self, cfg): try: args, varargs, varkw, defaults = inspect.getargspec(self.initialize) except AttributeError: fullargspec = inspect.getfullargspec(self.initialize) args, defaults = fullargspec.args, fullargspec.defaults if not defaults is None: nopt = len(defaults) else: nopt = 0 if nopt>0: req = args[2:-nopt] opt = args[-nopt:] else: req = args[2:] opt = [] try: optDict = cfg['options'] except KeyError: optDict = {} cfgKeys = set(optDict.keys()) reqKeys = set(req) optKeys = set(opt) if not reqKeys.issubset(cfgKeys): for key in reqKeys.difference(cfgKeys): log.error("Required option '" + key + "' for plugin '" + self.__class__.__name__ + "' not specified") raise AstraError("Missing required options") if not cfgKeys.issubset(reqKeys | optKeys): log.warn(self.__class__.__name__ + ": unused configuration option: " + str(list(cfgKeys.difference(reqKeys | optKeys)))) args = [optDict[k] for k in req] kwargs = dict((k,optDict[k]) for k in opt if k in optDict) self.initialize(cfg, *args, **kwargs) class ReconstructionAlgorithm2D(base): def astra_init(self, cfg): self.pid = cfg['ProjectorId'] self.s = data2d.get_shared(cfg['ProjectionDataId']) self.v = data2d.get_shared(cfg['ReconstructionDataId']) self.vg = projector.volume_geometry(self.pid) self.pg = projector.projection_geometry(self.pid) if not data2d_c.check_compatible(cfg['ProjectionDataId'], self.pid): raise AstraError("Projection data and projector not compatible") if not data2d_c.check_compatible(cfg['ReconstructionDataId'], self.pid): raise AstraError("Reconstruction data and projector not compatible") super(ReconstructionAlgorithm2D,self).astra_init(cfg) class ReconstructionAlgorithm3D(base): def astra_init(self, cfg): self.pid = cfg['ProjectorId'] self.s = data3d.get_shared(cfg['ProjectionDataId']) self.v = data3d.get_shared(cfg['ReconstructionDataId']) self.vg = data3d.get_geometry(cfg['ReconstructionDataId']) self.pg = data3d.get_geometry(cfg['ProjectionDataId']) super(ReconstructionAlgorithm3D,self).astra_init(cfg) def register(className): """Register plugin with ASTRA. :param className: Class name or class object to register :type className: :class:`str` or :class:`class` """ p.register(className) def get_registered(): """Get dictionary of registered plugins. :returns: :class:`dict` -- Registered plugins. """ return p.get_registered() def get_help(name): """Get help for registered plugin. :param name: Plugin name to get help for :type name: :class:`str` :returns: :class:`str` -- Help string (docstring). """ return p.get_help(name) astra-toolbox-2.3.0/python/astra/plugin_c.pyx000066400000000000000000000051571475635207100213310ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # # distutils: language = c++ # distutils: libraries = astra import inspect from libcpp.string cimport string from libcpp cimport bool cdef CPythonPluginAlgorithmFactory *fact = getSingletonPtr() from .utils import wrap_from_bytes, wrap_to_bytes cdef extern from "src/PythonPluginAlgorithm.h" namespace "astra": cdef cppclass CPythonPluginAlgorithmFactory: bool registerPlugin(string className) bool registerPlugin(string name, string className) bool registerPluginClass(object className) bool registerPluginClass(string name, object className) object getRegistered() string getHelp(string &name) cdef extern from "src/PythonPluginAlgorithmFactory.h" namespace "astra::CPythonPluginAlgorithmFactory": cdef CPythonPluginAlgorithmFactory* getSingletonPtr() cdef extern from "astra/PluginAlgorithmFactory.h" namespace "astra::CPluginAlgorithmFactory": # NB: Using wrong pointer type here for convenience cdef void registerFactory(CPythonPluginAlgorithmFactory *) def register(className, name=None): if inspect.isclass(className): if name==None: fact.registerPluginClass(className) else: fact.registerPluginClass(wrap_to_bytes(name), className) else: if name==None: fact.registerPlugin(wrap_to_bytes(className)) else: fact.registerPlugin(wrap_to_bytes(name), wrap_to_bytes(className)) def get_registered(): return fact.getRegistered() def get_help(name): return wrap_from_bytes(fact.getHelp(wrap_to_bytes(name))) # Register python plugin factory with astra registerFactory(fact) astra-toolbox-2.3.0/python/astra/plugins/000077500000000000000000000000001475635207100204405ustar00rootroot00000000000000astra-toolbox-2.3.0/python/astra/plugins/__init__.py000066400000000000000000000020761475635207100225560ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from .sirt import SIRTPlugin from .cgls import CGLSPlugin astra-toolbox-2.3.0/python/astra/plugins/cgls.py000066400000000000000000000053721475635207100217510ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np class CGLSPlugin(astra.plugin.base): """CGLS.""" astra_name = "CGLS-PLUGIN" def initialize(self,cfg): self.W = astra.OpTomo(cfg['ProjectorId']) self.vid = cfg['ReconstructionDataId'] self.sid = cfg['ProjectionDataId'] try: v = astra.data2d.get_shared(self.vid) s = astra.data2d.get_shared(self.sid) self.data_mod = astra.data2d except Exception: v = astra.data3d.get_shared(self.vid) s = astra.data3d.get_shared(self.sid) self.data_mod = astra.data3d def run(self, its): v = self.data_mod.get_shared(self.vid) s = self.data_mod.get_shared(self.sid) z = np.zeros(v.shape, dtype=np.float32) p = np.zeros(v.shape, dtype=np.float32) r = np.zeros(s.shape, dtype=np.float32) w = np.zeros(s.shape, dtype=np.float32) W = self.W # r = s - W*v W.FP(v, out=w) r[:] = s r -= w # p = W'*r W.BP(r, out=p) # gamma = gamma = np.dot(p.ravel(), p.ravel()) for i in range(its): # w = W * p W.FP(p, out=w) # alpha = gamma / alpha = gamma / np.dot(w.ravel(), w.ravel()) # v += alpha * p z[:] = p z *= alpha v += z # r -= alpha * w w *= -alpha; r += w # z = W' * r W.BP(r, out=z) # beta = / gamma newgamma = np.dot(z.ravel(), z.ravel()) beta = newgamma / gamma # gamma = gamma = newgamma # p = z + beta * p p *= beta p += z astra-toolbox-2.3.0/python/astra/plugins/sirt.py000066400000000000000000000057431475635207100220040ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np class SIRTPlugin(astra.plugin.base): """SIRT. Options: 'Relaxation': relaxation factor (optional) 'MinConstraint': constrain values to at least this (optional) 'MaxConstraint': constrain values to at most this (optional) """ astra_name = "SIRT-PLUGIN" def initialize(self,cfg, Relaxation = 1, MinConstraint = None, MaxConstraint = None): self.W = astra.OpTomo(cfg['ProjectorId']) self.vid = cfg['ReconstructionDataId'] self.sid = cfg['ProjectionDataId'] self.min_constraint = MinConstraint self.max_constraint = MaxConstraint try: v = astra.data2d.get_shared(self.vid) s = astra.data2d.get_shared(self.sid) self.data_mod = astra.data2d except Exception: v = astra.data3d.get_shared(self.vid) s = astra.data3d.get_shared(self.sid) self.data_mod = astra.data3d self.R = self.W * np.ones(v.shape,dtype=np.float32).ravel(); self.R[self.R < 0.000001] = np.inf self.R = 1 / self.R self.R = self.R.reshape(s.shape) self.mrC = self.W.T * np.ones(s.shape,dtype=np.float32).ravel(); self.mrC[self.mrC < 0.000001] = np.inf self.mrC = -Relaxation / self.mrC self.mrC = self.mrC.reshape(v.shape) def run(self, its): v = self.data_mod.get_shared(self.vid) s = self.data_mod.get_shared(self.sid) tv = np.zeros(v.shape, dtype=np.float32) ts = np.zeros(s.shape, dtype=np.float32) W = self.W mrC = self.mrC R = self.R for i in range(its): W.FP(v,out=ts) ts -= s ts *= R # ts = R * (W*v - s) W.BP(ts,out=tv) tv *= mrC v += tv # v = v - rel * C * W' * ts if self.min_constraint is not None or self.max_constraint is not None: v.clip(min=self.min_constraint, max=self.max_constraint, out=v) astra-toolbox-2.3.0/python/astra/projector.py000066400000000000000000000053511475635207100213440ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from . import projector_c as p def create(config): """Create projector object. :param config: Projector options. :type config: :class:`dict` :returns: :class:`int` -- the ID of the constructed object. """ return p.create(config) def delete(ids): """Delete a projector object. :param ids: ID or list of ID's to delete. :type ids: :class:`int` or :class:`list` """ return p.delete(ids) def clear(): """Clear all projector objects.""" return p.clear() def info(): """Print info on projector objects in memory.""" return p.info() def projection_geometry(i): """Get projection geometry of a projector. :param i: ID of projector. :type i: :class:`int` :returns: :class:`dict` -- projection geometry """ return p.projection_geometry(i) def volume_geometry(i): """Get volume geometry of a projector. :param i: ID of projector. :type i: :class:`int` :returns: :class:`dict` -- volume geometry """ return p.volume_geometry(i) def weights_single_ray(i, projection_index, detector_index): return p.weights_single_ray(i, projection_index, detector_index) def weights_projection(i, projection_index): return p.weights_projection(i, projection_index) def splat(i, row, col): return p.splat(i, row, col) def is_cuda(i): """Check whether a projector is a CUDA projector. :param i: ID of projector. :type i: :class:`int` :returns: :class:`bool` -- True if the projector is a CUDA projector. """ return p.is_cuda(i) def matrix(i): """Get sparse matrix of a projector. :param i: ID of projector. :type i: :class:`int` :returns: :class:`int` -- ID of sparse matrix. """ return p.matrix(i) astra-toolbox-2.3.0/python/astra/projector3d.py000066400000000000000000000050451475635207100215730ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from . import projector3d_c as p def create(config): """Create projector object. :param config: Projector options. :type config: :class:`dict` :returns: :class:`int` -- the ID of the constructed object. """ return p.create(config) def delete(ids): """Delete a projector object. :param ids: ID or list of ID's to delete. :type ids: :class:`int` or :class:`list` """ return p.delete(ids) def clear(): """Clear all projector objects.""" return p.clear() def info(): """Print info on projector objects in memory.""" return p.info() def projection_geometry(i): """Get projection geometry of a projector. :param i: ID of projector. :type i: :class:`int` :returns: :class:`dict` -- projection geometry """ return p.projection_geometry(i) def volume_geometry(i): """Get volume geometry of a projector. :param i: ID of projector. :type i: :class:`int` :returns: :class:`dict` -- volume geometry """ return p.volume_geometry(i) def weights_single_ray(i, projection_index, detector_index): return p.weights_single_ray(i, projection_index, detector_index) def weights_projection(i, projection_index): return p.weights_projection(i, projection_index) def splat(i, row, col): return p.splat(i, row, col) def is_cuda(i): """Check whether a projector is a CUDA projector. :param i: ID of projector. :type i: :class:`int` :returns: :class:`bool` -- True if the projector is a CUDA projector. """ return p.is_cuda(i) astra-toolbox-2.3.0/python/astra/projector3d_c.pyx000066400000000000000000000072251475635207100222670ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # # distutils: language = c++ # distutils: libraries = astra from __future__ import print_function from .PyIncludes cimport * from . cimport utils from .utils import wrap_from_bytes from .log import AstraError from . cimport PyProjector3DFactory from .PyProjector3DFactory cimport CProjector3DFactory from . cimport PyProjector3DManager from .PyProjector3DManager cimport CProjector3DManager from . cimport PyXMLDocument from .PyXMLDocument cimport XMLDocument cdef CProjector3DManager * manProj = PyProjector3DManager.getSingletonPtr() include "config.pxi" IF HAVE_CUDA: cdef extern from *: CCudaProjector3D* dynamic_cast_cuda_projector "dynamic_cast" (CProjector3D*) def create(config): cdef XMLConfig * cfg = utils.dictToConfig(b'Projector3D', config) cdef CProjector3D * proj proj = PyProjector3DFactory.getSingletonPtr().create(cfg.self.getAttribute(b'type')) if proj == NULL: del cfg raise AstraError("Unknown Projector3D type") if not proj.initialize(cfg[0]): del cfg del proj raise AstraError("Unable to initialize Projector3D", append_log=True) del cfg return manProj.store(proj) def delete(ids): try: for i in ids: manProj.remove(i) except TypeError: manProj.remove(ids) def clear(): manProj.clear() def info(): print(wrap_from_bytes(manProj.info())) cdef CProjector3D * getObject(i) except NULL: cdef CProjector3D * proj = manProj.get(i) if proj == NULL: raise AstraError("Projector not found") if not proj.isInitialized(): raise AstraError("Projector not initialized") return proj def projection_geometry(i): cdef CProjector3D * proj = getObject(i) cdef Config * cfg = proj.getProjectionGeometry().getConfiguration() dct = utils.configToDict(cfg) del cfg return dct def volume_geometry(i): cdef CProjector3D * proj = getObject(i) cdef Config * cfg = proj.getVolumeGeometry().getConfiguration() dct = utils.configToDict(cfg) del cfg return dct def weights_single_ray(i, projection_index, detector_index): raise NotImplementedError("Not yet implemented") def weights_projection(i, projection_index): raise NotImplementedError("Not yet implemented") def splat(i, row, col): raise NotImplementedError("Not yet implemented") def is_cuda(i): cdef CProjector3D * proj = getObject(i) IF HAVE_CUDA==True: cdef CCudaProjector3D * cudaproj = NULL cudaproj = dynamic_cast_cuda_projector(proj) if cudaproj==NULL: return False else: return True ELSE: return False astra-toolbox-2.3.0/python/astra/projector_c.pyx000066400000000000000000000102021475635207100220250ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # # distutils: language = c++ # distutils: libraries = astra from __future__ import print_function from .PyIncludes cimport * from . cimport utils from .utils import wrap_from_bytes from .log import AstraError from . cimport PyProjector2DFactory from .PyProjector2DFactory cimport CProjector2DFactory from . cimport PyProjector2DManager from .PyProjector2DManager cimport CProjector2DManager from . cimport PyXMLDocument from .PyXMLDocument cimport XMLDocument from . cimport PyMatrixManager from .PyMatrixManager cimport CMatrixManager cdef CProjector2DManager * manProj = PyProjector2DManager.getSingletonPtr() cdef CMatrixManager * manM = PyMatrixManager.getSingletonPtr() include "config.pxi" IF HAVE_CUDA: cdef extern from *: CCudaProjector2D* dynamic_cast_cuda_projector "dynamic_cast" (CProjector2D*) def create(config): cdef XMLConfig * cfg = utils.dictToConfig(b'Projector2D', config) cdef CProjector2D * proj proj = PyProjector2DFactory.getSingletonPtr().create(cfg.self.getAttribute(b'type')) if proj == NULL: del cfg raise AstraError("Unknown Projector2D type") if not proj.initialize(cfg[0]): del cfg del proj raise AstraError("Unable to initialize Projector2D", append_log=True) del cfg return manProj.store(proj) def delete(ids): try: for i in ids: manProj.remove(i) except TypeError: manProj.remove(ids) def clear(): manProj.clear() def info(): print(wrap_from_bytes(manProj.info())) cdef CProjector2D * getObject(i) except NULL: cdef CProjector2D * proj = manProj.get(i) if proj == NULL: raise AstraError("Projector not found") if not proj.isInitialized(): raise AstraError("Projector not initialized") return proj def projection_geometry(i): cdef CProjector2D * proj = getObject(i) cdef Config * cfg = proj.getProjectionGeometry().getConfiguration() dct = utils.configToDict(cfg) del cfg return dct def volume_geometry(i): cdef CProjector2D * proj = getObject(i) cdef Config * cfg = proj.getVolumeGeometry().getConfiguration() dct = utils.configToDict(cfg) del cfg return dct def weights_single_ray(i, projection_index, detector_index): raise NotImplementedError("Not yet implemented") def weights_projection(i, projection_index): raise NotImplementedError("Not yet implemented") def splat(i, row, col): raise NotImplementedError("Not yet implemented") def is_cuda(i): cdef CProjector2D * proj = getObject(i) IF HAVE_CUDA==True: cdef CCudaProjector2D * cudaproj = NULL cudaproj = dynamic_cast_cuda_projector(proj) if cudaproj==NULL: return False else: return True ELSE: return False def matrix(i): cdef CProjector2D * proj = getObject(i) cdef CSparseMatrix * mat = proj.getMatrix() if mat == NULL: del mat raise AstraError("Data object not found") if not mat.isInitialized(): del mat raise AstraError("Data object not initialized properly") return manM.store(mat) astra-toolbox-2.3.0/python/astra/pythonutils.py000066400000000000000000000066541475635207100217460ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- """Additional purely Python functions for PyAstraToolbox. .. moduleauthor:: Daniel M. Pelt """ import numpy as np def geom_size(geom, dim=None): """Returns the size of a volume or sinogram, based on the projection or volume geometry. :param geom: Geometry to calculate size from :type geometry: :class:`dict` :param dim: Optional axis index to return :type dim: :class:`int` """ if 'GridSliceCount' in geom: # 3D Volume geometry? s = (geom['GridSliceCount'], geom[ 'GridRowCount'], geom['GridColCount']) elif 'GridColCount' in geom: # 2D Volume geometry? s = (geom['GridRowCount'], geom['GridColCount']) elif geom['type'] == 'parallel' or geom['type'] == 'fanflat': s = (len(geom['ProjectionAngles']), geom['DetectorCount']) elif geom['type'] == 'parallel3d' or geom['type'] == 'cone': s = (geom['DetectorRowCount'], len( geom['ProjectionAngles']), geom['DetectorColCount']) elif geom['type'] == 'fanflat_vec' or geom['type'] == 'parallel_vec': s = (geom['Vectors'].shape[0], geom['DetectorCount']) elif geom['type'] == 'parallel3d_vec' or geom['type'] == 'cone_vec': s = (geom['DetectorRowCount'], geom[ 'Vectors'].shape[0], geom['DetectorColCount']) if dim != None: s = s[dim] return s def checkArrayForLink(data): """Check if a numpy array is suitable for direct usage (contiguous, etc.) This function raises an exception if not. """ if not isinstance(data, np.ndarray): raise ValueError("Data should be numpy.ndarray") if data.dtype != np.float32: raise ValueError("Numpy array should be float32") if not (data.flags['C_CONTIGUOUS'] and data.flags['ALIGNED']): raise ValueError("Numpy array should be C_CONTIGUOUS and ALIGNED") class GPULink(object): """Utility class for astra.data3d.link with a CUDA pointer The CUDA pointer ptr must point to an array of floats. x is the fastest-changing coordinate, z the slowest-changing. pitch is the width in bytes of the memory block. For a contiguous memory block, pitch is equal to sizeof(float) * x. For a memory block allocated by cudaMalloc3D, pitch is the pitch as returned by cudaMalloc3D. """ def __init__(self, ptr, x, y, z, pitch): self.ptr = ptr self.x = x self.y = y self.z = z self.pitch = pitch astra-toolbox-2.3.0/python/astra/src/000077500000000000000000000000001475635207100175465ustar00rootroot00000000000000astra-toolbox-2.3.0/python/astra/src/PythonPluginAlgorithm.cpp000066400000000000000000000147501475635207100245700ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_PYTHON #include "PythonPluginAlgorithm.h" #include "astra/XMLConfig.h" #include "astra/Logging.h" #include "astra/Utilities.h" #include #include #include #include #include "bytesobject.h" namespace astra { void logPythonError(){ if(PyErr_Occurred()){ PyObject *ptype, *pvalue, *ptraceback; PyErr_Fetch(&ptype, &pvalue, &ptraceback); PyErr_NormalizeException(&ptype, &pvalue, &ptraceback); PyObject *traceback = PyImport_ImportModule("traceback"); if(traceback!=NULL){ PyObject *exc; if(ptraceback==NULL){ exc = PyObject_CallMethod(traceback,"format_exception_only","OO",ptype, pvalue); }else{ exc = PyObject_CallMethod(traceback,"format_exception","OOO",ptype, pvalue, ptraceback); } if(exc!=NULL){ PyObject *iter = PyObject_GetIter(exc); if(iter!=NULL){ PyObject *line; std::string errStr = ""; while((line = PyIter_Next(iter))){ PyObject *retb = PyUnicode_AsASCIIString(line); if(retb!=NULL){ errStr += std::string(PyBytes_AsString(retb)); Py_DECREF(retb); } Py_DECREF(line); } ASTRA_ERROR("%s",errStr.c_str()); Py_DECREF(iter); } Py_DECREF(exc); } Py_DECREF(traceback); } if(ptype!=NULL) Py_DECREF(ptype); if(pvalue!=NULL) Py_DECREF(pvalue); if(ptraceback!=NULL) Py_DECREF(ptraceback); } } CPluginAlgorithm::CPluginAlgorithm(PyObject* pyclass){ instance = PyObject_CallObject(pyclass, NULL); if(instance==NULL) logPythonError(); } CPluginAlgorithm::~CPluginAlgorithm(){ if(instance!=NULL){ Py_DECREF(instance); instance = NULL; } } bool CPluginAlgorithm::initialize(const Config& _cfg){ if(instance==NULL) return false; const XMLConfig *xmlcfg = dynamic_cast(&_cfg); if (xmlcfg==NULL) return false; PyObject *cfgDict = XMLNode2dict(xmlcfg->self); PyObject *retVal = PyObject_CallMethod(instance, "astra_init", "O",cfgDict); Py_DECREF(cfgDict); if(retVal==NULL){ logPythonError(); return false; } m_bIsInitialized = true; Py_DECREF(retVal); return m_bIsInitialized; } bool CPluginAlgorithm::run(int _iNrIterations){ if(instance==NULL) return false; PyGILState_STATE state = PyGILState_Ensure(); PyObject *retVal = PyObject_CallMethod(instance, "run", "i",_iNrIterations); if(retVal==NULL){ logPythonError(); }else{ Py_DECREF(retVal); } PyGILState_Release(state); return (retVal != NULL); } PyObject *CPluginAlgorithm::getInstance() const { if (instance) Py_INCREF(instance); return instance; } PyObject * pyStringFromString(std::string str){ return PyUnicode_FromString(str.c_str()); } PyObject* stringToPythonValue(std::string str){ if(str.find(";")!=std::string::npos){ std::vector rows, row; StringUtil::splitString(rows, str, ";"); PyObject *mat = PyList_New(rows.size()); for(unsigned int i=0; i vec; StringUtil::splitString(vec, str, ","); PyObject *veclist = PyList_New(vec.size()); for(unsigned int i=0;i nodes = node.getNodes(); std::list::iterator it = nodes.begin(); while(it!=nodes.end()){ XMLNode subnode = *it; if(subnode.getName()=="Option"){ PyObject *obj; if(subnode.hasAttribute("value")){ obj = stringToPythonValue(subnode.getAttribute("value")); }else{ obj = stringToPythonValue(subnode.getContent()); } PyDict_SetItemString(opts, subnode.getAttribute("key").c_str(), obj); Py_DECREF(obj); }else{ PyObject *obj = stringToPythonValue(subnode.getContent()); PyDict_SetItemString(dct, subnode.getName().c_str(), obj); Py_DECREF(obj); } ++it; } PyDict_SetItemString(dct, "options", opts); Py_DECREF(opts); return dct; } } #endif astra-toolbox-2.3.0/python/astra/src/PythonPluginAlgorithm.h000066400000000000000000000031661475635207100242340ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_PYTHONPLUGINALGORITHM #define _INC_PYTHONPLUGINALGORITHM #ifdef ASTRA_PYTHON #include "astra/Algorithm.h" #include "astra/Singleton.h" #include "astra/XMLDocument.h" #include "astra/XMLNode.h" #include "astra/PluginAlgorithmFactory.h" #include namespace astra { class CPluginAlgorithm : public CAlgorithm { public: CPluginAlgorithm(PyObject* pyclass); ~CPluginAlgorithm(); bool initialize(const Config& _cfg); bool run(int _iNrIterations); // Return instance (including INCREF) PyObject *getInstance() const; private: PyObject * instance; }; PyObject* XMLNode2dict(XMLNode node); } #endif #endif astra-toolbox-2.3.0/python/astra/src/PythonPluginAlgorithmFactory.cpp000066400000000000000000000144201475635207100261120ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2016, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_PYTHON #include "PythonPluginAlgorithmFactory.h" #include "PythonPluginAlgorithm.h" #include "astra/Logging.h" #include "astra/Utilities.h" #include #include #include #include #include "bytesobject.h" namespace astra { void logPythonError(); CPythonPluginAlgorithmFactory::CPythonPluginAlgorithmFactory(){ if(!Py_IsInitialized()){ Py_Initialize(); // This function is no longer required as of Python 3.7 and deprecated as of Python 3.9 #if PY_VERSION_HEX < 0x03070000 PyEval_InitThreads(); #endif } pluginDict = PyDict_New(); inspect = PyImport_ImportModule("inspect"); } CPythonPluginAlgorithmFactory::~CPythonPluginAlgorithmFactory(){ if(pluginDict!=NULL){ Py_DECREF(pluginDict); } if(inspect!=NULL) Py_DECREF(inspect); } PyObject * getClassFromString(std::string str){ std::vector items; StringUtil::splitString(items, str, "."); PyObject *pyclass = PyImport_ImportModule(items[0].c_str()); if(pyclass==NULL){ logPythonError(); return NULL; } PyObject *submod = pyclass; for(unsigned int i=1;i CPythonPluginAlgorithmFactory::getRegisteredMap(){ std::map ret; PyObject *key, *value; Py_ssize_t pos = 0; while (PyDict_Next(pluginDict, &pos, &key, &value)) { PyObject *keystr = PyObject_Str(key); if(keystr!=NULL){ PyObject *valstr = PyObject_Str(value); if(valstr!=NULL){ PyObject * keyb = PyUnicode_AsASCIIString(keystr); if(keyb!=NULL){ PyObject * valb = PyUnicode_AsASCIIString(valstr); if(valb!=NULL){ ret[PyBytes_AsString(keyb)] = PyBytes_AsString(valb); Py_DECREF(valb); } Py_DECREF(keyb); } Py_DECREF(valstr); } Py_DECREF(keystr); } logPythonError(); } return ret; } std::string CPythonPluginAlgorithmFactory::getHelp(const std::string &name){ PyObject *className = PyDict_GetItemString(pluginDict, name.c_str()); if(className==NULL){ ASTRA_ERROR("Plugin %s not found!",name.c_str()); PyErr_Clear(); return ""; } std::string ret = ""; PyObject *pyclass; if(PyBytes_Check(className)){ std::string str = std::string(PyBytes_AsString(className)); pyclass = getClassFromString(str); }else{ pyclass = className; } if(pyclass==NULL) return ""; if(inspect!=NULL){ PyObject *retVal = PyObject_CallMethod(inspect,"getdoc","O",pyclass); if(retVal!=NULL){ if(retVal!=Py_None){ PyObject *retb = PyUnicode_AsASCIIString(retVal); if(retb!=NULL){ ret = std::string(PyBytes_AsString(retb)); Py_DECREF(retb); } } Py_DECREF(retVal); }else{ logPythonError(); } } if(PyBytes_Check(className)){ Py_DECREF(pyclass); } return ret; } DEFINE_SINGLETON(CPythonPluginAlgorithmFactory); } #endif astra-toolbox-2.3.0/python/astra/src/PythonPluginAlgorithmFactory.h000066400000000000000000000037301475635207100255610ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef _INC_PYTHONPLUGINALGORITHMFACTORY #define _INC_PYTHONPLUGINALGORITHMFACTORY #ifdef ASTRA_PYTHON #include "astra/Singleton.h" #include "astra/Algorithm.h" #include "astra/PluginAlgorithmFactory.h" #include namespace astra { class CPythonPluginAlgorithmFactory : public CPluginAlgorithmFactory, public Singleton { public: CPythonPluginAlgorithmFactory(); virtual ~CPythonPluginAlgorithmFactory(); virtual CAlgorithm * getPlugin(const std::string &name); virtual bool registerPlugin(std::string name, std::string className); virtual bool registerPlugin(std::string className); bool registerPluginClass(std::string name, PyObject * className); bool registerPluginClass(PyObject * className); PyObject * getRegistered(); virtual std::map getRegisteredMap(); virtual std::string getHelp(const std::string &name); private: PyObject * pluginDict; PyObject *inspect; }; } #endif #endif astra-toolbox-2.3.0/python/astra/src/dlpack.cpp000066400000000000000000000173241475635207100215170ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "dlpack/dlpack.h" #include "astra/Data3D.h" #include #include template class CDataStorageDLPackCPU : public astra::CDataMemory { public: CDataStorageDLPackCPU(DLT *tensor); virtual ~CDataStorageDLPackCPU(); protected: DLT *m_pTensor; }; #ifdef ASTRA_CUDA template class CDataStorageDLPackGPU : public astra::CDataGPU { public: CDataStorageDLPackGPU(DLT* tensor); virtual ~CDataStorageDLPackGPU(); protected: DLT *m_pTensor; }; #endif template CDataStorageDLPackCPU::CDataStorageDLPackCPU(DLT *tensor_m) : m_pTensor(tensor_m) { // We assume all sanity checks have already been done uint8_t* data = static_cast(m_pTensor->dl_tensor.data); data += m_pTensor->dl_tensor.byte_offset; this->m_pfData = reinterpret_cast(data); } template CDataStorageDLPackCPU::~CDataStorageDLPackCPU() { if (m_pTensor) { assert(m_pTensor->deleter); m_pTensor->deleter(m_pTensor); } m_pTensor = nullptr; // Prevent the parent destructor from deleting this memory m_pfData = nullptr; } #ifdef ASTRA_CUDA template CDataStorageDLPackGPU::CDataStorageDLPackGPU(DLT *tensor_m) : m_pTensor(tensor_m) { DLTensor *tensor = &m_pTensor->dl_tensor; uint8_t* data = static_cast(tensor->data); data += tensor->byte_offset; unsigned int pitch = tensor->shape[0]; if (tensor->strides) pitch = tensor->strides[1]; m_hnd = astraCUDA3d::wrapHandle(reinterpret_cast(data), tensor->shape[2], tensor->shape[1], tensor->shape[0], pitch); } template CDataStorageDLPackGPU::~CDataStorageDLPackGPU() { if (m_pTensor) { assert(m_pTensor->deleter); m_pTensor->deleter(m_pTensor); } m_pTensor = nullptr; } #endif bool isContiguous(DLTensor *tensor, bool allowPitch) { if (!tensor->strides) return true; // For dimensions of size 1 the stride is not relevant, and it's optionall // for tensor producers to set it to 1 as well std::vector nonsingular_dims, nonsingular_strides; for (int i = 0; i < tensor->ndim; ++i) { if (tensor->shape[i] > 1) { nonsingular_dims.push_back(tensor->shape[i]); nonsingular_strides.push_back(tensor->strides[i]); } } if (nonsingular_dims.size() < 2) return true; int64_t accumulator = 1; for (int i = nonsingular_dims.size() - 1; i >= 0; --i) { if (nonsingular_strides[i] != accumulator) return false; if (allowPitch && i == nonsingular_dims.size()-1) // Accept non-contiguous second-to-last non-singular dimension, // since such data can be represented using cudaPitchedPtr accumulator *= nonsingular_strides[i-1]; else accumulator *= nonsingular_dims[i]; } return true; } bool checkDLTensor(DLTensor *tensor, std::array dims, bool allowPitch, std::string &error) { // data type if (tensor->dtype.code != kDLFloat || tensor->dtype.bits != 32) { error = "Data must be float32"; return false; } if (tensor->dtype.lanes != 1) { error = "Data must be single-channel"; return false; } // shape if (tensor->ndim != 3) { error = "Data must be three-dimensional"; return false; } if (tensor->shape[0] != dims[2] || tensor->shape[1] != dims[1] || tensor->shape[2] != dims[0]) { error = astra::StringUtil::format("Data shape (%zd x %zd x %zd) does not match geometry (%d x %d x %d)", tensor->shape[0], tensor->shape[1], tensor->shape[2], dims[2], dims[1], dims[0]); return false; } if (!isContiguous(tensor, allowPitch)) { error = "Data must be contiguous"; return false; } error = ""; return true; } template astra::CDataStorage *getDLTensorStorage(DLT *tensor_m, std::array dims, std::string &error) { DLTensor *tensor = &tensor_m->dl_tensor; switch (tensor->device.device_type) { case kDLCPU: case kDLCUDAHost: if (!checkDLTensor(tensor, dims, false, error)) return nullptr; return new CDataStorageDLPackCPU(tensor_m); #ifdef ASTRA_CUDA case kDLCUDA: case kDLCUDAManaged: if (!checkDLTensor(tensor, dims, true, error)) return nullptr; return new CDataStorageDLPackGPU(tensor_m); #endif default: error = "Unsupported dlpack device type"; return nullptr; } } astra::CDataStorage *getDLTensorStorage(PyObject *obj, std::array dims, std::string &error) { if (!PyCapsule_CheckExact(obj)) { error = "Invalid capsule"; return nullptr; } astra::CDataStorage *storage = nullptr; if (PyCapsule_IsValid(obj, "dltensor")) { // DLPack pre-1.0 unversioned interface void *ptr = PyCapsule_GetPointer(obj, "dltensor"); if (!ptr) { error = "Invalid dlpack capsule"; return nullptr; } DLManagedTensor *tensor_m = static_cast(ptr); storage = getDLTensorStorage(tensor_m, dims, error); if (storage) { // All checks passed, so we can officially consume this dltensor PyCapsule_SetName(obj, "used_dltensor"); } } else if (PyCapsule_IsValid(obj, "dltensor_versioned")) { // DLPack 1.0 versioned interface void *ptr = PyCapsule_GetPointer(obj, "dltensor_versioned"); if (!ptr) { error = "Invalid dlpack capsule"; return nullptr; } DLManagedTensorVersioned *tensor_m = static_cast(ptr); if (tensor_m->version.major > 1) { error = astra::StringUtil::format("Unsupported dlpack version %d.%d", tensor_m->version.major, tensor_m->version.minor); return nullptr; } if (tensor_m->flags & DLPACK_FLAG_BITMASK_READ_ONLY) { error = "Read-only dlpack tensor was provided"; return nullptr; } // NB: We ignore the DLPACK_FLAG_BITMASK_IS_COPIED flag. // If we ever add explicit support for differentiating between // input and output objects, we may want to return an error // when using a copied tensor for output. storage = getDLTensorStorage(tensor_m, dims, error); if (storage) { // All checks passed, so we can officially consume this dltensor PyCapsule_SetName(obj, "used_dltensor_versioned"); } } return storage; } astra::CFloat32VolumeData3D* getDLTensor(PyObject *obj, const astra::CVolumeGeometry3D &pGeom, std::string &error) { if (!PyCapsule_CheckExact(obj)) return nullptr; // x,y,z std::array dims{pGeom.getGridColCount(), pGeom.getGridRowCount(), pGeom.getGridSliceCount()}; astra::CDataStorage *storage = getDLTensorStorage(obj, dims, error); if (!storage) return nullptr; return new astra::CFloat32VolumeData3D(pGeom, storage); } astra::CFloat32ProjectionData3D* getDLTensor(PyObject *obj, const astra::CProjectionGeometry3D &pGeom, std::string &error) { if (!PyCapsule_CheckExact(obj)) return nullptr; // x,y,z std::array dims{pGeom.getDetectorColCount(), pGeom.getProjectionCount(), pGeom.getDetectorRowCount()}; astra::CDataStorage *storage = getDLTensorStorage(obj, dims, error); if (!storage) return nullptr; return new astra::CFloat32ProjectionData3D(pGeom, storage); } astra-toolbox-2.3.0/python/astra/src/dlpack.h000066400000000000000000000027171475635207100211640ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifndef ASTRA_PYTHON_SRC_DLPACK_H #define ASTRA_PYTHON_SRC_DLPACK_H namespace astra { class CFloat32VolumeData3D; class CVolumeGeometry3D; class CFloat32ProjectionData3D; class CProjectionGeometry3D; } astra::CFloat32ProjectionData3D* getDLTensor(PyObject *obj, const astra::CProjectionGeometry3D &pGeom, std::string &error); astra::CFloat32VolumeData3D* getDLTensor(PyObject *obj, const astra::CVolumeGeometry3D &pGeom, std::string &error); void dump_dltensor_info(PyObject *obj); #endif astra-toolbox-2.3.0/python/astra/tests.py000066400000000000000000000062021475635207100204730ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from __future__ import print_function, absolute_import def _basic_par2d_fp(type): import astra import numpy as np vg = astra.create_vol_geom(2, 32) pg = astra.create_proj_geom('parallel', 1, 32, [0]) proj_id = astra.create_projector(type, pg, vg) vol = np.random.rand(2, 32) (sino_id, sino) = astra.create_sino(vol, proj_id) astra.data2d.delete(sino_id) astra.projector.delete(proj_id) err = np.max(np.abs(sino[0,:] - np.sum(vol,axis=0))) return err < 1e-6 def _basic_par3d_fp(): import astra import numpy as np vg = astra.create_vol_geom(2, 32, 32) pg = astra.create_proj_geom('parallel3d', 1, 1, 32, 32, [0]) vol = np.random.rand(32, 2, 32) (sino_id, sino) = astra.create_sino3d_gpu(vol, pg, vg) astra.data3d.delete(sino_id) err = np.max(np.abs(sino[:,0,:] - np.sum(vol,axis=1))) return err < 1e-6 def _basic_par2d(): print("Testing basic CPU 2D functionality... ", end="") if _basic_par2d_fp('line'): print("Ok") return True else: print("Error") return False def _basic_par2d_cuda(): print("Testing basic CUDA 2D functionality... ", end="") if _basic_par2d_fp('cuda'): print("Ok") return True else: print("Error") return False def _basic_par3d_cuda(): print("Testing basic CUDA 3D functionality... ", end="") if _basic_par3d_fp(): print("Ok") return True else: print("Error") return False def test_noCUDA(): """Perform a very basic functionality test, without CUDA""" import astra print("ASTRA Toolbox v%s" % (astra.__version__,)) ok = _basic_par2d() if not ok: raise RuntimeError("Test failed") def test_CUDA(): """Perform a very basic functionality test, including CUDA""" import astra print("ASTRA Toolbox v%s" % (astra.__version__,)) print("Getting GPU info... ", end="") print(astra.get_gpu_info()) ok1 = _basic_par2d() ok2 = _basic_par2d_cuda() ok3 = _basic_par3d_cuda() if not (ok1 and ok2 and ok3): raise RuntimeError("Test failed") def test(): """Perform a very basic functionality test""" import astra if astra.use_cuda(): test_CUDA() else: print("No GPU support available") test_noCUDA() astra-toolbox-2.3.0/python/astra/utils.pxd000066400000000000000000000032421475635207100206350ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from libcpp.string cimport string from libcpp.memory cimport unique_ptr from . cimport PyXMLDocument from .PyXMLDocument cimport XMLDocument from .PyXMLDocument cimport XMLNode from .PyIncludes cimport * cdef configToDict(Config *) cdef XMLConfig * dictToConfig(string rootname, dc) except NULL cdef CFloat32VolumeData3D* linkVolFromGeometry(const CVolumeGeometry3D &pGeometry, data) except NULL cdef CFloat32ProjectionData3D* linkProjFromGeometry(const CProjectionGeometry3D &pGeometry, data) except NULL cdef unique_ptr[CProjectionGeometry3D] createProjectionGeometry3D(geometry) except * cdef unique_ptr[CVolumeGeometry3D] createVolumeGeometry3D(geometry) except * astra-toolbox-2.3.0/python/astra/utils.pyx000066400000000000000000000274121475635207100206670ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # # distutils: language = c++ # distutils: libraries = astra import sys cimport numpy as np import numpy as np import builtins from libcpp.string cimport string from libcpp.vector cimport vector from libcpp.list cimport list from libcpp.utility cimport move from cython.operator cimport dereference as deref, preincrement as inc from cpython.pycapsule cimport PyCapsule_IsValid from . cimport PyXMLDocument from .PyXMLDocument cimport XMLDocument from .PyXMLDocument cimport XMLNode from .PyIncludes cimport * from .pythonutils import GPULink, checkArrayForLink from .log import AstraError cdef extern from "Python.h": void* PyLong_AsVoidPtr(object) cdef extern from *: XMLConfig* dynamic_cast_XMLConfig "dynamic_cast" (Config*) cdef extern from "src/dlpack.h": CFloat32VolumeData3D* getDLTensor(obj, const CVolumeGeometry3D &pGeom, string &error) CFloat32ProjectionData3D* getDLTensor(obj, const CProjectionGeometry3D &pGeom, string &error) include "config.pxi" cdef XMLConfig * dictToConfig(string rootname, dc) except NULL: cdef XMLConfig * cfg = new XMLConfig(rootname) try: readDict(cfg.self, dc) except: del cfg raise return cfg def convert_item(item): if isinstance(item, str): return item.encode('ascii') if type(item) is not dict: return item out_dict = {} for k in item: out_dict[convert_item(k)] = convert_item(item[k]) return out_dict def wrap_to_bytes(value): if isinstance(value, bytes): return value return str(value).encode('ascii') def wrap_from_bytes(value): return value.decode('ascii') cdef bool readDict(XMLNode root, _dc) except False: cdef XMLNode listbase cdef XMLNode itm cdef int i cdef int j cdef double* data dc = convert_item(_dc) for item in dc: val = dc[item] if isinstance(val, builtins.list) or isinstance(val, tuple): val = np.array(val,dtype=np.float64) if isinstance(val, np.ndarray): if val.size == 0: break listbase = root.addChildNode(item) contig_data = np.ascontiguousarray(val,dtype=np.float64) data = np.PyArray_DATA(contig_data) if val.ndim == 2: listbase.setContent(data, val.shape[1], val.shape[0], False) elif val.ndim == 1: listbase.setContent(data, val.shape[0]) else: raise AstraError("Only 1 or 2 dimensions are allowed") elif isinstance(val, dict): if item == b'option' or item == b'options' or item == b'Option' or item == b'Options': readOptions(root, val) else: itm = root.addChildNode(item) readDict(itm, val) else: if item == b'type': root.addAttribute(< string > b'type', wrap_to_bytes(val)) else: if isinstance(val, builtins.bool): val = int(val) itm = root.addChildNode(item, wrap_to_bytes(val)) return True cdef bool readOptions(XMLNode node, dc) except False: cdef XMLNode listbase cdef XMLNode itm cdef int i cdef int j cdef double* data for item in dc: val = dc[item] if node.hasOption(item): raise AstraError('Duplicate Option: %s' % item) if isinstance(val, builtins.list) or isinstance(val, tuple): val = np.array(val,dtype=np.float64) if isinstance(val, np.ndarray): if val.size == 0: break listbase = node.addChildNode(b'Option') listbase.addAttribute(< string > b'key', < string > item) contig_data = np.ascontiguousarray(val,dtype=np.float64) data = np.PyArray_DATA(contig_data) if val.ndim == 2: listbase.setContent(data, val.shape[1], val.shape[0], False) elif val.ndim == 1: listbase.setContent(data, val.shape[0]) else: raise AstraError("Only 1 or 2 dimensions are allowed") else: if isinstance(val, builtins.bool): val = int(val) node.addOption(item, wrap_to_bytes(val)) return True cdef configToDict(Config *cfg): cdef XMLConfig* xmlcfg; xmlcfg = dynamic_cast_XMLConfig(cfg); if not xmlcfg: return None return XMLNode2dict(xmlcfg.self) def castString(input): return input.decode('utf-8') def stringToPythonValue(inputIn): input = castString(inputIn) # matrix if ';' in input: input = input.rstrip(';') row_strings = input.split(';') col_strings = row_strings[0].split(',') nRows = len(row_strings) nCols = len(col_strings) out = np.empty((nRows,nCols)) for ridx, row in enumerate(row_strings): col_strings = row.split(',') for cidx, col in enumerate(col_strings): out[ridx,cidx] = float(col) return out # vector if ',' in input: input = input.rstrip(',') items = input.split(',') out = np.empty(len(items)) for idx,item in enumerate(items): out[idx] = float(item) return out try: # integer return int(input) except ValueError: try: #float return float(input) except ValueError: # string return str(input) cdef XMLNode2dict(XMLNode node): cdef XMLNode subnode cdef list[XMLNode] nodes cdef list[XMLNode].iterator it dct = {} opts = {} if node.hasAttribute(b'type'): dct['type'] = castString(node.getAttribute(b'type')) nodes = node.getNodes() it = nodes.begin() while it != nodes.end(): subnode = deref(it) if castString(subnode.getName())=="Option": if subnode.hasAttribute(b'value'): opts[castString(subnode.getAttribute(b'key'))] = stringToPythonValue(subnode.getAttribute(b'value')) else: opts[castString(subnode.getAttribute(b'key'))] = stringToPythonValue(subnode.getContent()) else: dct[castString(subnode.getName())] = stringToPythonValue(subnode.getContent()) inc(it) if len(opts)>0: dct['options'] = opts return dct def getDLPackCapsule(data): # backward compatibility: check if the object is a dltensor capsule already if PyCapsule_IsValid(data, "dltensor"): return data if not hasattr(data, "__dlpack__"): return None capsule = None # TODO: investigate the stream argument to __dlpack__(). try: capsule = data.__dlpack__(max_version = (1,0)) except AttributeError: return None except TypeError: # unsupported max_version argument raises a TypeError pass if capsule is not None: return capsule try: capsule = data.__dlpack__() except AttributeError: return None return capsule cdef CFloat32VolumeData3D* linkVolFromGeometry(const CVolumeGeometry3D &pGeometry, data) except NULL: cdef CFloat32VolumeData3D * pDataObject3D = NULL cdef CDataStorage * pStorage cdef string dlerror = b"" # TODO: investigate the stream argument to __dlpack__(). capsule = getDLPackCapsule(data) if capsule is not None: pDataObject3D = getDLTensor(capsule, pGeometry, dlerror) if not pDataObject3D: raise ValueError("Failed to link dlpack array: " + wrap_from_bytes(dlerror)) return pDataObject3D if isinstance(data, GPULink): geom_shape = (pGeometry.getGridSliceCount(), pGeometry.getGridRowCount(), pGeometry.getGridColCount()) data_shape = (data.z, data.y, data.x) if geom_shape != data_shape: raise ValueError("The dimensions of the data {} do not match those " "specified in the geometry {}".format(data_shape, geom_shape)) IF HAVE_CUDA==True: hnd = wrapHandle(PyLong_AsVoidPtr(data.ptr), data.x, data.y, data.z, data.pitch/4) pStorage = new CDataGPU(hnd) ELSE: raise AstraError("CUDA support is not enabled in ASTRA") pDataObject3D = new CFloat32VolumeData3D(pGeometry, pStorage) return pDataObject3D raise TypeError("Data should be an array with DLPack support, or a GPULink object") cdef CFloat32ProjectionData3D* linkProjFromGeometry(const CProjectionGeometry3D &pGeometry, data) except NULL: cdef CFloat32ProjectionData3D * pDataObject3D = NULL cdef CDataStorage * pStorage cdef string dlerror = b"" # TODO: investigate the stream argument to __dlpack__(). capsule = getDLPackCapsule(data) if capsule is not None: pDataObject3D = getDLTensor(capsule, pGeometry, dlerror) if not pDataObject3D: raise ValueError("Failed to link dlpack array: " + wrap_from_bytes(dlerror)) return pDataObject3D if isinstance(data, GPULink): geom_shape = (pGeometry.getDetectorRowCount(), pGeometry.getProjectionCount(), pGeometry.getDetectorColCount()) data_shape = (data.z, data.y, data.x) if geom_shape != data_shape: raise ValueError("The dimensions of the data {} do not match those " "specified in the geometry {}".format(data_shape, geom_shape)) IF HAVE_CUDA==True: hnd = wrapHandle(PyLong_AsVoidPtr(data.ptr), data.x, data.y, data.z, data.pitch/4) pStorage = new CDataGPU(hnd) ELSE: raise AstraError("CUDA support is not enabled in ASTRA") pDataObject3D = new CFloat32ProjectionData3D(pGeometry, pStorage) return pDataObject3D raise TypeError("Data should be an array with DLPack support, or a GPULink object") cdef unique_ptr[CProjectionGeometry3D] createProjectionGeometry3D(geometry) except *: cdef XMLConfig *cfg cdef unique_ptr[CProjectionGeometry3D] pGeometry cfg = dictToConfig(b'ProjectionGeometry', geometry) tpe = cfg.self.getAttribute(b'type') pGeometry = constructProjectionGeometry3D(tpe) if not pGeometry: raise ValueError("'{}' is not a valid 3D geometry type".format(tpe)) if not pGeometry.get().initialize(cfg[0]): del cfg raise AstraError('Geometry class could not be initialized', append_log=True) del cfg return move(pGeometry) cdef unique_ptr[CVolumeGeometry3D] createVolumeGeometry3D(geometry) except *: cdef XMLConfig *cfg cdef CVolumeGeometry3D * pGeometry cfg = dictToConfig(b'VolumeGeometry', geometry) pGeometry = new CVolumeGeometry3D() if not pGeometry.initialize(cfg[0]): del cfg del pGeometry raise AstraError('Geometry class could not be initialized', append_log=True) del cfg return unique_ptr[CVolumeGeometry3D](pGeometry) astra-toolbox-2.3.0/python/builder.py000066400000000000000000000130141475635207100176440ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import os import sys import numpy as np from distutils.core import setup from setuptools import Command from Cython.Distutils import build_ext from Cython.Build import cythonize import argparse import sys # We write a cython include file config.pxi containing the HAVE_CUDA setting # to the directory passed by --astra_build_config_dir on the command line, # or to the source dir otherwise. if sys.version_info.major > 2: parser = argparse.ArgumentParser(allow_abbrev=False, add_help=False) else: parser = argparse.ArgumentParser(add_help=False) parser.add_argument('--astra_build_config_dir') parser.add_argument('--astra_build_cython_dir') parser.add_argument('--astra_cuda_dependencies', action='store_true') args, script_args = parser.parse_known_args() if args.astra_build_cython_dir is None: build_dir = '.' else: build_dir = args.astra_build_cython_dir if args.astra_cuda_dependencies: extra_install_requires = ['nvidia-cuda-runtime-cu12==12.1.105', 'nvidia-cufft-cu12==11.0.2.54'] else: extra_install_requires = [ ] use_cuda = ('-DASTRA_CUDA' in os.environ.get('CPPFLAGS', '') or '/DASTRA_CUDA' in os.environ.get('CL', '')) cfg_string = 'DEF HAVE_CUDA=' + str(use_cuda) + '\n' update_cfg = True self_path = os.path.dirname(os.path.abspath(__file__)) include_path = [] if args.astra_build_config_dir is None: cfg_file = os.path.join(self_path, 'astra', 'config.pxi') else: include_path += [args.astra_build_config_dir] cfg_file = os.path.join(args.astra_build_config_dir, 'config.pxi') try: with open(cfg_file, 'r') as cfg: cfg_fromfile = cfg.read() if cfg_fromfile == cfg_string: update_cfg = False except IOError: pass if update_cfg: with open(cfg_file, 'w') as cfg: cfg.write(cfg_string) cmdclass = {} # Custom command to (forcefully) override bdist's dist_dir setting used # by install/easy_install internally. # We use this to allow setting dist_dir to an out-of-tree build directory. class SetDistDirCommand(Command): user_options = [ ('dist-dir=', 'd', "directory to put final built distributions in") ] def initialize_options(self): self.dist_dir = None def finalize_options(self): bdist = self.reinitialize_command('bdist') bdist.dist_dir = self.dist_dir bdist.ensure_finalized() def run(self): pass # Custom command to add files (typically .so/.dll) to the module's directory # when installing (or building a wheel) class AddExtraLibCommand(Command): user_options = [ ('file=', 'f', "extra platlib file(s) to install"), ] def initialize_options(self): self.file = None def finalize_options(self): build_ext = self.get_finalized_command('build_ext') self.build_lib = os.path.join(build_ext.build_lib, 'astra') def run(self): import shutil for F in self.file.split(';'): print("Installing", F, "to", self.build_lib) shutil.copy2(F, self.build_lib) # TODO: Do we need get_outputs()? ext_modules = cythonize(os.path.join('.', 'astra', '*.pyx'), include_path=include_path, build_dir=build_dir, language_level=3) cmdclass = {'build_ext': build_ext, 'set_dist_dir': SetDistDirCommand, 'add_extra_lib': AddExtraLibCommand } for m in ext_modules: if m.name in ('astra.plugin_c', 'astra.algorithm_c'): m.sources.append(os.path.join('.', 'astra', 'src', 'PythonPluginAlgorithm.cpp')) if m.name in ('astra.plugin_c'): m.sources.append(os.path.join('.', 'astra', 'src', 'PythonPluginAlgorithmFactory.cpp')) if m.name in ('astra.utils'): m.sources.append(os.path.join('.', 'astra', 'src', 'dlpack.cpp')) with open('README.md', 'r', encoding='utf-8') as f: long_description = f.read() setup( script_args=script_args, ext_modules=ext_modules, include_dirs=[np.get_include()], cmdclass=cmdclass, packages=['astra', 'astra.plugins'], install_requires=['numpy', 'scipy'] + extra_install_requires, name='astra-toolbox', version='2.3.0', description='High-performance GPU primitives and algorithms for 2D and 3D tomography', long_description=long_description, long_description_content_type='text/markdown', license='GPLv3', project_urls={ 'Home page': 'https://astra-toolbox.com', 'Source': 'https://github.com/astra-toolbox/astra-toolbox' } ) astra-toolbox-2.3.0/samples/000077500000000000000000000000001475635207100157705ustar00rootroot00000000000000astra-toolbox-2.3.0/samples/matlab/000077500000000000000000000000001475635207100172305ustar00rootroot00000000000000astra-toolbox-2.3.0/samples/matlab/bunny.stl000066400000000000000000001345741475635207100211250ustar00rootroot00000000000000COLOR= MATERIAL=   ´‹¾c¼U?­H?à¨Ë¼Ð¹û=1É<â½ô¼Õwþ=Óë<¨û¼8¤ñ=ªÏú<m½¢°v?v“…>˽´”,>áU½@þ½»+>{Æô¼»Ê½1E)>_wŸ¼_§¿ç/û=jPM¿œø/½%:0=Fj¼Ìû.½¸ =𶜼ÒR½œ1 =ù_S¼ÈR!¿Û­>¹Ô2¿Qvé¼ßS¹=Dî¼&I½“xÄ=ÅüÁ¼Ÿ«í¼~Ú=€÷©¼"Ž ¿'C?˜4¬>!t¼ãª>Çï’<éè¼}uõ=°_= Oh¼ìL>²?Ú<H§F¾ùÿq?\?†>!t¼ãª>Çï’<‹7ò¼>›Âo<â½ô¼Õwþ=Óë<öƒ¾KÍJ?¥œ ?.Á»!>žøà<éè¼}uõ=°_=©Ê»XYû=Ð|=Àr1?Ì›?Á–¼>œff½Ù@ >¸Z=½@D½{g>ÝäÜ<ØU2½d">ÿ¡<"& >1m{?¼L>UÞŽ<7Â>KÖ—å‘ä<®~l<†!>Îl×<]÷>Þ¬_¿¡àf½zã¼£²=Ù>= y½4=Ufï<‰\½ ˜=4’<,ÈN>MòN? ?â½ô¼Õwþ=Óë<ØU2½d">ÿ¡<|L½hvý=>žÌ<Ž¥g¾ƒr?âbp>ž£Ä»‡> ’Çï’< Oh¼ìL>²?Ú<áZ¾"}B?x6?.Á»!>žøà< Oh¼ìL>²?Ú<éè¼}uõ=°_=ªZ¿—„>³ é¾Ôî·½!”>Bç;•"®½ð‰>bÀ;¥‚¯½M0 >rÑû:™©O?Mmí>ëk¶>Ã{S½${>5d—<Âþf½JF>»CŠ<îéj½ÿw>²=ö誼”u?ëR> Ã;(+>'’<.Á»!>žøàå‘ä<Z¿V+¿G<Á]¡½°ó= §à;6¾ ½$q= ëŽ<µ­½õ£=ƒæ­;ëY½³A&>ÏQ"½7Ì‹½Ð|.>?þ2½Ë½´”,>áU½Oz’¾Òw=zÏt¿:н1y>™x ¼2¤¤½Æ¾>èí™»™2‹½YN>´Cø»ØV½ÄŸ?œ'c¼”ÆÃ;¤ÿ>9â½;ž£Ä»‡> ’< Ã;(+>'’<avK>¦ßV>Ýu?±½föù= ÿS=ñ±½ßÃ>¡ÒM=û²Ÿ½0»>ÀSR= „!½H”;\Ì?ÞŸ½ì÷=´ˆR=±½föù= ÿS=û²Ÿ½0»>ÀSR=»Ä>N$?•*3?û²Ÿ½0»>ÀSR==lˆ½¨6>ö7=xh£½Ò5>‘å.=]ý¬=×j>Ŧt?=lˆ½¨6>ö7=rዽËM>Ur)=xh£½Ò5>‘å.=ÀtW¿Ë? ¿:®»6¾ ½$q= ëŽ<¥ç¬½0_£=§¤˜<µ­½õ£=ƒæ­;ýo¾¯ãx?¢2…¼-Z»W>ÆÃ;!t¼ãª>Çï’<ž£Ä»‡> ’<{+ù¼Š¸P? ?uþ;)">å‘ä<.Á»!>žøà<©Ê»XYû=Ð|=ºûL¾1:¿¨)(¿!õh¼w02>zÕÔ¼(s¼åa1>þ ټž.¼Þ+> £¼ƒÝ¬=Ž©×>š,g?ﲺ;ÛÜØ=£b2=ð^Þ; >í=Óô=VÄ»Ž[ì=–«$=ZŒ]?kƒù>!t¼ãª>Çï’<â½ô¼Õwþ=Óë<à¨Ë¼Ð¹û=1É<ë6>×?%mU?±Gr<òPº=„ÁP=7ê—<`,Â= žC=ﲺ;ÛÜØ=£b2=‰Þ¾¦î}? »Ù/޼û¬>Q¦¬;‹7ò¼>›ÂoÇï’<~o?n`²¾»vo½žüg½^–@=Ì„ú<óuY½ihh=‚Ðð<"šj½’1=/=Ã%m?©9>ÎÒ±>ªÆA½Æ£>÷˜á;Ã{S½${>5d—<_&J½#¤>Óhr<:’¿$6a»¹Žl=-ªº½ÎUó=Ó{ô<ïP¹½Sêò=ÚÐ(=lʺ½§‘>^êñ<X «>¸A?f??Ó°©;èѾ=[~[=±Gr<òPº=„ÁP=ﲺ;ÛÜØ=£b2=Ïn¿Ø)¼¾Ënyº¥ç¬½0_£=§¤˜<Ô÷µ½¯Oº='ª’<µ­½õ£=ƒæ­;f«»¾ÚÁ¾Y»Y¿7w¼\&>‚^n¼s&’¼\“.>qd”¼Å¾.¼Þ+> £¼‹¤Œ>ëß¾m[?«†ó<I.=åÕù< 91=K·;=wñã1v¿'+‹½"t+=ôÛ;.óg½\`=yúó; [s½üA=—õÍ;Tï¾E¹¾¥Vz?ü d½º‡¿=f5=†‹½¦ Û=¢]%=u«Œ½±,½=š×,=Pî)<áÿ>Ku?ü d½º‡¿=f5=áOc½zÈÔ=ñ‚(=†‹½¦ Û=¢]%=ã{Y=à\¿®H?Ð¥Ñ;fÿ2=ŸÌ?=Õèv;ÚšC=e¢M=U.Ø»/=7C@=‘ Û>å˾¿²O?Ð¥Ñ;fÿ2=ŸÌ?=ä{­;d=ŸDZ=Õèv;ÚšC=e¢M='ff¾÷L.½«2y?ñd½ÁR¢=/P2=ü d½º‡¿=f5=F勽儤=yÀ&=!c¾ôžy?œŠ»Ù/޼û¬>Q¦¬;!t¼ãª>Çï’<-Z»W>ÆÃ;}{(=2ŽÀ>Óøl?"Âû‡àØ=~4=ﲺ;ÛÜØ=£b2=VÄ»Ž[ì=–«$=nuª=/' ½Šõ~?j<è;à‰=ò¨b=B€ç;ÖG¦=&¢d=dùÆ»¥«Š=88g=R Á½t¥~? ÿ&=Ù/޼û¬>Q¦¬;qÄÿ¼ÖU>õº¨;‹7ò¼>›Âo<¬í=Óô=±LÃ;aãú=@Õ =©Ê»XYû=Ð|=z»<·ž»¾Ên?Õèv;ÚšC=e¢M=ä{­;d=ŸDZ=¢X¸»‡pg=*³\=”…½,ä¾a—d?U.Ø»/=7C@=Õèv;ÚšC=e¢M=¢X¸»‡pg=*³\= tb>Èt4¿›‡,¿9<.½í+>IŒõ»:w½E×%>æ¿Q¼´…ñ¼">ÌëÊ»xuo¿qä´¾üfw¼Ô÷µ½¯Oº='ª’<·À´½ ¸=$Eä;µ­½õ£=ƒæ­;Mj¾3Z?JÈð>éè¼}uõ=°_=à¨Ë¼Ð¹û=1É<¨û¼8¤ñ=ªÏú<Š•e?F]Í>Ú$?> c=®I×=Ø®Ú<•2$=Wx×=šŽ<º=ñ/â=é<X ¾cµ´=r¿2¤¤½Æ¾>èí™»%/¡½¼>ø~»™2‹½YN>´Cø»º< Ë¿©E=¨K¼Í =ˆ¿»æ õ¼@=}î¾»«©‘¼Û°=A#}¼GÞ{>¶#?³D?ÕŸõ;ºkÉ=?ýG=ﲺ;ÛÜØ=£b2=Ó°©;èѾ=[~[=ÍZ~>g ?êmN?±LÃ;aãú=@Õ =ð^Þ; >í=Óô= •š<5zõ=M¿=q[-¾L¨a?,¶á>!t¼ãª>Çï’<à¨Ë¼Ð¹û=1É<éè¼}uõ=°_=^$—=îÀs?õÔ—>’ò.½¯´,=æ.=žüg½^–@=Ì„ú<"šj½’1=/=ž$¿O–£>ü2?-ð0½q¥c=V =óuY½ihh=‚Ðð<žüg½^–@=Ì„ú<Ög?ð…Ù½£âK¿'‡U.u?ää4½@ßÖ=í‰$=áOc½zÈÔ=ñ‚(=ü d½º‡¿=f5=¾$Ì=1øv>!w?„ê0½§¼=»:1=ää4½@ßÖ=í‰$=ü d½º‡¿=f5=û=ê>w€c?ää4½@ßÖ=í‰$=©"f½2åã=¦=áOc½zÈÔ=ñ‚(=•-¯>£)?VTH?ää4½@ßÖ=í‰$=ö2½Öð=yÜ=fð\½»Òò=bã=×䓽°’‘>¼t?©"f½2åã=¦=ää4½@ßÖ=í‰$=fð\½»Òò=bã=¾ëÌ>†c˜¾+â]? 91=K·;=wñã<¥0/=…e=D=yã = h]=X´=èu¿¾·>>G¹E?’ò.½¯´,=æ.=-ð0½q¥c=V =žüg½^–@=Ì„ú<î·]½ë—?µÌ¼”ÆÃ;¤ÿ>9â½;-Z»W>ÆÃ;ž£Ä»‡> ’< ¾qÀ>¾tPx?°[_½ÇY’=ß "=œ0½[Š=S'=ræ2½¶ž¡=µ¨/=ßZ¾ ?a`L?ää4½@ßÖ=í‰$=×Þ'½ºKâ=çé=ö2½Öð=yÜ=B€c>W+x¿„¥Õ½9<.½í+>IŒõ»´…ñ¼">ÌëÊ»ç½éÕ >¿è:¦EÚ¾ŠÀ^?”2}>ïC4½9 +>ú;ds½›U/>Ðà:¾Á2½(->ML´»é ½ã¡„>nw?©®ò¼d}^= i#=-ð0½q¥c=V =’ò.½¯´,=æ.=O¸¾¹S?> ??èè½ïŸ8=Ê8=©®ò¼d}^= i#=’ò.½¯´,=æ.=^“½¨|¾‰Í|?©®ò¼d}^= i#=œ0½[Š=S'=-ð0½q¥c=V =¬´½<ê:¾~Š{?ï+½Î}£=/'2=ræ2½¶ž¡=µ¨/=œ0½[Š=S'=Ý9ú»S|T¾”kz?ð¼j‹=e(=ï+½Î}£=/'2=œ0½[Š=S'=Gl§½„y½|ª~?ï+½Î}£=/'2=i¢ô¼“‰À=/16=„ê0½§¼=»:1=-Ö@½Òqß¼ìž?ræ2½¶ž¡=µ¨/=ï+½Î}£=/'2=„ê0½§¼=»:1=kŽõ½´Pf>„‹w?i¢ô¼“‰À=/16=ää4½@ßÖ=í‰$=„ê0½§¼=»:1=àe®¼(¶¯>dp?i¢ô¼“‰À=/16=w<÷¼Y¿Ù=M¼#=ää4½@ßÖ=í‰$=„_±>ÁÜ–>8þc?ää4½@ßÖ=í‰$=¨û¼8¤ñ=ªÏú<×Þ'½ºKâ=çé=Ù<Úã!?&2F?×Þ'½ºKâ=çé=¨û¼8¤ñ=ªÏú<ö2½Öð=yÜ=†6ì=7°o>n!w?›Ä»3¹¢=ˆÀl=B€ç;ÖG¦=&¢d=Üýû8“À=>D^=ÁÎa¼3¯¥½ð"?ð¼j‹=e(=œ0½[Š=S'=©®ò¼d}^= i#=©a?0%Î>C¥|¾m¼%=°T×=" Ò;Á§=É9Ñ=¦À—»ô˜=Óâ=óÜ-;ôsܽž (?$??3½ =â==»=¨û¼8¤ñ=ªÏú<ää4½@ßÖ=í‰$=zK8½8?~2S?w<÷¼Y¿Ù=M¼#=3½ =â==»=ää4½@ßÖ=í‰$=Þµº½3ªz? Ö9¾ds½›U/>Ðà:¾½|.>—л¾Á2½(->ML´»I!I½O“w? £> Ã;(+>'’<ž£Ä»‡> ’<.Á»!>žøà<l®[¾gr?+Ž{>ž£Ä»‡> ’< Oh¼ìL>²?Ú<.Á»!>žøà<å¶'>Þè½#Üz?B€ç;ÖG¦=&¢d=›Ä»3¹¢=ˆÀl=dùÆ»¥«Š=88g=üŠ=¼8?M?VÄ»Ž[ì=–«$=ð^Þ; >í=Óô=©Ê»XYû=Ð|=Výê=8šÕ>·Ëf?Isš<%°Ù=XÃ*=ð^Þ; >í=Óô=ﲺ;ÛÜØ=£b2=F¯<>¶Á?í5C?w<÷¼Y¿Ù=M¼#=¨û¼8¤ñ=ªÏú<3½ =â==»=z™ ¿3Õ?Ôú ?sÝï¼ó+=©…R=©®ò¼d}^= i#=èè½ïŸ8=Ê8=Ä4?ÁiC>Ä.¿¦ÿ6½WZ>?ïº;šlM½ž±>tž);ªÆA½Æ£>÷˜á;åÜH?ñ¡°>Yß¿­¦+=­»=ï¶»3«=ïQÄ=9%E¼—G=34Þ=·‡ó»`~?mÅW=™‡Ë=Âþf½JF>»CŠ<ÜMk½â<>§é<îéj½ÿw>²=CJv¿$‚‰>?ÿC=óuY½ihh=‚Ðð<á2]½c=ðŒ‘<…çj½Ã0=¤<"Ö¾G? Ù,?£h¯¼— `={Ù6=©®ò¼d}^= i#=sÝï¼ó+=©…R=7)B½mxø>4_?³B‘¼]-=š²S=£h¯¼— `={Ù6=sÝï¼ó+=©…R=/4¾¯3«>‘m?·Ð•¼˜ÛÝ=.*=w<÷¼Y¿Ù=M¼#=i¢ô¼“‰À=/16=r°ˆ¾ªÁË>Ȱ`?_³œ¼HìÉ=ó ;=·Ð•¼˜ÛÝ=.*=i¢ô¼“‰À=/16=h_y¾–—?+C?éè¼}uõ=°_=¨û¼8¤ñ=ªÏú$·3>ŒÀg?å·¨šÖu?‘yå» Ã;(+>'’KÖ—<üûŒ<º>k÷§;2yo?~3­>µöÑ=³B‘¼]-=š²S=^༌a3=\;1=£h¯¼— `={Ù6=lÿ®½œæz?¨“7>—©‰½ç>J†’<êô¢½D‹>2õ<âvˆ½à…>¶ãü<P$˜>Bt?B„=â´½ —Ø=*µ<ò®½Ô×=M|^;±´½"9Ù=…» <Z¹<‹_E?î"?uþ;)">å‘ä<©Ê»XYû=Ð|=±LÃ;aãú=@Õ =ë«‹>¿Dv?AR¼”ÆÃ;¤ÿ>9â½; Ã;(+>'’<üûŒ<º>k÷§;þx>Ê„u¿6€¾´…ñ¼">ÌëÊ»7w¼\&>‚^n¼¥Ý¨¼‰›#>ë»!Âz?VœÊ=IŒ3¾•eh=O^=1î§;Äÿb=ú‘Œ=®7Á;²jk=ß·‹=nnŒ<P$˜¾Bt¿B„½â´½ —Ø=*µ<±´½"9Ù=…» <ò®½Ô×=M|^;¸#~?QÓ3=:yå½=8n=\Ia=ª“<•eh=O^=1î§;²jk=ß·‹=nnŒ< ô€>5rm?e>UÞŽ<7Â>KÖ—< Ã;(+>'’å‘ä<¾Aã¾)ù?£;?‡“j¼~Žï=ìù=éè¼}uõ=°_=·Ð•¼˜ÛÝ=.*=Äéá=¶˜ì>³Ca?7ê—<`,Â= žC=Isš<%°Ù=XÃ*=ﲺ;ÛÜØ=£b2=…šž=ëÇ<¾kÔz?ä{­;d=ŸDZ=j<è;à‰=ò¨b=dùÆ»¥«Š=88g=•¿Xö½¡G¿+úc½JM1=œ§ø»œø/½%:0=Fj¼2†c½¸õ =“ÖÊ»j=A2c¾pry?¢X¸»‡pg=*³\=ä{­;d=ŸDZ=dùÆ»¥«Š=88g=kž%¿’¤¨¾Ã0?^༌a3=\;1=}J„¼SBk=¶€P=£h¯¼— `={Ù6=–Š<§>k¿ µÉ¾ž#ò¼qª%>æäj¼7w¼\&>‚^n¼´…ñ¼">ÌëÊ»6'=52|?Ô*>³B‘¼]-=š²S=U.Ø»/=7C@=^༌a3=\;1=Ô'¾z÷Ô¾:]]?U.Ø»/=7C@=¢X¸»‡pg=*³\=}J„¼SBk=¶€P=P˨¾DôྮëU?^༌a3=\;1=U.Ø»/=7C@=}J„¼SBk=¶€P=è–¾¤ f¾yåm?¢X¸»‡pg=*³\=dùÆ»¥«Š=88g=qœŠ¼@µ‹=¶³Y=¢(‘¾]%\¾H?o?}J„¼SBk=¶€P=¢X¸»‡pg=*³\=qœŠ¼@µ‹=¶³Y=Ρ ¾M$å½ô3{?dùÆ»¥«Š=88g=›Ä»3¹¢=ˆÀl=.*˜¼.»¡=³Jd=ov•¾Þˆ¾54k?qœŠ¼@µ‹=¶³Y=dùÆ»¥«Š=88g=.*˜¼.»¡=³Jd=ª½?^)>”{?Üýû8“À=>D^=ºž¼Ç¼=ç¡[=.*˜¼.»¡=³Jd=ÚY*¾In>ŽMu?›Ä»3¹¢=ˆÀl=Üýû8“À=>D^=.*˜¼.»¡=³Jd=;)ð½ T¿>pŒk?"Âû‡àØ=~4=VÄ»Ž[ì=–«$=·Ð•¼˜ÛÝ=.*=à)¾÷å>È`?VÄ»Ž[ì=–«$=‡“j¼~Žï=ìù=·Ð•¼˜ÛÝ=.*=Ó«‚¾»o5?m^(?©Ê»XYû=Ð|=éè¼}uõ=°_=‡“j¼~Žï=ìù=Fï½¢®?›ÈL?VÄ»Ž[ì=–«$=©Ê»XYû=Ð|=‡“j¼~Žï=ìù=@Þ{¾¶#¿³D¿Ó°©;èѾ=[~[=ﲺ;ÛÜØ=£b2=ÕŸõ;ºkÉ=?ýG=‡Õ¾¾Î–¾é7\¿`--½9Y=?E„¼ ÷(½¢=Xþ¡¼*_½ˆ=ŽëŸ¼ü?Ú¿³r?™çÒ<<=%"=é–<ÙU-=]ü-=«†ó<I.=åÕù<ó˜=|‚F>ulz?B€ç;ÖG¦=&¢d=Ó°©;èѾ=[~[=Üýû8“À=>D^=€~o¿n`²>»vo=óuY½ihh=‚Ðð<žüg½^–@=Ì„ú<"šj½’1=/= Û>û<:Úu?,¾\= _d=jÚ<÷Z=ôl†=G‹Ø<¯Ž-=¬½‰=÷Qò<˜dÎ>$ô0>df?¥0/=…e=D=,¾\= _d=jÚ<¯Ž-=¬½‰=÷Qò<”2d?…O3½/÷æ¾-N+=úGŠ=É»´»Ó%-=†í¢=¶à½»ÅºB=nÚ‘=α;É•>Ç”œ¾wñg?Ð¥Ñ;fÿ2=ŸÌ?=é–<ÙU-=]ü-=ò·˜±ø4¿­ô¿¸Iû<¢R6=Ê»vQô<Å=]: ;+1<;=^D»O¿¾$â0>š y¿·*½"%,=”É#¼+úc½JM1=œ§ø»ea½Ïh=÷®»²ö>‘é<%>`?+#¿¼·–9>Âö8¼=àð¼¤S7>e¼¾½|.>—лÇ5Ê>Ó/´¾£?Y?ò·˜B•‹¾­¦+=­»=ï¶»Á§=É9Ñ=¦À—»,7=z¾=QS¦;te?z»T¿-ì¼èö2=ú½(=‰)‘<‡R=¡ >=x:¾;Z=»íB=Ó•<xe?Ep—¾„©>ªÆA½Æ£>÷˜á;Šô1½’Ë>¡ÈÉ;Ã{S½${>5d—<«¹>I->Q¸j?Cú’<ÆZ¢= †T=±Gr<òPº=„ÁP=B€ç;ÖG¦=&¢d=XD>¾’d¿í[G?-‰½ ýì=ßïA=±½föù= ÿS=†‹½¦ Û=¢]%=RØ8?ÿ†>¥¾#?=lˆ½¨6>ö7=ñ±½ßÃ>¡ÒM=±½föù= ÿS=Ÿ¸B?Œ"ý¾çh×>=8n=\Ia=ª“<,¾\= _d=jÚ<Z=»íB=Ó•<ën?Ëû=fж>=lˆ½¨6>ö7=±½föù= ÿS=-‰½ ýì=ßïA=û?>…]¾òkI?ÞÁô<;@‹=_±+=v¬d1?ß, ?s&’¼\“.>qd”¼¾½|.>—л—Û¢¼Ïe*>c¥8¼ê.m?7Ý>Qµ±>¦ÿ6½WZ>?ïº;ªÆA½Æ£>÷˜á;_&J½#¤>Óhr<Ag=?•,ᄅá÷>ss½Áõ=t½#=-‰½ ýì=ßïA=†‹½¦ Û=¢]%=Ïk+?ÒÏ=ý\ö7=-‰½ ýì=ßïA=¶Ð?Ÿòª¾×Œ6?™çÒ<<=%"=yã = h]=X´=’zê<Ïöh=¡÷&=?à? 7Ì;jT?ss½Áõ=t½#=´Ás½µn>Ó¯#==lˆ½¨6>ö7=?à¿ 7Ì»jT¿´Ás½µn>Ó¯#=ss½Áõ=t½#==lˆ½¨6>ö7=?à? 7Ì;jT?ss½Áõ=t½#=´Ás½µn>Ó¯#==lˆ½¨6>ö7=3p?š;¿Nšä>¾½|.>—лds½›U/>Ðà:ïC4½9 +>ú;R#?¡¹>ë-?¾l=I ¼=ã=Í`=¤4Û=ƒâ=€ßõ<Ú¿=ö¢,=úE›>Ià,¾®p?©"f½2åã=¦=ss½Áõ=t½#=†‹½¦ Û=¢]%=PÍ,¾¶þX¾•mv¿fð\½»Òò=bã=©"f½2åã=¦=†‹½¦ Û=¢]%=$5+?ºo´>¯•'?œff½Ù@ >¸Z==lˆ½¨6>ö7=´Ás½µn>Ó¯#=R#?UÊ•>¯‹6?îéj½ÿw>²=rዽËM>Ur)==lˆ½¨6>ö7=å$8?Å[:>ç +?œff½Ù@ >¸Z=îéj½ÿw>²==lˆ½¨6>ö7=2Å>.%k¿¢è¸=Ö¾¥¼Ö5:>X#–¼+#¿¼·–9>Âö8¼=àð¼¤S7>e¼EàP?ÌT÷¾ô¥¢>Ö¾¥¼Ö5:>X#–¼+#¿¼·–9>Âö8¼¾½|.>—л˲­>J²Ž¾yf¿+%U=:”a=W¢"»lÕ)=¡æ:=iUh»1T/=> b=@&Æ»ëÆ$?k¦<<ÆåC?©"f½2åã=¦=fð\½»Òò=bã=ss½Áõ=t½#=õ\¶> Ú>‹ìT?Isš<%°Ù=XÃ*=#ßX#–¼=àð¼¤S7>e¼+#¿¼·–9>Âö8¼än?ØÞ=›t¯>s&’¼\“.>qd”¼—Û¢¼Ïe*>c¥8¼¥Ý¨¼‰›#>ë»Z$?C³á;Y D?ss½Áõ=t½#=fð\½»Òò=bã=´Ás½µn>Ó¯#=]‘M?9E>^b?fð\½»Òò=bã=œff½Ù@ >¸Z=´Ás½µn>Ó¯#=´Å=O¿ÂM¿Çh½Éç>y/-»qu@½ðø>X×»šlM½ž±>tž);Ce?-d¾qM?’zê<Ïöh=¡÷&=ò·˜kº>:Z?å·¨vÓ¾þiT?ò·˜ù@?s&’¼\“.>qd”¼Ö¾¥¼Ö5:>X#–¼¾½|.>—лE-Q?Çæ¿Ì×b½‡R=¡ >=x:¾;•eh=O^=1î§;=8n=\Ia=ª“<xKç>ù”>€¥X?€ßõ<Ú¿=ö¢,=7ê—<`,Â= žC=å·¨=x:¾;=8n=\Ia=ª“<õ|?IÈ!?J?Í`=¤4Û=ƒâ= •š<5zõ=M¿=¸ÓÌ<ˆâ=ºë=á?…˜µ=Ú°R?€ßõ<Ú¿=ö¢,=å·¨D >ï9d?€ßõ<Ú¿=ö¢,=#ßßI9?fð\½»Òò=bã=½@D½{g>ÝäÜ<œff½Ù@ >¸Z=lä}?Æÿ=ëæç<,7=z¾=QS¦;HL5=d¿=T…< Œ<=mŒ¢="‘‘<°ës?€ =Mjš¾Ó%-=†í¢=¶à½»­¦+=­»=ï¶»*<=l£=ï½;0í$?ñ²=K†B?¾l=I ¼=ã=€ßõ<Ú¿=ö¢,=]û<é¢=ʾ0=Þ:¨>ß•‹½ó'q?ß=v<¹Š=PDU=Cú’<ÆZ¢= †T=B€ç;ÖG¦=&¢d=Xi?w+­>¶¦o¾,7=z¾=QS¦;Á§=É9Ñ=¦À—»m¼%=°T×=" Ò;†9(?³>Añ*?€ßõ<Ú¿=ö¢,=Í`=¤4Û=ƒâ=#ß뻾½|.>—л´…ñ¼">ÌëÊ»UŠÃ>6ä½ul?j<è;à‰=ò¨b=ß=v<¹Š=PDU=B€ç;ÖG¦=&¢d=–±±>Gæ>™­R?ö2½Öð=yÜ=½@D½{g>ÝäÜc«¥>ò¼/¼Qù7>Nã¼Å¾.¼Þ+> £¼(s¼åa1>þ Ù¼-éb¿(m>;šƒÖ¼Çþh=;•ç¼áõô¼—€.=ñúç¼OÏû¼»f=÷°—¼Õ¼>UöR¾Úh?ä{­;d=ŸDZ=º×n‹Þt¿&I½“xÄ=ÅüÁ¼“8+½íóØ=¼Ä¢¼Ÿ«í¼~Ú=€÷©¼o¶?;¾ÖïX?’zê<Ïöh=¡÷&=ÞÁô<;@‹=_±+=ò·˜.Rɾã6º½"Ž>ö›<•"®½ð‰>bÀ;Ôî·½!”>Bç;Ùq?¾˜«>Zö÷²jk=ß·‹=nnŒ<÷Z=ôl†=G‹Ø<,¾\= _d=jÚ<åUe?âÛš=i3à>=8n=\Ia=ª“<²jk=ß·‹=nnŒ<,¾\= _d=jÚ<Ï Š>ž1™=UÃu?s&’¼\“.>qd”¼Å¾.¼Þ+> £¼Ö¾¥¼Ö5:>X#–¼žJâ=…Î3?Ú4?Šô1½’Ë>¡ÈÉ;Le½xc!>p×;Ã{S½${>5d—<ƒ?àð¼lùK?#á=®ôŠ=à=HS=üL¢=þ´=ÞÁô<;@‹=_±+=°>Ü/Œ¾óe¿mÖ½Üö>=ëÿ¼c}½8"> ð¼CHŒ½+À>ÊRë¼# 0?l=ë¾£æ?«†ó<I.=åÕù¶Å+=©£= iòP^¾•f?ß=v<¹Š=PDU=j<è;à‰=ò¨b=ä{­;d=ŸDZ=Öim? qJ>­˜¢> Œ<=mŒ¢="‘‘<¶Å+=©£= iò<¼í==Nu’=œ±<gP?ᆮ>«¾ð>¾l=I ¼=ã=Lå(=@ż=J÷é<Í`=¤4Û=ƒâ=ùÎU?P½Šˆ ?¶Å+=©£= iò¦Oš¾•Õ_? 91=K·;=wñã<,¾\= _d=jÚ<¥0/=…e=D=ˆð ?¿H¾éÆO?º×n·þX>–mv?©"f½2åã=¦=fð\½»Òò=bã=†‹½¦ Û=¢]%=æ@?x¾ w?¯Ž-=¬½‰=÷Qò<#á=®ôŠ=à=yã = h]=X´= ž+g?Lå(=@ż=J÷é< c=®I×=Ø®Ú<Í`=¤4Û=ƒâ=·©I?é€ß<•Š?¯Ž-=¬½‰=÷Qò<¶Å+=©£= iò<#á=®ôŠ=à=tÖ¤>¸3>A+n?¥0/=…e=D=¯Ž-=¬½‰=÷Qòþ?ž.¼Þ+> £¼ò¼/¼Qù7>Nã¼Ö¾¥¼Ö5:>X#–¼{¯?æ²ð½kÚX?ß=v<¹Š=PDU=v¬ÊSV¿+¬Š½>¾=ÝJмÇöš½y½º= h`¼|颽ªcÕ=[@Ë»_Ž?»{6¿y¨¾–Àœ<.]¼±K¹<£Q&=¦¼+1<;=^D»>Ë0?j†þ½ðd6?HS=üL¢=þ´=]û<é¢=ʾ0=ÞÁô<;@‹=_±+=ÀÙ?á´ß>#U+?Í`=¤4Û=ƒâ=¸ÓÌ<ˆâ=ºë=#ßQ`Ú>…?F?—Û¢¼Ïe*>c¥8¼¾½|.>—л¥Ý¨¼‰›#>ë»z& ?:~=?Å>Ö¾¥¼Ö5:>X#–¼ò¼/¼Qù7>Nã¼_²q¼5C:>,E×¼è›b?ðÏü<ê«í>¼í==Nu’=œ±<¶Å+=©£= iò<¯Ž-=¬½‰=÷Qò<¾w>f/ ?mM?Isš<%°Ù=XÃ*= •š<5zõ=M¿=ð^Þ; >í=Óô=L{o?ôãC>n"˜¾Ó%-=†í¢=¶à½»*<=l£=ï½;źB=nÚ‘=α;Ì­> “P¿­ð¾f2=c(=á¾;vQô<Å=]: ;lÕ)=¡æ:=iUh»¹P›>‡¯a>‡Rm?±Gr<òPº=„ÁP=Ó°©;èѾ=[~[=B€ç;ÖG¦=&¢d=‚|{¿6[’=1ç0¾ÒJ»½óZ >ÀŒ<ã6º½"Ž>ö›<Ôî·½!”>Bç;. |>O ?NÔL?¸ÓÌ<ˆâ=ºë= •š<5zõ=M¿=Isš<%°Ù=XÃ*=’ù?nª¾N¾:?™çÒ<<=%"=’zê<Ïöh=¡÷&=é–<ÙU-=]ü-=5Ø2?*^;>Ø1¿a c½™>Ÿ»šlM½ž±>tž);¦ÿ6½WZ>?ïº;Lo?2VŽ\ëQ?]û<é¢=ʾ0=Cú’<ÆZ¢= †T=v¬iÞ>IÄP?¾½|.>—лïC4½9 +>ú;Šô1½’Ë>¡ÈÉ;]8_?¿ìa>ÿÅß>¥Ý¨¼‰›#>ë»7w¼\&>‚^n¼s&’¼\“.>qd”¼¹³?¾—Y¾A­T?ò·˜9€Ñ>·þK?€ßõ<Ú¿=ö¢,=Isš<%°Ù=XÃ*=7ê—<`,Â= žC=Êýä>ÃOï>C7C?Âß<¢—ñ=šrê< •š<5zõ=M¿=Í`=¤4Û=ƒâ=&õE¼t|?˜g)>´…ñ¼">ÌëÊ»Šô1½’Ë>¡ÈÉ;ç½éÕ >¿è:×J%?[Õ>‰Å@?´…ñ¼">ÌëÊ»¾½|.>—лŠô1½’Ë>¡ÈÉ;Æ 2?Ý¿ì0ú¾+%U=:”a=W¢"»•eh=O^=1î§;‡R=¡ >=x:¾;'Ö?(q¿; ¿lÕ)=¡æ:=iUh»+%U=:”a=W¢"»‡R=¡ >=x:¾;à¡8¾^Èþ¼¯­{¿:w½E×%>æ¿Q¼ho½É,>ó¡U¼ž#ò¼qª%>æäj¼’?;a¾É~L?ÞÁô<;@‹=_±+=]û<é¢=ʾ0=v¬šc?†‹½¦ Û=¢]%=áOc½zÈÔ=ñ‚(=©"f½2åã=¦=ÙZŽ<Î߿ťռ¨K¼Í =ˆ¿»×@é¼B=QÉ;æ õ¼@=}î¾»ðn¿ÙÈ=}Šk½Ô÷µ½¯Oº='ª’<â´½ —Ø=*µ<·À´½ ¸=$Eä;Ž€d?ÀB㾈*¢=Šô1½’Ë>¡ÈÉ;qu@½ðø>X×»9<.½í+>IŒõ»aø}¿ÝOõ½œÑ½´kœ½‹þ >—Zм@þ½»+>{Æô¼…œ½…–%>Ö¹ó¼žì¶¾V4d¾¨2h¿Ü:)½â¼¹=® ¹¼Œw½H‡§=§Ý¹¼ ÷(½¢=Xþ¡¼Q7ì>@‘a¿ñŠÔ½v‹½x{ >•.½c}½8"> ð¼mÖ½Üö>=ëÿ¼ÆÙ>?ÿÚ!?õôW>Y½³A&>ÏQ"½Ë½´”,>áU½c}½8"> ð¼Nø€<âé¾|q¿£º/½,'f=¬59¼`--½9Y=?E„¼€½Ûúi=ÈV<¼0ð¿Ð1?DÂÀ>xh£½Ò5>‘å.=Í󶽿+>ÆÕí<,Mµ½«% >^!=×=?(–¿ƒ–ˆ¾Y½³A&>ÏQ"½c}½8"> ð¼v‹½x{ >•.½*ž{¿–?º=0$>ïP¹½Sêò=ÚÐ(=Îò¶½oc>wQ/=lʺ½§‘>^êñ<Xï>—ÉD¿½Iß¾ÚMÕ<ñíB=†Ñ‡¼¸Iû<¢R6=Ê»±K¹<£Q&=¦¼3_¿ É?=ˆ ú¾·À´½ ¸=$Eä;±´½"9Ù=…» <ò®½Ô×=M|^;?Hp¿ÞW ¾ˆˆ¢>Þ̨½"ÊÂ=Ø«þ<¥ç¬½0_£=§¤˜<ú¥½¯Í¦=û<Òü >té|¿ÊBœ= y½4=Ufï<’Q/½5=3À*=²É+½YÇ =ï|õ<’Z{¿²«œ=µ1¾ÒJ»½óZ >ÀŒ<Ôî·½!”>Bç;º¹½xbö=BçÚ;\R¾=¥÷?£D[¿±´¾;Ù=Ä/«¼…¤Ã»á^Ù=ê¶¼¹åÀ»„×î=µƒ¼0Ü?á?F¿†Z°¾™2‹½YN>´Cø»î‡Ž½í>Ø·“¼€½Lˆ>\Hv¼jƒy¿^+È=½N¾¥Ã¾½fÙó=Õi“<ÒJ»½óZ >ÀŒ<º¹½xbö=BçÚ;ÛY?g;?ÜÞî¼ØU2½d">ÿ¡<¦ÿ6½WZ>?ïº;_&J½#¤>Óhr<`úc?z4Þ¾/ ¾oÓ„<ÔÔ=VÑ„¼–Àœ<.]¼+1<;=^D» ,?Nì2¿§ z¾¬§½ÙyÛ=Dþ<â´½ —Ø=*µ<œG¯½îÎÚ=²<gW<¿ž ¿gÇÍ>¬§½ÙyÛ=Dþ<-ªº½ÎUó=Ó{ô<â´½ —Ø=*µ<f«»>ÚÁ>Z»Y?ž.¼Þ+> £¼s&’¼\“.>qd”¼7w¼\&>‚^n¼°×y¿_3>³A+¾™²½÷\Á= @;·À´½ ¸=$Eä;ò®½Ô×=M|^;ÆÄ4¿’‰õ=§2¿¥‚¯½M0 >rÑû:%/¡½¼>ø~»2¤¤½Æ¾>èí™»Bg?ÿv=}›Ù¾>=µp´=ƒ4c¼­¦+=­»=ï¶»Ó%-=†í¢=¶à½»ÔéH?à?¸='ø¿µÿ=±B‹=sY–¼-N+=úGŠ=É»´»1T/=> b=@&Æ»Ï+I?hå2½~í¿-N+=úGŠ=É»´»KÇ=+É =Z¼Ó%-=†í¢=¶à½»±ý_¿ÿ>¾eï¾ò®½Ô×=M|^;±´½"9Ù=…» <)²½ |è=š{†;€”>:¢õ¾•T¿ŸÊ«;ŽJ =2ʳ¼–Àœ<.]¼oÓ„<ÔÔ=VÑ„¼É}z¿PÌõ=)Ý+>¥Ã¾½fÙó=Õi“<-ªº½ÎUó=Ó{ô<ÒJ»½óZ >ÀŒ<Nè)?$q†>M3¿­¦+=­»=ï¶»—G=34Þ=·‡ó»Á§=É9Ñ=¦À—»Üó¿t¨»“z™<-ªº½ÎUó=Ó{ô^êñ<ÒJ»½óZ >ÀŒ<Cš?¥#:¿þií> Ož<ÉÄ=J=«†ó<I.=åÕù<é–<ÙU-=]ü-=_;r?>p¥>|Ö„<,7=z¾=QS¦;m¼%=°T×=" Ò;HL5=d¿=T…<Y|¿«K)> ½c=lʺ½§‘>^êñ<Í󶽿+>ÆÕí<ÒJ»½óZ >ÀŒ<q²j?ô`>ü «>Í}½’z>Tª¼‡=ƒ½e7#>†Š¼€½Lˆ>\Hv¼ !?3N½ ÊF¿¼Ïñ¿-N+=úGŠ=É»´»µÿ=±B‹=sY–¼KÇ=+É =Z¼Óé ?d¿£^¿î‡Ž½í>Ø·“¼Í}½’z>Tª¼€½Lˆ>\Hv¼‹|¿r8ŒÒJ»½óZ >ÀŒ<Í󶽿+>ÆÕí<ã6º½"Ž>ö›<Vã?Q|~½d\¿v‹½x{ >•.½7Ì‹½Ð|.>?þ2½Y½³A&>ÏQ"½ò%á>ÉÜD?jŽí¾mÖ½Üö>=ëÿ¼‡=ƒ½e7#>†Š¼Í}½’z>Tª¼é%|¿m,0¾î…¼±´½"9Ù=…» <â´½ —Ø=*µ<º¹½xbö=BçÚ;³V?0k ¿~˜»CHŒ½+À>ÊRë¼c}½8"> ð¼î‡Ž½í>Ø·“¼Aój¿( ¤¾p7p¾â´½ —Ø=*µ<¥Ã¾½fÙó=Õi“<º¹½xbö=BçÚ;,g-? ¿Eíû¾KÇ=+É =Z¼Rl=[§=ég¼Ó%-=†í¢=¶à½»“&c?ô9é»eì¾Rl=[§=ég¼>=µp´=ƒ4c¼Ó%-=†í¢=¶à½»´cô¾‘RV?ç­ˆ>c}½8"> ð¼mÖ½Üö>=ëÿ¼Í}½’z>Tª¼£7?¸¼)¿È/[¾c}½8"> ð¼Í}½’z>Tª¼î‡Ž½í>Ø·“¼¶¿U?¾ä@¾Ô^¿¼Ïñã¤%¿2è<¿ŸÊ«;ŽJ =2ʳ¼oÓ„<ÔÔ=VÑ„¼q~·;ø =¶å’¼¾£I¿8µU¾Íf¿)²½ |è=š{†;±´½"9Ù=…» <º¹½xbö=BçÚ; ªp¿=à«>òbs½žüg½^–@=Ì„ú<óuY½ihh=‚Ðð<…çj½Ã0=¤<M»ž<ßà¿–Sļfj’¼Þñ=FoÄ;×@é¼B=QÉ;¨K¼Í =ˆ¿»£r¿ùU?ªÑ?»Ê½1E)>_wŸ¼@þ½»+>{Æô¼´kœ½‹þ >—ZмŠIŒõ»ç½éÕ >¿è:Šô1½’Ë>¡ÈÉ;¢]f?ch =Ö¢Þ>‡=ƒ½e7#>†Š¼t ½?!>{Oá»™2‹½YN>´Cø»¬'?zI€>Š 7?€½Lˆ>\Hv¼‡=ƒ½e7#>†Š¼™2‹½YN>´Cø»N5¾¦åÉ=4°z¿å8½’€ =¸è¼}%+½Jy=A[ؼ‘?½1¶+=þLؼ™ÓK?ðľÌï¾ÚMÕ<ñíB=†Ñ‡¼¼ÏñGÖç¾õ™N¿µÿ=±B‹=sY–¼1T/=> b=@&ƻݹ =Ué]=„`¼Iau?œª›=ñ§Œ>HL5=d¿=T… Œ<=mŒ¢="‘‘sk“>HL5=d¿=T…<•2$=Wx×=šŽ$VH>•2$=Wx×=šŽ< c=®I×=خڡË>Ã{S½${>5d—<îéj½ÿw>²=œff½Ù@ >¸Z=Oõ(?Vä¾à?‰ˆ½ÅË#>Ý›Z½—÷}½¬p+>BíW½7Ì‹½Ð|.>?þ2½ô?¡6¿†“¨¾–Àœ<.]¼ÚMÕ<ñíB=†Ñ‡¼±K¹<£Q&=¦¼3¨|?q`Èv‹½x{ >•.½‰ˆ½ÅË#>Ý›Z½7Ì‹½Ð|.>?þ2½Å?íPI?&>—÷}½¬p+>BíW½ †½v/>Šh½7Ì‹½Ð|.>?þ2½vB¿õö>Žýá¾ÒR½œ1 =ù_S¼2†c½¸õ =“ÖÊ»œø/½%:0=Fj¼%#?˜‰¾XG¿‰ˆ½ÅË#>Ý›Z½ †½v/>Šh½—÷}½¬p+>BíW½©çv?7öU>Š%¾mÖ½Üö>=ëÿ¼CHŒ½+À>ÊRë¼y½™~>W¥½,[?I2ó>™S¾ØU2½d">ÿ¡<_&J½#¤>Óhr<Ã{S½${>5d—<Q“ѽIP}?NéÐ=Hнnû>r‚¾;bÿ›½ÜJ>õ8»›Ç¡½û­>&šÕ;Ns8>Øpw?tà:>Âþf½JF>»CŠ<âvˆ½à…>¶ãü<ÜMk½â<>§é<­?¬?ÔV?rዽËM>Ur)=îéj½ÿw>²=âvˆ½à…>¶ãü<áz^?A¸ì>D44>Le½xc!>p×;Âþf½JF>»CŠ<Ã{S½${>5d—<u­ ?AM=?x?Ä>»Ê½1E)>_wŸ¼t ½?!>{Oệ=ƒ½e7#>†Š¼on>— h?)‰´>²jk=ß·‹=nnŒ<¯Ž-=¬½‰=÷Qò<÷Z=ôl†=G‹Ø<cÅá>’“-?Á‹?²jk=ß·‹=nnŒ<¼í==Nu’=œ±<¯Ž-=¬½‰=÷Qò<îIt?Î4Œ¾Þõ=mÖ½Üö>=ëÿ¼y½™~>W¥½v‹½x{ >•.½|”ä>mÉÓ>b K?²jk=ß·‹=nnŒ< Œ<=mŒ¢="‘‘<¼í==Nu’=œ±<‚ãŸ>æ!s?Ͱ³¼ÅºB=nÚ‘=α;²jk=ß·‹=nnŒ<Äÿb=ú‘Œ=®7Á;ö8$¿Œ0=?8¯R>ÿd§½j>7E‘<ã6º½"Ž>ö›<êô¢½D‹>2õ<2¿÷H$?­h¥>êô¢½D‹>2õ<ã6º½"Ž>ö›<Í󶽿+>ÆÕí<ݳ2?ÎI7?•°&<*<=l£=ï½; Œ<=mŒ¢="‘‘<²jk=ß·‹=nnŒ<`ÊE?ì<2>ˆL¿ÅºB=nÚ‘=α;*<=l£=ï½;²jk=ß·‹=nnŒ<“‘>Ï­¹>;Fc?ÜMk½â<>§é<âvˆ½à…>¶ãü<îéj½ÿw>²=Â'?µO¾ME:?%ƒ¢½}‹£=}æÑ»²–½Š"Ÿ=(ìG¼‚‹½_¤=˜“†¼`™>@‰b?“ù²¾ç4½ ƒ>LàV»¦ÿ6½WZ>?ïº;qÄÿ¼ÖU>õº¨;…°>Eão?·œz=¦ÿ6½WZ>?ïº;ØU2½d">ÿ¡õº¨;ÔT">•y?” +>Âþf½JF>»CŠ<—©‰½ç>J†’<âvˆ½à…>¶ãü<šºˆ>ï¡v?žz¼¼ØU2½d">ÿ¡<‹7ò¼>›Âoõº¨;£>wj?2º—¾ç4½ ƒ>LàV»qÄÿ¼ÖU>õº¨;z“ú¼v¤ú=VTл®]E?G·Ä>D?ØU2½d">ÿ¡<Ã{S½${>5d—<œff½Ù@ >¸Z=$> y?ëO+>7Ì‹½Ð|.>?þ2½fvœ½èf/>­Z(½Ë½´”,>áU½Ú— >Ëh?žx>‹7ò¼>›Âo<ØU2½d">ÿ¡<â½ô¼Õwþ=Óë<Tø3?¢?óoù¾»Ê½1E)>_wŸ¼‡=ƒ½e7#>†Š¼mÖ½Üö>=ëÿ¼WÛ?lhϼÒû²<˽´”,>áU½»Ê½1E)>_wŸ¼mÖ½Üö>=ëÿ¼Ìi=(]#>fL|?=àð¼¤S7>e¼¾Á2½(->ML´»¾½|.>—лÉAõ½*œ}?1i…½›Ç¡½û­>&šÕ;ÿd§½j>7E‘<—©‰½ç>J†’<P—>V&?>Z3?7Ì‹½Ð|.>?þ2½ŸÁ›½%ê5>ÙA½fvœ½èf/>­Z(½DÕç>VËí>-×B?|L½hvý=>žÌ<½@D½{g>ÝäÜ<ö2½Öð=yÜ=jo#¿ò ?7Xã>t ½?!>{Oỻʽ1E)>_wŸ¼´kœ½‹þ >—Zмfe ¼º]P?¶?rዽËM>Ur)=êô¢½D‹>2õ‘å.=q`Ò½-ZC?žX#?âvˆ½à…>¶ãü<êô¢½D‹>2õUr)=»´&?ðX!?òqØ>º=ñ/â=é<å1ù<àÕò=¸š<Í`=¤4Û=ƒâ=zÒ«½QGl?ùRÀ¾¾¾–¼ôMú=ôþý»qÄÿ¼ÖU>õº¨;Ù/޼û¬>Q¦¬;É"H?›?¹ŸS¾•2$=Wx×=šŽ<å1ù<àÕò=¸š<º=ñ/â=é<^Í ?<G?ÇŸ>ØU2½d">ÿ¡<½@D½{g>ÝäÜ<|L½hvý=>žÌ<®5E¿ù?î)W¾›Ç¡½û­>&šÕ;•"®½ð‰>bÀ;ÿd§½j>7E‘<b*¿t/?'©˜¾•"®½ð‰>bÀ;ã6º½"Ž>ö›<ÿd§½j>7E‘<t^L?Ä ?¡ÂÍc“½É6>ÖeB½7Ì‹½Ð|.>?þ2½…ƽT77>ºN^½$ O?/?íQ“>å1ù<àÕò=¸š<Âß<¢—ñ=šrê<Í`=¤4Û=ƒâ=Y-€žÌ<ö2½Öð=yÜ=Ê2K>øhP?c· ?â½ô¼Õwþ=Óë<|L½hvý=>žÌ<¨û¼8¤ñ=ªÏú<ûŠJ?•Ö?¶¶è=ô˜=Óâ=óÜ-;´-ù<‚­ò=¼;m¼%=°T×=" Ò;å:=«ï>õëa?Íc“½É6>ÖeB½ŸÁ›½%ê5>ÙA½7Ì‹½Ð|.>?þ2½jj?¬·²>buJ>…ƽT77>ºN^½7Ì‹½Ð|.>?þ2½ †½v/>Šh½ˆþ%?ÍÞ*?Žu»¾—G=34Þ=·‡ó»´-ù<‚­ò=¼;ô˜=Óâ=óÜ-;Ø’ç½î¯}?'´“½›Ç¡½û­>&šÕ;—©‰½ç>J†’r‚¾;ª÷½ås{?úë>—©‰½ç>J†’<ÿd§½j>7E‘<êô¢½D‹>2õ<ú6¿r4J?xª€¾%òƒ½Ñ)=?mù<žüg½^–@=Ì„ú<…çj½Ã0=¤<”Z >OÏ|?/Ëš½Hнnû>r‚¾;—©‰½ç>J†’<Âþf½JF>»CŠ<\—?œL?g>UÞŽ<7Â>KÖ—<Âß<¢—ñ=šrê<å1ù<àÕò=¸š<Ÿ‚À>¯R?Ú>UÞŽ<7Â>KÖ—< •š<5zõ=M¿=Âß<¢—ñ=šrê<ƒñÁ=G?‡H?ﲺ;ÛÜØ=£b2=Üýû8“À=>D^=Ó°©;èѾ=[~[=Ë-W¾–¸p?. ‰>Hнnû>r‚¾;Âþf½JF>»CŠp×;Äʃ¾Qq?úÛ]>t ½?!>{Oá»bÿ›½ÜJ>õ8»Hнnû>r‚¾;¼;?øfQ?Ú›@¼üûŒ<º>k÷§;UÞŽ<7Â>KÖ—<å1ù<àÕò=¸š<Ò?ÆDR?Ñl¡»´-ù<‚­ò=¼;üûŒ<º>k÷§;å1ù<àÕò=¸š<Hà½GC}?C€>Íc“½É6>ÖeB½…ƽT77>ºN^½ŸÁ›½%ê5>ÙA½‚#+?:Î,?uÒŸ>UÞŽ<7Â>KÖ—<®~l<†!>Îl×< •š<5zõ=M¿=*<¿~7W?«ª2>,§ƒ½q_"=ÇL"="šj½’1=/=%òƒ½Ñ)=?mù<´?ŸõJ?¢s¾üûŒ<º>k÷§;´-ù<‚­ò=¼;;ê<ümï=8`€»b„>¥16?øF'?®~l<†!>Îl×<±LÃ;aãú=@Õ = •š<5zõ=M¿=tA…¾À!?r;?"Âû‡àØ=~4=_³œ¼HìÉ=ó ;=Üýû8“À=>D^=yF#¾ïú??Œ[$?Üýû8“À=>D^=_³œ¼HìÉ=ó ;=ºž¼Ç¼=ç¡[=ŧN>QÓA? ?®~l<†!>Îl×å‘ä<±LÃ;aãú=@Õ =có)?cÜ?“¢Ø>Í`=¤4Û=ƒâ= c=®I×=Ø®Ú<º=ñ/â=é<мJ¿ÁU=k¾?”¾Ð¼º’£=¡Q=Ð=쇔µ=^N=ï+½Î}£=/'2=¶\¿n:? NξŸÁ›½%ê5>ÙA½…ƽT77>ºN^½´›½Àë3>2¤O½‚±p?kk¬¾söP½qu@½ðø>X×»Šô1½’Ë>¡ÈÉ;ªÆA½Æ£>÷˜á;np>¿Cê$¾ &?£h¯¼— `={Ù6=qœŠ¼@µ‹=¶³Y=‹Ž¿¼o†=C¸8=¥†>ßÉ%?Ü07¿jÚe½¯†= =œ0½[Š=S'= ìG½«1‡=q,=šnû>£M?Ûí¬¾;ê<ümï=8`€»@q›<#Øø=V˜ã»üûŒ<º>k÷§;b"F¿÷,í<ð!¿:w½E×%>æ¿Q¼9<.½í+>IŒõ»¾Á2½(->ML´»HD²¾îrx=–zo¿+úc½JM1=œ§ø»Ñÿ‡½6 =¥ˆˆ»Ž‚†½·@'=רƒ»-ú¾ÜÊÜ>µ1B?ºž¼Ç¼=ç¡[=i¢ô¼“‰À=/16=Ð=쇔µ=^N=aɉ¾í’s?gê>t ½?!>{Oá»´kœ½‹þ >—Zмbÿ›½ÜJ>õ8»5Q5¿ÐD¾Ûä-?£h¯¼— `={Ù6=}J„¼SBk=¶€P=qœŠ¼@µ‹=¶³Y=R·†¾|7q¿M*T>ï+½Î}£=/'2=.*˜¼.»¡=³Jd=”¾Ð¼º’£=¡Q=í¿·Œ<>›S?”¾Ð¼º’£=¡Q=.*˜¼.»¡=³Jd=ºž¼Ç¼=ç¡[=áH¿à7 <;­¿¾Á2½(->ML´»ho½É,>ó¡U¼:w½E×%>æ¿Q¼Õ±þ¾Hàq½]?©®ò¼d}^= i#=£h¯¼— `={Ù6=ð¼j‹=e(=‹®¿[¾jˆI?£h¯¼— `={Ù6=‹Ž¿¼o†=C¸8=ð¼j‹=e(=-|“>OûH?U_ ?=lˆ½¨6>ö7=û²Ÿ½0»>ÀSR=ñ±½ßÃ>¡ÒM=˜GؾÌI3¾«c?¢ŸD½ àf=ù‹=œ0½[Š=S'=jÚe½¯†= =ð£3¿’¿¤©û>óuY½ihh=‚Ðð<¢ŸD½ àf=ù‹=jÚe½¯†= =š޾€”ý½‡Wd?u«Œ½±,½=š×,=J3œ½éÓº=e =F勽儤=yÀ&=ûNº¾)?ÓC? Ç­½¾K >¢•@=û²Ÿ½0»>ÀSR=xh£½Ò5>‘å.=àÉ ¿_Q½™ÆF?œª½=Öì=¸Î?=ÞŸ½ì÷=´ˆR=ïP¹½Sêò=ÚÐ(=:e-¿§‰¾åN/?qœŠ¼@µ‹=¶³Y=.*˜¼.»¡=³Jd=ï+½Î}£=/'2= À ¿ÜV½’ÉF?ÞŸ½ì÷=´ˆR=Îò¶½oc>wQ/=ïP¹½Sêò=ÚÐ(=•4¹¾;Ï.?ó|"?ºž¼Ç¼=ç¡[=_³œ¼HìÉ=ó ;=i¢ô¼“‰À=/16==g¾t¨w¿±‹ê=óuY½ihh=‚Ðð<-ð0½q¥c=V =¢ŸD½ àf=ù‹=/ï¿ |—¾ÁØ9?F勽儤=yÀ&=J3œ½éÓº=e =ú¥½¯Í¦=û<æß¾PÈ̾Å3N?ÝþÀSR= Ç­½¾K >¢•@={v"¿\ÚE?Îò¶½oc>wQ/=ÞŸ½ì÷=´ˆR= Ç­½¾K >¢•@=pºÊ¾uì”=ÊVj?”¾Ð¼º’£=¡Q=ºž¼Ç¼=ç¡[=Ð=쇔µ=^N=˜ƒ½áÿÀ>Š‹l¿ú½¬ì6=7‰¼£º/½,'f=¬59¼€½Ûúi=ÈV<¼úrw¿µ‹½ û|>;…½ä±=‘ç„<'+‹½"t+=ôÛ;Þqнðb =ú}Á;úš¿[DJ=t“Ï<,§ƒ½q_"=ÇL"=%òƒ½Ñ)=?mù<ऄ½5¯=$=ñ<[ÿQ¿sˆ•¾Åû>¥ç¬½0_£=§¤˜<Þ̨½"ÊÂ=Ø«þ<Ô÷µ½¯Oº='ª’<Þ´¿Š]Uº D=4ºƒ½>I=»ã=,§ƒ½q_"=ÇL"=ऄ½5¯=$=ñ<¹±0¿-Ÿ¾`F'?‹Ž¿¼o†=C¸8=qœŠ¼@µ‹=¶³Y=ï+½Î}£=/'2=UÀ¸½-lj¿›|Ⱦ:w½E×%>æ¿Q¼ž#ò¼qª%>æäj¼´…ñ¼">ÌëÊ»ÓÎ =šÀ&?" B?"Âû‡àØ=~4=Üýû8“À=>D^=ﲺ;ÛÜØ=£b2=¿ÌÝ?–J? Ç­½¾K >¢•@=xh£½Ò5>‘å.=,Mµ½«% >^!=UúI¿€¼>Ûû>ÿø½µ´)=í¼h<'+‹½"t+=ôÛ;;…½ä±=‘ç„<ž<ß¾AÝ?çú.?J3œ½éÓº=e =u«Œ½±,½=š×,=Þ̨½"ÊÂ=Ø«þ<ý¥¾òŽo¾·¦j¿ho½É,>ó¡U¼ÐŒô¼¸/>¹ª‡¼ž#ò¼qª%>æäj¼g)+¿>,,?o¢¾’¹­½ >…9=êô¢½D‹>2õ<Í󶽿+>ÆÕí<ñ8¿³z…¾›Ü=?ð¼j‹=e(=‹Ž¿¼o†=C¸8=ï+½Î}£=/'2=ÕÛ÷¾KóN?t«>ÿø½µ´)=í¼h< [s½üA=—õÍ;'+‹½"t+=ôÛ;[7¿f$。*Y?¢ŸD½ àf=ù‹=-ð0½q¥c=V =œ0½[Š=S'=á¿ ½=¯ÁG?Þ̨½"ÊÂ=Ø«þ¡ÈÉ;×XX½v&>ØsB;Le½xc!>p×;œÊ¿ðL?³;ž>ÿø½µ´)=í¼h<…çj½Ã0=¤< [s½üA=—õÍ;ÁÏã¾CE>ö[¿Êð¼$Ò6>Œ}n¼Ö¾¥¼Ö5:>X#–¼ÐŒô¼¸/>¹ª‡¼Ó>{Åm¿z²>¶’¼Â=¹žò<^༌a3=\;1=zã¼£²=Ù>=d6M¿[y‚>Îr ?xh£½Ò5>‘å.=’¹­½ >…9=Í󶽿+>ÆÕí<i=P¿>/¿Qvé¼ßS¹=Dºý¼8Á¢=Âúÿ¼Œw½H‡§=§Ý¹¼z`³>xl8>àLk¿Çh½Éç>y/-»a c½™>Ÿ»™2‹½YN>´Cø»úñ0¿óóÃ>î?…çj½Ã0=¤<ÿø½µ´)=í¼h<;…½ä±=‘ç„< µl¿‚fœ>éèh>…çj½Ã0=¤<.óg½\`=yúó; [s½üA=—õÍ;OíP¿X6‚>G×? Ç­½¾K >¢•@=,Mµ½«% >^!=Îò¶½oc>wQ/=áI¿Xfþ½–,?ú¥½¯Í¦=û^q?×XX½v&>ØsB;Šô1½’Ë>¡ÈÉ;ïC4½9 +>ú;ÑÂn¿Aâ”>¢¢Z>…çj½Ã0=¤<á2]½c=ðŒ‘<.óg½\`=yúó; !¿3N= ÊF?¼ÏñŠh½´›½Àë3>2¤O½…ƽT77>ºN^½HÒ¾+}¿ ”½7Ì‹½Ð|.>?þ2½fvœ½èf/>­Z(½ †½v/>Šh½÷< ¿å×?¿óq»¾ †½v/>Šh½fvœ½èf/>­Z(½´›½Àë3>2¤O½úÌ¿eƒþ<-Ǽ´›½Àë3>2¤O½fvœ½èf/>­Z(½ŸÁ›½%ê5>ÙA½¨#{¿‡j Ý›Z½7Ì‹½Ð|.>?þ2½ †½v/>Šh½*? ½Cñ?\ŸX¿œ¹‘¼¿bí=M¼sÇ‘¼ ®Ù=äe²¼‹Õð¼äeí=ˆóp¼3¨|¿q`ȼE#¾‰ˆ½ÅË#>Ý›Z½v‹½x{ >•.½7Ì‹½Ð|.>?þ2½(Œ°¾¯Î}¾GÅg¿ ÷(½¢=Xþ¡¼Œw½H‡§=§Ý¹¼*_½ˆ=ŽëŸ¼›7Ÿ¾— ¡½éxr¿v‹½x{ >•.½fvœ½èf/>­Z(½7Ì‹½Ð|.>?þ2½æÚS¼rS?"Á¿ê¿½»ž&>¯Ø:»TOÍ;Á>*ÈO»SÞù»Žvü=gÑ ¼îIt¿Î4Œ>Þõ½mÖ½Üö>=ëÿ¼v‹½x{ >•.½y½™~>W¥½Û–H¿M&ù¾jÌÅ>v‹½x{ >•.½Ë½´”,>áU½fvœ½èf/>­Z(½%ɘ¾ð¾j¿Õ‡>¨9ʹ&õ=eÓ[<ɤ¯;´ =¯Y“=ëÿ¼…œ½…–%>Ö¹ó¼v‹½x{ >•.½8I$¿8Oô>»³¿v‹½x{ >•.½…œ½…–%>Ö¹ó¼Ë½´”,>áU½¤¹|¿Ÿ÷½YÕ½…œ½…–%>Ö¹ó¼@þ½»+>{Æô¼fvœ½èf/>­Z(½6 ?sÏ$¿ Ò ¿Ë½´”,>áU½…œ½…–%>Ö¹ó¼fvœ½èf/>­Z(½Ûmº¾ëÃm?ë½Êð¼$Ò6>Œ}n¼=àð¼¤S7>e¼Ö¾¥¼Ö5:>X#–¼LH¿q4¯¾ýÌ? <æ;HaÈ=y‘ɼ±´¾;Ù=Ä/«¼€þ­;Õ¿=ôF÷¼¯Þ}¿ Ì­»\¶¾œø/½%:0=Fj¼}%+½Jy=A[ؼÌû.½¸ =𶜼ž(G¿Y›Û>#뾜ø/½%:0=Fj¼‘?½1¶+=þLؼ}%+½Jy=A[ؼjŒf¿Å!b¾¶³¿¾OÏû¼»f=÷°—¼*_½ˆ=ŽëŸ¼7î¼¥Š=«þí¼íä]¿§Â‰¾ÁûÖ¾šƒÖ¼Çþh=;•ç¼OÏû¼»f=÷°—¼7î¼¥Š=«þí¼:b¿ˆ u¾’’ξ*_½ˆ=ŽëŸ¼Œw½H‡§=§Ý¹¼«ºý¼8Á¢=Âúÿ¼R4g¿§FW¾µª¿¾7î¼¥Š=«þí¼*_½ˆ=ŽëŸ¼«ºý¼8Á¢=Âúÿ¼šß¿M3-?H÷õ¾«ºý¼8Á¢=Âúÿ¼ ÷(½¢=Xþ¡¼Œw½H‡§=§Ý¹¼TN:¾ÙÇT?½€¿ê¿½»ž&>¯Ø:»SÞù»Žvü=gÑ ¼¾¾–¼ôMú=ôþý»ÈT¿Fý¾ûø¾î‡Ž½í>Ø·“¼´kœ½‹þ >—ZмmÖ½Üö>=ëÿ¼oªE¿ªÕ¿ô S¾mÖ½Üö>=ëÿ¼´kœ½‹þ >—Zм…œ½…–%>Ö¹ó¼”;{¿‹±>öe>Îò¶½oc>wQ/=,Mµ½«% >^!=Í󶽿+>ÆÕí<l¿à¥>¿©‰Ÿ¾·ãö;ñ¦==Õ»;²‡:Áù=‰žy:¬ ;K=ž뻤oÀf¿ú½¬ì6=7‰¼œø/½%:0=Fj¼£º/½,'f=¬59¼õFx¿9,>¡±4>lʺ½§‘>^êñ<Îò¶½oc>wQ/=Í󶽿+>ÆÕí<G4>ùRY¾1v?.óg½\`=yúó;'+‹½"t+=ôÛ; [s½üA=—õÍ;“)J¾S *>ÆUw¿Ü:)½â¼¹=® ¹¼“8+½íóØ=¼Ä¢¼&I½“xÄ=ÅüÁ¼è¿¾Ìø ?á2A¿Qvé¼ßS¹=Dî¼Ü:)½â¼¹=® ¹¼&I½“xÄ=ÅüÁ¼ .>½Nš?µÛ[¿¹åÀ»„×î=µƒ¼…¤Ã»á^Ù=ê¶¼œ¹‘¼¿bí=M¼Î×½;Ü%>D/{?œø/½%:0=Fj¼ú½¬ì6=7‰¼OÏû¼»f=÷°—¼0mF¿)H¾¿€¿%ƒ¢½}‹£=}æÑ»}Ϩ½ô¾=…4¶»Çöš½y½º= h`¼$¿Ä¶ƒ¾ò49¿‚‹½_¤=˜“†¼%ƒ¢½}‹£=}æÑ»Çöš½y½º= h`¼ž¸>¿â†>%H&¿}Ϩ½ô¾=…4¶»|颽ªcÕ=[@Ë»Çöš½y½º= h`¼Â'¿"µO>NE:¿²–½Š"Ÿ=(ìG¼%ƒ¢½}‹£=}æÑ»‚‹½_¤=˜“†¼O ¥¾w#$¿tC2¿™2‹½YN>´Cø»%/¡½¼>ø~»´kœ½‹þ >—Zм©-Y¿"¿þs=½î‡Ž½í>Ø·“¼™2‹½YN>´Cø»´kœ½‹þ >—Zмßçx¿å2i>ª}X=%/¡½¼>ø~»bÿ›½ÜJ>õ8»´kœ½‹þ >—ZмÔ÷=)¿8n½¾Á2½(->ML´»ÐŒô¼¸/>¹ª‡¼¾½|.>—лfä¿ ·"?õ^¿¾Á2½(->ML´»=àð¼¤S7>e¼ÐŒô¼¸/>¹ª‡¼97>ÄaC?sF¿\hxª•.»@q›<#Øø=V˜ã»Í®;úAý=]#¼OóT>O?bÝ ¿TOÍ;Á>*ÈO»\hxª•.»Í®;úAý=]#¼ ,¿Qì2?¥ z>â´½ —Ø=*µ<¬§½ÙyÛ=Dþ<œG¯½îÎÚ=²<Ý\ü¾„%2?|µ¿ho½É,>ó¡U¼¾Á2½(->ML´»ÐŒô¼¸/>¹ª‡¼¾B6>tɼdÕ{¿‘¼›Ÿ =JÔ¼å8½’€ =¸è¼áõô¼—€.=ñú缇Aྀ^¾‚[¿Œw½H‡§=§Ý¹¼Ü:)½â¼¹=® ¹¼Qvé¼ßS¹=Dî¼ÞÓˆ>£ 6?oz&¿@q›<#Øø=V˜ã» „åh9?+à#¿Í®;úAý=]#¼@q›<#Øø=V˜ã»Ðð;c)ò=â±z¼(Õ>ÄÞÌ>YçP¿¦<è-¾=¡r¼_&Š<×/¸=3sܼn‘< Ù=ø{˜¼Â¼l¾²þF<– y?˽´”,>áU½mÖ½Üö>=ëÿ¼c}½8"> ð¼QW侕éÒ¾ÃkK¿ÓŒ½‹¢Œ=oœÖ»{sž½Ò–=F†»%ƒ¢½}‹£=}æÑ»½Iö¾³ôß=î®^¿(¦½N›ñ=5’k»8”½eá=‹¼|颽ªcÕ=[@Ë»§þÎ=œ2k¿š!Z½%M¾8»¾Á2½(->ML´»9<.½í+>IŒõ»9[k?ü‹¾ º‘>Ò_ž¼ôŒÈ=˜˼XÔ–¼˜)Â=•Ùû¼sÇ‘¼ ®Ù=äe²¼ y¿—Á¼=³-Y¾ú½¬ì6=7‰¼OÏû¼»f=÷°—¼áõô¼—€.=ñúç¼Gˆ«¾’na?–«¾ [s½üA=—õÍ;+úc½JM1=œ§ø»Ž‚†½·@'=רƒ»Ð—]¿6š½>j’¬¾ [s½üA=—õÍ;.óg½\`=yúó;+úc½JM1=œ§ø»Ã}¿År‡=iÓé½.óg½\`=yúó;ea½Ïh=÷®»+úc½JM1=œ§ø»Gï0¿V*.¿d°y¾s›•½ìú…=-)µ;Á]¡½°ó= §à;{sž½Ò–=F†»jh¿™1C¿‚Ǿӌ½‹¢Œ=oœÖ»s›•½ìú…=-)µ;{sž½Ò–=F†»­F¿Ÿ ¿ÛϪ¾Á]¡½°ó= §à;µ­½õ£=ƒæ­;%ƒ¢½}‹£=}æÑ»ßîj¿iˆ°¾y J¾{sž½Ò–=F†»Á]¡½°ó= §à;%ƒ¢½}‹£=}æÑ»kÊZ¿@õ}¾F‹é¾µ­½õ£=ƒæ­;·À´½ ¸=$Eä;}Ϩ½ô¾=…4¶» Of¿×P@¾‰Ðɾ%ƒ¢½}‹£=}æÑ»µ­½õ£=ƒæ­;}Ϩ½ô¾=…4¶»6îb¿ ¾5%ã¾·À´½ ¸=$Eä;™²½÷\Á= @;}Ϩ½ô¾=…4¶»7Y]¿à\ >R÷¾™²½÷\Á= @;ò®½Ô×=M|^;}Ϩ½ô¾=…4¶»y/T¿-8>1Ÿ¿ò®½Ô×=M|^;|颽ªcÕ=[@Ë»}Ϩ½ô¾=…4¶»MRZ¿E†O:`¯¿ò®½Ô×=M|^;(¦½N›ñ=5’k»|颽ªcÕ=[@Ë»…=¿*Cì½à‹)¿ò®½Ô×=M|^;º¹½xbö=BçÚ;(¦½N›ñ=5’k»+Í8¿Ÿ÷–= &0¿º¹½xbö=BçÚ;Ôî·½!”>Bç;(¦½N›ñ=5’k»\ H¿Ú¢¼ »¿Ôî·½!”>Bç;2¤¤½Æ¾>èí™»(¦½N›ñ=5’k»ýÈG¿(蹃 ¿Ôî·½!”>Bç;¥‚¯½M0 >rÑû:2¤¤½Æ¾>èí™»m”F¿î'˜>&‡¿¥‚¯½M0 >rÑû:•"®½ð‰>bÀ;%/¡½¼>ø~»?2(¿ÎÁ ?Ø"¿•"®½ð‰>bÀ;›Ç¡½û­>&šÕ;%/¡½¼>ø~»‡¼v¿MO9>goH¾›Ç¡½û­>&šÕ;bÿ›½ÜJ>õ8»%/¡½¼>ø~»ÎÙd¿OÔº>Ë8…¾Le½xc!>p×;×XX½v&>ØsB;š!Z½%M¾8»£xÕ¾,Ê_?Ëì~>×XX½v&>ØsB;ïC4½9 +>ú;¾Á2½(->ML´»XTA¿XÜ?”Ͼš!Z½%M¾8»×XX½v&>ØsB;¾Á2½(->ML´»§â¿ÊK?ud[>%òƒ½Ñ)=?mù<"šj½’1=/=žüg½^–@=Ì„ú<G4¾ùRY>1v¿'+‹½"t+=ôÛ;.óg½\`=yúó; [s½üA=—õÍ;,ž-±,¾/h¿`--½9Y=?E„¼*_½ˆ=ŽëŸ¼OÏû¼»f=÷°—¼‚>¿¯A¿´z´¾€½Ûúi=ÈV<¼`--½9Y=?E„¼OÏû¼»f=÷°—¼§€#?RÓf=¡tD?ò®½Ô×=M|^;)²½ |è=š{†;º¹½xbö=BçÚ;¯>g¿d43=äÚ>Ô÷µ½¯Oº='ª’<Þ̨½"ÊÂ=Ø«þ<¬§½ÙyÛ=Dþ<G4>øRY¾1v? [s½üA=—õÍ;.óg½\`=yúó;'+‹½"t+=ôÛ;4»3?h¾6>{0¿a c½™>Ÿ»Çh½Éç>y/-»šlM½ž±>tž);fð->¯‚?µsC¿…¤Ã»á^Ù=ê¶¼€þ­;Õ¿=ôF÷¼ª#Ï»e;=V»½ ?¥§Ø=‚V¿Ýþ=µp´=ƒ4c¼KÇ=+É =Z¼´[?£?Ç=*¿#äR=ƃ‚="åºÄÿb=ú‘Œ=®7Á;+%U=:”a=W¢"»=È¿D»T?,JC¾'+‹½"t+=ôÛ; [s½üA=—õÍ;Ž‚†½·@'=רƒ»äR¾:UY?©\ù¾Õñs¼ø>¹;%»ê¿½»ž&>¯Ø:»¾¾–¼ôMú=ôþý»ã‹²=êU?‹rO¿…¤Ã»á^Ù=ê¶¼±´¾;Ù=Ä/«¼€þ­;Õ¿=ôF÷¼“??bf·>´ë¿@q›<#Øø=V˜ã»n‘< Ù=ø{˜¼ „f¯#¿Èê<#i×=ëFi¼—G=34Þ=·‡ó»ÝþpqÞ¼Å=„˜P=sÝï¼ó+=©…R=Ë+ü¼`= W1=”Ý’>*®D?r~¿—G=34Þ=·‡ó»Èê<#i×=ëFi¼n‘< Ù=ø{˜¼Œ3?%à¥>ëŠ"¿—G=34Þ=·‡ó»Á§=É9Ñ=¦À—»Ýþk÷§;@q›<#Øø=V˜ã»\hxª•.»åɽz‰W?(ä ¿TOÍ;Á>*ÈO»Í®;úAý=]#¼SÞù»Žvü=gÑ ¼”õ>Œy ?5{.¿—G=34Þ=·‡ó»@q›<#Øø=V˜ã»;ê<ümï=8`€»Ó÷½)?;n=ÐŒô¼¸/>¹ª‡¼¾Á2½(->ML´»¾½|.>—л© ¿>âì¨>Ìû]¿n‘< Ù=ø{˜¼Èê<#i×=ëFi¼ÝþýÌ¿±´¾;Ù=Ä/«¼ <æ;HaÈ=y‘ɼ€þ­;Õ¿=ôF÷¼±|>>Lm?š´¾”ÆÃ;¤ÿ>9â½;üûŒ<º>k÷§;TOÍ;Á>*ÈO»¡p?(fÀ>ew1¿—G=34Þ=·‡ó»3«=ïQÄ=9%E¼Ýþr‚¾;™2‹½YN>´Cø»t ½?!>{Oỿ5ƼD&r?[ª¥¾qÄÿ¼ÖU>õº¨;¾¾–¼ôMú=ôþý»z“ú¼v¤ú=VTл@W=ò";?KÄ-¿Hнnû>r‚¾;Çh½Éç>y/-»™2‹½YN>´Cø»¹ã>¡Ó?WÈ;¿@q›<#Øø=V˜ã»—G=34Þ=·‡ó»n‘< Ù=ø{˜¼U*¤½«é"?LgD¿Çh½Éç>y/-»Hнnû>r‚¾;Le½xc!>p×;Ò¼ÄL8?‘Ž1¿ª#Ï»e;=V»½w¶l¼H´=ŽXÔ–¼˜)Â=•Ùû¼Á!k¿…Е>\4ˆ¾š!Z½%M¾8»Çh½Éç>y/-»Le½xc!>p×;Í×=9Ü%¾D/{¿ú½¬ì6=7‰¼œø/½%:0=Fj¼OÏû¼»f=÷°—¼:[k¿ü‹> º‘¾XÔ–¼˜)Â=•Ùû¼Ò_ž¼ôŒÈ=˜˼sÇ‘¼ ®Ù=äe²¼ñZ¿FÝ>Í5¿w¶l¼H´=Ž•"ž¼ ê¥=ªj½XÔ–¼˜)Â=•Ùû¼Î:E¾Ø¹C?Æw¿¾¾–¼ôMú=ôþý»SÞù»Žvü=gÑ ¼œ¹‘¼¿bí=M¼’Ö¨¾De´>Ù7`¿Ì a½ûÌÙ=b˜¼;ˆ½S•Ö=Ïos¼.ˆ½ð=‰¼!¼>¾€ãÌ>~?_¿7-b½eð=M [¼Ì a½ûÌÙ=b˜¼.ˆ½ð=‰¼!¼Q9d½bç ?#êU¿‹Õð¼äeí=ˆóp¼sÇ‘¼ ®Ù=äe²¼Ÿ«í¼~Ú=€÷©¼Ïôܾh;¿1ñ¿s&’¼\“.>qd”¼!õh¼w02>zÕԼž.¼Þ+> £¼a°?#P.>½_X¿_&Š<×/¸=3sܼÝþ¡ Ñ>z d¿ „ÙöÊ>if¿Ðð;c)ò=â±z¼n‘< Ù=ø{˜¼±´¾;Ù=Ä/«¼­c{»Äx?û­q¾-Z»W>ÆÃ;TOÍ;Á>*ÈO»ê¿½»ž&>¯Ø:»l¹i½VwH? ‹¿¾¾–¼ôMú=ôþý»œ¹‘¼¿bí=M¼‹Õð¼äeí=ˆóp¼šIu½ËõH?Ù¿z“ú¼v¤ú=VTл¾¾–¼ôMú=ôþý»‹Õð¼äeí=ˆóp¼«<Â>]Ä«>…¾\¿ÝþQ¦¬;Õñs¼ø>¹;%»¾¾–¼ôMú=ôþý»Æþ‘=nô„¾%‹v¿¨Ù¼(91=°Áç¼Â ª»Q0=êß¼‘¼›Ÿ =JÔ¼BâѼjN?´8Y¿…¤Ã»á^Ù=ê¶¼sÇ‘¼ ®Ù=äe²¼œ¹‘¼¿bí=M¼7¿\½ô3,?9ì<¿SÞù»Žvü=gÑ ¼¹åÀ»„×î=µƒ¼œ¹‘¼¿bí=M¼3e¾3a=¥y¿å8½’€ =¸è¼‘?½1¶+=þLؼáõô¼—€.=ñúç¼Pua>e˜q?‚°|¾\hxª•.»TOÍ;Á>*ÈO»üûŒ<º>k÷§;©c)?ш?Òé¿­¦+=­»=ï¶»>=µp´=ƒ4c¼ÝþLàV»‹÷(½/Nü=ù¼`,b½mäú=–¼Kº˜>øÑ>tÞ\¿a c½™>Ÿ»ç4½ ƒ>LàV»`,b½mäú=–¼oÿ‰½‰±Û>B—f¿Â'½Çõï=ºän¼“8+½íóØ=¼Ä¢¼7-b½eð=M [¼…ÿœ½‹'×> xg¿“8+½íóØ=¼Ä¢¼Ì a½ûÌÙ=b˜¼7-b½eð=M [¼Kð^¾A™r?î.o¾-Z»W>ÆÃ;꿽»ž&>¯Ø:»Õñs¼ø>¹;%»6B¤¾Ù>•¶X¿XÔ–¼˜)Â=•Ùû¼Qvé¼ßS¹=D«í¼~Ú=€÷©¼5ÚÅ>Þ‘=‘hk¿#äR=ƃ‚="åº1T/=> b=@&Æ»-N+=úGŠ=É»´»óaM¾•gr?®€¾Ù/޼û¬>Q¦¬;-Z»W>ÆÃ;Õñs¼ø>¹;%»%9Õ>Ä%Y?¼ˆ§¾ç4½ ƒ>LàV»a c½™>Ÿ»¦ÿ6½WZ>?ïº;Ç^½””?ù J¿Â'½Çõï=ºän¼7-b½eð=M [¼`,b½mäú=–¼4½ì #?g!E¿‹÷(½/Nü=ù¼Â'½Çõï=ºän¼`,b½mäú=–¼¢F½°Ç?ù6I¿sÇ‘¼ ®Ù=äe²¼XÔ–¼˜)Â=•Ùû¼Ÿ«í¼~Ú=€÷©¼‚¿‘|=âkɻЌô¼¸/>¹ª‡¼=àð¼¤S7>e¼Êð¼$Ò6>Œ}n¼‡çY?‰»×=N¡¿Äÿb=ú‘Œ=®7Á;•eh=O^=1î§;+%U=:”a=W¢"»ÛL?Ð` ?3¦x¾´-ù<‚­ò=¼;—G=34Þ=·‡ó»;ê<ümï=8`€»jc½EÄs?¡È™¾-Z»W>ÆÃ;”ÆÃ;¤ÿ>9â½;TOÍ;Á>*ÈO»æß>PÈÌ>Å3N¿Ýþ˽´”,>áU½fvœ½èf/>­Z(½@þ½»+>{Æô¼¾–B¾¾*¢x¿+úc½JM1=œ§ø»2†c½¸õ =“ÖÊ»Ñÿ‡½6 =¥ˆˆ»Kq>½Ž1?Öy8¿Í®;úAý=]#¼Ðð;c)ò=â±z¼¹åÀ»„×î=µƒ¼Ýg·>¸óÓ>•;V¿n‘< Ù=ø{˜¼_&Š<×/¸=3sܼ€þ­;Õ¿=ôF÷¼B*>ãL?9ºM¿±´¾;Ù=Ä/«¼n‘< Ù=ø{˜¼€þ­;Õ¿=ôF÷¼eή¼€ "?ð¡E¿…¤Ã»á^Ù=ê¶¼ª#Ï»e;=V»½sÇ‘¼ ®Ù=äe²¼Üšô>òÍ9?Ijý¾#äR=ƃ‚="åº-N+=úGŠ=É»´»ÅºB=nÚ‘=α;`\Ž> ŸïÏ=„Um¿+%U=:”a=W¢"»1T/=> b=@&Æ»#äR=ƃ‚="åºì<>ÉÊ}¿­€<‰\½ ˜=4’< y½4=Ufï<²É+½YÇ =ï|õ<H“e½9È?™I¿ª#Ï»e;=V»½XÔ–¼˜)Â=•Ùû¼sÇ‘¼ ®Ù=äe²¼]W>ŸýY?"÷õ¾z“ú¼v¤ú=VTл‹÷(½/Nü=ù¼ç4½ ƒ>LàV»Ã÷‰>Yb?\<¿‹÷(½/Nü=ù¼z“ú¼v¤ú=VTлÂ'½Çõï=ºän¼y¢=¿#K?µu¿z“ú¼v¤ú=VTл‹Õð¼äeí=ˆóp¼Â'½Çõï=ºän¼#v´½JþÛ>…f¿Ÿ«í¼~Ú=€÷©¼“8+½íóØ=¼Ä¢¼Â'½Çõï=ºän¼ËŸN=’½ ?wsU¿‹Õð¼äeí=ˆóp¼Ÿ«í¼~Ú=€÷©¼Â'½Çõï=ºän¼;¶¾³ì ?²yC¿‘øË¼¯é¦=Yþ½Qvé¼ßS¹=Dî¼XÔ–¼˜)Â=•Ùû¼em콪(¿[M>¿»\i½t…=žþ¼%®c½"”=Ý„¼`--½9Y=?E„¼šß?M3-¿H÷õ>«ºý¼8Á¢=Âúÿ¼Œw½H‡§=§Ý¹¼ ÷(½¢=Xþ¡¼x‚>„Œ¾ƒ]m¿lÕ)=¡æ:=iUh»¸Iû<¢R6=Ê»1T/=> b=@&Æ»OAÃ>ÉÕ¾ #S¿¸Iû<¢R6=ʻݹ =Ué]=„`¼1T/=> b=@&Æ»m»ÂºvEr¾H»x¿ ÷(½¢=Xþ¡¼ÑU_½ˆ“¥==¥¼Ü:)½â¼¹=® ¹¼`yÛ¾žæ¢¾‡xX¿š!Z½%M¾8»9<.½í+>IŒõ»qu@½ðø>X×»%4ü½éJ(¿™P>¿~£‚½Ùõ‘=Êùb¼%®c½"”=Ý„¼»\i½t…=žþ¼É>º¾!rE¿°¶¿ÓŒ½‹¢Œ=oœÖ»~£‚½Ùõ‘=Êùb¼»\i½t…=žþ¼3ž-%k? è¸½=àð¼¤S7>e¼+#¿¼·–9>Âö8¼Ö¾¥¼Ö5:>X#–¼NFR¾Æâº¾”wh¿~£‚½Ùõ‘=Êùb¼‚‹½_¤=˜“†¼%®c½"”=Ý„¼Ž>³¾v0÷¾½~M¿ÓŒ½‹¢Œ=oœÖ»»\i½t…=žþ¼ea½Ïh=÷®»Uµ»½„V9>­z¿[Çb½ˆa¼=~­¼Ì a½ûÌÙ=b˜¼“8+½íóØ=¼Ä¢¼±à¼¾äæ€> e¿8”½eá=‹¼(¦½N›ñ=5’k».ˆ½ð=‰¼!¼ÑŸ¼+×»¾Ân¿`--½9Y=?E„¼ÑU_½ˆ“¥==¥¼ ÷(½¢=Xþ¡¼ãJã>“½>rÿP¿_&Š<×/¸=3sܼ¦<è-¾=¡r¼Ýþr;¿‹4î¾lÕ)=¡æ:=iUh»‡R=¡ >=x:¾;f2=c(=á¾;¿_æ>¬¥'¿Kq¿+1<;=^D»±K¹<£Q&=¦¼¸Iû<¢R6=Ê»Ì|2?$ïõ¾©9¿–Àœ<.]¼J¯²<^LS=ø»Á¼¼Ïñ]¼¼Ïñ€ú†¾‘=S¿KÇ=+É =Z¼í¥„<-Ò¤=TYï¼Ì·*½"%,=”É#¼£º/½,'f=¬59¼œø/½%:0=Fj¼g÷̾¾.G>å>e¿+¬Š½>¾=ÝJмÇöš½y½º= h`¼;ˆ½S•Ö=Ïos¼5É!¿Hïø¾¿ÓŒ½‹¢Œ=oœÖ»‚‹½_¤=˜“†¼~£‚½Ùõ‘=Êùb¼×v‘¾ß:¡=4Ÿt¿2¤¤½Æ¾>èí™»:н1y>™x ¼.ˆ½ð=‰¼!¼g÷Ì>¾.G¾å>e?Çöš½y½º= h`¼+¬Š½>¾=ÝJм;ˆ½S•Ö=Ïos¼imŒ>ÿ¾ß’R¿–Àœ<.]¼'‡ÿO¿;ˆ½S•Ö=Ïos¼|颽ªcÕ=[@Ë»8”½eá=‹¼ß0x¿Wqé=3^¾Ñÿ‡½6 =¥ˆˆ»'+‹½"t+=ôÛ;Ž‚†½·@'=רƒ»imŒ¾ÿ>ß’R?–Àœ<.]¼J¯²<^LS=ø»Á¼'‡ðã¿»;>¿–Àœ<.]¼ª´j·IľmÞV¿J¯²<^LS=ø»Á¼ª´jy/-»š!Z½%M¾8»qu@½ðø>X×»›"ʾɊ|½¸¬j¿(¦½N›ñ=5’k»2¤¤½Æ¾>èí™».ˆ½ð=‰¼!¼æß>PÈÌ>Å3N¿n‘< Ù=ø{˜¼Ýþ=µp´=ƒ4c¼Rl=[§=ég¼KÇ=+É =Z¼¤e¾¸¹?yÇC¿`,b½mäú=–¼7-b½eð=M [¼.ˆ½ð=‰¼!¼ç&¿³n¾µÂ8¿Ä©¼»É‰=™¸½šƒÖ¼Çþh=;•ç¼7î¼¥Š=«þí¼Êq^>§Á ¿œÉM¿ŸÊ«;ŽJ =2ʳ¼ýÜÌ;÷B<=ؼª´jÌš¿ç"9¿–Àœ<.]¼ŸÊ«;ŽJ =2ʳ¼ª´j¼±¨¾,dl¿ª´jn)À¾Hke¿ýÜÌ;÷B<=ؼhòÅ;Ÿ[c=EKù¼'‡3“9¾hhn¿hòÅ;Ÿ[c=EKù¼JT¹;K~Š=×½å<î ‹=í¼ð…>¾¦u¿'‡eÅC½ª½óÇq¿JT¹;K~Š=×½[Nº;nç¡=‹î ½å<î ‹=í¼l‡¼> ¡e¿[Nº;nç¡=‹î ½€þ­;Õ¿=ôF÷¼í¥„<-Ò¤=TYï¼–¤>±²O>EÉl¿€þ­;Õ¿=ôF÷¼_&Š<×/¸=3sÜ¼í¥„<-Ò¤=TYï¼c›¾=/›¾Ò9g¿»\i½t…=žþ¼`--½9Y=?E„¼£º/½,'f=¬59¼^>ξ^ùö¾U!G¿ea½Ïh=÷®»»\i½t…=žþ¼£º/½,'f=¬59¼, ¿ù¼H>3VP¿+¬Š½>¾=ÝJм|颽ªcÕ=[@Ë»;ˆ½S•Ö=Ïos¼Úh뾫ԩ>¤áR¿;ˆ½S•Ö=Ïos¼8”½eá=‹¼.ˆ½ð=‰¼!¼VK¼¾ä#©¼4n¿Çöš½y½º= h`¼+¬Š½>¾=ÝJм‚‹½_¤=˜“†¼Œ>´Ê¡=7Wu¿:н1y>™x ¼™2‹½YN>´Cø»a c½™>Ÿ»×â½Ì>Éi?·Ð•¼˜ÛÝ=.*=_³œ¼HìÉ=ó ;="Âû‡àØ=~4=¨‰©<οDŸ½fj’¼Þñ=FoÄ;þ‹Ö¼d©=Ñ‘<×@é¼B=QÉ;ã¾moö½ ac¿ea½Ïh=÷®»£º/½,'f=¬59¼·*½"%,=”É#¼ ˜½ ­Ö¾§¡g¿`--½9Y=?E„¼%®c½"”=Ý„¼ÑU_½ˆ“¥==¥¼Ê;>â"×>}e¿:н1y>™x ¼a c½™>Ÿ»`,b½mäú=–¼©\ >Sù'¿Þø=¿G-⻬â =Už¥¼Â ª»Q0=êß¼ŸÊ«;ŽJ =2ʳ¼ >f×'¿ >¿q~·;ø =¶å’¼G-⻬â =Už¥¼ŸÊ«;ŽJ =2ʳ¼÷xU>Ë ¿òZN¿Â ª»Q0=êß¼ýÜÌ;÷B<=ؼŸÊ«;ŽJ =2ʳ¼>1>Kpþ÷mh¿ýÜÌ;÷B<=ؼ ª»Q0=êß¼hòÅ;Ÿ[c=EKù¼BtC>›pʾ™f¿Â ª»Q0=êß¼ÐÉ»BË_=G¬½hòÅ;Ÿ[c=EKù¼*`d>\¦d¾Éêr¿ÐÉ»BË_=G¬½¡Oà»6‹=s6½JT¹;K~Š=×½–@>öuD¾Ç–v¿hòÅ;Ÿ[c=EKù¼ÐÉ»BË_=G¬½JT¹;K~Š=×½öŒ>²Î ¾*Ès¿¡Oà»6‹=s6½÷Ê]»ŒÃ¤=pj½[Nº;nç¡=‹î ½«Qm>M­®½.x¿JT¹;K~Š=×½¡Oà»6‹=s6½[Nº;nç¡=‹î ½7’]>°[>Ükq¿ª#Ï»e;=V»½€þ­;Õ¿=ôF÷¼[Nº;nç¡=‹î ½)z¦>{®>íÒa¿÷Ê]»ŒÃ¤=pj½ª#Ï»e;=V»½[Nº;nç¡=‹î ½+Wi»õOØ>fh¿Ðð;c)ò=â±z¼±´¾;Ù=Ä/«¼¹åÀ»„×î=µƒ¼¸Øy¼-üÝ=9v~¿.ˆ½ð=‰¼!¼:н1y>™x ¼`,b½mäú=–¼HD²¾ðrx=–zo¿Ñÿ‡½6 =¥ˆˆ»Ž‚†½·@'=רƒ»+úc½JM1=œ§ø»(ž ¿| >Þ(S?†‹½¦ Û=¢]%=¬§½ÙyÛ=DþÞø½UÒw¿¡Oà»6‹=s6½q?¼ë¡¢=—½÷Ê]»ŒÃ¤=pj½ÿÖ>¥B¡> Up¿q?¼ë¡¢=—½ª#Ï»e;=V»½÷Ê]»ŒÃ¤=pj½Ÿ‚¥¾oû6>åm¿;ˆ½S•Ö=Ïos¼Ì a½ûÌÙ=b˜¼[Çb½ˆa¼=~­¼yž¾:¶C>Îin¿+¬Š½>¾=ÝJм;ˆ½S•Ö=Ïos¼[Çb½ˆa¼=~­¼gâg¿CwK=gq×>¬§½ÙyÛ=Dþ<â´½ —Ø=*µ<Ô÷µ½¯Oº='ª’<ù)?C‡Ü¾´{¿•"ž¼ ê¥=ªj½w¶l¼H´=Žª#Ï»e;=V»½™_Å=ÚÀ¦>Èp¿q?¼ë¡¢=—½•"ž¼ ê¥=ªj½ª#Ï»e;=V»½4\å¾›Äv¾Íg\¿_²q¼5C:>,E×¼ò¼/¼Qù7>Nã¼(s¼åa1>þ Ù¼ò¡õ½_x:½{â}¿!õh¼w02>zÕÔ¼_²q¼5C:>,E×¼(s¼åa1>þ Ù¼ªúj½Æüœ¾ò9s¿¨Ù¼(91=°Áç¼á/œ¼ í_=t„½ÐÉ»BË_=G¬½¯ƒ=TMÒ¾¨Óh¿Â ª»Q0=êß¼¨Ù¼(91=°Áç¼ÐÉ»BË_=G¬½õÁn½ðìx¾Cßw¿ÐÉ»BË_=G¬½á/œ¼ í_=t„½¡Oà»6‹=s6½sŠo=¢©®¾^-p¿á/œ¼ í_=t„½Ä©¼»É‰=™¸½¡Oà»6‹=s6½Ás=Ÿ¿À\¿Ä©¼»É‰=™¸½òò¼{=ù’½¡Oà»6‹=s6½ñt2=â”K¼µ¼¿òò¼{=ù’½•"ž¼ ê¥=ªj½q?¼ë¡¢=—½!4>ý<¾!²y¿¡Oà»6‹=s6½òò¼{=ù’½q?¼ë¡¢=—½>„¾záÙ½3Ïu¿‚‹½_¤=˜“†¼[Çb½ˆa¼=~­¼ÑU_½ˆ“¥==¥¼ ®Ý½ºȽ–C}¿ÑU_½ˆ“¥==¥¼[Çb½ˆa¼=~­¼Ü:)½â¼¹=® ¹¼«©¾˜·¼Ý”q¿‚‹½_¤=˜“†¼+¬Š½>¾=ÝJм[Çb½ˆa¼=~­¼HD²>îrx½–zo?+úc½JM1=œ§ø»Ž‚†½·@'=רƒ»Ñÿ‡½6 =¥ˆˆ»X ~¿è½I5ó½Þqнðb =ú}Á;'+‹½"t+=ôÛ;Ñÿ‡½6 =¥ˆˆ»³ae¾öàɾó)d¿‚‹½_¤=˜“†¼ÑU_½ˆ“¥==¥¼%®c½"”=Ý„¼Ìؼï—¿LßK½Ë+ü¼`= W1=’Q/½5=3À*= y½4=Ufï<£R¿@t½ò¿Ö¾¥¼Ö5:>X#–¼_²q¼5C:>,E×¼!õh¼w02>zÕÔ¼y[[¿`îA¾z€õ¾s&’¼\“.>qd”¼Ö¾¥¼Ö5:>X#–¼!õh¼w02>zÕÔ¼oC©½£˜0>pF{¿Ü:)½â¼¹=® ¹¼[Çb½ˆa¼=~­¼“8+½íóØ=¼Ä¢¼³¦>½f}¿™>G-⻬â =Už¥¼Âö“¼Ÿo= ¼‘¼›Ÿ =JÔ¼þQÝ=ê|?ÿâ=·*½"%,=”É#¼œø/½%:0=Fj¼+úc½JM1=œ§ø»ú~T¿.q>÷Þ ¿à¤„½5¯=$=ñ<%òƒ½Ñ)=?mù<…çj½Ã0=¤<N§¿Þ¿æ@$¿%ƒ¢½}‹£=}æÑ»‚‹½_¤=˜“†¼ÓŒ½‹¢Œ=oœÖ»='8¿®1?¤»é<ऄ½5¯=$=ñ<…çj½Ã0=¤<;…½ä±=‘ç„</—€<4óƒ¾çQw¿‘¼›Ÿ =JÔ¼áõô¼—€.=ñú缨ټ(91=°Áç¼ôL¿uÓ§¾FdL¿šƒÖ¼Çþh=;•ç¼á/œ¼ í_=t„½¨Ù¼(91=°Áç¼]׿ç\·¾ ­H¿šƒÖ¼Çþh=;•ç¼Ä©¼»É‰=™¸½á/œ¼ í_=t„½¶Ž!¿·¯¾QB2¿7î¼¥Š=«þí¼Ä©¼»É‰=™¸½á/œ¼ í_=t„½¶Ž!?·¯>QB2?Ä©¼»É‰=™¸½7î¼¥Š=«þí¼á/œ¼ í_=t„½+Kp¾·)m½ïhx¿‘øË¼¯é¦=Yþ½•"ž¼ ê¥=ªj½òò¼{=ù’½&r²¾ƒÑ ¾‹Pm¿Ä©¼»É‰=™¸½‘øË¼¯é¦=Yþ½òò¼{=ù’½zž¾bɾ”òh¿ÐŒô¼¸/>¹ª‡¼s&’¼\“.>qd”¼7w¼\&>‚^n¼²Ð¾½ï;½Î}¿s&’¼\“.>qd”¼ÐŒô¼¸/>¹ª‡¼Ö¾¥¼Ö5:>X#–¼Q>$¾{ú>!“[¿XÔ–¼˜)Â=•Ùû¼•"ž¼ ê¥=ªj½‘øË¼¯é¦=Yþ½w?ÐWS¿Ž» ºf2=c(=á¾;‡R=¡ >=x:¾;èö2=ú½(=‰)‘<É—)¼lßr¾\®x¿7w¼\&>‚^n¼ž#ò¼qª%>æäj¼ÐŒô¼¸/>¹ª‡¼™7;€)<;™ÿ¿áõô¼—€.=ñú缚ƒÖ¼Çþh=;•缨ټ(91=°Áç¼³j ?$J)¾üöB¿'‡¹„Y?¦Âû>«ºý¼8Á¢=Âúÿ¼•"ž¼ ê¥=ªj½‘øË¼¯é¦=Yþ½ùB¾¹„Y¿©Âû¾•"ž¼ ê¥=ªj½«ºý¼8Á¢=Âúÿ¼‘øË¼¯é¦=Yþ½g6¿/A’>íâ#¿«ºý¼8Á¢=Âúÿ¼Qvé¼ßS¹=DøË¼¯é¦=Yþ½ÂJ©¼Äž½¼,?³B‘¼]-=š²S=sÝï¼ó+=©…R=pqÞ¼Å=„˜P=¥½•¾û[d¿´o°>N’•:lB=P·¬§½ÙyÛ=Dþ<†‹½¦ Û=¢]%=v™ ½¾‰á=†*+=®>«±õ¾[W?é–<ÙU-=]ü-=Ð¥Ñ;fÿ2=ŸÌ?=&¿;ýÌ=h,=ÂáÅ=náa¾Cwx?ǽyN’•:lB=P·<ÿC´;zì=jûò½Àt¿HS‚=‰\½ ˜=4’<þ‹Ö¼d©=Ñ‘=©æ <å ¿«,°=+Wx>Øt¿ Ož<ÉÄ=J=(A$<}k=$ñò<ÿC´;zì=jûò<kƒ&¾-Wx¾Øt?(A$<}k=$ñò< Ož<ÉÄ=J=ÿC´;zì=jûò<yTm¿°¾¡è>â´½ —Ø=*µ<-ªº½ÎUó=Ó{ô<¥Ã¾½fÙó=Õi“<zϽjr¿ Dl½+1<;=^D»·ãö;ñ¦==Õ»oÓ„<ÔÔ=VÑ„¼«b¾*šH?Ï÷ ?’¹­½ >…9=xh£½Ò5>‘å.=êô¢½D‹>2õ<_s¿'ÂY¿¢qŒ½@xºaa= †)<¨9ʹ&õ=eÓ[’Ìê<Î=Ø™<«†ó<I.=åÕù< Ož<ÉÄ=J=c%½<\U¿Šë‹= Ož<ÉÄ=J=(A$<}k=$ñò<ɤ¯;´ =¯Y“<îS绘¿Ãd=£#”¼V™=»é;=pqÞ¼Å=„˜P=Ë+ü¼`= W1=„"T?ƒç¾„ð¨> Ož<ÉÄ=J=é–<ÙU-=]ü-=ǽyɤ¯;´ =¯Y“<ÿC´;zì=jûò=ªá‘¼žs=7é‘<=¥q¿[d=Jž<_=]¡< Ož<ÉÄ=J=ɤ¯;´ =¯Y“<–ÜÇ>Š 7¾_4g?šÖ»+ƒ= ñ9=U.Ø»/=7C@=³B‘¼]-=š²S=ÑéÙ>„9¿¼ ?èö2=ú½(=‰)‘<«†ó<I.=åÕù<ëÚ=:=c¬<Zs~?Ø‘¼½¾mu=³B‘¼]-=š²S=q~·;ø =¶å’¼·ãö;ñ¦==Õ»ŸÊ«;ŽJ =2ʳ¼D[=¿ª¸ ¿»ÅÆ>¬§½ÙyÛ=Dþ>Ýp¿õwû<èö2=ú½(=‰)‘<ëÚ=:=c¬<’Ìê<Î=Ø™<êŠc?áæ¾åª¦½šlM½ž±>tž);qu@½ðø>X×»ªÆA½Æ£>÷˜á;Dìe¼GͿڄ=FFÅ»™'=•Þm¼¨K¼Í =ˆ¿»«©‘¼Û°=A#}¼ÑWe>ÿ'G¿ÏH¿lÕ)=¡æ:=iUh»vQô<Å=]: ;¸Iû<¢R6=Ê»g ;uü¿Õ'¼^™¿»e =&˜Á;ªá‘¼žs=7é‘ïP¹½Sêò=ÚÐ(=v™ ½¾‰á=†*+=œª½=Öì=¸Î?=Η”>—]¿ÔSÑ> 91=K·;=wñã<«†ó<I.=åÕù<èö2=ú½(=‰)‘<¬Û¬¾¥uX¿HÄÓ¾«©‘¼Û°=A#}¼æ õ¼@=}î¾»ú½¬ì6=7‰¼'8Ï= 5~¿"Íy=oÓ„<ÔÔ=VÑ„¼·ãö;ñ¦==Õ»q~·;ø =¶å’¼Ã½™¸W¿7¨?G-⻬â =Už¥¼q~·;ø =¶å’¼FFÅ»™'=•Þm¼òã+¾ˆv¿1ÕW>×|ûòO=±%õ>’d?í[G¿-‰½ ýì=ßïA=†‹½¦ Û=¢]%=±½föù= ÿS=¨çv¿6öU¾Š%>y½™~>W¥½CHŒ½+À>ÊRë¼mÖ½Üö>=ëÿ¼F7¾íX¾Êv?Ë+ü¼`= W1=èè½ïŸ8=Ê8=’ò.½¯´,=æ.=Ð+.¿ß”;¿8Ùk¿'÷Õ:Á]¡½°ó= §à;j{•½`_†=É×”<6¾ ½$q= ëŽ<ÚN¿|ø ¿áÖk>6¾ ½$q= 뎥笽0_£=§¤˜j{•½`_†=É×”j{•½`_†=É×”<À猽ËoŒ=õ4ûÊAA¾Ôr?U.Ø»/=7C@=šÖ»+ƒ= ñ9=&¿;ýÌ=h,=%’ྪ;c¿ðÖ¾·ãö;ñ¦==Õ»6˧;.´=5·;;²‡:Áù=‰žy:±Ë"¿Õ8?¿a´F¾ea½Ïh=÷®».óg½\`=yúó;ÓŒ½‹¢Œ=oœÖ»å¨ó¾d$K¿Ø.¾ӌ½‹¢Œ=oœÖ».óg½\`=yúó;F‡½Sz=Tß¿;Œâî<¤Ñ¿-‡Â¼ážÂ»ð‡=a»fj’¼Þñ=FoÄ;¨K¼Í =ˆ¿»Æ[=B¢¿¿$W¹ Ož<ÉÄ=J=ǽy(A$<}k=$ñò<ÿC´;zì=jûò<ɤ¯;´ =¯Y“< ú¾ORL¿=£´> 9†½]ýx=nð<À猽ËoŒ=õ4û.óg½\`=yúó;á2]½c=ðŒ‘<F‡½Sz=Tß¿;ßÓ¾¡ i¿cRí»F‡½Sz=Tß¿;á2]½c=ðŒ‘< 9†½]ýx=nð< ½Â¯¿D÷½ñba½‹µ = N¿;Þqнðb =ú}Á;Ñÿ‡½6 =¥ˆˆ»ªƒ½¤|¿Û÷»2†c½¸õ =“ÖÊ»ñba½‹µ = N¿;Ñÿ‡½6 =¥ˆˆ»òϿѡ¿äs"?À猽ËoŒ=õ4û<6 ‚½Å’=”m=F勽儤=yÀ&=èy?0G¿ªnÁ>Z=»íB=Ó•< 91=K·;=wñã<èö2=ú½(=‰)‘<rH½³Ÿ}¿d>ñba½‹µ = N¿;;…½ä±=‘ç„<Þqнðb =ú}Á;ƒ*Ó¾gÿf¿«D>óuY½ihh=‚Ðð< 9†½]ýx=nð<á2]½c=ðŒ‘<\³ ¿_èD¿ßƒ£>óuY½ihh=‚Ðð<À猽ËoŒ=õ4û< 9†½]ýx=nð<?Ð;¾ {¿€¼»´f½ð =¼“<;…½ä±=‘ç„<ñba½‹µ = N¿;;¾=JC^¿•‹ù>ëÚ=:=c¬<«†ó<I.=åÕù<’Ìê<Î=Ø™<×±¾P ¿‡±C?óuY½ihh=‚ÐðÊRë¼î‡Ž½í>Ø·“¼mÖ½Üö>=ëÿ¼ûu½>Æ$¿R¦+¿‘¼›Ÿ =JԼ ª»Q0=êß¼G-⻬â =Už¥¼6¥½úï|¿y—¾Ìû.½¸ =𶜼2†c½¸õ =“ÖÊ»ÒR½œ1 =ù_S¼ááL=ݾ=}?’ò.½¯´,=æ.=xAi½ì=ó-=’Q/½5=3À*=ݲ¾)œ¿H`À猽ËoŒ=õ4û<°[_½ÇY’=ß "=6 ‚½Å’=”m=T`=¾ÈyǾ0øf?6 ‚½Å’=”m=°[_½ÇY’=ß "=F勽儤=yÀ&=%ɘ¾ð¾j¿Õ‡>¨9ʹ&õ=eÓ[<ɤ¯;´ =¯Y“ð¾j?Ô‡¾É¤¯;´ =¯Y“<¨9ʹ&õ=eÓ[Ë+ü¼`= W1=sÝï¼ó+=©…R=èè½ïŸ8=Ê8= Ÿ=\ÿþ9™¼ Ož<ÉÄ=J=&¿;ýÌ=h,=(A$<}k=$ñò<{2&¿·«A¿ç8¡½ŸÊ«;ŽJ =2ʳ¼·ãö;ñ¦==Õ»¬ ;K=ž뻑ì5¾¿6v¿\fU¾å8½’€ =¸è¼Ìû.½¸ =𶜼}%+½Jy=A[ؼhnµ½Kñ~¿g£<¬ c½O =ô<4ºƒ½>I=»ã=ऄ½5¯=$=ñ<à6ô<’¹~¿–ÏÂ=ážÂ»ð‡=a»¨K¼Í =ˆ¿»FFÅ»™'=•Þm¼6S=xu¿‚:"=ªá‘¼žs=7é‘=þ‹Ö¼d©=Ñ‘<>{½9<¿î@=xAi½ì=ó-=4ºƒ½>I=»ã=¬ c½O =ô< m´>¿~o¿’›É<f2=c(=á¾;’Ìê<Î=Ø™×|ûòO=±%õ<^༌a3=\;1=¶’¼Â=¹žò<ª7=!ö¿Ú®J?šÖ»+ƒ= ñ9=³B‘¼]-=š²S=£#”¼V™=»é;=dî={¿´R>âV½Ö=R/“¼Ìû.½¸ =𶜼å8½’€ =¸è¼ÒBß¾²3'¾/Žb?4ºƒ½>I=»ã=xAi½ì=ó-=,§ƒ½q_"=ÇL"=á¹|?Áí!¾g¥<·*½"%,=”É#¼A0½qÕ =,u¹»Ìû.½¸ =𶜼:=`iD¿­î#?âV½Ö=R/“¼·*½"%,=”É#¼Ìû.½¸ =𶜼ØǾI¦Ž½1k?xAi½ì=ó-="šj½’1=/=,§ƒ½q_"=ÇL"= Å«½£Û¾vz|?âV½Ö=R/“¼ú½¬ì6=7‰¼Ìû.½¸ =𶜼 Å«=£Û>vz|¿ú½¬ì6=7‰¼âV½Ö=R/“¼Ìû.½¸ =𶜼¼©? D‰½†M?ú½¬ì6=7‰¼·*½"%,=”É#¼âV½Ö=R/“¼sv>…ìý¾öZ¿·*½"%,=”É#¼æ õ¼@=}î¾»A0½qÕ =,u¹»ÎX=´5¿2ÃS?-‰½ ýì=ßïA=±½föù= ÿS=ÞŸ½ì÷=´ˆR=Ä)޾™k¿P™>? ìG½«1‡=q,=°[_½ÇY’=ß "=jÚe½¯†= =¶‘‘¾Q‹¿ª=?œ0½[Š=S'=°[_½ÇY’=ß "= ìG½«1‡=q,=ˆ̽u;_¿Á`õ¾æ õ¼@=}î¾»·*½"%,=”É#¼ú½¬ì6=7‰¼Îñ«>!q¿‚B;èö2=ú½(=‰)‘<’Ìê<Î=Ø™<f2=c(=á¾;ʉÅ>ö"?ÿÈ+?q~·;ø =¶å’¼ŸÊ«;ŽJ =2ʳ¼FFÅ»™'=•Þm¼èþ;]™¿Oïb=²É+½YÇ =ï|õÂö“¼Ÿo= ¼âV½Ö=R/“¼å8½’€ =¸è¼¥üZH}¿KË>‘¼›Ÿ =JÔ¼Âö“¼Ÿo= ¼å8½’€ =¸è¼Np¾ÂÓ¾î4z?«©‘¼Û°=A#}¼ú½¬ì6=7‰¼âV½Ö=R/“¼e/ =‰ÇN¿R®?Âö“¼Ÿo= ¼«©‘¼Û°=A#}¼âV½Ö=R/“¼q‚É<@Ç¿;s =×|ûòO=±%õ<¶’¼Â=¹žò<ªá‘¼žs=7é‘<¨m¬½ƒt}¿[¿æ=‰\½ ˜=4’<½ì-½B =Þ†Á;a7 ½áF =°ë;astra-toolbox-2.3.0/samples/matlab/s001_sinogram_par2d.m000066400000000000000000000023071475635207100230620ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- % Create a basic 256x256 square volume geometry vol_geom = astra_create_vol_geom(256, 256); % Create a parallel beam geometry with 180 angles between 0 and pi, and % 384 detector pixels of width 1. % For more details on available geometries, see the online help of the % function astra_create_proj_geom . proj_geom = astra_create_proj_geom('parallel', 1.0, 384, linspace2(0,pi,180)); % Create a 256x256 phantom image using matlab's built-in phantom() function P = phantom(256); % Create a sinogram using the GPU. % Note that the first time the GPU is accessed, there may be a delay % of up to 10 seconds for initialization. [sinogram_id, sinogram] = astra_create_sino_gpu(P, proj_geom, vol_geom); figure(1); imshow(P, []); figure(2); imshow(sinogram, []); % Free memory astra_mex_data2d('delete', sinogram_id); astra-toolbox-2.3.0/samples/matlab/s002_data2d.m000066400000000000000000000027251475635207100213170ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- vol_geom = astra_create_vol_geom(256, 256); proj_geom = astra_create_proj_geom('parallel', 1.0, 384, linspace2(0,pi,180)); % Create volumes % initialized to zero v0 = astra_mex_data2d('create', '-vol', vol_geom); % initialized to 3.0 v1 = astra_mex_data2d('create', '-vol', vol_geom, 3.0); % initialized to a matrix. A may be a single, double or logical (0/1) array. A = phantom(256); v2 = astra_mex_data2d('create', '-vol', vol_geom, A); % Projection data s0 = astra_mex_data2d('create', '-sino', proj_geom); % Initialization to a scalar or a matrix also works, exactly as with a volume. % Update data % set to zero astra_mex_data2d('store', v0, 0); % set to a matrix astra_mex_data2d('store', v2, A); % Retrieve data R = astra_mex_data2d('get', v2); imshow(R, []); % Retrieve data as a single array. Since astra internally stores % data as single precision, this is more efficient: R = astra_mex_data2d('get_single', v2); % Free memory astra_mex_data2d('delete', v0); astra_mex_data2d('delete', v1); astra_mex_data2d('delete', v2); astra_mex_data2d('delete', s0); astra-toolbox-2.3.0/samples/matlab/s003_gpu_reconstruction.m000066400000000000000000000034111475635207100241060ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- vol_geom = astra_create_vol_geom(256, 256); proj_geom = astra_create_proj_geom('parallel', 1.0, 384, linspace2(0,pi,180)); % As before, create a sinogram from a phantom P = phantom(256); [sinogram_id, sinogram] = astra_create_sino_gpu(P, proj_geom, vol_geom); figure(1); imshow(P, []); figure(2); imshow(sinogram, []); astra_mex_data2d('delete', sinogram_id); % We now re-create the sinogram data object as we would do when loading % an external sinogram sinogram_id = astra_mex_data2d('create', '-sino', proj_geom, sinogram); % Create a data object for the reconstruction rec_id = astra_mex_data2d('create', '-vol', vol_geom); % Set up the parameters for a reconstruction algorithm using the GPU cfg = astra_struct('SIRT_CUDA'); cfg.ReconstructionDataId = rec_id; cfg.ProjectionDataId = sinogram_id; % Available algorithms: % SIRT_CUDA, SART_CUDA, EM_CUDA, FBP_CUDA (see the FBP sample) % Create the algorithm object from the configuration structure alg_id = astra_mex_algorithm('create', cfg); % Run 150 iterations of the algorithm astra_mex_algorithm('iterate', alg_id, 150); % Get the result rec = astra_mex_data2d('get', rec_id); figure(3); imshow(rec, []); % Clean up. Note that GPU memory is tied up in the algorithm object, % and main RAM in the data objects. astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', rec_id); astra_mex_data2d('delete', sinogram_id); astra-toolbox-2.3.0/samples/matlab/s004_cpu_reconstruction.m000066400000000000000000000040101475635207100240770ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- vol_geom = astra_create_vol_geom(256, 256); proj_geom = astra_create_proj_geom('parallel', 1.0, 384, linspace2(0,pi,180)); % For CPU-based algorithms, a "projector" object specifies the projection % model used. In this case, we use the "strip" model. proj_id = astra_create_projector('strip', proj_geom, vol_geom); % Create a sinogram from a phantom P = phantom(256); [sinogram_id, sinogram] = astra_create_sino(P, proj_id); figure(1); imshow(P, []); figure(2); imshow(sinogram, []); astra_mex_data2d('delete', sinogram_id); % We now re-create the sinogram data object as we would do when loading % an external sinogram sinogram_id = astra_mex_data2d('create', '-sino', proj_geom, sinogram); % Create a data object for the reconstruction rec_id = astra_mex_data2d('create', '-vol', vol_geom); % Set up the parameters for a reconstruction algorithm using the CPU % The main difference with the configuration of a GPU algorithm is the % extra ProjectorId setting. cfg = astra_struct('SIRT'); cfg.ReconstructionDataId = rec_id; cfg.ProjectionDataId = sinogram_id; cfg.ProjectorId = proj_id; % Available algorithms: % ART, SART, SIRT, CGLS, FBP % Create the algorithm object from the configuration structure alg_id = astra_mex_algorithm('create', cfg); % Run 20 iterations of the algorithm % This will have a runtime in the order of 10 seconds. astra_mex_algorithm('iterate', alg_id, 20); % Get the result rec = astra_mex_data2d('get', rec_id); figure(3); imshow(rec, []); % Clean up. astra_mex_projector('delete', proj_id); astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', rec_id); astra_mex_data2d('delete', sinogram_id); astra-toolbox-2.3.0/samples/matlab/s005_3d_geometry.m000066400000000000000000000047771475635207100224150ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- vol_geom = astra_create_vol_geom(64, 64, 64); % There are two main 3d projection geometry types: cone beam and parallel beam. % Each has a regular variant, and a 'vec' variant. % The 'vec' variants are completely free in the placement of source/detector, % while the regular variants assume circular trajectories around the z-axis. % ------------- % Parallel beam % ------------- % Circular % Parameters: width of detector column, height of detector row, #rows, #columns angles = linspace2(0, 2*pi, 48); proj_geom = astra_create_proj_geom('parallel3d', 1.0, 1.0, 32, 64, angles); % Free % We generate the same geometry as the circular one above. vectors = zeros(numel(angles), 12); for i = 1:numel(angles) % ray direction vectors(i,1) = sin(angles(i)); vectors(i,2) = -cos(angles(i)); vectors(i,3) = 0; % center of detector vectors(i,4:6) = 0; % vector from detector pixel (0,0) to (0,1) vectors(i,7) = cos(angles(i)); vectors(i,8) = sin(angles(i)); vectors(i,9) = 0; % vector from detector pixel (0,0) to (1,0) vectors(i,10) = 0; vectors(i,11) = 0; vectors(i,12) = 1; end % Parameters: #rows, #columns, vectors proj_geom = astra_create_proj_geom('parallel3d_vec', 32, 64, vectors); % ---------- % Cone beam % ---------- % Circular % Parameters: width of detector column, height of detector row, #rows, #columns, % angles, distance source-origin, distance origin-detector angles = linspace2(0, 2*pi, 48); proj_geom = astra_create_proj_geom('cone', 1.0, 1.0, 32, 64, ... angles, 1000, 0); % Free vectors = zeros(numel(angles), 12); for i = 1:numel(angles) % source vectors(i,1) = sin(angles(i)) * 1000; vectors(i,2) = -cos(angles(i)) * 1000; vectors(i,3) = 0; % center of detector vectors(i,4:6) = 0; % vector from detector pixel (0,0) to (0,1) vectors(i,7) = cos(angles(i)); vectors(i,8) = sin(angles(i)); vectors(i,9) = 0; % vector from detector pixel (0,0) to (1,0) vectors(i,10) = 0; vectors(i,11) = 0; vectors(i,12) = 1; end % Parameters: #rows, #columns, vectors proj_geom = astra_create_proj_geom('cone_vec', 32, 64, vectors); astra-toolbox-2.3.0/samples/matlab/s006_3d_data.m000066400000000000000000000034421475635207100214600ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- % Create a 3D volume geometry. % Parameter order: rows, colums, slices (y, x, z) vol_geom = astra_create_vol_geom(64, 48, 32); % Create volumes % initialized to zero v0 = astra_mex_data3d('create', '-vol', vol_geom); % initialized to 3.0 v1 = astra_mex_data3d('create', '-vol', vol_geom, 3.0); % initialized to a matrix. A may be a single or double array. % Coordinate order: column, row, slice (x, y, z) A = zeros(48, 64, 32); v2 = astra_mex_data3d('create', '-vol', vol_geom, A); % Projection data % 2 projection directions, along x and y axis resp. V = [ 1 0 0 0 0 0 0 1 0 0 0 1 ; ... 0 1 0 0 0 0 -1 0 0 0 0 1 ]; % 32 rows (v), 64 columns (u) proj_geom = astra_create_proj_geom('parallel3d_vec', 32, 64, V); s0 = astra_mex_data3d('create', '-proj3d', proj_geom); % Initialization to a scalar or zero works exactly as with a volume. % Initialized to a matrix: % Coordinate order: column (u), angle, row (v) A = zeros(64, 2, 32); s1 = astra_mex_data3d('create', '-proj3d', proj_geom, A); % Retrieve data: R = astra_mex_data3d('get', v1); % Retrieve data as a single array. Since astra internally stores % data as single precision, this is more efficient: R = astra_mex_data3d('get_single', v1); % Delete all created data objects astra_mex_data3d('delete', v0); astra_mex_data3d('delete', v1); astra_mex_data3d('delete', v2); astra_mex_data3d('delete', s0); astra_mex_data3d('delete', s1); astra-toolbox-2.3.0/samples/matlab/s007_3d_reconstruction.m000066400000000000000000000033571475635207100236360ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- vol_geom = astra_create_vol_geom(128, 128, 128); angles = linspace2(0, pi, 180); proj_geom = astra_create_proj_geom('parallel3d', 1.0, 1.0, 128, 192, angles); % Create a simple hollow cube phantom cube = zeros(128,128,128); cube(17:112,17:112,17:112) = 1; cube(33:96,33:96,33:96) = 0; % Create projection data from this [proj_id, proj_data] = astra_create_sino3d_cuda(cube, proj_geom, vol_geom); % Display a single projection image figure, imshow(squeeze(proj_data(:,20,:))',[]) % Create a data object for the reconstruction rec_id = astra_mex_data3d('create', '-vol', vol_geom); % Set up the parameters for a reconstruction algorithm using the GPU cfg = astra_struct('SIRT3D_CUDA'); cfg.ReconstructionDataId = rec_id; cfg.ProjectionDataId = proj_id; % Create the algorithm object from the configuration structure alg_id = astra_mex_algorithm('create', cfg); % Run 150 iterations of the algorithm % Note that this requires about 750MB of GPU memory, and has a runtime % in the order of 10 seconds. astra_mex_algorithm('iterate', alg_id, 150); % Get the result rec = astra_mex_data3d('get', rec_id); figure, imshow(squeeze(rec(:,:,65)),[]); % Clean up. Note that GPU memory is tied up in the algorithm object, % and main RAM in the data objects. astra_mex_algorithm('delete', alg_id); astra_mex_data3d('delete', rec_id); astra_mex_data3d('delete', proj_id); astra-toolbox-2.3.0/samples/matlab/s008_gpu_selection.m000066400000000000000000000023451475635207100230240ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- % Switch to GPU #1. The default is GPU #0. astra_set_gpu_index(1); vol_geom = astra_create_vol_geom(256, 256); proj_geom = astra_create_proj_geom('parallel', 1.0, 384, linspace2(0,pi,180)); P = phantom(256); % Create a sinogram from a phantom. [sinogram_id, sinogram] = astra_create_sino_gpu(P, proj_geom, vol_geom); % Set up the parameters for a reconstruction algorithm using the GPU rec_id = astra_mex_data2d('create', '-vol', vol_geom); cfg = astra_struct('SIRT_CUDA'); cfg.ReconstructionDataId = rec_id; cfg.ProjectionDataId = sinogram_id; % Run 150 iterations of the algorithm alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('iterate', alg_id, 150); rec = astra_mex_data2d('get', rec_id); % Clean up. astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', rec_id); astra_mex_data2d('delete', sinogram_id); astra-toolbox-2.3.0/samples/matlab/s009_projection_matrix.m000066400000000000000000000035451475635207100237300ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- vol_geom = astra_create_vol_geom(256, 256); proj_geom = astra_create_proj_geom('parallel', 1.0, 384, linspace2(0,pi,180)); % For CPU-based algorithms, a "projector" object specifies the projection % model used. In this case, we use the "strip" model. proj_id = astra_create_projector('strip', proj_geom, vol_geom); % Generate the projection matrix for this projection model. % This creates a matrix W where entry w_{i,j} corresponds to the % contribution of volume element j to detector element i. matrix_id = astra_mex_projector('matrix', proj_id); % Get the projection matrix as a Matlab sparse matrix. W = astra_mex_matrix('get', matrix_id); % Manually use this projection matrix to do a projection: P = phantom(256)'; s = W * P(:); s = reshape(s, [proj_geom.DetectorCount size(proj_geom.ProjectionAngles, 2)])'; figure(1), imshow(s,[]); % Because Matlab's matrices are stored transposed in memory compared to C++, % reshaping them to a vector doesn't give the right ordering for multiplication % with W. We have to take the transpose of the input and output to get the same % results (up to numerical noise) as using the toolbox directly. % Each row of the projection matrix corresponds to a detector element. % Detector t for angle p is for row 1 + t + p*proj_geom.DetectorCount. % Each column corresponds to a volume pixel. % Pixel (x,y) corresponds to column 1 + x + y*vol_geom.GridColCount. astra_mex_projector('delete', proj_id); astra_mex_matrix('delete', matrix_id); astra-toolbox-2.3.0/samples/matlab/s010_supersampling.m000066400000000000000000000031631475635207100230450ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- vol_geom = astra_create_vol_geom(256, 256); proj_geom = astra_create_proj_geom('parallel', 3.0, 128, linspace2(0,pi,180)); P = phantom(256); % We create a projector set up to use 3 rays per detector element cfg_proj = astra_struct('cuda'); cfg_proj.option.DetectorSuperSampling = 3; cfg_proj.ProjectionGeometry = proj_geom; cfg_proj.VolumeGeometry = vol_geom; proj_id = astra_mex_projector('create', cfg_proj); [sinogram_id sinogram3] = astra_create_sino(P, proj_id); figure(1); imshow(P, []); figure(2); imshow(sinogram3, []); % Create a reconstruction, also using supersampling rec_id = astra_mex_data2d('create', '-vol', vol_geom); cfg = astra_struct('SIRT_CUDA'); cfg.ReconstructionDataId = rec_id; cfg.ProjectionDataId = sinogram_id; cfg.ProjectorId = proj_id; % There is also an option for supersampling during the backprojection step. % This should be used if your detector pixels are smaller than the voxels. % Set up 2 rays per image pixel dimension, for 4 rays total per image pixel. % cfg_proj.option.PixelSuperSampling = 2; alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('iterate', alg_id, 150); astra_mex_algorithm('delete', alg_id); rec = astra_mex_data2d('get', rec_id); figure(3); imshow(rec, []); astra-toolbox-2.3.0/samples/matlab/s011_object_info.m000066400000000000000000000020461475635207100224350ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- % Create two volume geometries vol_geom1 = astra_create_vol_geom(256, 256); vol_geom2 = astra_create_vol_geom(512, 256); % Create volumes v0 = astra_mex_data2d('create', '-vol', vol_geom1); v1 = astra_mex_data2d('create', '-vol', vol_geom2); v2 = astra_mex_data2d('create', '-vol', vol_geom2); % Show the currently allocated volumes astra_mex_data2d('info'); astra_mex_data2d('delete', v2); astra_mex_data2d('info'); astra_mex_data2d('clear'); astra_mex_data2d('info'); % The same clear and info command also work for other object types: astra_mex_algorithm('info'); astra_mex_data3d('info'); astra_mex_projector('info'); astra_mex_matrix('info'); astra-toolbox-2.3.0/samples/matlab/s012_masks.m000066400000000000000000000037131475635207100212750ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- % In this example we will create a reconstruction in a circular region, % instead of the usual rectangle. % This is done by placing a circular mask on the square reconstruction volume: c = -127.5:127.5; [x y] = meshgrid(-127.5:127.5,-127.5:127.5); mask = (x.^2 + y.^2 < 127.5^2); figure(1); imshow(mask, []); vol_geom = astra_create_vol_geom(256, 256); proj_geom = astra_create_proj_geom('parallel', 1.0, 384, linspace2(0,pi,50)); % As before, create a sinogram from a phantom P = phantom(256); [sinogram_id, sinogram] = astra_create_sino_gpu(P, proj_geom, vol_geom); figure(2); imshow(P, []); figure(3); imshow(sinogram, []); % Create a data object for the reconstruction rec_id = astra_mex_data2d('create', '-vol', vol_geom); % Create a data object for the mask mask_id = astra_mex_data2d('create', '-vol', vol_geom, mask); % Set up the parameters for a reconstruction algorithm using the GPU cfg = astra_struct('SIRT_CUDA'); cfg.ReconstructionDataId = rec_id; cfg.ProjectionDataId = sinogram_id; cfg.option.ReconstructionMaskId = mask_id; % Create the algorithm object from the configuration structure alg_id = astra_mex_algorithm('create', cfg); % Run 150 iterations of the algorithm astra_mex_algorithm('iterate', alg_id, 150); % Get the result rec = astra_mex_data2d('get', rec_id); figure(4); imshow(rec, []); % Clean up. Note that GPU memory is tied up in the algorithm object, % and main RAM in the data objects. astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', mask_id); astra_mex_data2d('delete', rec_id); astra_mex_data2d('delete', sinogram_id); astra-toolbox-2.3.0/samples/matlab/s013_constraints.m000066400000000000000000000031701475635207100225240ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- % In this example we will create a reconstruction constrained to % greyvalues between 0 and 1 vol_geom = astra_create_vol_geom(256, 256); proj_geom = astra_create_proj_geom('parallel', 1.0, 384, linspace2(0,pi,50)); % As before, create a sinogram from a phantom P = phantom(256); [sinogram_id, sinogram] = astra_create_sino_gpu(P, proj_geom, vol_geom); figure(1); imshow(P, []); figure(2); imshow(sinogram, []); % Create a data object for the reconstruction rec_id = astra_mex_data2d('create', '-vol', vol_geom); % Set up the parameters for a reconstruction algorithm using the GPU cfg = astra_struct('SIRT_CUDA'); cfg.ReconstructionDataId = rec_id; cfg.ProjectionDataId = sinogram_id; cfg.option.MinConstraint = 0; cfg.option.MaxConstraint = 1; % Create the algorithm object from the configuration structure alg_id = astra_mex_algorithm('create', cfg); % Run 150 iterations of the algorithm astra_mex_algorithm('iterate', alg_id, 150); % Get the result rec = astra_mex_data2d('get', rec_id); figure(3); imshow(rec, []); % Clean up. Note that GPU memory is tied up in the algorithm object, % and main RAM in the data objects. astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', rec_id); astra_mex_data2d('delete', sinogram_id); astra-toolbox-2.3.0/samples/matlab/s014_FBP.m000066400000000000000000000032251475635207100205660ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- vol_geom = astra_create_vol_geom(256, 256); proj_geom = astra_create_proj_geom('fanflat', 1.0, 384, linspace2(0,2*pi,1800), 500, 0); % As before, create a sinogram from a phantom P = phantom(256); [sinogram_id, sinogram] = astra_create_sino_gpu(P, proj_geom, vol_geom); figure(1); imshow(P, []); figure(2); imshow(sinogram, []); % Create a data object for the reconstruction rec_id = astra_mex_data2d('create', '-vol', vol_geom); % create configuration cfg = astra_struct('FBP_CUDA'); cfg.ReconstructionDataId = rec_id; cfg.ProjectionDataId = sinogram_id; cfg.option.FilterType = 'Ram-Lak'; % possible values for FilterType: % none, ram-lak, shepp-logan, cosine, hamming, hann, tukey, lanczos, % triangular, gaussian, barlett-hann, blackman, nuttall, blackman-harris, % blackman-nuttall, flat-top, kaiser, parzen % Create and run the algorithm object from the configuration structure alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('run', alg_id); % Get the result rec = astra_mex_data2d('get', rec_id); figure(3); imshow(rec, []); % Clean up. Note that GPU memory is tied up in the algorithm object, % and main RAM in the data objects. astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', rec_id); astra_mex_data2d('delete', sinogram_id); astra-toolbox-2.3.0/samples/matlab/s015_fp_bp.m000066400000000000000000000042411475635207100212450ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- % This example demonstrates using the FP and BP primitives with Matlab's lsqr % solver. Calls to FP (astra_create_sino_cuda) and % BP (astra_create_backprojection_cuda) are wrapped in a function astra_wrap, % and a handle to this function is passed to lsqr. % Because in this case the inputs/outputs of FP and BP have to be vectors % instead of images (matrices), the calls require reshaping to and from vectors. function s015_fp_bp % FP/BP wrapper function function Y = astra_wrap(X,T) if strcmp(T, 'notransp') % X is passed as a vector. Reshape it into an image. [sid, s] = astra_create_sino_cuda(reshape(X,[vol_geom.GridRowCount vol_geom.GridColCount])', proj_geom, vol_geom); astra_mex_data2d('delete', sid); % now s is the sinogram. Reshape it back into a vector Y = reshape(s',[prod(size(s)) 1]); else % X is passed as a vector. Reshape it into a sinogram. v = astra_create_backprojection_cuda(reshape(X, [proj_geom.DetectorCount size(proj_geom.ProjectionAngles,2)])', proj_geom, vol_geom); % now v is the resulting volume. Reshape it back into a vector Y = reshape(v',[prod(size(v)) 1]); end end vol_geom = astra_create_vol_geom(256, 256); proj_geom = astra_create_proj_geom('parallel', 1.0, 384, linspace2(0,pi,180)); % Create a 256x256 phantom image using matlab's built-in phantom() function P = phantom(256); % Create a sinogram using the GPU. [sinogram_id, sinogram] = astra_create_sino_gpu(P, proj_geom, vol_geom); % Reshape the sinogram into a vector b = reshape(sinogram',[prod(size(sinogram)) 1]); % Call Matlab's lsqr with ASTRA FP and BP Y = lsqr(@astra_wrap,b,1e-4,25); % Reshape the result into an image Y = reshape(Y,[vol_geom.GridRowCount vol_geom.GridColCount])'; imshow(Y,[]); astra_mex_data2d('delete', sinogram_id); end astra-toolbox-2.3.0/samples/matlab/s016_plots.m000066400000000000000000000034071475635207100213240ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- vol_geom = astra_create_vol_geom(256, 256); proj_geom = astra_create_proj_geom('parallel', 1.0, 384, linspace2(0,pi,180)); % As before, create a sinogram from a phantom P = phantom(256); [sinogram_id, sinogram] = astra_create_sino_gpu(P, proj_geom, vol_geom); figure(1); imshow(P, []); figure(2); imshow(sinogram, []); % Create a data object for the reconstruction rec_id = astra_mex_data2d('create', '-vol', vol_geom); % Set up the parameters for a reconstruction algorithm using the GPU cfg = astra_struct('SIRT_CUDA'); cfg.ReconstructionDataId = rec_id; cfg.ProjectionDataId = sinogram_id; % Create the algorithm object from the configuration structure alg_id = astra_mex_algorithm('create', cfg); % Run 1500 iterations of the algorithm one at a time, keeping track of errors nIters = 1500; phantom_error = zeros(1, nIters); residual_error = zeros(1, nIters); for i = 1:nIters; % Run a single iteration astra_mex_algorithm('iterate', alg_id, 1); residual_error(i) = astra_mex_algorithm('get_res_norm', alg_id); rec = astra_mex_data2d('get', rec_id); phantom_error(i) = sqrt(sumsqr(rec - P)); end % Get the result rec = astra_mex_data2d('get', rec_id); figure(3); imshow(rec, []); figure(4); plot(residual_error) figure(5); plot(phantom_error) % Clean up. astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', rec_id); astra_mex_data2d('delete', sinogram_id); astra-toolbox-2.3.0/samples/matlab/s017_opTomo.m000066400000000000000000000034341475635207100214410ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- % This sample illustrates the use of opTomo. % % opTomo is a wrapper around the FP and BP operations of the ASTRA Toolbox, % to allow you to use them as you would a matrix. % % This class requires the Spot Linear-Operator Toolbox to be installed. % You can download this at http://www.cs.ubc.ca/labs/scl/spot/ % load a phantom image im = phantom(256); % and flatten it to a vector x = im(:); %% Setting up the geometry % projection geometry proj_geom = astra_create_proj_geom('parallel', 1, 256, linspace2(0,pi,180)); % object dimensions vol_geom = astra_create_vol_geom(256,256); %% Generate projection data % Create the Spot operator for ASTRA using the GPU. W = opTomo('cuda', proj_geom, vol_geom); p = W*x; % reshape the vector into a sinogram sinogram = reshape(p, W.proj_size); imshow(sinogram, []); %% Reconstruction % We use a least squares solver lsqr from Matlab to solve the % equation W*x = p. % Max number of iterations is 100, convergence tolerance of 1e-6. y = lsqr(W, p, 1e-6, 100); % the output is a vector, so we reshape it into an image reconstruction = reshape(y, W.vol_size); subplot(1,3,1); imshow(reconstruction, []); title('Reconstruction'); subplot(1,3,2); imshow(im, []); title('Ground truth'); % The transpose of the operator corresponds to the backprojection. backProjection = W'*p; subplot(1,3,3); imshow(reshape(backProjection, W.vol_size), []); title('Backprojection'); astra-toolbox-2.3.0/samples/matlab/s020_3d_multiGPU.m000066400000000000000000000024751475635207100222560ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- % Set up multi-GPU usage. % This only works for 3D GPU forward projection and back projection. astra_mex('set_gpu_index', [0 1]); % Optionally, you can also restrict the amount of GPU memory ASTRA will use. % The line commented below sets this to 1GB. %astra_mex('set_gpu_index', [0 1], 'memory', 1024*1024*1024); vol_geom = astra_create_vol_geom(1024, 1024, 1024); angles = linspace2(0, pi, 1024); proj_geom = astra_create_proj_geom('parallel3d', 1.0, 1.0, 1024, 1024, angles); % Create a simple hollow cube phantom cube = zeros(1024,1024,1024); cube(129:896,129:896,129:896) = 1; cube(257:768,257:768,257:768) = 0; % Create projection data from this [proj_id, proj_data] = astra_create_sino3d_cuda(cube, proj_geom, vol_geom); % Backproject projection data [bproj_id, bproj_data] = astra_create_backprojection3d_cuda(proj_data, proj_geom, vol_geom); astra_mex_data3d('delete', proj_id); astra_mex_data3d('delete', bproj_id); astra-toolbox-2.3.0/samples/matlab/s022_fbp_cor.m000066400000000000000000000044701475635207100215730ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- cor_shift = 3.6; vol_geom = astra_create_vol_geom(256, 256); proj_geom = astra_create_proj_geom('parallel', 1.0, 256, linspace2(0,pi,180)); % Projection geometry with shifted center of rotation proj_geom_cor = astra_geom_postalignment(proj_geom, cor_shift); % As before, create a sinogram from a phantom, using the shifted center of rotation P = phantom(256); [sinogram_id, sinogram] = astra_create_sino_gpu(P, proj_geom_cor, vol_geom); figure(1); imshow(P, []); figure(2); imshow(sinogram, []); astra_mex_data2d('delete', sinogram_id); % We now re-create the sinogram data object as we would do when loading % an external sinogram, using a standard geometry, and try to do a reconstruction, % to show the misalignment artifacts caused by the shifted center of rotation sinogram_id = astra_mex_data2d('create', '-sino', proj_geom, sinogram); % Create a data object for the reconstruction rec_id = astra_mex_data2d('create', '-vol', vol_geom); % Set up the parameters for a reconstruction algorithm using the GPU cfg = astra_struct('FBP_CUDA'); cfg.ReconstructionDataId = rec_id; cfg.ProjectionDataId = sinogram_id; alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('run', alg_id); % Get the result rec = astra_mex_data2d('get', rec_id); figure(3); imshow(rec, []); astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', rec_id); % Now change back to the proper, shifted geometry, and do another reconstruction astra_mex_data2d('change_geometry', sinogram_id, proj_geom_cor); rec_id = astra_mex_data2d('create', '-vol', vol_geom); cfg = astra_struct('FBP_CUDA'); cfg.ReconstructionDataId = rec_id; cfg.ProjectionDataId = sinogram_id; alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('run', alg_id); % Get the result rec = astra_mex_data2d('get', rec_id); figure(4); imshow(rec, []); astra_mex_algorithm('delete', alg_id); astra_mex_data2d('delete', rec_id); astra_mex_data2d('delete', sinogram_id); astra-toolbox-2.3.0/samples/matlab/s023_FBP_filters.m000066400000000000000000000060321475635207100223150ustar00rootroot00000000000000% ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- % This sample script illustrates three ways of passing filters to FBP. % They work with both the FBP (CPU) and the FBP_CUDA (GPU) algorithms. N = 256; vol_geom = astra_create_vol_geom(N, N); proj_geom = astra_create_proj_geom('parallel', 1.0, N, linspace2(0,pi,180)); proj_id = astra_create_projector('strip', proj_geom, vol_geom); P = phantom(256); [sinogram_id, sinogram] = astra_create_sino(P, proj_id); rec_id = astra_mex_data2d('create', '-vol', vol_geom); cfg = astra_struct('FBP'); cfg.ReconstructionDataId = rec_id; cfg.ProjectionDataId = sinogram_id; cfg.ProjectorId = proj_id; % 1. Use a standard Ram-Lak filter cfg.option.FilterType = 'ram-lak'; alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('run', alg_id); rec_RL = astra_mex_data2d('get', rec_id); astra_mex_algorithm('delete', alg_id); % 2. Define a filter in Fourier space % This is assumed to be symmetric, and ASTRA therefore expects only half % The full filter size should be the smallest power of two that is at least % twice the number of detector pixels. fullFilterSize = 2*N; kernel = [linspace2(0, 1, floor(fullFilterSize / 2)) linspace2(1, 0, ceil(fullFilterSize / 2))]; halfFilterSize = floor(fullFilterSize / 2) + 1; filter = kernel(1:halfFilterSize); filter_geom = astra_create_proj_geom('parallel', 1.0, halfFilterSize, [0]); filter_id = astra_mex_data2d('create', '-sino', filter_geom, filter); cfg.option.FilterType = 'projection'; cfg.option.FilterSinogramId = filter_id; alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('run', alg_id); rec_filter = astra_mex_data2d('get', rec_id); astra_mex_algorithm('delete', alg_id); % 3. Define a (spatial) convolution kernel directly % For a kernel of odd size 2*k+1, the central component is at kernel(k+1) % For a kernel of even size 2*k, the central component is at kernel(k+1) kernel = zeros(1, N); for i = 0:floor(N/4)-1 f = pi * (2*i + 1); val = -2.0 / (f * f); kernel(floor(N/2) + 1 + (2*i+1)) = val; kernel(floor(N/2) + 1 - (2*i+1)) = val; end kernel(floor(N/2)+1) = 0.5; kernel_geom = astra_create_proj_geom('parallel', 1.0, N, [0]); kernel_id = astra_mex_data2d('create', '-sino', kernel_geom, kernel); cfg.option.FilterType = 'rprojection'; cfg.option.FilterSinogramId = kernel_id; alg_id = astra_mex_algorithm('create', cfg); astra_mex_algorithm('run', alg_id); rec_kernel = astra_mex_data2d('get', rec_id); astra_mex_algorithm('delete', alg_id); figure(1); imshow(P, []); figure(2); imshow(rec_RL, []); figure(3); imshow(rec_filter, []); figure(4); imshow(rec_kernel, []); astra_mex_data2d('delete', rec_id); astra_mex_data2d('delete', sinogram_id); astra_mex_projector('delete', proj_id); astra-toolbox-2.3.0/samples/matlab/s024_plot_geometry.m000066400000000000000000000030541475635207100230510ustar00rootroot00000000000000%% s024_plot_geometry.m % brief example of usage for astra_plot_geom command % - last update 16.11.2018 % ----------------------------------------------------------------------- % This file is part of the ASTRA Toolbox % % Copyright: 2010-2022, imec Vision Lab, University of Antwerp % 2014-2022, CWI, Amsterdam % License: Open Source under GPLv3 % Contact: astra@astra-toolbox.com % Website: http://www.astra-toolbox.com/ % ----------------------------------------------------------------------- %% close all; if exist('astra_create_example_cone') ~= 2 error('Please add astra/algorithms/plot_geom to your path to use this function') end % proj_geom = astra_create_example_cone('vec'); % proj_geom = astra_create_example_cone('normal'); proj_geom = astra_create_example_cone('helix'); % proj_geom = astra_create_example_parallel3d('vec'); % proj_geom = astra_create_example_fanflat('vec'); % proj_geom = astra_create_example_fanflat(); % proj_geom = astra_create_example_parallel3d(); % proj_geom = astra_create_example_cone('deform_vec'); astra_plot_geom(proj_geom); hold on; vol_magn = 20; phantom_size = 5; phantom_px = 1500; vx_size = phantom_size / phantom_px; % voxel size vol_geom = astra_create_vol_geom(phantom_px, phantom_px, phantom_px); line_width = 1; % line width for phantom astra_plot_geom(vol_geom, vx_size, 'Magnification', vol_magn,... 'LineWidth', line_width, 'Color', 'r'); % this magnification is empirically chosen to fit the stl file cad_magn = 900; astra_plot_geom('bunny.stl', cad_magn); hold off; axis equal; astra-toolbox-2.3.0/samples/python/000077500000000000000000000000001475635207100173115ustar00rootroot00000000000000astra-toolbox-2.3.0/samples/python/s001_sinogram_par2d.py000066400000000000000000000037511475635207100233430ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np # Create a basic 256x256 square volume geometry vol_geom = astra.create_vol_geom(256, 256) # Create a parallel beam geometry with 180 angles between 0 and pi, and # 384 detector pixels of width 1. # For more details on available geometries, see the online help of the # function astra_create_proj_geom . proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,180,False)) # Generate a 256x256 phantom image phantom_id, P = astra.data2d.shepp_logan(vol_geom) # Create a sinogram using the GPU. # Note that the first time the GPU is accessed, there may be a delay # of up to 10 seconds for initialization. proj_id = astra.create_projector('cuda',proj_geom,vol_geom) sinogram_id, sinogram = astra.create_sino(P, proj_id) import pylab pylab.gray() pylab.figure(1) pylab.imshow(P) pylab.figure(2) pylab.imshow(sinogram) pylab.show() # Free memory astra.data2d.delete(sinogram_id) astra.data2d.delete(phantom_id) astra.projector.delete(proj_id) astra-toolbox-2.3.0/samples/python/s002_data2d.py000066400000000000000000000037351475635207100215760ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,180,False)) # Create volumes # initialized to zero v0 = astra.data2d.create('-vol', vol_geom) # initialized to 3.0 v1 = astra.data2d.create('-vol', vol_geom, 3.0) # initialized to a matrix. A may be a single, double or logical (0/1) array. phantom_id, A = astra.data2d.shepp_logan(vol_geom) v2 = astra.data2d.create('-vol', vol_geom, A) # Projection data s0 = astra.data2d.create('-sino', proj_geom) # Initialization to a scalar or a matrix also works, exactly as with a volume. # Update data # set to zero astra.data2d.store(v0, 0) # set to a matrix astra.data2d.store(v2, A) # Retrieve data R = astra.data2d.get(v2) import pylab pylab.gray() pylab.imshow(R) pylab.show() # Free memory astra.data2d.delete(v0) astra.data2d.delete(v1) astra.data2d.delete(v2) astra.data2d.delete(s0) astra.data2d.delete(phantom_id) astra-toolbox-2.3.0/samples/python/s003_gpu_reconstruction.py000066400000000000000000000045341475635207100243720ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,180,False)) # As before, create a sinogram from a phantom phantom_id, P = astra.data2d.shepp_logan(vol_geom) proj_id = astra.create_projector('cuda',proj_geom,vol_geom) sinogram_id, sinogram = astra.create_sino(P, proj_id) import pylab pylab.gray() pylab.figure(1) pylab.imshow(P) pylab.figure(2) pylab.imshow(sinogram) # Create a data object for the reconstruction rec_id = astra.data2d.create('-vol', vol_geom) # Set up the parameters for a reconstruction algorithm using the GPU cfg = astra.astra_dict('SIRT_CUDA') cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = sinogram_id # Available algorithms: # SIRT_CUDA, SART_CUDA, EM_CUDA, FBP_CUDA (see the FBP sample) # Create the algorithm object from the configuration structure alg_id = astra.algorithm.create(cfg) # Run 150 iterations of the algorithm astra.algorithm.run(alg_id, 150) # Get the result rec = astra.data2d.get(rec_id) pylab.figure(3) pylab.imshow(rec) pylab.show() # Clean up. Note that GPU memory is tied up in the algorithm object, # and main RAM in the data objects. astra.algorithm.delete(alg_id) astra.data2d.delete(rec_id) astra.data2d.delete(sinogram_id) astra.data2d.delete(phantom_id) astra.projector.delete(proj_id) astra-toolbox-2.3.0/samples/python/s004_cpu_reconstruction.py000066400000000000000000000050201475635207100243560ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,180,False)) # For CPU-based algorithms, a "projector" object specifies the projection # model used. In this case, we use the "strip" model. proj_id = astra.create_projector('strip', proj_geom, vol_geom) # Create a sinogram from a phantom phantom_id, P = astra.data2d.shepp_logan(vol_geom) sinogram_id, sinogram = astra.create_sino(phantom_id, proj_id) import pylab pylab.gray() pylab.figure(1) pylab.imshow(P) pylab.figure(2) pylab.imshow(sinogram) # Create a data object for the reconstruction rec_id = astra.data2d.create('-vol', vol_geom) # Set up the parameters for a reconstruction algorithm using the CPU # The main difference with the configuration of a GPU algorithm is the # extra ProjectorId setting. cfg = astra.astra_dict('SIRT') cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = sinogram_id cfg['ProjectorId'] = proj_id # Available algorithms: # ART, SART, SIRT, CGLS, FBP # Create the algorithm object from the configuration structure alg_id = astra.algorithm.create(cfg) # Run 20 iterations of the algorithm # This will have a runtime in the order of 10 seconds. astra.algorithm.run(alg_id, 20) # Get the result rec = astra.data2d.get(rec_id) pylab.figure(3) pylab.imshow(rec) pylab.show() # Clean up. astra.algorithm.delete(alg_id) astra.data2d.delete(rec_id) astra.data2d.delete(sinogram_id) astra.data2d.delete(phantom_id) astra.projector.delete(proj_id) astra-toolbox-2.3.0/samples/python/s005_3d_geometry.py000066400000000000000000000063571475635207100226660ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- try: from six.moves import range except ImportError: # six 1.3.0 from six.moves import xrange as range import astra import numpy as np vol_geom = astra.create_vol_geom(64, 64, 64) # There are two main 3d projection geometry types: cone beam and parallel beam. # Each has a regular variant, and a 'vec' variant. # The 'vec' variants are completely free in the placement of source/detector, # while the regular variants assume circular trajectories around the z-axis. # ------------- # Parallel beam # ------------- # Circular # Parameters: width of detector column, height of detector row, #rows, #columns angles = np.linspace(0, 2*np.pi, 48, False) proj_geom = astra.create_proj_geom('parallel3d', 1.0, 1.0, 32, 64, angles) # Free # We generate the same geometry as the circular one above. vectors = np.zeros((len(angles), 12)) for i in range(len(angles)): # ray direction vectors[i,0] = np.sin(angles[i]) vectors[i,1] = -np.cos(angles[i]) vectors[i,2] = 0 # center of detector vectors[i,3:6] = 0 # vector from detector pixel (0,0) to (0,1) vectors[i,6] = np.cos(angles[i]) vectors[i,7] = np.sin(angles[i]) vectors[i,8] = 0; # vector from detector pixel (0,0) to (1,0) vectors[i,9] = 0 vectors[i,10] = 0 vectors[i,11] = 1 # Parameters: #rows, #columns, vectors proj_geom = astra.create_proj_geom('parallel3d_vec', 32, 64, vectors) # ---------- # Cone beam # ---------- # Circular # Parameters: width of detector column, height of detector row, #rows, #columns, # angles, distance source-origin, distance origin-detector angles = np.linspace(0, 2*np.pi, 48, False) proj_geom = astra.create_proj_geom('cone', 1.0, 1.0, 32, 64, angles, 1000, 0) # Free vectors = np.zeros((len(angles), 12)) for i in range(len(angles)): # source vectors[i,0] = np.sin(angles[i]) * 1000 vectors[i,1] = -np.cos(angles[i]) * 1000 vectors[i,2] = 0 # center of detector vectors[i,3:6] = 0 # vector from detector pixel (0,0) to (0,1) vectors[i,6] = np.cos(angles[i]) vectors[i,7] = np.sin(angles[i]) vectors[i,8] = 0 # vector from detector pixel (0,0) to (1,0) vectors[i,9] = 0 vectors[i,10] = 0 vectors[i,11] = 1 # Parameters: #rows, #columns, vectors proj_geom = astra.create_proj_geom('cone_vec', 32, 64, vectors) astra-toolbox-2.3.0/samples/python/s006_3d_data.py000066400000000000000000000043241475635207100217350ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np # Create a 3D volume geometry. # Parameter order: rows, colums, slices (y, x, z) vol_geom = astra.create_vol_geom(64, 48, 32) # Create volumes # initialized to zero v0 = astra.data3d.create('-vol', vol_geom) # initialized to 3.0 v1 = astra.data3d.create('-vol', vol_geom, 3.0) # initialized to a matrix. A may be a single or double array. # Coordinate order: slice, row, column (z, y, x) A = np.zeros((32, 64, 48)) v2 = astra.data3d.create('-vol', vol_geom, A) # Projection data # 2 projection directions, along x and y axis resp. V = np.array([[ 1,0,0, 0,0,0, 0,1,0, 0,0,1], [0,1,0, 0,0,0, -1,0,0, 0,0,1]],dtype=np.float) # 32 rows (v), 64 columns (u) proj_geom = astra.create_proj_geom('parallel3d_vec', 32, 64, V) s0 = astra.data3d.create('-proj3d', proj_geom) # Initialization to a scalar or zero works exactly as with a volume. # Initialized to a matrix: # Coordinate order: row (v), angle, column (u) A = np.zeros((32, 2, 64)) s1 = astra.data3d.create('-proj3d', proj_geom, A) # Retrieve data: R = astra.data3d.get(v1) # Delete all created data objects astra.data3d.delete(v0) astra.data3d.delete(v1) astra.data3d.delete(v2) astra.data3d.delete(s0) astra.data3d.delete(s1) astra-toolbox-2.3.0/samples/python/s007_3d_reconstruction.py000066400000000000000000000045661475635207100241160ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np vol_geom = astra.create_vol_geom(128, 128, 128) angles = np.linspace(0, np.pi, 180,False) proj_geom = astra.create_proj_geom('parallel3d', 1.0, 1.0, 128, 192, angles) # Create a simple hollow cube phantom cube = np.zeros((128,128,128)) cube[17:113,17:113,17:113] = 1 cube[33:97,33:97,33:97] = 0 # Create projection data from this proj_id, proj_data = astra.create_sino3d_gpu(cube, proj_geom, vol_geom) # Display a single projection image import pylab pylab.gray() pylab.figure(1) pylab.imshow(proj_data[:,20,:]) # Create a data object for the reconstruction rec_id = astra.data3d.create('-vol', vol_geom) # Set up the parameters for a reconstruction algorithm using the GPU cfg = astra.astra_dict('SIRT3D_CUDA') cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = proj_id # Create the algorithm object from the configuration structure alg_id = astra.algorithm.create(cfg) # Run 150 iterations of the algorithm # Note that this requires about 750MB of GPU memory, and has a runtime # in the order of 10 seconds. astra.algorithm.run(alg_id, 150) # Get the result rec = astra.data3d.get(rec_id) pylab.figure(2) pylab.imshow(rec[:,:,65]) pylab.show() # Clean up. Note that GPU memory is tied up in the algorithm object, # and main RAM in the data objects. astra.algorithm.delete(alg_id) astra.data3d.delete(rec_id) astra.data3d.delete(proj_id) astra-toolbox-2.3.0/samples/python/s008_gpu_selection.py000066400000000000000000000037361475635207100233060ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np # Switch GPU to GPU #1. The default is #0. astra.set_gpu_index(1) vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,180,False)) phantom_id = astra.data2d.shepp_logan(vol_geom, returnData=False) proj_id = astra.create_projector('cuda',proj_geom,vol_geom) # Create a sinogram from a phantom sinogram_id, sinogram = astra.create_sino(P, proj_id) # Set up the parameters for a reconstruction algorithm using the GPU rec_id = astra.data2d.create('-vol', vol_geom) cfg = astra.astra_dict('SIRT_CUDA') cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = sinogram_id # Run 150 iterations of the algorithm alg_id = astra.algorithm.create(cfg) astra.algorithm.run(alg_id, 150) rec = astra.data2d.get(rec_id) # Clean up. astra.algorithm.delete(alg_id) astra.data2d.delete(rec_id) astra.data2d.delete(sinogram_id) astra.data2d.delete(phantom_id) astra.projector.delete(proj_id) astra-toolbox-2.3.0/samples/python/s009_projection_matrix.py000066400000000000000000000044411475635207100242010ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,180,False)) # For CPU-based algorithms, a "projector" object specifies the projection # model used. In this case, we use the "line" model. proj_id = astra.create_projector('line', proj_geom, vol_geom) # Generate the projection matrix for this projection model. # This creates a matrix W where entry w_{i,j} corresponds to the # contribution of volume element j to detector element i. matrix_id = astra.projector.matrix(proj_id) # Get the projection matrix as a Scipy sparse matrix. W = astra.matrix.get(matrix_id) # Manually use this projection matrix to do a projection: phantom_id, P = astra.data2d.shepp_logan(vol_geom) s = W.dot(P.ravel()) s = np.reshape(s, (len(proj_geom['ProjectionAngles']),proj_geom['DetectorCount'])) import pylab pylab.gray() pylab.figure(1) pylab.imshow(s) pylab.show() # Each row of the projection matrix corresponds to a detector element. # Detector t for angle p is for row 1 + t + p*proj_geom.DetectorCount. # Each column corresponds to a volume pixel. # Pixel (x,y) corresponds to column 1 + x + y*vol_geom.GridColCount. astra.projector.delete(proj_id) astra.data2d.delete(phantom_id) astra.matrix.delete(matrix_id) astra-toolbox-2.3.0/samples/python/s010_supersampling.py000066400000000000000000000051531475635207100233230ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 3.0, 128, np.linspace(0,np.pi,180,False)) phantom_id, P = astra.data2d.shepp_logan(vol_geom) # Because the astra.create_sino method does not have support for # all possible algorithm options, we manually create a sinogram sinogram_id = astra.data2d.create('-sino', proj_geom) cfg = astra.astra_dict('FP_CUDA') cfg['VolumeDataId'] = phantom_id cfg['ProjectionDataId'] = sinogram_id # Set up 3 rays per detector element cfg['option'] = {} cfg['option']['DetectorSuperSampling'] = 3 alg_id = astra.algorithm.create(cfg) astra.algorithm.run(alg_id) astra.algorithm.delete(alg_id) astra.data2d.delete(phantom_id) sinogram3 = astra.data2d.get(sinogram_id) import pylab pylab.gray() pylab.figure(1) pylab.imshow(P) pylab.figure(2) pylab.imshow(sinogram3) # Create a reconstruction, also using supersampling rec_id = astra.data2d.create('-vol', vol_geom) cfg = astra.astra_dict('SIRT_CUDA') cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = sinogram_id # Set up 3 rays per detector element cfg['option'] = {} cfg['option']['DetectorSuperSampling'] = 3 # There is also an option for supersampling during the backprojection step. # This should be used if your detector pixels are smaller than the voxels. # Set up 2 rays per image pixel dimension, for 4 rays total per image pixel. # cfg['option']['PixelSuperSampling'] = 2 alg_id = astra.algorithm.create(cfg) astra.algorithm.run(alg_id, 150) astra.algorithm.delete(alg_id) rec = astra.data2d.get(rec_id) pylab.figure(3) pylab.imshow(rec) pylab.show() astra-toolbox-2.3.0/samples/python/s011_object_info.py000066400000000000000000000031201475635207100227040ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra # Create two volume geometries vol_geom1 = astra.create_vol_geom(256, 256) vol_geom2 = astra.create_vol_geom(512, 256) # Create volumes v0 = astra.data2d.create('-vol', vol_geom1) v1 = astra.data2d.create('-vol', vol_geom2) v2 = astra.data2d.create('-vol', vol_geom2) # Show the currently allocated volumes astra.data2d.info() astra.data2d.delete(v2) astra.data2d.info() astra.data2d.clear() astra.data2d.info() # The same clear and info command also work for other object types: astra.algorithm.info() astra.data3d.info() astra.projector.info() astra.matrix.info() astra-toolbox-2.3.0/samples/python/s012_masks.py000066400000000000000000000054241475635207100215530ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np # In this example we will create a reconstruction in a circular region, # instead of the usual rectangle. # This is done by placing a circular mask on the square reconstruction volume: c = np.linspace(-127.5,127.5,256) x, y = np.meshgrid(c,c) mask = np.array((x**2 + y**2 < 127.5**2),dtype=np.float32) import pylab pylab.gray() pylab.figure(1) pylab.imshow(mask) vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,50,False)) # As before, create a sinogram from a phantom phantom_id, P = astra.data2d.shepp_logan(vol_geom) proj_id = astra.create_projector('cuda',proj_geom,vol_geom) sinogram_id, sinogram = astra.create_sino(P, proj_id) pylab.figure(2) pylab.imshow(P) pylab.figure(3) pylab.imshow(sinogram) # Create a data object for the reconstruction rec_id = astra.data2d.create('-vol', vol_geom) # Create a data object for the mask mask_id = astra.data2d.create('-vol', vol_geom, mask) # Set up the parameters for a reconstruction algorithm using the GPU cfg = astra.astra_dict('SIRT_CUDA') cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = sinogram_id cfg['option'] = {} cfg['option']['ReconstructionMaskId'] = mask_id # Create the algorithm object from the configuration structure alg_id = astra.algorithm.create(cfg) # Run 150 iterations of the algorithm astra.algorithm.run(alg_id, 150) # Get the result rec = astra.data2d.get(rec_id) pylab.figure(4) pylab.imshow(rec) pylab.show() # Clean up. Note that GPU memory is tied up in the algorithm object, # and main RAM in the data objects. astra.algorithm.delete(alg_id) astra.data2d.delete(mask_id) astra.data2d.delete(rec_id) astra.data2d.delete(sinogram_id) astra.data2d.delete(phantom_id) astra.projector.delete(proj_id) astra-toolbox-2.3.0/samples/python/s013_constraints.py000066400000000000000000000046701475635207100230070ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np # In this example we will create a reconstruction constrained to # greyvalues between 0 and 1 vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,50,False)) # As before, create a sinogram from a phantom phantom_id, P = astra.data2d.shepp_logan(vol_geom) proj_id = astra.create_projector('cuda',proj_geom,vol_geom) sinogram_id, sinogram = astra.create_sino(P, proj_id) import pylab pylab.gray() pylab.figure(1) pylab.imshow(P) pylab.figure(2) pylab.imshow(sinogram) # Create a data object for the reconstruction rec_id = astra.data2d.create('-vol', vol_geom) # Set up the parameters for a reconstruction algorithm using the GPU cfg = astra.astra_dict('SIRT_CUDA') cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = sinogram_id cfg['option']={} cfg['option']['MinConstraint'] = 0 cfg['option']['MaxConstraint'] = 1 # Create the algorithm object from the configuration structure alg_id = astra.algorithm.create(cfg) # Run 150 iterations of the algorithm astra.algorithm.run(alg_id, 150) # Get the result rec = astra.data2d.get(rec_id) pylab.figure(3) pylab.imshow(rec) pylab.show() # Clean up. Note that GPU memory is tied up in the algorithm object, # and main RAM in the data objects. astra.algorithm.delete(alg_id) astra.data2d.delete(rec_id) astra.data2d.delete(sinogram_id) astra.data2d.delete(phantom_id) astra.projector.delete(proj_id) astra-toolbox-2.3.0/samples/python/s014_FBP.py000066400000000000000000000046751475635207100210550ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,180,False)) # As before, create a sinogram from a phantom phantom_id, P = astra.data2d.shepp_logan(vol_geom) proj_id = astra.create_projector('cuda',proj_geom,vol_geom) sinogram_id, sinogram = astra.create_sino(P, proj_id) import pylab pylab.gray() pylab.figure(1) pylab.imshow(P) pylab.figure(2) pylab.imshow(sinogram) # Create a data object for the reconstruction rec_id = astra.data2d.create('-vol', vol_geom) # create configuration cfg = astra.astra_dict('FBP_CUDA') cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = sinogram_id cfg['option'] = { 'FilterType': 'Ram-Lak' } # possible values for FilterType: # none, ram-lak, shepp-logan, cosine, hamming, hann, tukey, lanczos, # triangular, gaussian, barlett-hann, blackman, nuttall, blackman-harris, # blackman-nuttall, flat-top, kaiser, parzen # Create and run the algorithm object from the configuration structure alg_id = astra.algorithm.create(cfg) astra.algorithm.run(alg_id) # Get the result rec = astra.data2d.get(rec_id) pylab.figure(3) pylab.imshow(rec) pylab.show() # Clean up. Note that GPU memory is tied up in the algorithm object, # and main RAM in the data objects. astra.algorithm.delete(alg_id) astra.data2d.delete(rec_id) astra.data2d.delete(sinogram_id) astra.data2d.delete(phantom_id) astra.projector.delete(proj_id) astra-toolbox-2.3.0/samples/python/s015_fp_bp.py000066400000000000000000000061041475635207100215220ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- # This example demonstrates using the FP and BP primitives with Matlab's lsqr # solver. Calls to FP (astra.create_sino) and # BP (astra.create_backprojection) are wrapped in a function astra_wrap, # and a handle to this function is passed to lsqr. # Because in this case the inputs/outputs of FP and BP have to be vectors # instead of images (matrices), the calls require reshaping to and from vectors. import astra import numpy as np # FP/BP wrapper class class astra_wrap(object): def __init__(self,proj_geom,vol_geom): self.proj_id = astra.create_projector('cuda',proj_geom,vol_geom) self.shape = (proj_geom['DetectorCount']*len(proj_geom['ProjectionAngles']),vol_geom['GridColCount']*vol_geom['GridRowCount']) self.dtype = np.float32 def matvec(self,v): sid, s = astra.create_sino(np.reshape(v,(vol_geom['GridRowCount'],vol_geom['GridColCount'])),self.proj_id) astra.data2d.delete(sid) return s.ravel() def rmatvec(self,v): bid, b = astra.create_backprojection(np.reshape(v,(len(proj_geom['ProjectionAngles']),proj_geom['DetectorCount'],)),self.proj_id) astra.data2d.delete(bid) return b.ravel() vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,180,False)) # Create a 256x256 phantom image phantom_id, P = astra.data2d.shepp_logan(vol_geom) # Create a sinogram using the GPU. proj_id = astra.create_projector('cuda',proj_geom,vol_geom) sinogram_id, sinogram = astra.create_sino(P, proj_id) # Reshape the sinogram into a vector b = sinogram.ravel() # Call lsqr with ASTRA FP and BP import scipy.sparse.linalg wrapper = astra_wrap(proj_geom,vol_geom) result = scipy.sparse.linalg.lsqr(wrapper,b,atol=1e-4,btol=1e-4,iter_lim=25) # Reshape the result into an image Y = np.reshape(result[0],(vol_geom['GridRowCount'], vol_geom['GridColCount'])); import pylab pylab.gray() pylab.imshow(Y) pylab.show() astra.data2d.delete(sinogram_id) astra.data2d.delete(phantom_id) astra.projector.delete(proj_id) astra.projector.delete(wrapper.proj_id) astra-toolbox-2.3.0/samples/python/s016_plots.py000066400000000000000000000052541475635207100216030ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- try: from six.moves import range except ImportError: # six 1.3.0 from six.moves import xrange as range import astra import numpy as np vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,180,False)) # As before, create a sinogram from a phantom phantom_id, P = astra.data2d.shepp_logan(vol_geom) proj_id = astra.create_projector('cuda',proj_geom,vol_geom) sinogram_id, sinogram = astra.create_sino(P, proj_id) import pylab pylab.gray() pylab.figure(1) pylab.imshow(P) pylab.figure(2) pylab.imshow(sinogram) # Create a data object for the reconstruction rec_id = astra.data2d.create('-vol', vol_geom) # Set up the parameters for a reconstruction algorithm using the GPU cfg = astra.astra_dict('SIRT_CUDA') cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = sinogram_id # Create the algorithm object from the configuration structure alg_id = astra.algorithm.create(cfg) # Run 1500 iterations of the algorithm one at a time, keeping track of errors nIters = 1500 phantom_error = np.zeros(nIters) residual_error = np.zeros(nIters) for i in range(nIters): # Run a single iteration astra.algorithm.run(alg_id, 1) residual_error[i] = astra.algorithm.get_res_norm(alg_id) rec = astra.data2d.get(rec_id) phantom_error[i] = np.sqrt(((rec - P)**2).sum()) # Get the result rec = astra.data2d.get(rec_id) pylab.figure(3) pylab.imshow(rec) pylab.figure(4) pylab.plot(residual_error) pylab.figure(5) pylab.plot(phantom_error) pylab.show() # Clean up. astra.algorithm.delete(alg_id) astra.data2d.delete(rec_id) astra.data2d.delete(sinogram_id) astra.data2d.delete(phantom_id) astra.projector.delete(proj_id) astra-toolbox-2.3.0/samples/python/s017_OpTomo.py000066400000000000000000000034711475635207100216570ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np import scipy.sparse.linalg vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,180,False)) # As before, create a sinogram from a phantom phantom_id, P = astra.data2d.shepp_logan(vol_geom) proj_id = astra.create_projector('cuda',proj_geom,vol_geom) # construct the OpTomo object W = astra.OpTomo(proj_id) sinogram = W * P sinogram = sinogram.reshape([180, 384]) import pylab pylab.gray() pylab.figure(1) pylab.imshow(P) pylab.figure(2) pylab.imshow(sinogram) # Run the lsqr linear solver output = scipy.sparse.linalg.lsqr(W, sinogram.ravel(), iter_lim=150) rec = output[0].reshape([256, 256]) pylab.figure(3) pylab.imshow(rec) pylab.show() # Clean up. astra.projector.delete(proj_id) astra.data2d.delete(phantom_id) astra-toolbox-2.3.0/samples/python/s018_plugin.py000066400000000000000000000115221475635207100217350ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- from __future__ import print_function import astra import numpy as np # Define the plugin class (has to subclass astra.plugin.base) # Note that usually, these will be defined in a separate package/module class LandweberPlugin(astra.plugin.base): """Example of an ASTRA plugin class, implementing a simple 2D Landweber algorithm. Options: 'Relaxation': relaxation factor (optional) """ # The astra_name variable defines the name to use to # call the plugin from ASTRA astra_name = "LANDWEBER-PLUGIN" def initialize(self,cfg, Relaxation = 1.0): self.W = astra.OpTomo(cfg['ProjectorId']) self.vid = cfg['ReconstructionDataId'] self.sid = cfg['ProjectionDataId'] self.rel = Relaxation def run(self, its): v = astra.data2d.get_shared(self.vid) s = astra.data2d.get_shared(self.sid) tv = np.zeros(v.shape, dtype=np.float32) ts = np.zeros(s.shape, dtype=np.float32) W = self.W for i in range(its): W.FP(v,out=ts) ts -= s # ts = W*v - s W.BP(ts,out=tv) tv *= self.rel / s.size v -= tv # v = v - rel * W'*(W*v-s) / s.size if __name__=='__main__': vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 384, np.linspace(0,np.pi,180,False)) # As before, create a sinogram from a phantom phantom_id, P = astra.data2d.shepp_logan(vol_geom) proj_id = astra.create_projector('cuda',proj_geom,vol_geom) # construct the OpTomo object W = astra.OpTomo(proj_id) sinogram = W * P sinogram = sinogram.reshape([180, 384]) # Register the plugin with ASTRA # First we import the package that contains the plugin import s018_plugin # Then, we register the plugin class with ASTRA astra.plugin.register(s018_plugin.LandweberPlugin) # Get a list of registered plugins print(astra.plugin.get_registered()) # To get help on a registered plugin, use get_help print(astra.plugin.get_help('LANDWEBER-PLUGIN')) # Create data structures sid = astra.data2d.create('-sino', proj_geom, sinogram) vid = astra.data2d.create('-vol', vol_geom) # Create config using plugin name cfg = astra.astra_dict('LANDWEBER-PLUGIN') cfg['ProjectorId'] = proj_id cfg['ProjectionDataId'] = sid cfg['ReconstructionDataId'] = vid # Create algorithm object alg_id = astra.algorithm.create(cfg) # Run algorithm for 100 iterations astra.algorithm.run(alg_id, 100) # Get reconstruction rec = astra.data2d.get(vid) # Options for the plugin go in cfg['option'] cfg = astra.astra_dict('LANDWEBER-PLUGIN') cfg['ProjectorId'] = proj_id cfg['ProjectionDataId'] = sid cfg['ReconstructionDataId'] = vid cfg['option'] = {} cfg['option']['Relaxation'] = 1.5 alg_id_rel = astra.algorithm.create(cfg) astra.algorithm.run(alg_id_rel, 100) rec_rel = astra.data2d.get(vid) # We can also use OpTomo to call the plugin rec_op = W.reconstruct('LANDWEBER-PLUGIN', sinogram, 100, extraOptions={'Relaxation':1.5}) # ASTRA also comes with built-in plugins: astra.plugin.register(astra.plugins.SIRTPlugin) astra.plugin.register(astra.plugins.CGLSPlugin) rec_sirt = W.reconstruct('SIRT-PLUGIN', sinogram, 100, extraOptions={'Relaxation':1.5}) rec_cgls = W.reconstruct('CGLS-PLUGIN', sinogram, 100) import pylab as pl pl.gray() pl.figure(1) pl.imshow(rec,vmin=0,vmax=1) pl.figure(2) pl.imshow(rec_rel,vmin=0,vmax=1) pl.figure(3) pl.imshow(rec_op,vmin=0,vmax=1) pl.figure(4) pl.imshow(rec_sirt,vmin=0,vmax=1) pl.figure(5) pl.imshow(rec_cgls,vmin=0,vmax=1) pl.show() # Clean up. astra.projector.delete(proj_id) astra.algorithm.delete([alg_id, alg_id_rel]) astra.data2d.delete([vid, sid, phantom_id]) astra-toolbox-2.3.0/samples/python/s019_experimental_multires.py000066400000000000000000000053521475635207100250650ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np from astra.experimental import do_composite_FP astra.log.setOutputScreen(astra.log.STDERR, astra.log.DEBUG) # low res part (voxels of 4x4x4) vol_geom1 = astra.create_vol_geom(32, 16, 32, -64, 0, -64, 64, -64, 64) # high res part (voxels of 1x1x1) vol_geom2 = astra.create_vol_geom(128, 64, 128, 0, 64, -64, 64, -64, 64) # Split the output in two parts as well, for demonstration purposes angles1 = np.linspace(0, np.pi/2, 90, False) angles2 = np.linspace(np.pi/2, np.pi, 90, False) proj_geom1 = astra.create_proj_geom('parallel3d', 1.0, 1.0, 128, 192, angles1) proj_geom2 = astra.create_proj_geom('parallel3d', 1.0, 1.0, 128, 192, angles2) # Create a simple hollow cube phantom cube1 = np.zeros((32,32,16)) cube1[4:28,4:28,4:16] = 1 cube2 = np.zeros((128,128,64)) cube2[16:112,16:112,0:112] = 1 cube2[33:97,33:97,4:28] = 0 vol1 = astra.data3d.create('-vol', vol_geom1, cube1) vol2 = astra.data3d.create('-vol', vol_geom2, cube2) proj1 = astra.data3d.create('-proj3d', proj_geom1, 0) proj2 = astra.data3d.create('-proj3d', proj_geom2, 0) # The actual geometries don't matter for this composite FP/BP case projector = astra.create_projector('cuda3d', proj_geom1, vol_geom1) do_composite_FP(projector, [vol1, vol2], [proj1, proj2]) proj_data1 = astra.data3d.get(proj1) proj_data2 = astra.data3d.get(proj2) # Display a single projection image import pylab pylab.gray() pylab.figure(1) pylab.imshow(proj_data1[:,0,:]) pylab.figure(2) pylab.imshow(proj_data2[:,0,:]) pylab.show() # Clean up. Note that GPU memory is tied up in the algorithm object, # and main RAM in the data objects. astra.data3d.delete(vol1) astra.data3d.delete(vol2) astra.data3d.delete(proj1) astra.data3d.delete(proj2) astra.projector3d.delete(projector) astra-toolbox-2.3.0/samples/python/s020_3d_multiGPU.py000066400000000000000000000040201475635207100225170ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np # Set up multi-GPU usage. # This only works for 3D GPU forward projection and back projection. astra.set_gpu_index([0,1]) # Optionally, you can also restrict the amount of GPU memory ASTRA will use. # The line commented below sets this to 1GB. #astra.set_gpu_index([0,1], memory=1024*1024*1024) vol_geom = astra.create_vol_geom(1024, 1024, 1024) angles = np.linspace(0, np.pi, 1024,False) proj_geom = astra.create_proj_geom('parallel3d', 1.0, 1.0, 1024, 1024, angles) # Create a simple hollow cube phantom cube = np.zeros((1024,1024,1024)) cube[128:895,128:895,128:895] = 1 cube[256:767,256:767,256:767] = 0 # Create projection data from this proj_id, proj_data = astra.create_sino3d_gpu(cube, proj_geom, vol_geom) # Backproject projection data bproj_id, bproj_data = astra.create_backprojection3d_gpu(proj_data, proj_geom, vol_geom) # Clean up. Note that GPU memory is tied up in the algorithm object, # and main RAM in the data objects. astra.data3d.delete(proj_id) astra.data3d.delete(bproj_id) astra-toolbox-2.3.0/samples/python/s021_pygpu.py000066400000000000000000000047471475635207100216100ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np import pygpu import pylab # Initialize pygpu ctx = pygpu.init('cuda') pygpu.set_default_context(ctx) vol_geom = astra.create_vol_geom(128, 128, 128) angles = np.linspace(0, 2 * np.pi, 180, False) proj_geom = astra.create_proj_geom('cone', 1.0, 1.0, 128, 192, angles, 1000, 0) # Create a simple hollow cube phantom, as a pygpu gpuarray vol_gpuarr = pygpu.gpuarray.zeros(astra.geom_size(vol_geom), dtype='float32') vol_gpuarr[17:113, 17:113, 17:113] = 1 vol_gpuarr[33:97, 33:97, 33:97] = 0 # Create a pygpu gpuarray for the output projection data proj_gpuarr = pygpu.gpuarray.zeros(astra.geom_size(proj_geom), dtype='float32') # Create the astra GPULink objects and create astra data3d objects from them z, y, x = proj_gpuarr.shape proj_data_link = astra.data3d.GPULink(proj_gpuarr.gpudata, x, y, z, proj_gpuarr.strides[-2]) z, y, x = vol_gpuarr.shape vol_link = astra.data3d.GPULink(vol_gpuarr.gpudata, x, y, z, vol_gpuarr.strides[-2]) proj_id = astra.data3d.link('-sino', proj_geom, proj_data_link) vol_id = astra.data3d.link('-vol', vol_geom, vol_link) # Run a 3D FP cfg = astra.astra_dict('FP3D_CUDA') cfg['VolumeDataId'] = vol_id cfg['ProjectionDataId'] = proj_id alg_id = astra.algorithm.create(cfg) astra.algorithm.run(alg_id) pylab.figure(1) pylab.gray() pylab.imshow(proj_gpuarr[:, 20, :]) pylab.show() astra.algorithm.delete(alg_id) astra.data3d.delete(vol_id) astra.data3d.delete(proj_id) astra-toolbox-2.3.0/samples/python/s022_fbp_cor.py000066400000000000000000000057451475635207100220560ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np cor_shift = 3.6 vol_geom = astra.create_vol_geom(256, 256) proj_geom = astra.create_proj_geom('parallel', 1.0, 256, np.linspace(0,np.pi,180,False)) # Projection geometry with shifted center of rotation proj_geom_cor = astra.geom_postalignment(proj_geom, cor_shift) # As before, create a sinogram from a phantom, using the shifted center of rotation phantom_id, P = astra.data2d.shepp_logan(vol_geom) proj_id_cor = astra.create_projector('cuda',proj_geom_cor,vol_geom) sinogram_id, sinogram = astra.create_sino(P, proj_id_cor) # Change the projection geometry metadata attached to the sinogram to standard geometry, # and try to do a reconstruction, to show the misalignment artifacts caused by # the shifted center of rotation astra.data2d.change_geometry(sinogram_id, proj_geom) import pylab pylab.gray() pylab.figure(1) pylab.imshow(P) pylab.figure(2) pylab.imshow(sinogram) # Create a data object for the reconstruction rec_id = astra.data2d.create('-vol', vol_geom) # Set up the parameters for a reconstruction algorithm using the GPU cfg = astra.astra_dict('FBP_CUDA') cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = sinogram_id alg_id = astra.algorithm.create(cfg) astra.algorithm.run(alg_id) # Get the result rec = astra.data2d.get(rec_id) pylab.figure(3) pylab.imshow(rec) astra.algorithm.delete(alg_id) astra.data2d.delete(rec_id) # Now change back to the proper, shifted geometry, and do another reconstruction astra.data2d.change_geometry(sinogram_id, proj_geom_cor) rec_id = astra.data2d.create('-vol', vol_geom) cfg = astra.astra_dict('FBP_CUDA') cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = sinogram_id alg_id = astra.algorithm.create(cfg) astra.algorithm.run(alg_id) # Get the result rec = astra.data2d.get(rec_id) pylab.figure(4) pylab.imshow(rec) pylab.show() astra.data2d.delete(sinogram_id) astra.data2d.delete(phantom_id) astra.projector.delete(proj_id_cor) astra.algorithm.delete(alg_id) astra.data2d.delete(rec_id) astra-toolbox-2.3.0/samples/python/s023_FBP_filters.py000066400000000000000000000073631475635207100226020ustar00rootroot00000000000000# ----------------------------------------------------------------------- # Copyright: 2010-2022, imec Vision Lab, University of Antwerp # 2013-2022, CWI, Amsterdam # # Contact: astra@astra-toolbox.com # Website: http://www.astra-toolbox.com/ # # This file is part of the ASTRA Toolbox. # # # The ASTRA Toolbox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # The ASTRA Toolbox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ASTRA Toolbox. If not, see . # # ----------------------------------------------------------------------- import astra import numpy as np import scipy.io # This sample script illustrates three ways of passing filters to FBP. # They work with both the FBP (CPU) and the FBP_CUDA (GPU) algorithms. N = 256 vol_geom = astra.create_vol_geom(N, N) proj_geom = astra.create_proj_geom('parallel', 1.0, N, np.linspace(0,np.pi,180,False)) phantom_id, P = astra.data2d.shepp_logan(vol_geom) proj_id = astra.create_projector('strip',proj_geom,vol_geom) sinogram_id, sinogram = astra.create_sino(P, proj_id) rec_id = astra.data2d.create('-vol', vol_geom) cfg = astra.astra_dict('FBP') cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = sinogram_id cfg['ProjectorId'] = proj_id cfg['option'] = {} # 1. Use a standard Ram-Lak filter cfg['option']['FilterType'] = 'ram-lak' alg_id = astra.algorithm.create(cfg) astra.algorithm.run(alg_id) rec_RL = astra.data2d.get(rec_id) astra.algorithm.delete(alg_id) # 2. Define a filter in Fourier space # This is assumed to be symmetric, and ASTRA therefore expects only half # The full filter size should be the smallest power of two that is at least # twice the number of detector pixels. fullFilterSize = 2*N kernel = np.append( np.linspace(0, 1, fullFilterSize//2, endpoint=False), np.linspace(1, 0, fullFilterSize//2, endpoint=False) ) halfFilterSize = fullFilterSize // 2 + 1 filter = np.reshape(kernel[0:halfFilterSize], (1, halfFilterSize)) filter_geom = astra.create_proj_geom('parallel', 1.0, halfFilterSize, [0]); filter_id = astra.data2d.create('-sino', filter_geom, filter); cfg['option']['FilterType'] = 'projection' cfg['option']['FilterSinogramId'] = filter_id alg_id = astra.algorithm.create(cfg) astra.algorithm.run(alg_id) rec_filter = astra.data2d.get(rec_id) astra.algorithm.delete(alg_id) # 3. Define a (spatial) convolution kernel directly # For a kernel of odd size 2*k+1, the central component is at kernel[k] # For a kernel of even size 2*k, the central component is at kernel[k] kernel = np.zeros((1, N)) for i in range(0,N//4): f = np.pi * (2*i + 1) val = -2.0 / (f * f) kernel[0, N//2 + (2*i+1)] = val kernel[0, N//2 - (2*i+1)] = val kernel[0, N//2] = 0.5 kernel_geom = astra.create_proj_geom('parallel', 1.0, N, [0]); kernel_id = astra.data2d.create('-sino', kernel_geom, kernel); cfg['option']['FilterType'] = 'rprojection' cfg['option']['FilterSinogramId'] = kernel_id alg_id = astra.algorithm.create(cfg) astra.algorithm.run(alg_id) rec_kernel = astra.data2d.get(rec_id) astra.algorithm.delete(alg_id) import pylab pylab.figure() pylab.imshow(P) pylab.figure() pylab.imshow(rec_RL) pylab.figure() pylab.imshow(rec_filter) pylab.figure() pylab.imshow(rec_kernel) pylab.show() astra.data2d.delete(rec_id) astra.data2d.delete(sinogram_id) astra.data2d.delete(phantom_id) astra.projector.delete(proj_id) astra-toolbox-2.3.0/src/000077500000000000000000000000001475635207100151135ustar00rootroot00000000000000astra-toolbox-2.3.0/src/Algorithm.cpp000066400000000000000000000025431475635207100175510ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Algorithm.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CAlgorithm::CAlgorithm() : configCheckData(0) { } //---------------------------------------------------------------------------------------- // Destructor CAlgorithm::~CAlgorithm() { } } // namespace astra astra-toolbox-2.3.0/src/ArtAlgorithm.cpp000066400000000000000000000217241475635207100202220ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ArtAlgorithm.h" #include "astra/AstraObjectManager.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CArtAlgorithm::CArtAlgorithm() : CReconstructionAlgorithm2D() { m_fLambda = 1.0f; m_iCurrentRay = 0; m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Destructor CArtAlgorithm::~CArtAlgorithm() { } //--------------------------------------------------------------------------------------- // Clear - Constructors void CArtAlgorithm::_clear() { CReconstructionAlgorithm2D::_clear(); m_piDetectorOrder.clear(); m_piProjectionOrder.clear(); m_iCurrentRay = 0; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CArtAlgorithm::clear() { CReconstructionAlgorithm2D::clear(); m_piDetectorOrder.clear(); m_piProjectionOrder.clear(); m_fLambda = 1.0f; m_iCurrentRay = 0; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Check bool CArtAlgorithm::_check() { // check base class ASTRA_CONFIG_CHECK(CReconstructionAlgorithm2D::_check(), "ART", "Error in ReconstructionAlgorithm2D initialization"); ASTRA_CONFIG_CHECK(m_piProjectionOrder.size() == m_piDetectorOrder.size(), "ART", "Different sizes of ray order lists."); // check ray order list for (unsigned int i = 0; i < m_piProjectionOrder.size(); i++) { if (m_piProjectionOrder[i] < 0 || m_piProjectionOrder[i] > m_pSinogram->getAngleCount()-1) { ASTRA_CONFIG_CHECK(false, "ART", "Invalid value in ray order list."); } if (m_piDetectorOrder[i] < 0 || m_piDetectorOrder[i] > m_pSinogram->getDetectorCount()-1) { ASTRA_CONFIG_CHECK(false, "ART", "Invalid value in ray order list."); } } // success return true; } //--------------------------------------------------------------------------------------- // Initialize - Config bool CArtAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("ArtAlgorithm", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CReconstructionAlgorithm2D::initialize(_cfg)) { return false; } // ray order std::string projOrder; if (!CR.getOptionString("RayOrder", projOrder, "sequential")) return false; m_iCurrentRay = 0; int iRayCount = m_pProjector->getProjectionGeometry().getProjectionAngleCount() * m_pProjector->getProjectionGeometry().getDetectorCount(); if (projOrder == "sequential") { m_piProjectionOrder.resize(iRayCount); m_piDetectorOrder.resize(iRayCount); for (int i = 0; i < iRayCount; i++) { m_piProjectionOrder[i] = i / m_pProjector->getProjectionGeometry().getDetectorCount(); m_piDetectorOrder[i] = i % m_pProjector->getProjectionGeometry().getDetectorCount(); } } else if (projOrder == "custom") { std::vector rayOrderList; if (!CR.getOptionIntArray("RayOrderList", rayOrderList)) return false; iRayCount = rayOrderList.size() / 2; m_piProjectionOrder.resize(iRayCount); m_piDetectorOrder.resize(iRayCount); for (int i = 0; i < iRayCount; i++) { m_piProjectionOrder[i] = rayOrderList[2*i]; m_piDetectorOrder[i] = rayOrderList[2*i+1]; } } else { ASTRA_ERROR("Unknown RayOrder"); return false; } bool ok = true; // "Lambda" is replaced by the more descriptive "Relaxation" if (CR.hasOption("Relaxation")) ok &= CR.getOptionNumerical("Relaxation", m_fLambda, 1.0f); else ok &= CR.getOptionNumerical("Lambda", m_fLambda, 1.0f); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Initialize - C++ bool CArtAlgorithm::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // required classes m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; // ray order m_iCurrentRay = 0; int iRayCount = _pProjector->getProjectionGeometry().getDetectorCount() * _pProjector->getProjectionGeometry().getProjectionAngleCount(); m_piProjectionOrder.resize(iRayCount); m_piDetectorOrder.resize(iRayCount); for (int i = 0; i < iRayCount; i++) { m_piProjectionOrder[i] = i / _pProjector->getProjectionGeometry().getDetectorCount(); m_piDetectorOrder[i] = i % _pProjector->getProjectionGeometry().getDetectorCount(); } // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Set the relaxation factor. void CArtAlgorithm::setLambda(float32 _fLambda) { m_fLambda = _fLambda; } //---------------------------------------------------------------------------------------- // Set the order in which the rays will be selected void CArtAlgorithm::setRayOrder(int* _piProjectionOrder, int* _piDetectorOrder, int _iRayCount) { m_iCurrentRay = 0; m_piProjectionOrder.resize(_iRayCount); m_piDetectorOrder.resize(_iRayCount); for (int i = 0; i < _iRayCount; i++) { m_piProjectionOrder[i] = _piProjectionOrder[i]; m_piDetectorOrder[i] = _piDetectorOrder[i]; } } //---------------------------------------------------------------------------------------- // Iterate bool CArtAlgorithm::run(int _iNrIterations) { // check initialized assert(m_bIsInitialized); // variables int iIteration, iPixel; int iUsedPixels, iProjection, iDetector; float32 fRayForwardProj, fSumSquaredWeights; float32 fProjectionDifference, fBackProjectionFactor; // create a pixel buffer int iPixelBufferSize = m_pProjector->getProjectionWeightsCount(0); SPixelWeight* pPixels = new SPixelWeight[iPixelBufferSize]; // start iterations for (iIteration = _iNrIterations-1; iIteration >= 0; --iIteration) { // step0: compute single weight rays iProjection = m_piProjectionOrder[m_iCurrentRay]; iDetector = m_piDetectorOrder[m_iCurrentRay]; m_iCurrentRay = (m_iCurrentRay + 1) % m_piProjectionOrder.size(); if (m_bUseSinogramMask && m_pSinogramMask->getData2D()[iProjection][iDetector] == 0) continue; m_pProjector->computeSingleRayWeights(iProjection, iDetector, pPixels, iPixelBufferSize, iUsedPixels); // step1: forward projections fRayForwardProj = 0.0f; fSumSquaredWeights = 0.0f; for (iPixel = iUsedPixels-1; iPixel >= 0; --iPixel) { if (m_bUseReconstructionMask && m_pReconstructionMask->getDataConst()[pPixels[iPixel].m_iIndex] == 0) continue; fRayForwardProj += pPixels[iPixel].m_fWeight * m_pReconstruction->getDataConst()[pPixels[iPixel].m_iIndex]; fSumSquaredWeights += pPixels[iPixel].m_fWeight * pPixels[iPixel].m_fWeight; } if (fSumSquaredWeights == 0) continue; // step2: difference fProjectionDifference = m_pSinogram->getData2D()[iProjection][iDetector] - fRayForwardProj; // step3: back projection fBackProjectionFactor = m_fLambda * fProjectionDifference / fSumSquaredWeights; for (iPixel = iUsedPixels-1; iPixel >= 0; --iPixel) { // pixel must be loose if (m_bUseReconstructionMask && m_pReconstructionMask->getDataConst()[pPixels[iPixel].m_iIndex] == 0) continue; // update m_pReconstruction->getData()[pPixels[iPixel].m_iIndex] += fBackProjectionFactor * pPixels[iPixel].m_fWeight; // constraints if (m_bUseMinConstraint && m_pReconstruction->getData()[pPixels[iPixel].m_iIndex] < m_fMinValue) { m_pReconstruction->getData()[pPixels[iPixel].m_iIndex] = m_fMinValue; } if (m_bUseMaxConstraint && m_pReconstruction->getData()[pPixels[iPixel].m_iIndex] > m_fMaxValue) { m_pReconstruction->getData()[pPixels[iPixel].m_iIndex] = m_fMaxValue; } } } delete[] pPixels; // update statistics m_pReconstruction->updateStatistics(); return true; } //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/AstraObjectFactory.cpp000066400000000000000000000027731475635207100213610ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/AstraObjectFactory.h" namespace astra { DEFINE_SINGLETON2(CAstraObjectFactory) DEFINE_SINGLETON2(CAstraObjectFactory) DEFINE_SINGLETON2(CAstraObjectFactory) template <> CAlgorithm* CAstraObjectFactory::findPlugin(std::string _sType) { CPluginAlgorithmFactory *fac = CPluginAlgorithmFactory::getFactory(); if (fac) return fac->getPlugin(_sType); else return 0; } } // end namespace astra-toolbox-2.3.0/src/AstraObjectManager.cpp000066400000000000000000000024471475635207100213220ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/AstraObjectManager.h" namespace astra { DEFINE_SINGLETON(CProjector2DManager) DEFINE_SINGLETON(CProjector3DManager) DEFINE_SINGLETON(CData2DManager) DEFINE_SINGLETON(CData3DManager) DEFINE_SINGLETON(CAlgorithmManager) DEFINE_SINGLETON(CMatrixManager) DEFINE_SINGLETON(CAstraIndexManager) } // end namespace astra-toolbox-2.3.0/src/BackProjectionAlgorithm.cpp000066400000000000000000000113661475635207100223720ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/BackProjectionAlgorithm.h" #include "astra/AstraObjectManager.h" #include "astra/DataProjectorPolicies.h" #include "astra/Logging.h" using namespace std; namespace astra { #include "astra/Projector2DImpl.inl" //---------------------------------------------------------------------------------------- // Constructor CBackProjectionAlgorithm::CBackProjectionAlgorithm() { _clear(); } //--------------------------------------------------------------------------------------- // Initialize - C++ CBackProjectionAlgorithm::CBackProjectionAlgorithm(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { _clear(); initialize(_pProjector, _pSinogram, _pReconstruction); } //---------------------------------------------------------------------------------------- // Destructor CBackProjectionAlgorithm::~CBackProjectionAlgorithm() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CBackProjectionAlgorithm::_clear() { CReconstructionAlgorithm2D::_clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CBackProjectionAlgorithm::clear() { CReconstructionAlgorithm2D::_clear(); m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Check bool CBackProjectionAlgorithm::_check() { // check base class ASTRA_CONFIG_CHECK(CReconstructionAlgorithm2D::_check(), "BP", "Error in ReconstructionAlgorithm2D initialization"); return true; } //--------------------------------------------------------------------------------------- // Initialize - Config bool CBackProjectionAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("BackProjectionAlgorithm", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CReconstructionAlgorithm2D::initialize(_cfg)) { return false; } // init data objects and data projectors _init(); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CBackProjectionAlgorithm::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // required classes m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; // init data objects and data projectors _init(); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize Data Projectors - private void CBackProjectionAlgorithm::_init() { } //---------------------------------------------------------------------------------------- // Iterate bool CBackProjectionAlgorithm::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); CDataProjectorInterface* pBackProjector; pBackProjector = dispatchDataProjector( m_pProjector, SinogramMaskPolicy(m_pSinogramMask), // sinogram mask ReconstructionMaskPolicy(m_pReconstructionMask), // reconstruction mask DefaultBPPolicy(m_pReconstruction, m_pSinogram), // backprojection m_bUseSinogramMask, m_bUseReconstructionMask, true // options on/off ); m_pReconstruction->setData(0.0f); pBackProjector->project(); ASTRA_DELETE(pBackProjector); return true; } //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/CglsAlgorithm.cpp000066400000000000000000000161501475635207100203610ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/CglsAlgorithm.h" #include "astra/AstraObjectManager.h" #include "astra/Logging.h" using namespace std; namespace astra { #include "astra/Projector2DImpl.inl" //---------------------------------------------------------------------------------------- // Constructor CCglsAlgorithm::CCglsAlgorithm() { _clear(); } //--------------------------------------------------------------------------------------- // Initialize - C++ CCglsAlgorithm::CCglsAlgorithm(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { _clear(); initialize(_pProjector, _pSinogram, _pReconstruction); } //---------------------------------------------------------------------------------------- // Destructor CCglsAlgorithm::~CCglsAlgorithm() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CCglsAlgorithm::_clear() { CReconstructionAlgorithm2D::_clear(); r = NULL; w = NULL; z = NULL; p = NULL; alpha = 0.0f; beta = 0.0f; gamma = 0.0f; m_iIteration = 0; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CCglsAlgorithm::clear() { CReconstructionAlgorithm2D::_clear(); ASTRA_DELETE(r); ASTRA_DELETE(w); ASTRA_DELETE(z); ASTRA_DELETE(p); alpha = 0.0f; beta = 0.0f; gamma = 0.0f; m_iIteration = 0; m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Check bool CCglsAlgorithm::_check() { // check base class ASTRA_CONFIG_CHECK(CReconstructionAlgorithm2D::_check(), "CGLS", "Error in ReconstructionAlgorithm2D initialization"); return true; } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCglsAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("CglsAlgorithm", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CReconstructionAlgorithm2D::initialize(_cfg)) { return false; } // member variables r = new CFloat32ProjectionData2D(m_pSinogram->getGeometry()); w = new CFloat32ProjectionData2D(m_pSinogram->getGeometry()); z = new CFloat32VolumeData2D(m_pReconstruction->getGeometry()); p = new CFloat32VolumeData2D(m_pReconstruction->getGeometry()); alpha = 0.0f; beta = 0.0f; gamma = 0.0f; // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CCglsAlgorithm::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // required classes m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; // member variables r = new CFloat32ProjectionData2D(m_pSinogram->getGeometry()); w = new CFloat32ProjectionData2D(m_pSinogram->getGeometry()); z = new CFloat32VolumeData2D(m_pReconstruction->getGeometry()); p = new CFloat32VolumeData2D(m_pReconstruction->getGeometry()); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Iterate bool CCglsAlgorithm::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); // data projectors CDataProjectorInterface* pForwardProjector; CDataProjectorInterface* pBackProjector; // forward projection data projector pForwardProjector = dispatchDataProjector( m_pProjector, SinogramMaskPolicy(m_pSinogramMask), // sinogram mask ReconstructionMaskPolicy(m_pReconstructionMask), // reconstruction mask DefaultFPPolicy(p, w), // forward projection m_bUseSinogramMask, m_bUseReconstructionMask, true // options on/off ); // backprojection data projector pBackProjector = dispatchDataProjector( m_pProjector, SinogramMaskPolicy(m_pSinogramMask), // sinogram mask ReconstructionMaskPolicy(m_pReconstructionMask), // reconstruction mask DefaultBPPolicy(z, r), // backprojection m_bUseSinogramMask, m_bUseReconstructionMask, true // options on/off ); size_t i; if (m_iIteration == 0) { // r = b; r->copyData(m_pSinogram->getData()); // z = A'*b; z->setData(0.0f); pBackProjector->project(); if (m_bUseMinConstraint) z->clampMin(m_fMinValue); if (m_bUseMaxConstraint) z->clampMax(m_fMaxValue); // p = z; p->copyData(z->getData()); // gamma = dot(z,z); gamma = 0.0f; for (i = 0; i < z->getSize(); ++i) { gamma += z->getData()[i] * z->getData()[i]; } m_iIteration++; } // start iterations for (int iIteration = _iNrIterations-1; iIteration >= 0; --iIteration) { // w = A*p; w->setData(0.0f); // ensure masked out elements are zeroed pForwardProjector->project(); // alpha = gamma/dot(w,w); float32 tmp = 0; for (i = 0; i < w->getSize(); ++i) { tmp += w->getData()[i] * w->getData()[i]; } alpha = gamma / tmp; // x = x + alpha*p; for (i = 0; i < m_pReconstruction->getSize(); ++i) { m_pReconstruction->getData()[i] += alpha * p->getData()[i]; } // r = r - alpha*w; for (i = 0; i < r->getSize(); ++i) { r->getData()[i] -= alpha * w->getData()[i]; } // z = A'*r; z->setData(0.0f); pBackProjector->project(); // CHECKME: should these be here? if (m_bUseMinConstraint) z->clampMin(m_fMinValue); if (m_bUseMaxConstraint) z->clampMax(m_fMaxValue); // beta = 1/gamma; beta = 1.0f / gamma; // gamma = dot(z,z); gamma = 0; for (i = 0; i < z->getSize(); ++i) { gamma += z->getData()[i] * z->getData()[i]; } // beta = gamma*beta; beta *= gamma; // p = z + beta*p; for (i = 0; i < z->getSize(); ++i) { p->getData()[i] = z->getData()[i] + beta * p->getData()[i]; } m_iIteration++; } return true; } //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/CompositeGeometryManager.cpp000066400000000000000000001231151475635207100225730ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/CompositeGeometryManager.h" #ifdef ASTRA_CUDA #include "astra/GeometryUtil3D.h" #include "astra/VolumeGeometry3D.h" #include "astra/ConeProjectionGeometry3D.h" #include "astra/ConeVecProjectionGeometry3D.h" #include "astra/ParallelProjectionGeometry3D.h" #include "astra/ParallelVecProjectionGeometry3D.h" #include "astra/Projector3D.h" #include "astra/CudaProjector3D.h" #include "astra/Data3D.h" #include "astra/Logging.h" #include "astra/cuda/2d/astra.h" #include "astra/cuda/3d/mem3d.h" #include #include #include #include #include #include namespace astra { SGPUParams* CCompositeGeometryManager::s_params = 0; CCompositeGeometryManager::CCompositeGeometryManager() { m_iMaxSize = 0; if (s_params) { m_iMaxSize = s_params->memory; m_GPUIndices = s_params->GPUIndices; } } // JOB: // // VolumePart // ProjectionPart // FP-or-BP // SET-or-ADD // Running a set of jobs: // // [ Assume OUTPUT Parts in a single JobSet don't alias?? ] // Group jobs by output Part // One thread per group? // Automatically split parts if too large // Performance model for odd-sized tasks? // Automatically split parts if not enough tasks to fill available GPUs // Splitting: // Constraints: // number of sub-parts divisible by N // max size of sub-parts // For splitting on both input and output side: // How to divide up memory? (Optimization problem; compute/benchmark) // (First approach: 0.5/0.5) class _AstraExport CGPUMemory { public: astraCUDA3d::MemHandle3D hnd; // Only required to be valid between allocate/free virtual bool allocateGPUMemory(unsigned int x, unsigned int y, unsigned int z, astraCUDA3d::Mem3DZeroMode zero)=0; virtual bool copyToGPUMemory(const astraCUDA3d::SSubDimensions3D &pos)=0; virtual bool copyFromGPUMemory(const astraCUDA3d::SSubDimensions3D &pos)=0; virtual ~CGPUMemory() { } }; class CExistingGPUMemory : public astra::CGPUMemory { public: CExistingGPUMemory(CData3D *d); virtual bool allocateGPUMemory(unsigned int x, unsigned int y, unsigned int z, astraCUDA3d::Mem3DZeroMode zero); virtual bool copyToGPUMemory(const astraCUDA3d::SSubDimensions3D &pos); virtual bool copyFromGPUMemory(const astraCUDA3d::SSubDimensions3D &pos); private: unsigned int x, y, z; }; class CDefaultGPUMemory : public astra::CGPUMemory { public: CDefaultGPUMemory(CData3D* d) : ptr(nullptr) { assert(d->getStorage()->isMemory()); if (d->getStorage()->isFloat32()) ptr = dynamic_cast*>(d->getStorage())->getData(); else assert(false); } virtual ~CDefaultGPUMemory() { freeGPUMemory(); } virtual bool allocateGPUMemory(unsigned int x, unsigned int y, unsigned int z, astraCUDA3d::Mem3DZeroMode zero) { hnd = astraCUDA3d::allocateGPUMemory(x, y, z, zero); return (bool)hnd; } virtual bool copyToGPUMemory(const astraCUDA3d::SSubDimensions3D &pos) { return astraCUDA3d::copyToGPUMemory(ptr, hnd, pos); } virtual bool copyFromGPUMemory(const astraCUDA3d::SSubDimensions3D &pos) { return astraCUDA3d::copyFromGPUMemory(ptr, hnd, pos); } private: float *ptr; void freeGPUMemory() { astraCUDA3d::freeGPUMemory(hnd); } }; CExistingGPUMemory::CExistingGPUMemory(CData3D *d) { assert(d->getStorage()->isGPU()); CDataGPU *storage = dynamic_cast(d->getStorage()); hnd = storage->getHandle(); x = d->getWidth(); y = d->getHeight(); z = d->getDepth(); } bool CExistingGPUMemory::allocateGPUMemory(unsigned int x_, unsigned int y_, unsigned int z_, astraCUDA3d::Mem3DZeroMode zero) { assert(x_ == x); assert(y_ == y); assert(z_ == z); if (zero == astraCUDA3d::INIT_ZERO) return astraCUDA3d::zeroGPUMemory(hnd, x, y, z); else return true; } bool CExistingGPUMemory::copyToGPUMemory(const astraCUDA3d::SSubDimensions3D &pos) { assert(pos.nx == x); assert(pos.ny == y); assert(pos.nz == z); assert(pos.pitch == x); assert(pos.subx == 0); assert(pos.suby == 0); assert(pos.subnx == x); assert(pos.subny == y); // These are less necessary than x/y, but allowing access to // subvolumes needs an interface change assert(pos.subz == 0); assert(pos.subnz == z); return true; } bool CExistingGPUMemory::copyFromGPUMemory(const astraCUDA3d::SSubDimensions3D &pos) { assert(pos.nx == x); assert(pos.ny == y); assert(pos.nz == z); assert(pos.pitch == x); assert(pos.subx == 0); assert(pos.suby == 0); assert(pos.subnx == x); assert(pos.subny == y); // These are less necessary than x/y, but allowing access to // subvolumes needs an interface change assert(pos.subz == 0); assert(pos.subnz == z); return true; } CGPUMemory * createGPUMemoryHandler(CData3D *d) { if (d->getStorage()->isMemory()) return new CDefaultGPUMemory(d); else return new CExistingGPUMemory(d); } static astraCUDA3d::SSubDimensions3D getPartSubDims(const CCompositeGeometryManager::CPart *part) { size_t subnx, subny, subnz; part->getDims(subnx, subny, subnz); astraCUDA3d::SSubDimensions3D d; d.nx = part->pData->getWidth(); d.pitch = d.nx; d.ny = part->pData->getHeight(); d.nz = part->pData->getDepth(); d.subnx = subnx; d.subny = subny; d.subnz = subnz; d.subx = part->subX; d.suby = part->subY; d.subz = part->subZ; return d; } static void splitPart(int n, std::unique_ptr && base, CCompositeGeometryManager::TPartList& out, size_t maxSize, size_t maxDim, int div); static std::unique_ptr reducePart(const CCompositeGeometryManager::CPart *base, const CCompositeGeometryManager::CPart *other); bool CCompositeGeometryManager::splitJobs(TJobSetInternal &jobs, size_t maxSize, int div, TJobSetInternal &split) { int maxBlockDim = astraCUDA3d::maxBlockDimension(); ASTRA_DEBUG("Found max block dim %d", maxBlockDim); split.clear(); for (TJobSetInternal::iterator i = jobs.begin(); i != jobs.end(); ++i) { std::unique_ptr pOutput = std::move(i->first); TJobListInternal &L = i->second; // 1. Split output part // 2. Per sub-part: // a. reduce input part // b. split input part // c. create jobs for new (input,output) subparts TPartList splitOutput; splitPart(2, std::move(pOutput), splitOutput, maxSize/3, UINT_MAX, div); #if 0 TPartList splitOutput2; for (TPartList::iterator i_out = splitOutput.begin(); i_out != splitOutput.end(); ++i_out) { std::shared_ptr outputPart = *i_out; outputPart.get()->split(0, splitOutput2, UINT_MAX, UINT_MAX, 1); } splitOutput.clear(); for (TPartList::iterator i_out = splitOutput2.begin(); i_out != splitOutput2.end(); ++i_out) { std::shared_ptr outputPart = *i_out; outputPart.get()->split(1, splitOutput, UINT_MAX, UINT_MAX, 1); } splitOutput2.clear(); #endif for (TPartList::iterator i_out = splitOutput.begin(); i_out != splitOutput.end(); ++i_out) { TJobListInternal newjobs; std::unique_ptr outputPart{std::move(*i_out)}; for (const SJobInternal &job : L) { std::unique_ptr input = reducePart(job.pInput.get(), outputPart.get()); if (input->getSize() == 0) { ASTRA_DEBUG("Empty input"); SJobInternal newjob{nullptr}; newjob.eMode = job.eMode; newjob.pProjector = job.pProjector; newjob.FDKSettings = job.FDKSettings; newjob.eType = JOB_NOP; newjobs.push_back(std::move(newjob)); continue; } size_t remainingSize = ( maxSize - outputPart->getSize() ) / 2; TPartList splitInput; splitPart(2, std::move(input), splitInput, remainingSize, maxBlockDim, 1); TPartList splitInput2; for (std::unique_ptr &inputPart : splitInput) splitPart(0, std::move(inputPart), splitInput2, 1024ULL*1024*1024*1024, maxBlockDim, 1); splitInput.clear(); for (std::unique_ptr &inputPart : splitInput2) splitPart(1, std::move(inputPart), splitInput, 1024ULL*1024*1024*1024, maxBlockDim, 1); splitInput2.clear(); ASTRA_DEBUG("Input split into %zu parts", splitInput.size()); EJobMode eMode = job.eMode; for (std::unique_ptr &i_in : splitInput) { SJobInternal newjob{std::move(i_in)}; newjob.eMode = eMode; newjob.pProjector = job.pProjector; newjob.FDKSettings = job.FDKSettings; newjob.eType = job.eType; newjobs.push_back(std::move(newjob)); // Second and later (input) parts should always be added to // output of first (input) part. eMode = MODE_ADD; } } split.push_back(std::make_pair(std::move(outputPart), std::move(newjobs))); } } return true; } static std::pair reduceProjectionVertical(const CVolumeGeometry3D* pVolGeom, const CProjectionGeometry3D* pProjGeom) { double umin_g, umax_g; double vmin_g, vmax_g; double pixx = pVolGeom->getPixelLengthX(); double pixy = pVolGeom->getPixelLengthY(); double pixz = pVolGeom->getPixelLengthZ(); pProjGeom->getProjectedBBox(pVolGeom->getWindowMinX() - 0.5 * pixx, pVolGeom->getWindowMaxX() + 0.5 * pixx, pVolGeom->getWindowMinY() - 0.5 * pixy, pVolGeom->getWindowMaxY() + 0.5 * pixy, pVolGeom->getWindowMinZ() - 0.5 * pixz, pVolGeom->getWindowMaxZ() + 0.5 * pixz, umin_g, umax_g, vmin_g, vmax_g); if (vmin_g < -1.0) vmin_g = -1.0; if (vmax_g > pProjGeom->getDetectorRowCount()) vmax_g = pProjGeom->getDetectorRowCount(); if (vmin_g >= vmax_g) vmin_g = vmax_g = 0.0; return std::pair(vmin_g, vmax_g); } CCompositeGeometryManager::CPart::CPart(const CPart& other) { eType = other.eType; pData = other.pData; subX = other.subX; subY = other.subY; subZ = other.subZ; } CCompositeGeometryManager::CVolumePart::CVolumePart(const CVolumePart& other) : CPart(other) { pGeom = other.pGeom->clone(); } CCompositeGeometryManager::CVolumePart::CVolumePart(CFloat32VolumeData3D *pVolData) { eType = PART_VOL; pData = pVolData; pGeom = pVolData->getGeometry().clone(); } CCompositeGeometryManager::CVolumePart::~CVolumePart() { delete pGeom; } void CCompositeGeometryManager::CVolumePart::getDims(size_t &x, size_t &y, size_t &z) const { if (!pGeom) { x = y = z = 0; return; } x = pGeom->getGridColCount(); y = pGeom->getGridRowCount(); z = pGeom->getGridSliceCount(); } size_t CCompositeGeometryManager::CPart::getSize() const { size_t x, y, z; getDims(x, y, z); return x * y * z; } bool CCompositeGeometryManager::CPart::isFull() const { size_t x, y, z; getDims(x, y, z); return x == (size_t)pData->getWidth() && y == (size_t)pData->getHeight() && z == (size_t)pData->getDepth(); } bool CCompositeGeometryManager::CPart::canSplitAndReduce() const { return pData->getStorage()->isMemory(); } static CCompositeGeometryManager::CVolumePart* createSubVolumePart(const CCompositeGeometryManager::CVolumePart* base, unsigned int offset_x, unsigned int offset_y, unsigned int offset_z, CVolumeGeometry3D *pGeom) { CCompositeGeometryManager::CVolumePart *sub = new CCompositeGeometryManager::CVolumePart(); sub->subX = base->subX + offset_x; sub->subY = base->subY + offset_y; sub->subZ = base->subZ + offset_z; sub->pData = base->pData; sub->pGeom = pGeom; return sub; } static bool testVolumeRange(const std::pair& fullRange, const CVolumeGeometry3D *pVolGeom, const CProjectionGeometry3D *pProjGeom, int zmin, int zmax) { double pixz = pVolGeom->getPixelLengthZ(); CVolumeGeometry3D test(pVolGeom->getGridColCount(), pVolGeom->getGridRowCount(), zmax - zmin, pVolGeom->getWindowMinX(), pVolGeom->getWindowMinY(), pVolGeom->getWindowMinZ() + zmin * pixz, pVolGeom->getWindowMaxX(), pVolGeom->getWindowMaxY(), pVolGeom->getWindowMinZ() + zmax * pixz); std::pair subRange = reduceProjectionVertical(&test, pProjGeom); // empty if (subRange.first == subRange.second) return true; // fully outside of fullRange if (subRange.first >= fullRange.second || subRange.second <= fullRange.first) return true; return false; } static std::unique_ptr reduceVolumePart(const CCompositeGeometryManager::CVolumePart *base, const CCompositeGeometryManager::CPart *_other ) { if (!base->canSplitAndReduce()) return std::unique_ptr(base->clone()); const CCompositeGeometryManager::CProjectionPart *other = dynamic_cast(_other); assert(other); std::pair fullRange = reduceProjectionVertical(base->pGeom, other->pGeom); int top_slice = 0, bottom_slice = 0; if (fullRange.first < fullRange.second) { // TOP SLICE int zmin = 0; int zmax = base->pGeom->getGridSliceCount()-1; // (Don't try empty region) // Setting top slice to zmin is always valid. while (zmin < zmax) { int zmid = (zmin + zmax + 1) / 2; bool ok = testVolumeRange(fullRange, base->pGeom, other->pGeom, 0, zmid); //ASTRA_DEBUG("binsearch min: [%d,%d], %d, %s", zmin, zmax, zmid, ok ? "ok" : "removed too much"); if (ok) zmin = zmid; else zmax = zmid - 1; } top_slice = zmin; // BOTTOM SLICE zmin = top_slice + 1; // (Don't try empty region) zmax = base->pGeom->getGridSliceCount(); // Setting bottom slice to zmax is always valid while (zmin < zmax) { int zmid = (zmin + zmax) / 2; bool ok = testVolumeRange(fullRange, base->pGeom, other->pGeom, zmid, base->pGeom->getGridSliceCount()); //ASTRA_DEBUG("binsearch max: [%d,%d], %d, %s", zmin, zmax, zmid, ok ? "ok" : "removed too much"); if (ok) zmax = zmid; else zmin = zmid + 1; } bottom_slice = zmax; } ASTRA_DEBUG("found extent: %d - %d", top_slice, bottom_slice); top_slice -= 1; if (top_slice < 0) top_slice = 0; bottom_slice += 1; if (bottom_slice >= base->pGeom->getGridSliceCount()) bottom_slice = base->pGeom->getGridSliceCount(); ASTRA_DEBUG("adjusted extent: %d - %d", top_slice, bottom_slice); double pixz = base->pGeom->getPixelLengthZ(); CVolumeGeometry3D *pSubGeom = 0; if (top_slice != bottom_slice) { pSubGeom = new CVolumeGeometry3D(base->pGeom->getGridColCount(), base->pGeom->getGridRowCount(), bottom_slice - top_slice, base->pGeom->getWindowMinX(), base->pGeom->getWindowMinY(), base->pGeom->getWindowMinZ() + top_slice * pixz, base->pGeom->getWindowMaxX(), base->pGeom->getWindowMaxY(), base->pGeom->getWindowMinZ() + bottom_slice * pixz); } CCompositeGeometryManager::CVolumePart *sub = createSubVolumePart(base, 0, 0, top_slice, pSubGeom); ASTRA_DEBUG("Reduce volume from %d - %d to %d - %d ( %f - %f )", base->subZ, base->subZ + base->pGeom->getGridSliceCount(), base->subZ + top_slice, base->subZ + bottom_slice, base->pGeom->getWindowMinZ() + top_slice * pixz, base->pGeom->getWindowMinZ() + bottom_slice * pixz); return std::unique_ptr(sub); } static size_t ceildiv(size_t a, size_t b) { return (a + b - 1) / b; } static size_t computeLinearSplit(size_t maxBlock, int div, size_t sliceCount) { size_t blockSize = maxBlock; size_t blockCount; if (sliceCount <= blockSize) blockCount = 1; else blockCount = ceildiv(sliceCount, blockSize); // Increase number of blocks to be divisible by div size_t divCount = div * ceildiv(blockCount, div); // If divCount is above sqrt(number of slices), then // we can't guarantee divisibility by div, but let's try anyway if (ceildiv(sliceCount, ceildiv(sliceCount, divCount)) % div == 0) { blockCount = divCount; } else { // If divisibility isn't achievable, we may want to optimize // differently. // TODO: Figure out how to model and optimize this. } // Final adjustment to make blocks more evenly sized // (This can't make the blocks larger) blockSize = ceildiv(sliceCount, blockCount); ASTRA_DEBUG("%ld %ld -> %ld * %ld", sliceCount, maxBlock, blockCount, blockSize); assert(blockSize <= maxBlock); assert((divCount * divCount > sliceCount) || (blockCount % div) == 0); return blockSize; } // split self into sub-parts: // - each no bigger than maxSize // - number of sub-parts is divisible by div // - maybe all approximately the same size? static void splitVolumePart(int n, const CCompositeGeometryManager::CPart *base, CCompositeGeometryManager::TPartList& out, size_t maxSize, size_t maxDim, int div) { assert(base->canSplitAndReduce()); assert(n >= 0 && n < 3); const CCompositeGeometryManager::CVolumePart *pPart; pPart = dynamic_cast(base); assert(pPart); const CVolumeGeometry3D *pGeom = pPart->pGeom; size_t dims[3]; pPart->getDims(dims[0], dims[1], dims[2]); size_t sliceSize = 1; for (int i = 0; i < 3; ++i) if (i != n) sliceSize *= dims[i]; int sliceCount = dims[n]; size_t m = std::min(maxSize / sliceSize, maxDim); size_t blockSize = computeLinearSplit(m, div, sliceCount); int rem = blockSize - (sliceCount % blockSize); if ((size_t)rem == blockSize) rem = 0; ASTRA_DEBUG("From %d to %d step %zu", -(rem / 2), sliceCount, blockSize); for (int x = -(rem / 2); x < sliceCount; x += blockSize) { int newsubX = x; if (newsubX < 0) newsubX = 0; int endX = x + blockSize; if (endX > sliceCount) endX = sliceCount; int size = endX - newsubX; int offsets[3] = { 0, 0, 0 }; offsets[n] = newsubX; size_t sizes[3] = { dims[0], dims[1], dims[2] }; sizes[n] = size; double shifts[3] = { 0., 0., 0. }; if (n == 0) shifts[0] = pGeom->getPixelLengthX() * newsubX; else if (n == 1) shifts[1] = pGeom->getPixelLengthY() * newsubX; else if (n == 2) shifts[2] = pGeom->getPixelLengthZ() * newsubX; CVolumeGeometry3D *pSubGeom = new CVolumeGeometry3D(sizes[0], sizes[1], sizes[2], pGeom->getWindowMinX() + shifts[0], pGeom->getWindowMinY() + shifts[1], pGeom->getWindowMinZ() + shifts[2], pGeom->getWindowMinX() + shifts[0] + sizes[0] * pGeom->getPixelLengthX(), pGeom->getWindowMinY() + shifts[1] + sizes[1] * pGeom->getPixelLengthY(), pGeom->getWindowMinZ() + shifts[2] + sizes[2] * pGeom->getPixelLengthZ() ); CCompositeGeometryManager::CVolumePart *sub = createSubVolumePart(pPart, offsets[0], offsets[1], offsets[2], pSubGeom); ASTRA_DEBUG("VolumePart split %d %d %d -> %p", sub->subX, sub->subY, sub->subZ, (void*)sub); out.push_back(std::unique_ptr(sub)); } } CCompositeGeometryManager::CVolumePart* CCompositeGeometryManager::CVolumePart::clone() const { return new CVolumePart(*this); } CCompositeGeometryManager::CProjectionPart::CProjectionPart(const CProjectionPart& other) : CPart(other) { pGeom = other.pGeom->clone(); } CCompositeGeometryManager::CProjectionPart::CProjectionPart(CFloat32ProjectionData3D *pProjData) { eType = PART_PROJ; pData = pProjData; pGeom = pProjData->getGeometry().clone(); } CCompositeGeometryManager::CProjectionPart::~CProjectionPart() { delete pGeom; } void CCompositeGeometryManager::CProjectionPart::getDims(size_t &x, size_t &y, size_t &z) const { if (!pGeom) { x = y = z = 0; return; } x = pGeom->getDetectorColCount(); y = pGeom->getProjectionCount(); z = pGeom->getDetectorRowCount(); } static CCompositeGeometryManager::CProjectionPart* createSubProjectionPart(const CCompositeGeometryManager::CProjectionPart* base, unsigned int offset_u, unsigned int offset_th, unsigned int offset_v, CProjectionGeometry3D *pGeom) { CCompositeGeometryManager::CProjectionPart *sub = new CCompositeGeometryManager::CProjectionPart(); sub->subX = base->subX + offset_u; sub->subY = base->subY + offset_th; sub->subZ = base->subZ + offset_v; sub->pData = base->pData; sub->pGeom = pGeom; return sub; } static std::unique_ptr reduceProjectionPart(const CCompositeGeometryManager::CProjectionPart *base, const CCompositeGeometryManager::CPart *_other) { if (!base->canSplitAndReduce()) return std::unique_ptr(base->clone()); const CCompositeGeometryManager::CVolumePart *other = dynamic_cast(_other); assert(other); std::pair r = reduceProjectionVertical(other->pGeom, base->pGeom); // fprintf(stderr, "v extent: %f %f\n", r.first, r.second); int _vmin = (int)floor(r.first - 1.0); int _vmax = (int)ceil(r.second + 1.0); if (_vmin < 0) _vmin = 0; if (_vmax > base->pGeom->getDetectorRowCount()) _vmax = base->pGeom->getDetectorRowCount(); if (_vmin >= _vmax) { _vmin = _vmax = 0; } CProjectionGeometry3D *pSubGeom = 0; if (_vmin != _vmax) pSubGeom = getSubProjectionGeometry_V(base->pGeom, _vmin, _vmax - _vmin); CCompositeGeometryManager::CProjectionPart *sub = createSubProjectionPart(base, 0, 0, _vmin, pSubGeom); ASTRA_DEBUG("Reduce projection from %d - %d to %d - %d", base->subZ, base->subZ + base->pGeom->getDetectorRowCount(), base->subZ + _vmin, base->subZ + _vmax); return std::unique_ptr(sub); } static void splitProjectionPart(int n, const CCompositeGeometryManager::CPart *base, CCompositeGeometryManager::TPartList &out, size_t maxSize, size_t maxDim, int div) { assert(base->canSplitAndReduce()); assert(n >= 0 && n < 3); const CCompositeGeometryManager::CProjectionPart *pPart; pPart = dynamic_cast(base); assert(pPart); const CProjectionGeometry3D *pGeom = pPart->pGeom; size_t dims[3]; pPart->getDims(dims[0], dims[1], dims[2]); size_t sliceSize = 1; for (int i = 0; i < 3; ++i) if (i != n) sliceSize *= dims[i]; int sliceCount = dims[n]; size_t m = std::min(maxSize / sliceSize, maxDim); size_t blockSize = computeLinearSplit(m, div, sliceCount); int rem = blockSize - (sliceCount % blockSize); if ((size_t)rem == blockSize) rem = 0; // When splitting angles, just start at 0 if (n == 1) rem = 0; ASTRA_DEBUG("From %d to %d step %zu", -(rem / 2), sliceCount, blockSize); for (int x = -(rem / 2); x < sliceCount; x += blockSize) { int newsubX = x; if (newsubX < 0) newsubX = 0; int endX = x + blockSize; if (endX > sliceCount) endX = sliceCount; int size = endX - newsubX; int offsets[3] = { 0, 0, 0 }; offsets[n] = newsubX; CProjectionGeometry3D *pSubGeom = 0; if (n == 0) pSubGeom = getSubProjectionGeometry_U(pGeom, newsubX, size); else if (n == 1) pSubGeom = getSubProjectionGeometry_Angle(pGeom, newsubX, size); else if (n == 2) pSubGeom = getSubProjectionGeometry_V(pGeom, newsubX, size); CCompositeGeometryManager::CProjectionPart *sub; sub = createSubProjectionPart(pPart, offsets[0], offsets[1], offsets[2], pSubGeom); ASTRA_DEBUG("ProjectionPart split %d %d %d -> %p", sub->subX, sub->subY, sub->subZ, (void*)sub); out.push_back(std::unique_ptr(sub)); } } CCompositeGeometryManager::CProjectionPart* CCompositeGeometryManager::CProjectionPart::clone() const { return new CProjectionPart(*this); } static void splitPart(int n, std::unique_ptr && base, CCompositeGeometryManager::TPartList& out, size_t maxSize, size_t maxDim, int div) { if (!base.get()->canSplitAndReduce()) { out.push_back(std::move(base)); return; } if (base.get()->eType == CCompositeGeometryManager::CPart::PART_PROJ) splitProjectionPart(n, base.get(), out, maxSize, maxDim, div); else if (base.get()->eType == CCompositeGeometryManager::CPart::PART_VOL) splitVolumePart(n, base.get(), out, maxSize, maxDim, div); else assert(false); } static std::unique_ptr reducePart(const CCompositeGeometryManager::CPart *base, const CCompositeGeometryManager::CPart *other ) { if (base->eType == CCompositeGeometryManager::CPart::PART_PROJ) return reduceProjectionPart(dynamic_cast(base), other); else if (base->eType == CCompositeGeometryManager::CPart::PART_VOL) return reduceVolumePart(dynamic_cast(base), other); else assert(false); } CCompositeGeometryManager::SJob CCompositeGeometryManager::createJobFP(CProjector3D *pProjector, CFloat32VolumeData3D *pVolData, CFloat32ProjectionData3D *pProjData, EJobMode eMode) { ASTRA_DEBUG("CCompositeGeometryManager::createJobFP"); // Create single job for FP CVolumePart *input = new CVolumePart{pVolData}; ASTRA_DEBUG("Main FP VolumePart -> %p", (void*)input); CProjectionPart *output = new CProjectionPart{pProjData}; ASTRA_DEBUG("Main FP ProjectionPart -> %p", (void*)output); SJob FP{std::unique_ptr(input), std::unique_ptr(output)}; FP.pProjector = pProjector; FP.eType = JOB_FP; FP.eMode = eMode; return FP; } CCompositeGeometryManager::SJob CCompositeGeometryManager::createJobBP(CProjector3D *pProjector, CFloat32VolumeData3D *pVolData, CFloat32ProjectionData3D *pProjData, EJobMode eMode) { ASTRA_DEBUG("CCompositeGeometryManager::createJobBP"); // Create single job for BP CProjectionPart *input = new CProjectionPart{pProjData}; CVolumePart *output = new CVolumePart{pVolData}; SJob BP{std::unique_ptr(input), std::unique_ptr(output)}; BP.pProjector = pProjector; BP.eType = JOB_BP; BP.eMode = eMode; return BP; } bool CCompositeGeometryManager::doFP(CProjector3D *pProjector, CFloat32VolumeData3D *pVolData, CFloat32ProjectionData3D *pProjData, EJobMode eMode) { TJobList L; L.push_back(createJobFP(pProjector, pVolData, pProjData, eMode)); return doJobs(L); } bool CCompositeGeometryManager::doBP(CProjector3D *pProjector, CFloat32VolumeData3D *pVolData, CFloat32ProjectionData3D *pProjData, EJobMode eMode) { TJobList L; L.push_back(createJobBP(pProjector, pVolData, pProjData, eMode)); return doJobs(L); } bool CCompositeGeometryManager::doFDK(CProjector3D *pProjector, CFloat32VolumeData3D *pVolData, CFloat32ProjectionData3D *pProjData, bool bShortScan, const SFilterConfig &filterConfig, EJobMode eMode) { if (!dynamic_cast(&pProjData->getGeometry()) && !dynamic_cast(&pProjData->getGeometry())) { ASTRA_ERROR("CCompositeGeometryManager::doFDK: cone/cone_vec geometry required"); return false; } SJob job{createJobBP(pProjector, pVolData, pProjData, eMode)}; job.eType = JOB_FDK; job.FDKSettings.bShortScan = bShortScan; job.FDKSettings.filterConfig = filterConfig; TJobList L; L.push_back(std::move(job)); return doJobs(L); } bool CCompositeGeometryManager::doFP(CProjector3D *pProjector, const std::vector& volData, const std::vector& projData, EJobMode eMode) { ASTRA_DEBUG("CCompositeGeometryManager::doFP, multi-volume"); TJobSetInternal jobset; for (CFloat32ProjectionData3D *j : projData) { CProjectionPart *output = new CProjectionPart{j}; TJobListInternal L; EJobMode eNewMode = eMode; for (CFloat32VolumeData3D *i : volData) { CVolumePart *input = new CVolumePart{i}; SJobInternal FP{std::unique_ptr(input)}; FP.eMode = eNewMode; FP.pProjector = pProjector; FP.eType = JOB_FP; L.push_back(std::move(FP)); // Always ADD rest eNewMode = MODE_ADD; } jobset.push_back(std::make_pair(std::unique_ptr(output), std::move(L))); } return doJobs(jobset); } bool CCompositeGeometryManager::doBP(CProjector3D *pProjector, const std::vector& volData, const std::vector& projData, EJobMode eMode) { ASTRA_DEBUG("CCompositeGeometryManager::doBP, multi-volume"); TJobSetInternal jobset; for (CFloat32VolumeData3D *i : volData) { CVolumePart *output = new CVolumePart{i}; TJobListInternal L; EJobMode eNewMode = eMode; for (CFloat32ProjectionData3D *j : projData) { CProjectionPart *input = new CProjectionPart{j}; SJobInternal BP{std::unique_ptr(input)}; BP.eMode = eNewMode; BP.pProjector = pProjector; BP.eType = JOB_BP; L.push_back(std::move(BP)); // Always ADD rest eNewMode = MODE_ADD; } jobset.push_back(std::make_pair(std::unique_ptr(output), std::move(L))); } return doJobs(jobset); } static bool doJob(const CCompositeGeometryManager::TJobSetInternal::const_iterator& iter) { CCompositeGeometryManager::CPart* output = iter->first.get(); const CCompositeGeometryManager::TJobListInternal& L = iter->second; assert(!L.empty()); ASTRA_DEBUG("first mode: %d", L.begin()->eMode); bool zero = L.begin()->eMode == CCompositeGeometryManager::MODE_SET; size_t outx, outy, outz; output->getDims(outx, outy, outz); astraCUDA3d::SSubDimensions3D dstdims = getPartSubDims(output); ASTRA_DEBUG("dstdims: %d,%d,%d in %d,%d,%d", dstdims.subnx, dstdims.subny, dstdims.subnz, dstdims.nx, dstdims.ny, dstdims.nz); if (L.begin()->eType == CCompositeGeometryManager::JOB_NOP) { // just zero output? if (zero) { // TODO: This function shouldn't have to know about this difference // between Memory/GPU if (output->pData->getStorage()->isMemory()) { assert(output->pData->isFloat32Memory()); for (size_t z = 0; z < outz; ++z) { for (size_t y = 0; y < outy; ++y) { float* ptr = output->pData->getFloat32Memory(); ptr += (z + output->subX) * (size_t)output->pData->getHeight() * (size_t)output->pData->getWidth(); ptr += (y + output->subY) * (size_t)output->pData->getWidth(); ptr += output->subX; memset(ptr, 0, sizeof(float) * outx); } } } else { assert(output->pData->getStorage()->isGPU()); CDataGPU *gpuMem = dynamic_cast(output->pData->getStorage()); assert(gpuMem); assert(output->isFull()); // TODO: zero subset? zeroGPUMemory(gpuMem->getHandle(), outx, outy, outz); } } return true; } CGPUMemory *dstMem = createGPUMemoryHandler(output->pData); // Use a unique_ptr to delete dstMem when we return (early) std::unique_ptr dstUniquePtr{dstMem}; bool ok = dstMem->allocateGPUMemory(outx, outy, outz, zero ? astraCUDA3d::INIT_ZERO : astraCUDA3d::INIT_NO); // TODO: cleanup and return error code after any error // // TODO: Make the memory handler a stack object and clean up afterwards? // if (!ok) { ASTRA_ERROR("Error allocating GPU memory"); return false; } if (!zero) { // instead of zeroing output memory, copy from host ok = dstMem->copyToGPUMemory(dstdims); if (!ok) { ASTRA_ERROR("Error copying output data to GPU"); return false; } } for (CCompositeGeometryManager::TJobListInternal::const_iterator i = L.begin(); i != L.end(); ++i) { const CCompositeGeometryManager::SJobInternal &j = *i; assert(j.pInput); CCudaProjector3D *projector = dynamic_cast(j.pProjector); Cuda3DProjectionKernel projKernel = ker3d_default; int detectorSuperSampling = 1; int voxelSuperSampling = 1; if (projector) { projKernel = projector->getProjectionKernel(); detectorSuperSampling = projector->getDetectorSuperSampling(); voxelSuperSampling = projector->getVoxelSuperSampling(); } size_t inx, iny, inz; j.pInput->getDims(inx, iny, inz); CGPUMemory *srcMem = createGPUMemoryHandler(j.pInput->pData); // Use a unique_ptr to delete srcMem when we return (early) std::unique_ptr srcUniquePtr{srcMem}; astraCUDA3d::SSubDimensions3D srcdims = getPartSubDims(j.pInput.get()); ok = srcMem->allocateGPUMemory(inx, iny, inz, astraCUDA3d::INIT_NO); if (!ok) { ASTRA_ERROR("Error allocating GPU memory"); return false; } ok = srcMem->copyToGPUMemory(srcdims); if (!ok) { ASTRA_ERROR("Error copying input data to GPU"); return false; } switch (j.eType) { case CCompositeGeometryManager::JOB_FP: { assert(dynamic_cast(j.pInput.get())); assert(dynamic_cast(output)); ASTRA_DEBUG("CCompositeGeometryManager::doJobs: doing FP"); ok = astraCUDA3d::FP(((CCompositeGeometryManager::CProjectionPart*)output)->pGeom, dstMem->hnd, ((CCompositeGeometryManager::CVolumePart*)j.pInput.get())->pGeom, srcMem->hnd, detectorSuperSampling, projKernel); if (!ok) { ASTRA_ERROR("Error performing sub-FP"); return false; } ASTRA_DEBUG("CCompositeGeometryManager::doJobs: FP done"); } break; case CCompositeGeometryManager::JOB_BP: { assert(dynamic_cast(output)); assert(dynamic_cast(j.pInput.get())); ASTRA_DEBUG("CCompositeGeometryManager::doJobs: doing BP"); ok = astraCUDA3d::BP(((CCompositeGeometryManager::CProjectionPart*)j.pInput.get())->pGeom, srcMem->hnd, ((CCompositeGeometryManager::CVolumePart*)output)->pGeom, dstMem->hnd, voxelSuperSampling, projKernel); if (!ok) { ASTRA_ERROR("Error performing sub-BP"); return false; } ASTRA_DEBUG("CCompositeGeometryManager::doJobs: BP done"); } break; case CCompositeGeometryManager::JOB_FDK: { assert(dynamic_cast(output)); assert(dynamic_cast(j.pInput.get())); float fOutputScale = srcdims.subny; fOutputScale /= srcdims.ny; if (j.FDKSettings.bShortScan && srcdims.subny != srcdims.ny) { ASTRA_ERROR("CCompositeGeometryManager::doJobs: shortscan FDK unsupported for this data size currently"); return false; } else if (srcdims.subx) { ASTRA_ERROR("CCompositeGeometryManager::doJobs: data too large for FDK"); return false; } else { ASTRA_DEBUG("CCompositeGeometryManager::doJobs: doing FDK"); ok = astraCUDA3d::FDK(((CCompositeGeometryManager::CProjectionPart*)j.pInput.get())->pGeom, srcMem->hnd, ((CCompositeGeometryManager::CVolumePart*)output)->pGeom, dstMem->hnd, j.FDKSettings.bShortScan, j.FDKSettings.filterConfig, fOutputScale ); if (!ok) { ASTRA_ERROR("Error performing sub-FDK"); return false; } ASTRA_DEBUG("CCompositeGeometryManager::doJobs: FDK done"); } } break; default: ASTRA_ERROR("Internal error: invalid CGM job type"); return false; } // srcUniquePtr goes out of scope here, freeing srcMem } ok = dstMem->copyFromGPUMemory(dstdims); if (!ok) { ASTRA_ERROR("Error copying output data from GPU"); return false; } // dstUniquePtr goes out of scope here, freeing dstMem return true; } class WorkQueue { public: WorkQueue(CCompositeGeometryManager::TJobSetInternal &_jobs) : m_jobs(_jobs), failed(false) { m_iter = m_jobs.begin(); } void flag_failure() { failed = true; } bool has_failed() const { return failed; } bool receive(CCompositeGeometryManager::TJobSetInternal::const_iterator &i) { if (failed) return false; lock(); if (m_iter == m_jobs.end()) { unlock(); return false; } i = m_iter++; unlock(); return true; } void lock() { m_mutex.lock(); } void unlock() { m_mutex.unlock(); } private: CCompositeGeometryManager::TJobSetInternal &m_jobs; CCompositeGeometryManager::TJobSetInternal::const_iterator m_iter; std::atomic failed; std::mutex m_mutex; }; struct WorkThreadInfo { WorkQueue* m_queue; unsigned int m_iGPU; }; void runEntries(WorkThreadInfo* info) { ASTRA_DEBUG("Launching thread on GPU %d", info->m_iGPU); CCompositeGeometryManager::TJobSetInternal::const_iterator i; while (info->m_queue->receive(i)) { ASTRA_DEBUG("Running block on GPU %d", info->m_iGPU); astraCUDA3d::setGPUIndex(info->m_iGPU); if (!doJob(i)) { ASTRA_DEBUG("Thread on GPU %d reporting failure", info->m_iGPU); info->m_queue->flag_failure(); } } ASTRA_DEBUG("Finishing thread on GPU %d", info->m_iGPU); } void runWorkQueue(WorkQueue &queue, const std::vector & iGPUIndices) { int iThreadCount = iGPUIndices.size(); std::vector infos; std::vector threads; infos.resize(iThreadCount); threads.resize(iThreadCount); ASTRA_DEBUG("Thread count %d", iThreadCount); for (int i = 0; i < iThreadCount; ++i) { infos[i].m_queue = &queue; infos[i].m_iGPU = iGPUIndices[i]; threads[i] = new std::thread(runEntries, &infos[i]); } // Wait for them to finish for (int i = 0; i < iThreadCount; ++i) { threads[i]->join(); delete threads[i]; threads[i] = 0; } } void CCompositeGeometryManager::setGPUIndices(const std::vector& GPUIndices) { m_GPUIndices = GPUIndices; } bool CCompositeGeometryManager::doJobs(TJobList &jobs) { // Convert job list into (internal) job set. // We are assuming the outputs are disjoint, so each output is // associated with a single job. TJobSetInternal jobset; for (SJob &job : jobs) { TJobListInternal newjobs; SJobInternal newjob{std::move(job.pInput)}; newjob.eMode = job.eMode; newjob.pProjector = job.pProjector; newjob.FDKSettings = job.FDKSettings; newjob.eType = job.eType; newjobs.push_back(std::move(newjob)); jobset.push_back(std::make_pair(std::move(job.pOutput), std::move(newjobs))); } return doJobs(jobset); } bool CCompositeGeometryManager::doJobs(TJobSetInternal &jobset) { // TODO: Proper clean up if substeps fail (Or as proper as possible) ASTRA_DEBUG("CCompositeGeometryManager::doJobs starting"); size_t maxSize = m_iMaxSize; if (maxSize == 0) { // Get memory from first GPU. Not optimal... if (!m_GPUIndices.empty()) astraCUDA3d::setGPUIndex(m_GPUIndices[0]); maxSize = astraCUDA::availableGPUMemory(); if (maxSize == 0) { ASTRA_WARN("Unable to get available GPU memory. Defaulting to 1GB."); maxSize = 1024 * 1024 * 1024; } else { ASTRA_DEBUG("Detected %lu bytes of GPU memory", maxSize); } } else { ASTRA_DEBUG("Set to %lu bytes of GPU memory", maxSize); } maxSize = (maxSize * 9) / 10; maxSize /= sizeof(float); int div = 1; if (!m_GPUIndices.empty()) div = m_GPUIndices.size(); // Split jobs to fit TJobSetInternal split; splitJobs(jobset, maxSize, div, split); jobset.clear(); if (m_GPUIndices.size() <= 1) { // Run jobs ASTRA_DEBUG("Running single-threaded"); if (!m_GPUIndices.empty()) astraCUDA3d::setGPUIndex(m_GPUIndices[0]); for (TJobSetInternal::const_iterator iter = split.begin(); iter != split.end(); ++iter) { if (!doJob(iter)) { ASTRA_DEBUG("doJob failed, aborting"); return false; } } } else { ASTRA_DEBUG("Running multi-threaded"); WorkQueue wq(split); runWorkQueue(wq, m_GPUIndices); if (wq.has_failed()) return false; } ASTRA_DEBUG("CCompositeGeometryManager::doJobs done"); return true; } //static void CCompositeGeometryManager::setGlobalGPUParams(const SGPUParams& params) { delete s_params; s_params = new SGPUParams; *s_params = params; ASTRA_DEBUG("CompositeGeometryManager: Setting global GPU params:"); std::ostringstream s; s << "GPU indices:"; for (unsigned int i = 0; i < params.GPUIndices.size(); ++i) s << " " << params.GPUIndices[i]; std::string ss = s.str(); ASTRA_DEBUG(ss.c_str()); ASTRA_DEBUG("Memory: %zu", params.memory); } } #endif astra-toolbox-2.3.0/src/ConeProjectionGeometry3D.cpp000066400000000000000000000175501475635207100224530ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ConeProjectionGeometry3D.h" #include "astra/Logging.h" #include "astra/XMLConfig.h" #include "astra/GeometryUtil3D.h" #include using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Default constructor. CConeProjectionGeometry3D::CConeProjectionGeometry3D() : CProjectionGeometry3D() { m_fOriginSourceDistance = 0.0f; m_fOriginDetectorDistance = 0.0f; } //---------------------------------------------------------------------------------------- // Constructor. CConeProjectionGeometry3D::CConeProjectionGeometry3D(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, float32 _fDetectorWidth, float32 _fDetectorHeight, std::vector &&_pfProjectionAngles, float32 _fOriginSourceDistance, float32 _fOriginDetectorDistance) : CProjectionGeometry3D() { initialize(_iProjectionAngleCount, _iDetectorRowCount, _iDetectorColCount, _fDetectorWidth, _fDetectorHeight, std::move(_pfProjectionAngles), _fOriginSourceDistance, _fOriginDetectorDistance); } //---------------------------------------------------------------------------------------- // Destructor. CConeProjectionGeometry3D::~CConeProjectionGeometry3D() { } //--------------------------------------------------------------------------------------- // Initialize - Config bool CConeProjectionGeometry3D::initialize(const Config& _cfg) { ConfigReader CR("ConeProjectionGeometry3D", this, _cfg); // initialization of parent class if (!CProjectionGeometry3D::initialize(_cfg)) return false; bool ok = true; ok &= CR.getRequiredNumerical("DistanceOriginDetector", m_fOriginDetectorDistance); ok &= CR.getRequiredNumerical("DistanceOriginSource", m_fOriginSourceDistance); if (!ok) return false; // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization. bool CConeProjectionGeometry3D::initialize(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, float32 _fDetectorWidth, float32 _fDetectorHeight, std::vector &&_pfProjectionAngles, float32 _fOriginSourceDistance, float32 _fOriginDetectorDistance) { _initialize(_iProjectionAngleCount, _iDetectorRowCount, _iDetectorColCount, _fDetectorWidth, _fDetectorHeight, std::move(_pfProjectionAngles)); m_fOriginSourceDistance = _fOriginSourceDistance; m_fOriginDetectorDistance = _fOriginDetectorDistance; // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Clone CProjectionGeometry3D* CConeProjectionGeometry3D::clone() const { CConeProjectionGeometry3D* res = new CConeProjectionGeometry3D(); res->m_bInitialized = m_bInitialized; res->m_iProjectionAngleCount = m_iProjectionAngleCount; res->m_iDetectorRowCount = m_iDetectorRowCount; res->m_iDetectorColCount = m_iDetectorColCount; res->m_iDetectorTotCount = m_iDetectorTotCount; res->m_fDetectorSpacingX = m_fDetectorSpacingX; res->m_fDetectorSpacingY = m_fDetectorSpacingY; res->m_pfProjectionAngles = m_pfProjectionAngles; res->m_fOriginSourceDistance = m_fOriginSourceDistance; res->m_fOriginDetectorDistance = m_fOriginDetectorDistance; return res; } //---------------------------------------------------------------------------------------- // is equal bool CConeProjectionGeometry3D::isEqual(const CProjectionGeometry3D* _pGeom2) const { if (_pGeom2 == NULL) return false; // try to cast argument to CParallelProjectionGeometry3D const CConeProjectionGeometry3D* pGeom2 = dynamic_cast(_pGeom2); if (pGeom2 == NULL) return false; // both objects must be initialized if (!m_bInitialized || !pGeom2->m_bInitialized) return false; // check all values if (m_iProjectionAngleCount != pGeom2->m_iProjectionAngleCount) return false; if (m_iDetectorRowCount != pGeom2->m_iDetectorRowCount) return false; if (m_iDetectorColCount != pGeom2->m_iDetectorColCount) return false; if (m_iDetectorTotCount != pGeom2->m_iDetectorTotCount) return false; if (m_fDetectorSpacingX != pGeom2->m_fDetectorSpacingX) return false; if (m_fDetectorSpacingY != pGeom2->m_fDetectorSpacingY) return false; if (m_fOriginSourceDistance != pGeom2->m_fOriginSourceDistance) return false; if (m_fOriginDetectorDistance != pGeom2->m_fOriginDetectorDistance) return false; for (int i = 0; i < m_iProjectionAngleCount; ++i) { if (m_pfProjectionAngles[i] != pGeom2->m_pfProjectionAngles[i]) return false; } return true; } //---------------------------------------------------------------------------------------- // is of type bool CConeProjectionGeometry3D::isOfType(const std::string& _sType) const { return (_sType == "cone"); } //---------------------------------------------------------------------------------------- // Get the configuration object Config* CConeProjectionGeometry3D::getConfiguration() const { ConfigWriter CW("ProjectionGeometry3D", "cone"); CW.addInt("DetectorRowCount", m_iDetectorRowCount); CW.addInt("DetectorColCount", m_iDetectorColCount); CW.addNumerical("DetectorSpacingX", m_fDetectorSpacingX); CW.addNumerical("DetectorSpacingY", m_fDetectorSpacingY); CW.addNumerical("DistanceOriginDetector", m_fOriginDetectorDistance); CW.addNumerical("DistanceOriginSource", m_fOriginSourceDistance); CW.addNumericalArray("ProjectionAngles", &m_pfProjectionAngles[0], m_iProjectionAngleCount); return CW.getConfig(); } //---------------------------------------------------------------------------------------- void CConeProjectionGeometry3D::projectPoint(double fX, double fY, double fZ, int iAngleIndex, double &fU, double &fV) const { ASTRA_ASSERT(iAngleIndex >= 0); ASTRA_ASSERT(iAngleIndex < m_iProjectionAngleCount); double alpha = m_pfProjectionAngles[iAngleIndex]; // Project point onto optical axis // Projector direction is (cos(alpha), sin(alpha)) // Vector source->origin is (-sin(alpha), cos(alpha)) // Distance from source, projected on optical axis double fD = -sin(alpha) * fX + cos(alpha) * fY + m_fOriginSourceDistance; // Scale fZ to detector plane fV = detectorOffsetYToRowIndexFloat( (fZ * (m_fOriginSourceDistance + m_fOriginDetectorDistance)) / fD ); // Orthogonal distance in XY-plane to optical axis double fS = cos(alpha) * fX + sin(alpha) * fY; // Scale fS to detector plane fU = detectorOffsetXToColIndexFloat( (fS * (m_fOriginSourceDistance + m_fOriginDetectorDistance)) / fD ); } } // end namespace astra astra-toolbox-2.3.0/src/ConeVecProjectionGeometry3D.cpp000066400000000000000000000217321475635207100231060ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ConeVecProjectionGeometry3D.h" #include "astra/XMLConfig.h" #include "astra/Utilities.h" #include "astra/Logging.h" #include using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Default constructor. CConeVecProjectionGeometry3D::CConeVecProjectionGeometry3D() : CProjectionGeometry3D() { } //---------------------------------------------------------------------------------------- // Constructor. CConeVecProjectionGeometry3D::CConeVecProjectionGeometry3D(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, std::vector &&_pProjectionAngles) : CProjectionGeometry3D() { initialize(_iProjectionAngleCount, _iDetectorRowCount, _iDetectorColCount, std::move(_pProjectionAngles)); } //---------------------------------------------------------------------------------------- // Destructor. CConeVecProjectionGeometry3D::~CConeVecProjectionGeometry3D() { } //--------------------------------------------------------------------------------------- // Initialize - Config bool CConeVecProjectionGeometry3D::initialize(const Config& _cfg) { ConfigReader CR("ConeVecProjectionGeometry3D", this, _cfg); // initialization of parent class if (!CProjectionGeometry3D::initialize(_cfg)) return false; // success m_bInitialized = _check(); return m_bInitialized; } bool CConeVecProjectionGeometry3D::initializeAngles(const Config& _cfg) { ConfigReader CR("ConeVecProjectionGeometry3D", this, _cfg); // Required: Vectors vector data; if (!CR.getRequiredNumericalArray("Vectors", data)) return false; ASTRA_CONFIG_CHECK(data.size() % 12 == 0, "ConeVecProjectionGeometry3D", "Vectors doesn't consist of 12-tuples."); m_iProjectionAngleCount = data.size() / 12; m_ProjectionAngles.resize(m_iProjectionAngleCount); for (int i = 0; i < m_iProjectionAngleCount; ++i) { SConeProjection& p = m_ProjectionAngles[i]; p.fSrcX = data[12*i + 0]; p.fSrcY = data[12*i + 1]; p.fSrcZ = data[12*i + 2]; p.fDetUX = data[12*i + 6]; p.fDetUY = data[12*i + 7]; p.fDetUZ = data[12*i + 8]; p.fDetVX = data[12*i + 9]; p.fDetVY = data[12*i + 10]; p.fDetVZ = data[12*i + 11]; // The backend code currently expects the corner of the detector, while // the matlab interface supplies the center p.fDetSX = data[12*i + 3] - 0.5 * m_iDetectorRowCount * p.fDetVX - 0.5 * m_iDetectorColCount * p.fDetUX; p.fDetSY = data[12*i + 4] - 0.5 * m_iDetectorRowCount * p.fDetVY - 0.5 * m_iDetectorColCount * p.fDetUY; p.fDetSZ = data[12*i + 5] - 0.5 * m_iDetectorRowCount * p.fDetVZ - 0.5 * m_iDetectorColCount * p.fDetUZ; } return true; } //---------------------------------------------------------------------------------------- // Initialization. bool CConeVecProjectionGeometry3D::initialize(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, std::vector &&_pProjectionAngles) { m_iProjectionAngleCount = _iProjectionAngleCount; m_iDetectorRowCount = _iDetectorRowCount; m_iDetectorColCount = _iDetectorColCount; m_ProjectionAngles = std::move(_pProjectionAngles); // TODO: check? // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Clone CProjectionGeometry3D* CConeVecProjectionGeometry3D::clone() const { CConeVecProjectionGeometry3D* res = new CConeVecProjectionGeometry3D(); res->m_bInitialized = m_bInitialized; res->m_iProjectionAngleCount = m_iProjectionAngleCount; res->m_iDetectorRowCount = m_iDetectorRowCount; res->m_iDetectorColCount = m_iDetectorColCount; res->m_iDetectorTotCount = m_iDetectorTotCount; res->m_fDetectorSpacingX = m_fDetectorSpacingX; res->m_fDetectorSpacingY = m_fDetectorSpacingY; res->m_ProjectionAngles = m_ProjectionAngles; return res; } //---------------------------------------------------------------------------------------- // is equal bool CConeVecProjectionGeometry3D::isEqual(const CProjectionGeometry3D * _pGeom2) const { if (_pGeom2 == NULL) return false; // try to cast argument to CConeProjectionGeometry3D const CConeVecProjectionGeometry3D* pGeom2 = dynamic_cast(_pGeom2); if (pGeom2 == NULL) return false; // both objects must be initialized if (!m_bInitialized || !pGeom2->m_bInitialized) return false; // check all values if (m_iProjectionAngleCount != pGeom2->m_iProjectionAngleCount) return false; if (m_iDetectorRowCount != pGeom2->m_iDetectorRowCount) return false; if (m_iDetectorColCount != pGeom2->m_iDetectorColCount) return false; if (m_iDetectorTotCount != pGeom2->m_iDetectorTotCount) return false; //if (m_fDetectorSpacingX != pGeom2->m_fDetectorSpacingX) return false; //if (m_fDetectorSpacingY != pGeom2->m_fDetectorSpacingY) return false; for (int i = 0; i < m_iProjectionAngleCount; ++i) { if (memcmp(&m_ProjectionAngles[i], &pGeom2->m_ProjectionAngles[i], sizeof(m_ProjectionAngles[i])) != 0) return false; } return true; } //---------------------------------------------------------------------------------------- // is of type bool CConeVecProjectionGeometry3D::isOfType(const std::string& _sType) const { return (_sType == "cone_vec"); } //---------------------------------------------------------------------------------------- // Get the configuration object Config* CConeVecProjectionGeometry3D::getConfiguration() const { ConfigWriter CW("ProjectionGeometry3D", "cone_vec"); CW.addInt("DetectorRowCount", m_iDetectorRowCount); CW.addInt("DetectorColCount", m_iDetectorColCount); std::vector vectors; vectors.resize(12 * m_iProjectionAngleCount); for (int i = 0; i < m_iProjectionAngleCount; ++i) { const SConeProjection& p = m_ProjectionAngles[i]; vectors[12*i + 0] = p.fSrcX; vectors[12*i + 1] = p.fSrcY; vectors[12*i + 2] = p.fSrcZ; vectors[12*i + 3] = p.fDetSX + 0.5*m_iDetectorRowCount*p.fDetVX + 0.5*m_iDetectorColCount*p.fDetUX; vectors[12*i + 4] = p.fDetSY + 0.5*m_iDetectorRowCount*p.fDetVY + 0.5*m_iDetectorColCount*p.fDetUY; vectors[12*i + 5] = p.fDetSZ + 0.5*m_iDetectorRowCount*p.fDetVZ + 0.5*m_iDetectorColCount*p.fDetUZ; vectors[12*i + 6] = p.fDetUX; vectors[12*i + 7] = p.fDetUY; vectors[12*i + 8] = p.fDetUZ; vectors[12*i + 9] = p.fDetVX; vectors[12*i + 10] = p.fDetVY; vectors[12*i + 11] = p.fDetVZ; } CW.addNumericalMatrix("Vectors", &vectors[0], m_iProjectionAngleCount, 12); return CW.getConfig(); } //---------------------------------------------------------------------------------------- void CConeVecProjectionGeometry3D::projectPoint(double fX, double fY, double fZ, int iAngleIndex, double &fU, double &fV) const { ASTRA_ASSERT(iAngleIndex >= 0); ASTRA_ASSERT(iAngleIndex < m_iProjectionAngleCount); double fUX, fUY, fUZ, fUC; double fVX, fVY, fVZ, fVC; double fDX, fDY, fDZ, fDC; computeBP_UV_Coeffs(m_ProjectionAngles[iAngleIndex], fUX, fUY, fUZ, fUC, fVX, fVY, fVZ, fVC, fDX, fDY, fDZ, fDC); // The -0.5f shifts from corner to center of detector pixels double fD = fDX*fX + fDY*fY + fDZ*fZ + fDC; fU = (fUX*fX + fUY*fY + fUZ*fZ + fUC) / fD - 0.5; fV = (fVX*fX + fVY*fY + fVZ*fZ + fVC) / fD - 0.5; } //---------------------------------------------------------------------------------------- bool CConeVecProjectionGeometry3D::_check() { ASTRA_CONFIG_CHECK(m_ProjectionAngles.size() == m_iProjectionAngleCount, "ConeVecProjectionGeometry3D", "Number of vectors does not match number of angles"); // TODO return true; } } // end namespace astra astra-toolbox-2.3.0/src/Config.cpp000066400000000000000000000247361475635207100170400ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Config.h" // For explicit ConfigStackCheck instantiations #include "astra/Algorithm.h" #include "astra/VolumeGeometry2D.h" #include "astra/VolumeGeometry3D.h" #include "astra/ProjectionGeometry2D.h" #include "astra/ProjectionGeometry3D.h" #include "astra/Projector2D.h" #include "astra/Projector3D.h" #include "astra/Logging.h" #include using namespace std; namespace astra { template ConfigReader::ConfigReader(const char *_name, T* _obj, const Config& _cfg) : object(_obj), cfg(&_cfg), objName(_name) { assert(object); assert(cfg); if (!object->configCheckData) { object->configCheckData = new ConfigCheckData; object->configCheckData->parseDepth = 0; } object->configCheckData->parseDepth++; } template ConfigReader::~ConfigReader() { assert(object->configCheckData); assert(object->configCheckData->parseDepth > 0); if (object->configCheckData->parseDepth == 1) { // Entirely done with parsing this Config object if (object->isInitialized()) stopParsing(); delete object->configCheckData; object->configCheckData = 0; } else { object->configCheckData->parseDepth--; } } // returns true if no unused nodes/options template bool ConfigReader::stopParsing() { assert(object->configCheckData); assert(object->configCheckData->parseDepth > 0); if (object->configCheckData->parseDepth > 1) return true; // If this was the top-level parse function, check std::list errors = cfg->checkUnparsed(*object->configCheckData); if (!errors.empty()) { ostringstream os; os << objName << ": unused configuration options: "; os << errors.front(); errors.pop_front(); for (const std::string &str : errors) os << str; ASTRA_WARN(os.str().c_str()); return false; } return true; } template void ConfigReader::markNodeParsed(const std::string& nodeName) { assert(object->configCheckData); assert(object->configCheckData->parseDepth > 0); object->configCheckData->parsedNodes.insert(nodeName); } template void ConfigReader::markOptionParsed(const std::string& nodeName) { assert(object->configCheckData); assert(object->configCheckData->parseDepth > 0); object->configCheckData->parsedOptions.insert(nodeName); } template bool ConfigReader::has(const std::string &name) { return cfg->has(name); } template bool ConfigReader::getRequiredInt(const std::string &name, int &iValue) { if (!cfg->has(name)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: No %s tag specified.", objName, name.c_str()); return false; } markNodeParsed(name); if (!cfg->getInt(name, iValue)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: %s must be an integer.", objName, name.c_str()); return false; } return true; } template bool ConfigReader::getRequiredNumerical(const std::string &name, float &fValue) { if (!cfg->has(name)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: No %s tag specified.", objName, name.c_str()); return false; } markNodeParsed(name); if (!cfg->getFloat(name, fValue)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: %s must be numerical.", objName, name.c_str()); return false; } return true; } template bool ConfigReader::getRequiredID(const std::string &name, int &iValue) { iValue = -1; if (!cfg->has(name)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: No %s tag specified.", objName, name.c_str()); return false; } markNodeParsed(name); if (!cfg->getInt(name, iValue)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: Unable to parse %s as an ID.", objName, name.c_str()); iValue = -1; return false; } return true; } template bool ConfigReader::getRequiredNumericalArray(const std::string &name, std::vector &values) { values.clear(); if (!cfg->has(name)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: No %s tag specified.", objName, name.c_str()); return false; } markNodeParsed(name); if (!cfg->getDoubleArray(name, values)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: %s must be a numerical matrix.", objName, name.c_str()); return false; } return true; } template bool ConfigReader::getRequiredIntArray(const std::string &name, std::vector &values) { values.clear(); if (!cfg->has(name)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: No %s tag specified.", objName, name.c_str()); return false; } markNodeParsed(name); if (!cfg->getIntArray(name, values)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: %s must be an integer array.", objName, name.c_str()); return false; } return true; } template bool ConfigReader::getRequiredString(const std::string &name, std::string &sValue) { if (!cfg->has(name)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: No %s tag specified.", objName, name.c_str()); return false; } markNodeParsed(name); if (!cfg->getString(name, sValue)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: %s must be a string.", objName, name.c_str()); return false; } return true; } template bool ConfigReader::getID(const std::string &name, int &iValue) { iValue = -1; if (!cfg->has(name)) return false; markNodeParsed(name); if (!cfg->getInt(name, iValue)) { iValue = -1; return false; } return true; } template bool ConfigReader::getString(const std::string &name, std::string &sValue, const std::string &sDefaultValue) { sValue = sDefaultValue; if (!cfg->has(name)) return false; markNodeParsed(name); if (!cfg->getString(name, sValue)) return false; return true; } template bool ConfigReader::getRequiredSubConfig(const std::string &name, Config *&_cfg, std::string &type) { if (!cfg->has(name)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: No %s tag specified.", objName, name.c_str()); return false; } markNodeParsed(name); if (!cfg->getSubConfig(name, _cfg, type)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: %s must be a configuration object.", objName, name.c_str()); return false; } return true; } template bool ConfigReader::hasOption(const std::string &name) { return cfg->hasOption(name); } template bool ConfigReader::getOptionNumerical(const std::string &name, float &fValue, float fDefaultValue) { fValue = fDefaultValue; if (cfg->hasOption(name) && !cfg->getOptionFloat(name, fValue)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: option %s must be numerical.", objName, name.c_str()); return false; } markOptionParsed(name); return true; } template bool ConfigReader::getOptionInt(const std::string &name, int &iValue, int iDefaultValue) { iValue = iDefaultValue; if (cfg->hasOption(name) && !cfg->getOptionInt(name, iValue)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: option %s must be integer.", objName, name.c_str()); return false; } markOptionParsed(name); return true; } template bool ConfigReader::getOptionUInt(const std::string &name, unsigned int &iValue, unsigned int iDefaultValue) { iValue = iDefaultValue; if (cfg->hasOption(name) && !cfg->getOptionUInt(name, iValue)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: option %s must be unsigned integer.", objName, name.c_str()); return false; } markOptionParsed(name); return true; } template bool ConfigReader::getOptionBool(const std::string &name, bool &bValue, bool bDefaultValue) { bValue = bDefaultValue; if (cfg->hasOption(name) && !cfg->getOptionBool(name, bValue)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: option %s must be boolean.", objName, name.c_str()); return false; } markOptionParsed(name); return true; } template bool ConfigReader::getOptionString(const std::string &name, std::string &sValue, std::string sDefaultValue) { sValue = sDefaultValue; if (cfg->hasOption(name) && !cfg->getOptionString(name, sValue)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: option %s must be a string.", objName, name.c_str()); return false; } markOptionParsed(name); return true; } template bool ConfigReader::getOptionIntArray(const std::string &name, std::vector &values) { values.clear(); if (cfg->hasOption(name) && !cfg->getOptionIntArray(name, values)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: option %s must be an integer array.", objName, name.c_str()); return false; } markNodeParsed(name); return true; } template bool ConfigReader::getOptionID(const std::string &name, int &iValue) { iValue = -1; if (!cfg->hasOption(name)) return false; if (!cfg->getOptionInt(name, iValue)) { astra::CLogger::error(__FILE__, __LINE__, "Configuration error in %s: option %s must be an ID.", objName, name.c_str()); iValue = -1; return false; } markOptionParsed(name); return true; } // TODO: Add base class "Configurable" template class ConfigReader; template class ConfigReader; template class ConfigReader; template class ConfigReader; template class ConfigReader; template class ConfigReader; template class ConfigReader; } astra-toolbox-2.3.0/src/CudaBackProjectionAlgorithm.cpp000066400000000000000000000051201475635207100231560ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_CUDA #include "astra/CudaBackProjectionAlgorithm.h" #include "astra/cuda/2d/astra.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaBackProjectionAlgorithm::CCudaBackProjectionAlgorithm() { m_bIsInitialized = false; CCudaReconstructionAlgorithm2D::_clear(); } //---------------------------------------------------------------------------------------- // Destructor CCudaBackProjectionAlgorithm::~CCudaBackProjectionAlgorithm() { // The actual work is done by ~CCudaReconstructionAlgorithm2D } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaBackProjectionAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("CudaBackProjectionAlgorithm", this, _cfg); m_bIsInitialized = CCudaReconstructionAlgorithm2D::initialize(_cfg); if (!m_bIsInitialized) return false; m_pAlgo = new BPalgo(); m_bAlgoInit = false; return true; } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaBackProjectionAlgorithm::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { m_bIsInitialized = CCudaReconstructionAlgorithm2D::initialize(_pProjector, _pSinogram, _pReconstruction); if (!m_bIsInitialized) return false; m_pAlgo = new BPalgo(); m_bAlgoInit = false; return true; } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaBackProjectionAlgorithm3D.cpp000066400000000000000000000133301475635207100233470ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/CudaBackProjectionAlgorithm3D.h" #include "astra/AstraObjectManager.h" #include "astra/CudaProjector3D.h" #include "astra/ConeProjectionGeometry3D.h" #include "astra/ParallelProjectionGeometry3D.h" #include "astra/ParallelVecProjectionGeometry3D.h" #include "astra/ConeVecProjectionGeometry3D.h" #include "astra/CompositeGeometryManager.h" #include "astra/Logging.h" #include "astra/cuda/3d/astra3d.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaBackProjectionAlgorithm3D::CCudaBackProjectionAlgorithm3D() { m_bIsInitialized = false; m_iGPUIndex = -1; m_iVoxelSuperSampling = 1; m_bSIRTWeighting = false; } //---------------------------------------------------------------------------------------- // Constructor with initialization CCudaBackProjectionAlgorithm3D::CCudaBackProjectionAlgorithm3D(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pProjectionData, CFloat32VolumeData3D* _pReconstruction) { _clear(); initialize(_pProjector, _pProjectionData, _pReconstruction); } //---------------------------------------------------------------------------------------- // Destructor CCudaBackProjectionAlgorithm3D::~CCudaBackProjectionAlgorithm3D() { CReconstructionAlgorithm3D::_clear(); } //--------------------------------------------------------------------------------------- // Check bool CCudaBackProjectionAlgorithm3D::_check() { // check base class ASTRA_CONFIG_CHECK(CReconstructionAlgorithm3D::_check(), "BP3D_CUDA", "Error in ReconstructionAlgorithm3D initialization"); return true; } //--------------------------------------------------------------------------------------- void CCudaBackProjectionAlgorithm3D::initializeFromProjector() { m_iVoxelSuperSampling = 1; m_iGPUIndex = -1; CCudaProjector3D* pCudaProjector = dynamic_cast(m_pProjector); if (!pCudaProjector) { if (m_pProjector) { ASTRA_WARN("non-CUDA Projector3D passed to BP3D_CUDA"); } } else { m_iVoxelSuperSampling = pCudaProjector->getVoxelSuperSampling(); m_iGPUIndex = pCudaProjector->getGPUIndex(); } } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaBackProjectionAlgorithm3D::initialize(const Config& _cfg) { ConfigReader CR("CudaBackProjectionAlgorithm3D", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CReconstructionAlgorithm3D::initialize(_cfg)) { return false; } initializeFromProjector(); bool ok = true; // Deprecated options ok &= CR.getOptionInt("VoxelSuperSampling", m_iVoxelSuperSampling, m_iVoxelSuperSampling); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, m_iGPUIndex); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, m_iGPUIndex); ok &= CR.getOptionBool("SIRTWeighting", m_bSIRTWeighting, false); if (!ok) return false; // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaBackProjectionAlgorithm3D::initialize(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pSinogram, CFloat32VolumeData3D* _pReconstruction) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // required classes m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; m_bSIRTWeighting = false; initializeFromProjector(); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Iterate bool CCudaBackProjectionAlgorithm3D::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); CFloat32ProjectionData3D* pSinoMem = dynamic_cast(m_pSinogram); ASTRA_ASSERT(pSinoMem); CFloat32VolumeData3D* pReconMem = dynamic_cast(m_pReconstruction); ASTRA_ASSERT(pReconMem); const CProjectionGeometry3D& projgeom = pSinoMem->getGeometry(); const CVolumeGeometry3D& volgeom = pReconMem->getGeometry(); if (m_bSIRTWeighting) { ASTRA_ASSERT(m_pSinogram->isFloat32Memory()); ASTRA_ASSERT(m_pReconstruction->isFloat32Memory()); return astraCudaBP_SIRTWeighted(m_pReconstruction->getFloat32Memory(), m_pSinogram->getFloat32Memory(), &volgeom, &projgeom, m_iGPUIndex, m_iVoxelSuperSampling); } else { CCompositeGeometryManager cgm; return cgm.doBP(m_pProjector, pReconMem, pSinoMem); } } } // namespace astra astra-toolbox-2.3.0/src/CudaCglsAlgorithm.cpp000066400000000000000000000052471475635207100211630ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_CUDA #include "astra/CudaCglsAlgorithm.h" #include "astra/cuda/2d/cgls.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaCglsAlgorithm::CCudaCglsAlgorithm() { m_bIsInitialized = false; CReconstructionAlgorithm2D::_clear(); } //---------------------------------------------------------------------------------------- // Destructor CCudaCglsAlgorithm::~CCudaCglsAlgorithm() { // The actual work is done by ~CCudaReconstructionAlgorithm2D } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaCglsAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("CudaCglsAlgorithm", this, _cfg); if (CR.hasOption("SinogramMaskId")) { ASTRA_CONFIG_CHECK(false, "CGLS_CUDA", "Sinogram mask option is not supported."); } m_bIsInitialized = CCudaReconstructionAlgorithm2D::initialize(_cfg); if (!m_bIsInitialized) return false; m_pAlgo = new astraCUDA::CGLS(); m_bAlgoInit = false; return true; } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaCglsAlgorithm::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { m_bIsInitialized = CCudaReconstructionAlgorithm2D::initialize(_pProjector, _pSinogram, _pReconstruction); if (!m_bIsInitialized) return false; m_pAlgo = new astraCUDA::CGLS(); m_bAlgoInit = false; return true; } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaCglsAlgorithm3D.cpp000066400000000000000000000161511475635207100213460ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/CudaCglsAlgorithm3D.h" #include "astra/AstraObjectManager.h" #include "astra/CudaProjector3D.h" #include "astra/ConeProjectionGeometry3D.h" #include "astra/ParallelVecProjectionGeometry3D.h" #include "astra/ConeVecProjectionGeometry3D.h" #include "astra/VolumeGeometry3D.h" #include "astra/Logging.h" #include "astra/cuda/3d/astra3d.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaCglsAlgorithm3D::CCudaCglsAlgorithm3D() { m_bIsInitialized = false; m_pCgls = 0; m_iGPUIndex = -1; m_iVoxelSuperSampling = 1; m_iDetectorSuperSampling = 1; } //---------------------------------------------------------------------------------------- // Constructor with initialization CCudaCglsAlgorithm3D::CCudaCglsAlgorithm3D(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pProjectionData, CFloat32VolumeData3D* _pReconstruction) { _clear(); initialize(_pProjector, _pProjectionData, _pReconstruction); } //---------------------------------------------------------------------------------------- // Destructor CCudaCglsAlgorithm3D::~CCudaCglsAlgorithm3D() { delete m_pCgls; m_pCgls = 0; CReconstructionAlgorithm3D::_clear(); } //--------------------------------------------------------------------------------------- // Check bool CCudaCglsAlgorithm3D::_check() { // check base class ASTRA_CONFIG_CHECK(CReconstructionAlgorithm3D::_check(), "CGLS3D", "Error in ReconstructionAlgorithm3D initialization"); return true; } //--------------------------------------------------------------------------------------- void CCudaCglsAlgorithm3D::initializeFromProjector() { m_iVoxelSuperSampling = 1; m_iDetectorSuperSampling = 1; m_iGPUIndex = -1; CCudaProjector3D* pCudaProjector = dynamic_cast(m_pProjector); if (!pCudaProjector) { if (m_pProjector) { ASTRA_WARN("non-CUDA Projector3D passed to CGLS3D_CUDA"); } } else { m_iVoxelSuperSampling = pCudaProjector->getVoxelSuperSampling(); m_iDetectorSuperSampling = pCudaProjector->getDetectorSuperSampling(); m_iGPUIndex = pCudaProjector->getGPUIndex(); } } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaCglsAlgorithm3D::initialize(const Config& _cfg) { ConfigReader CR("CudaCglsAlgorithm3D", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CReconstructionAlgorithm3D::initialize(_cfg)) { return false; } initializeFromProjector(); bool ok = true; // Deprecated options ok &= CR.getOptionInt("VoxelSuperSampling", m_iVoxelSuperSampling, m_iVoxelSuperSampling); ok &= CR.getOptionInt("DetectorSuperSampling", m_iDetectorSuperSampling, m_iDetectorSuperSampling); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, m_iGPUIndex); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, m_iGPUIndex); if (!ok) return false; m_pCgls = new AstraCGLS3d(); m_bAstraCGLSInit = false; // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaCglsAlgorithm3D::initialize(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pSinogram, CFloat32VolumeData3D* _pReconstruction) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // required classes m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; initializeFromProjector(); m_pCgls = new AstraCGLS3d; m_bAstraCGLSInit = false; // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Iterate bool CCudaCglsAlgorithm3D::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); const CProjectionGeometry3D& projgeom = m_pSinogram->getGeometry(); const CVolumeGeometry3D& volgeom = m_pReconstruction->getGeometry(); bool ok = true; if (!m_bAstraCGLSInit) { ok &= m_pCgls->setGPUIndex(m_iGPUIndex); ok &= m_pCgls->setGeometry(&volgeom, &projgeom); ok &= m_pCgls->enableSuperSampling(m_iVoxelSuperSampling, m_iDetectorSuperSampling); if (m_bUseReconstructionMask) ok &= m_pCgls->enableVolumeMask(); #if 0 if (m_bUseSinogramMask) ok &= m_pCgls->enableSinogramMask(); #endif ASTRA_ASSERT(ok); ok &= m_pCgls->init(); ASTRA_ASSERT(ok); m_bAstraCGLSInit = true; } ASTRA_ASSERT(m_pSinogram->isFloat32Memory()); ok = m_pCgls->setSinogram(m_pSinogram->getFloat32Memory(), m_pSinogram->getGeometry().getDetectorColCount()); ASTRA_ASSERT(ok); if (m_bUseReconstructionMask) { ASTRA_ASSERT(m_pReconstructionMask->isFloat32Memory()); ok &= m_pCgls->setVolumeMask(m_pReconstructionMask->getFloat32Memory(), volgeom.getGridColCount()); } #if 0 if (m_bUseSinogramMask) { CFloat32ProjectionData3DMemory* pSMaskMem = dynamic_cast(m_pSinogramMask); ASTRA_ASSERT(m_pSinogramMask->isFloat32Memory()); ok &= m_pCgls->setSinogramMask(m_pSinogramMask->getFloat32Memory(), m_pSinogramMask->getGeometry()->getDetectorColCount()); } #endif ASTRA_ASSERT(m_pReconstruction->isFloat32Memory()); ok &= m_pCgls->setStartReconstruction(m_pReconstruction->getFloat32Memory(), volgeom.getGridColCount()); ASTRA_ASSERT(ok); #if 0 if (m_bUseMinConstraint) ok &= m_pCgls->setMinConstraint(m_fMinValue); if (m_bUseMaxConstraint) ok &= m_pCgls->setMaxConstraint(m_fMaxValue); #endif ok &= m_pCgls->iterate(_iNrIterations); ASTRA_ASSERT(ok); ok &= m_pCgls->getReconstruction(m_pReconstruction->getFloat32Memory(), volgeom.getGridColCount()); ASTRA_ASSERT(ok); return ok; } //---------------------------------------------------------------------------------------- bool CCudaCglsAlgorithm3D::getResidualNorm(float32& _fNorm) { if (!m_bIsInitialized || !m_pCgls) return false; _fNorm = m_pCgls->computeDiffNorm(); return true; } } // namespace astra astra-toolbox-2.3.0/src/CudaDartMaskAlgorithm.cpp000066400000000000000000000072401475635207100217740ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_CUDA #include "astra/CudaDartMaskAlgorithm.h" #include "astra/cuda/2d/astra.h" #include "astra/cuda/2d/darthelper.h" #include "astra/cuda/2d/algo.h" #include "astra/AstraObjectManager.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaDartMaskAlgorithm::CCudaDartMaskAlgorithm() { m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Destructor CCudaDartMaskAlgorithm::~CCudaDartMaskAlgorithm() { } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaDartMaskAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("CudaDartMaskAlgorithm", this, _cfg); bool ok = true; int id = -1; ok &= CR.getRequiredID("SegmentationDataId", id); m_pSegmentation = dynamic_cast(CData2DManager::getSingleton().get(id)); ok &= CR.getRequiredID("MaskDataId", id); m_pMask = dynamic_cast(CData2DManager::getSingleton().get(id)); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, -1); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, -1); ok &= CR.getOptionUInt("Connectivity", m_iConn, 8); ok &= CR.getOptionUInt("Threshold", m_iThreshold, 1); ok &= CR.getOptionUInt("Radius", m_iRadius, 1); if (!ok) return false; _check(); if (!m_bIsInitialized) return false; return true; } //--------------------------------------------------------------------------------------- // Initialize - C++ //bool CCudaDartMaskAlgorithm::initialize(CFloat32VolumeData2D* _pSegmentation, int _iConn) //{ // return false; //} //---------------------------------------------------------------------------------------- // Iterate bool CCudaDartMaskAlgorithm::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); const CVolumeGeometry2D& volgeom = m_pSegmentation->getGeometry(); unsigned int width = volgeom.getGridColCount(); unsigned int height = volgeom.getGridRowCount(); astraCUDA::setGPUIndex(m_iGPUIndex); astraCUDA::dartMask(m_pMask->getData(), m_pSegmentation->getDataConst(), m_iConn, m_iRadius, m_iThreshold, width, height); return true; } //---------------------------------------------------------------------------------------- // Check bool CCudaDartMaskAlgorithm::_check() { // connectivity: 4 or 8 ASTRA_CONFIG_CHECK(m_iConn == 4 || m_iConn == 8, "CudaDartMask", "Connectivity must be 4 or 8"); // gpuindex >= 0 // success m_bIsInitialized = true; return true; } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaDartMaskAlgorithm3D.cpp000066400000000000000000000077641475635207100221760ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_CUDA #include "astra/CudaDartMaskAlgorithm3D.h" #include "astra/cuda/3d/darthelper3d.h" #include "astra/cuda/3d/dims3d.h" #include "astra/AstraObjectManager.h" #include "astra/VolumeGeometry3D.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaDartMaskAlgorithm3D::CCudaDartMaskAlgorithm3D() { m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Destructor CCudaDartMaskAlgorithm3D::~CCudaDartMaskAlgorithm3D() { } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaDartMaskAlgorithm3D::initialize(const Config& _cfg) { ConfigReader CR("CudaDartMaskAlgorithm3D", this, _cfg); bool ok = true; int id = -1; ok &= CR.getRequiredID("SegmentationDataId", id); m_pSegmentation = dynamic_cast(CData3DManager::getSingleton().get(id)); ok &= CR.getRequiredID("MaskDataId", id); m_pMask = dynamic_cast(CData3DManager::getSingleton().get(id)); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, -1); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, -1); ok &= CR.getOptionUInt("Connectivity", m_iConn, 26); ok &= CR.getOptionUInt("Threshold", m_iThreshold, 1); ok &= CR.getOptionUInt("Radius", m_iRadius, 1); if (!ok) return false; _check(); if (!m_bIsInitialized) return false; return true; } //--------------------------------------------------------------------------------------- // Initialize - C++ //bool CCudaDartMaskAlgorithm3D::initialize(CFloat32VolumeData2D* _pSegmentation, int _iConn) //{ // return false; //} //---------------------------------------------------------------------------------------- // Iterate bool CCudaDartMaskAlgorithm3D::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); const CVolumeGeometry3D& volgeom = m_pSegmentation->getGeometry(); astraCUDA3d::SDimensions3D dims; dims.iVolX = volgeom.getGridColCount(); dims.iVolY = volgeom.getGridRowCount(); dims.iVolZ = volgeom.getGridSliceCount(); astraCUDA3d::setGPUIndex(m_iGPUIndex); astraCUDA3d::dartMasking(m_pMask->getFloat32Memory(), m_pSegmentation->getFloat32Memory(), m_iConn, m_iRadius, m_iThreshold, dims); return true; } //---------------------------------------------------------------------------------------- // Check bool CCudaDartMaskAlgorithm3D::_check() { ASTRA_CONFIG_CHECK(m_pMask->isFloat32Memory(), "CudaDartMask3D", "Mask data object must be float32/memory"); ASTRA_CONFIG_CHECK(m_pSegmentation->isFloat32Memory(), "CudaDartMask3D", "Segmentation data object must be float32/memory"); // connectivity: 6 or 26 ASTRA_CONFIG_CHECK(m_iConn == 6 || m_iConn == 26, "CudaDartMask3D", "Connectivity must be 6 or 26"); // gpuindex >= 0 // success m_bIsInitialized = true; return true; } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaDartSmoothingAlgorithm.cpp000066400000000000000000000067211475635207100230530ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_CUDA #include "astra/CudaDartSmoothingAlgorithm.h" #include "astra/cuda/2d/astra.h" #include "astra/cuda/2d/darthelper.h" #include "astra/cuda/2d/algo.h" #include "astra/AstraObjectManager.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaDartSmoothingAlgorithm::CCudaDartSmoothingAlgorithm() { m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Destructor CCudaDartSmoothingAlgorithm::~CCudaDartSmoothingAlgorithm() { } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaDartSmoothingAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("CudaDartSmoothingAlgorithm", this, _cfg); bool ok = true; int id = -1; ok &= CR.getRequiredID("InDataId", id); m_pIn = dynamic_cast(CData2DManager::getSingleton().get(id)); ok &= CR.getRequiredID("OutDataId", id); m_pOut = dynamic_cast(CData2DManager::getSingleton().get(id)); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, -1); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, -1); ok &= CR.getOptionNumerical("Intensity", m_fB, 0.3f); ok &= CR.getOptionUInt("Radius", m_iRadius, 1); if (!ok) return false; _check(); if (!m_bIsInitialized) return false; return true; } //--------------------------------------------------------------------------------------- // Initialize - C++ //bool CCudaDartMaskAlgorithm::initialize(CFloat32VolumeData2D* _pSegmentation, int _iConn) //{ // return false; //} //---------------------------------------------------------------------------------------- // Iterate bool CCudaDartSmoothingAlgorithm::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); const CVolumeGeometry2D& volgeom = m_pIn->getGeometry(); unsigned int width = volgeom.getGridColCount(); unsigned int height = volgeom.getGridRowCount(); astraCUDA::setGPUIndex(m_iGPUIndex); astraCUDA::dartSmoothing(m_pOut->getData(), m_pIn->getDataConst(), m_fB, m_iRadius, width, height); return true; } //---------------------------------------------------------------------------------------- // Check bool CCudaDartSmoothingAlgorithm::_check() { // success m_bIsInitialized = true; return true; } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaDartSmoothingAlgorithm3D.cpp000066400000000000000000000075161475635207100232450ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_CUDA #include "astra/CudaDartSmoothingAlgorithm3D.h" #include "astra/cuda/3d/darthelper3d.h" #include "astra/cuda/3d/dims3d.h" #include "astra/AstraObjectManager.h" #include "astra/VolumeGeometry3D.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaDartSmoothingAlgorithm3D::CCudaDartSmoothingAlgorithm3D() { m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Destructor CCudaDartSmoothingAlgorithm3D::~CCudaDartSmoothingAlgorithm3D() { } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaDartSmoothingAlgorithm3D::initialize(const Config& _cfg) { ConfigReader CR("CudaDartSmoothingAlgorithm3D", this, _cfg); bool ok = true; int id = -1; ok &= CR.getRequiredID("InDataId", id); m_pIn = dynamic_cast(CData3DManager::getSingleton().get(id)); ok &= CR.getRequiredID("OutDataId", id); m_pOut = dynamic_cast(CData3DManager::getSingleton().get(id)); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, -1); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, -1); ok &= CR.getOptionNumerical("Intensity", m_fB, 0.3f); ok &= CR.getOptionUInt("Radius", m_iRadius, 1); if (!ok) return false; _check(); if (!m_bIsInitialized) return false; return true; } //--------------------------------------------------------------------------------------- // Initialize - C++ //bool CCudaDartSmoothingAlgorithm3D::initialize(CFloat32VolumeData2D* _pSegmentation, int _iConn) //{ // return false; //} //---------------------------------------------------------------------------------------- // Iterate bool CCudaDartSmoothingAlgorithm3D::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); const CVolumeGeometry3D& volgeom = m_pIn->getGeometry(); astraCUDA3d::SDimensions3D dims; dims.iVolX = volgeom.getGridColCount(); dims.iVolY = volgeom.getGridRowCount(); dims.iVolZ = volgeom.getGridSliceCount(); astraCUDA3d::setGPUIndex(m_iGPUIndex); astraCUDA3d::dartSmoothing(m_pOut->getFloat32Memory(), m_pIn->getFloat32Memory(), m_fB, m_iRadius, dims); return true; } //---------------------------------------------------------------------------------------- // Check bool CCudaDartSmoothingAlgorithm3D::_check() { ASTRA_CONFIG_CHECK(m_pIn->isFloat32Memory(), "CudaDartSmoothing3D", "Input data object must be float32/memory"); ASTRA_CONFIG_CHECK(m_pOut->isFloat32Memory(), "CudaDartSmoothing3D", "Output data object must be float32/memory"); // geometry of inData must match that of outData // success m_bIsInitialized = true; return true; } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaDataOperationAlgorithm.cpp000066400000000000000000000115671475635207100230270ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_CUDA #include "astra/CudaDataOperationAlgorithm.h" #include "astra/cuda/2d/astra.h" #include "astra/AstraObjectManager.h" #include "astra/Logging.h" #include using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaDataOperationAlgorithm::CCudaDataOperationAlgorithm() { m_pMask = NULL; m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Destructor CCudaDataOperationAlgorithm::~CCudaDataOperationAlgorithm() { } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaDataOperationAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("CCudaDataOperationAlgorithm", this, _cfg); // operation if (!CR.getRequiredString("Operation", m_sOperation)) return false; m_sOperation.erase(std::remove(m_sOperation.begin(), m_sOperation.end(), ' '), m_sOperation.end()); // data vector data; if (!CR.getRequiredIntArray("DataId", data)) return false; for (vector::iterator it = data.begin(); it != data.end(); ++it){ m_pData.push_back(dynamic_cast(CData2DManager::getSingleton().get(*it))); } bool ok = true; ok &= CR.getRequiredNumericalArray("Scalar", m_fScalar); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, -1); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, -1); int id = -1; if (CR.getOptionID("MaskId", id)) { m_pMask = dynamic_cast(CData2DManager::getSingleton().get(id)); } _check(); if (!m_bIsInitialized) return false; return true; } //--------------------------------------------------------------------------------------- // Initialize - C++ //bool CCudaDartMaskAlgorithm::initialize(CFloat32VolumeData2D* _pSegmentation, int _iConn) //{ // return false; //} //---------------------------------------------------------------------------------------- // Iterate bool CCudaDataOperationAlgorithm::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); astraCUDA::setGPUIndex(m_iGPUIndex); astraCUDA::SDimensions dims; // We slightly abuse dims here: width/height is not necessarily a volume dims.iVolWidth = m_pData[0]->getWidth(); dims.iVolHeight = m_pData[0]->getHeight(); if (m_sOperation == "$1*s1" || m_sOperation == "$1.*s1") // data * scalar { if (m_pMask == NULL) astraCUDA::processVolCopy(m_pData[0]->getData(), m_fScalar[0], dims); else astraCUDA::processVolCopy(m_pData[0]->getData(), m_pMask->getDataConst(), m_fScalar[0], dims); } else if (m_sOperation == "$1/s1" || m_sOperation == "$1./s1") // data / scalar { if (m_pMask == NULL) astraCUDA::processVolCopy(m_pData[0]->getData(), 1.0f/m_fScalar[0], dims); else astraCUDA::processVolCopy(m_pData[0]->getData(), m_pMask->getDataConst(), 1.0f/m_fScalar[0], dims); } else if (m_sOperation == "$1+s1") // data + scalar { astraCUDA::processVolCopy(m_pData[0]->getData(), m_fScalar[0], dims); } else if (m_sOperation == "$1-s1") // data - scalar { astraCUDA::processVolCopy(m_pData[0]->getData(), -m_fScalar[0], dims); } else if (m_sOperation == "$1.*$2") // data .* data { astraCUDA::processVolCopy(m_pData[0]->getData(), m_pData[1]->getDataConst(), dims); } else if (m_sOperation == "$1+$2") // data + data { astraCUDA::processVolCopy(m_pData[0]->getData(), m_pData[1]->getDataConst(), dims); } return true; } //---------------------------------------------------------------------------------------- // Check bool CCudaDataOperationAlgorithm::_check() { // s*: 1 data + 1 scalar // success m_bIsInitialized = true; return true; } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaEMAlgorithm.cpp000066400000000000000000000054341475635207100205720ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_CUDA #include "astra/CudaEMAlgorithm.h" #include "astra/cuda/2d/em.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaEMAlgorithm::CCudaEMAlgorithm() { m_bIsInitialized = false; CCudaReconstructionAlgorithm2D::_clear(); } //---------------------------------------------------------------------------------------- // Destructor CCudaEMAlgorithm::~CCudaEMAlgorithm() { // The actual work is done by ~CCudaReconstructionAlgorithm2D } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaEMAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("CudaEMAlgorithm", this, _cfg); if (CR.hasOption("SinogramMaskId")) { ASTRA_CONFIG_CHECK(false, "EM_CUDA", "Sinogram mask option is not supported."); } if (CR.hasOption("ReconstructionMaskId")) { ASTRA_CONFIG_CHECK(false, "EM_CUDA", "Reconstruction mask option is not supported."); } m_bIsInitialized = CCudaReconstructionAlgorithm2D::initialize(_cfg); if (!m_bIsInitialized) return false; m_pAlgo = new astraCUDA::EM(); m_bAlgoInit = false; return true; } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaEMAlgorithm::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { m_bIsInitialized = CCudaReconstructionAlgorithm2D::initialize(_pProjector, _pSinogram, _pReconstruction); if (!m_bIsInitialized) return false; m_pAlgo = new astraCUDA::EM(); m_bAlgoInit = false; return true; } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaFDKAlgorithm3D.cpp000066400000000000000000000144051475635207100210620ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/CudaFDKAlgorithm3D.h" #include "astra/AstraObjectManager.h" #include "astra/CudaProjector3D.h" #include "astra/ConeProjectionGeometry3D.h" #include "astra/ConeVecProjectionGeometry3D.h" #include "astra/CompositeGeometryManager.h" #include "astra/VolumeGeometry3D.h" #include "astra/Float32ProjectionData2D.h" #include "astra/Logging.h" #include "astra/Filters.h" using namespace std; using namespace astraCUDA3d; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaFDKAlgorithm3D::CCudaFDKAlgorithm3D() { m_bIsInitialized = false; m_iGPUIndex = -1; m_iVoxelSuperSampling = 1; } //---------------------------------------------------------------------------------------- // Constructor with initialization CCudaFDKAlgorithm3D::CCudaFDKAlgorithm3D(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pProjectionData, CFloat32VolumeData3D* _pReconstruction) { _clear(); initialize(_pProjector, _pProjectionData, _pReconstruction); } //---------------------------------------------------------------------------------------- // Destructor CCudaFDKAlgorithm3D::~CCudaFDKAlgorithm3D() { CReconstructionAlgorithm3D::_clear(); } //--------------------------------------------------------------------------------------- // Check bool CCudaFDKAlgorithm3D::_check() { // check base class ASTRA_CONFIG_CHECK(CReconstructionAlgorithm3D::_check(), "CUDA_FDK", "Error in ReconstructionAlgorithm3D initialization"); const CProjectionGeometry3D& projgeom = m_pSinogram->getGeometry(); ASTRA_CONFIG_CHECK(dynamic_cast(&projgeom) || dynamic_cast(&projgeom), "CUDA_FDK", "Error setting FDK geometry"); const CVolumeGeometry3D& volgeom = m_pReconstruction->getGeometry(); bool cube = true; if (abs(volgeom.getPixelLengthX() / volgeom.getPixelLengthY() - 1.0) > 0.00001) cube = false; if (abs(volgeom.getPixelLengthX() / volgeom.getPixelLengthZ() - 1.0) > 0.00001) cube = false; ASTRA_CONFIG_CHECK(cube, "CUDA_FDK", "Voxels must be cubes for FDK"); return true; } //--------------------------------------------------------------------------------------- void CCudaFDKAlgorithm3D::initializeFromProjector() { m_iVoxelSuperSampling = 1; m_iGPUIndex = -1; CCudaProjector3D* pCudaProjector = dynamic_cast(m_pProjector); if (!pCudaProjector) { if (m_pProjector) { ASTRA_WARN("non-CUDA Projector3D passed to FDK_CUDA"); } } else { m_iVoxelSuperSampling = pCudaProjector->getVoxelSuperSampling(); m_iGPUIndex = pCudaProjector->getGPUIndex(); } } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaFDKAlgorithm3D::initialize(const Config& _cfg) { ConfigReader CR("CudaFDKAlgorithm3D", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CReconstructionAlgorithm3D::initialize(_cfg)) { return false; } initializeFromProjector(); // Deprecated options bool ok = true; ok &= CR.getOptionInt("VoxelSuperSampling", m_iVoxelSuperSampling, m_iVoxelSuperSampling); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, m_iGPUIndex); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, m_iGPUIndex); if (!ok) return false; if ((CR.has("FilterSinogramId") || CR.hasOption("FilterSinogramId")) && !(CR.has("FilterType") || CR.hasOption("FilterType"))) { ASTRA_WARN("Setting FilterSinogramId without FilterType is no longer supported. Set FilterType to 'sinogram' for the old behaviour."); } m_filterConfig = getFilterConfigForAlgorithm(_cfg, this); ok &= CR.getOptionBool("ShortScan", m_bShortScan, false); if (!ok) return false; // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaFDKAlgorithm3D::initialize(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pSinogram, CFloat32VolumeData3D* _pReconstruction) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // required classes m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Iterate bool CCudaFDKAlgorithm3D::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); CFloat32ProjectionData3D* pSinoMem = dynamic_cast(m_pSinogram); ASTRA_ASSERT(pSinoMem); CFloat32VolumeData3D* pReconMem = dynamic_cast(m_pReconstruction); ASTRA_ASSERT(pReconMem); #if 0 bool ok = true; ok = astraCudaFDK(pReconMem->getData(), pSinoMem->getDataConst(), &volgeom, conegeom, m_bShortScan, m_iGPUIndex, m_iVoxelSuperSampling, filter); ASTRA_ASSERT(ok); #endif CCompositeGeometryManager cgm; return cgm.doFDK(m_pProjector, pReconMem, pSinoMem, m_bShortScan, m_filterConfig); } //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/CudaFilteredBackProjectionAlgorithm.cpp000066400000000000000000000131231475635207100246370ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include #include #include "astra/AstraObjectManager.h" #include "astra/CudaProjector2D.h" #include "astra/Filters.h" #include "astra/cuda/2d/astra.h" #include "astra/cuda/2d/fbp.h" #include "astra/Logging.h" #include using namespace std; using namespace astra; CCudaFilteredBackProjectionAlgorithm::CCudaFilteredBackProjectionAlgorithm() { m_bIsInitialized = false; CCudaReconstructionAlgorithm2D::_clear(); } CCudaFilteredBackProjectionAlgorithm::~CCudaFilteredBackProjectionAlgorithm() { } bool CCudaFilteredBackProjectionAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("CudaFilteredBackProjectionAlgorithm", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } m_bIsInitialized = CCudaReconstructionAlgorithm2D::initialize(_cfg); if (!m_bIsInitialized) return false; m_filterConfig = getFilterConfigForAlgorithm(_cfg, this); // Fan beam short scan mode if (m_pSinogram && dynamic_cast(&m_pSinogram->getGeometry())) { bool ok = true; ok &= CR.getOptionBool("ShortScan", m_bShortScan, false); if (!ok) return false; } initializeFromProjector(); m_pAlgo = new astraCUDA::FBP(); m_bAlgoInit = false; return check(); } bool CCudaFilteredBackProjectionAlgorithm::initialize(CFloat32ProjectionData2D * _pSinogram, CFloat32VolumeData2D * _pReconstruction, E_FBPFILTER _eFilter, const float * _pfFilter /* = NULL */, int _iFilterWidth /* = 0 */, int _iGPUIndex /* = 0 */, float _fFilterParameter /* = -1.0f */) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // required classes m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; m_iGPUIndex = _iGPUIndex; m_filterConfig.m_eType = _eFilter; m_filterConfig.m_iCustomFilterWidth = _iFilterWidth; m_bShortScan = false; // success m_bIsInitialized = true; m_pAlgo = new astraCUDA::FBP(); m_bAlgoInit = false; if(_pfFilter != NULL) { int iFilterElementCount = 0; if((m_filterConfig.m_eType != FILTER_SINOGRAM) && (m_filterConfig.m_eType != FILTER_RSINOGRAM)) { iFilterElementCount = _iFilterWidth; } else { iFilterElementCount = m_pSinogram->getAngleCount(); } m_filterConfig.m_pfCustomFilter.resize(iFilterElementCount); memcpy(&m_filterConfig.m_pfCustomFilter[0], _pfFilter, iFilterElementCount * sizeof(float)); } else { m_filterConfig.m_pfCustomFilter.clear(); } m_filterConfig.m_fParameter = _fFilterParameter; return check(); } void CCudaFilteredBackProjectionAlgorithm::initCUDAAlgorithm() { CCudaReconstructionAlgorithm2D::initCUDAAlgorithm(); astraCUDA::FBP* pFBP = dynamic_cast(m_pAlgo); bool ok = pFBP->setFilter(m_filterConfig); if (!ok) { ASTRA_ERROR("CCudaFilteredBackProjectionAlgorithm: Failed to set filter"); ASTRA_ASSERT(ok); } ok &= pFBP->setShortScan(m_bShortScan); if (!ok) { ASTRA_ERROR("CCudaFilteredBackProjectionAlgorithm: Failed to set short-scan mode"); } const CVolumeGeometry2D& volGeom = m_pReconstruction->getGeometry(); float fPixelArea = volGeom.getPixelArea(); ok &= pFBP->setReconstructionScale(1.0f/fPixelArea); if (!ok) { ASTRA_ERROR("CCudaFilteredBackProjectionAlgorithm: Failed to set reconstruction scale"); } } bool CCudaFilteredBackProjectionAlgorithm::check() { // check pointers ASTRA_CONFIG_CHECK(m_pSinogram, "FBP_CUDA", "Invalid Projection Data Object."); ASTRA_CONFIG_CHECK(m_pReconstruction, "FBP_CUDA", "Invalid Reconstruction Data Object."); ASTRA_CONFIG_CHECK(m_filterConfig.m_eType != FILTER_ERROR, "FBP_CUDA", "Invalid filter name."); if((m_filterConfig.m_eType == FILTER_PROJECTION) || (m_filterConfig.m_eType == FILTER_SINOGRAM) || (m_filterConfig.m_eType == FILTER_RPROJECTION) || (m_filterConfig.m_eType == FILTER_RSINOGRAM)) { ASTRA_CONFIG_CHECK(!m_filterConfig.m_pfCustomFilter.empty(), "FBP_CUDA", "Invalid filter pointer."); } // check initializations ASTRA_CONFIG_CHECK(m_pSinogram->isInitialized(), "FBP_CUDA", "Projection Data Object Not Initialized."); ASTRA_CONFIG_CHECK(m_pReconstruction->isInitialized(), "FBP_CUDA", "Reconstruction Data Object Not Initialized."); // check gpu index ASTRA_CONFIG_CHECK(m_iGPUIndex >= -1, "FBP_CUDA", "GPUIndex must be a non-negative integer or -1."); // check pixel supersampling ASTRA_CONFIG_CHECK(m_iPixelSuperSampling >= 0, "FBP_CUDA", "PixelSuperSampling must be a non-negative integer."); ASTRA_CONFIG_CHECK(checkCustomFilterSize(m_filterConfig, m_pSinogram->getGeometry()), "FBP_CUDA", "Filter size mismatch"); // success m_bIsInitialized = true; return true; } astra-toolbox-2.3.0/src/CudaForwardProjectionAlgorithm.cpp000066400000000000000000000156451475635207100237370ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/CudaForwardProjectionAlgorithm.h" #ifdef ASTRA_CUDA #include "astra/cuda/2d/astra.h" #include "astra/AstraObjectManager.h" #include "astra/ParallelProjectionGeometry2D.h" #include "astra/FanFlatProjectionGeometry2D.h" #include "astra/FanFlatVecProjectionGeometry2D.h" #include "astra/Float32ProjectionData2D.h" #include "astra/Float32VolumeData2D.h" #include "astra/CudaProjector2D.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaForwardProjectionAlgorithm::CCudaForwardProjectionAlgorithm() { m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Destructor CCudaForwardProjectionAlgorithm::~CCudaForwardProjectionAlgorithm() { } //--------------------------------------------------------------------------------------- void CCudaForwardProjectionAlgorithm::initializeFromProjector() { m_iDetectorSuperSampling = 1; m_iGPUIndex = -1; // Projector CCudaProjector2D* pCudaProjector = dynamic_cast(m_pProjector); if (!pCudaProjector) { if (m_pProjector) { ASTRA_WARN("non-CUDA Projector2D passed to FP_CUDA"); } } else { m_iDetectorSuperSampling = pCudaProjector->getDetectorSuperSampling(); m_iGPUIndex = pCudaProjector->getGPUIndex(); } } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaForwardProjectionAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("CudaForwardProjectionAlgorithm", this, _cfg); bool ok = true; int id = -1; m_pProjector = nullptr; if (CR.has("ProjectorId")) { ok &= CR.getRequiredID("ProjectorId", id); m_pProjector = CProjector2DManager::getSingleton().get(id); if (!m_pProjector) { ASTRA_ERROR("ProjectorId is not a valid id"); return false; } } ok &= CR.getRequiredID("ProjectionDataId", id); m_pSinogram = dynamic_cast(CData2DManager::getSingleton().get(id)); ok &= CR.getRequiredID("VolumeDataId", id); m_pVolume = dynamic_cast(CData2DManager::getSingleton().get(id)); if (!ok) return false; initializeFromProjector(); // Deprecated options ok &= CR.getOptionInt("DetectorSuperSampling", m_iDetectorSuperSampling, m_iDetectorSuperSampling); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, -1); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, -1); if (!ok) return false; // return success return check(); } //---------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaForwardProjectionAlgorithm::initialize(CProjector2D* _pProjector, CFloat32VolumeData2D* _pVolume, CFloat32ProjectionData2D* _pSinogram) { // store classes m_pProjector = _pProjector; m_pVolume = _pVolume; m_pSinogram = _pSinogram; initializeFromProjector(); // return success return check(); } //---------------------------------------------------------------------------------------- // Check bool CCudaForwardProjectionAlgorithm::check() { // check pointers ASTRA_CONFIG_CHECK(m_pSinogram, "FP_CUDA", "No valid projection data object found."); ASTRA_CONFIG_CHECK(m_pSinogram->isInitialized(), "FP_CUDA", "Projection data not initialized."); ASTRA_CONFIG_CHECK(m_pVolume, "FP_CUDA", "No valid volume data object found."); ASTRA_CONFIG_CHECK(m_pVolume->isInitialized(), "FP_CUDA", "Volume data not initialized."); // check restrictions //int iImageSideBlocks = m_pReconstructionGeometry->getGridColCount() / G_BLOCKIMAGESIZE; //ASTRA_CONFIG_CHECK((iImageSideBlocks * G_BLOCKIMAGESIZE) == m_pVolume->getWidth(), "FP_CUDA", "Volume Width must be a multiple of G_BLOCKIMAGESIZE"); //ASTRA_CONFIG_CHECK((iImageSideBlocks * G_BLOCKIMAGESIZE) == m_pVolume->getHeight(), "FP_CUDA", "Volume Height must be a multiple of G_BLOCKIMAGESIZE"); //ASTRA_CONFIG_CHECK(m_pProjectionGeometry->getDetectorCount() == (m_pVolume->getWidth() * 3 / 2), "SIRT_CUDA", "Number of detectors must be 1.5 times the width of the image"); ASTRA_CONFIG_CHECK(m_iGPUIndex >= -1, "FP_CUDA", "GPUIndex must be a non-negative integer."); // success m_bIsInitialized = true; return true; } void CCudaForwardProjectionAlgorithm::setGPUIndex(int _iGPUIndex) { m_iGPUIndex = _iGPUIndex; } //---------------------------------------------------------------------------------------- // Run bool CCudaForwardProjectionAlgorithm::run(int) { // check initialized assert(m_bIsInitialized); bool ok; const CVolumeGeometry2D &pVolGeom = m_pVolume->getGeometry(); const CProjectionGeometry2D &pProjGeom = m_pSinogram->getGeometry(); astraCUDA::SDimensions dims; ok = convertAstraGeometry_dims(&pVolGeom, &pProjGeom, dims); if (!ok) return false; astraCUDA::SParProjection* pParProjs = 0; astraCUDA::SFanProjection* pFanProjs = 0; float fOutputScale = 1.0f; ok = convertAstraGeometry(&pVolGeom, &pProjGeom, pParProjs, pFanProjs, fOutputScale); if (!ok) return false; if (pParProjs) { assert(!pFanProjs); ok = astraCudaFP(m_pVolume->getDataConst(), m_pSinogram->getData(), pVolGeom.getGridColCount(), pVolGeom.getGridRowCount(), pProjGeom.getProjectionAngleCount(), pProjGeom.getDetectorCount(), pParProjs, m_iDetectorSuperSampling, 1.0f * fOutputScale, m_iGPUIndex); delete[] pParProjs; } else { assert(pFanProjs); ok = astraCudaFanFP(m_pVolume->getDataConst(), m_pSinogram->getData(), pVolGeom.getGridColCount(), pVolGeom.getGridRowCount(), pProjGeom.getProjectionAngleCount(), pProjGeom.getDetectorCount(), pFanProjs, m_iDetectorSuperSampling, fOutputScale, m_iGPUIndex); delete[] pFanProjs; } ASTRA_ASSERT(ok); return ok; } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaForwardProjectionAlgorithm3D.cpp000066400000000000000000000171331475635207100241200ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/CudaForwardProjectionAlgorithm3D.h" #ifdef ASTRA_CUDA #include "astra/AstraObjectManager.h" #include "astra/CudaProjector3D.h" #include "astra/ConeProjectionGeometry3D.h" #include "astra/ParallelProjectionGeometry3D.h" #include "astra/ParallelVecProjectionGeometry3D.h" #include "astra/ConeVecProjectionGeometry3D.h" #include "astra/CompositeGeometryManager.h" #include "astra/Logging.h" #include "astra/cuda/3d/astra3d.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaForwardProjectionAlgorithm3D::CCudaForwardProjectionAlgorithm3D() { m_bIsInitialized = false; m_iGPUIndex = -1; m_iDetectorSuperSampling = 1; m_pProjector = 0; m_pProjections = 0; m_pVolume = 0; } //---------------------------------------------------------------------------------------- // Destructor CCudaForwardProjectionAlgorithm3D::~CCudaForwardProjectionAlgorithm3D() { } //--------------------------------------------------------------------------------------- void CCudaForwardProjectionAlgorithm3D::initializeFromProjector() { m_iDetectorSuperSampling = 1; m_iGPUIndex = -1; CCudaProjector3D* pCudaProjector = dynamic_cast(m_pProjector); if (!pCudaProjector) { if (m_pProjector) { ASTRA_WARN("non-CUDA Projector3D passed to FP3D_CUDA"); } } else { m_iDetectorSuperSampling = pCudaProjector->getDetectorSuperSampling(); m_iGPUIndex = pCudaProjector->getGPUIndex(); } } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaForwardProjectionAlgorithm3D::initialize(const Config& _cfg) { ConfigReader CR("CudaForwardProjectionAlgorithm3D", this, _cfg); // projector m_pProjector = 0; int id = -1; if (CR.has("ProjectorId")) { CR.getID("ProjectorId", id); m_pProjector = CProjector3DManager::getSingleton().get(id); if (!m_pProjector) { ASTRA_WARN("Optional parameter ProjectorId is not a valid id"); } } bool ok = true; // sinogram data ok &= CR.getRequiredID("ProjectionDataId", id); m_pProjections = dynamic_cast(CData3DManager::getSingleton().get(id)); // reconstruction data ok &= CR.getRequiredID("VolumeDataId", id); m_pVolume = dynamic_cast(CData3DManager::getSingleton().get(id)); if (!ok) return false; initializeFromProjector(); // Deprecated options ok &= CR.getOptionInt("DetectorSuperSampling", m_iDetectorSuperSampling, m_iDetectorSuperSampling); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, -1); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, -1); if (!ok) return false; // success m_bIsInitialized = check(); if (!m_bIsInitialized) return false; return true; } bool CCudaForwardProjectionAlgorithm3D::initialize(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pProjections, CFloat32VolumeData3D* _pVolume, int _iGPUindex, int _iDetectorSuperSampling) { m_pProjector = _pProjector; // required classes m_pProjections = _pProjections; m_pVolume = _pVolume; CCudaProjector3D* pCudaProjector = dynamic_cast(m_pProjector); if (!pCudaProjector) { // TODO: Report m_iDetectorSuperSampling = _iDetectorSuperSampling; m_iGPUIndex = _iGPUindex; } else { m_iDetectorSuperSampling = pCudaProjector->getDetectorSuperSampling(); m_iGPUIndex = pCudaProjector->getGPUIndex(); } // success m_bIsInitialized = check(); if (!m_bIsInitialized) return false; return true; } //---------------------------------------------------------------------------------------- // Check bool CCudaForwardProjectionAlgorithm3D::check() { // check pointers //ASTRA_CONFIG_CHECK(m_pProjector, "Reconstruction2D", "Invalid Projector Object."); ASTRA_CONFIG_CHECK(m_pProjections, "FP3D_CUDA", "Invalid Projection Data Object."); ASTRA_CONFIG_CHECK(m_pVolume, "FP3D_CUDA", "Invalid Volume Data Object."); // check initializations //ASTRA_CONFIG_CHECK(m_pProjector->isInitialized(), "Reconstruction2D", "Projector Object Not Initialized."); ASTRA_CONFIG_CHECK(m_pProjections->isInitialized(), "FP3D_CUDA", "Projection Data Object Not Initialized."); ASTRA_CONFIG_CHECK(m_pVolume->isInitialized(), "FP3D_CUDA", "Volume Data Object Not Initialized."); ASTRA_CONFIG_CHECK(m_iDetectorSuperSampling >= 1, "FP3D_CUDA", "DetectorSuperSampling must be a positive integer."); ASTRA_CONFIG_CHECK(m_iGPUIndex >= -1, "FP3D_CUDA", "GPUIndex must be a non-negative integer."); // check compatibility between projector and data classes // ASTRA_CONFIG_CHECK(m_pSinogram->getGeometry()->isEqual(m_pProjector->getProjectionGeometry()), "SIRT_CUDA", "Projection Data not compatible with the specified Projector."); // ASTRA_CONFIG_CHECK(m_pReconstruction->getGeometry()->isEqual(m_pProjector->getVolumeGeometry()), "SIRT_CUDA", "Reconstruction Data not compatible with the specified Projector."); // todo: turn some of these back on // ASTRA_CONFIG_CHECK(m_pProjectionGeometry, "SIRT_CUDA", "ProjectionGeometry not specified."); // ASTRA_CONFIG_CHECK(m_pProjectionGeometry->isInitialized(), "SIRT_CUDA", "ProjectionGeometry not initialized."); // ASTRA_CONFIG_CHECK(m_pReconstructionGeometry, "SIRT_CUDA", "ReconstructionGeometry not specified."); // ASTRA_CONFIG_CHECK(m_pReconstructionGeometry->isInitialized(), "SIRT_CUDA", "ReconstructionGeometry not initialized."); // check dimensions //ASTRA_CONFIG_CHECK(m_pSinogram->getAngleCount() == m_pProjectionGeometry->getProjectionAngleCount(), "SIRT_CUDA", "Sinogram data object size mismatch."); //ASTRA_CONFIG_CHECK(m_pSinogram->getDetectorCount() == m_pProjectionGeometry->getDetectorCount(), "SIRT_CUDA", "Sinogram data object size mismatch."); //ASTRA_CONFIG_CHECK(m_pReconstruction->getWidth() == m_pReconstructionGeometry->getGridColCount(), "SIRT_CUDA", "Reconstruction data object size mismatch."); //ASTRA_CONFIG_CHECK(m_pReconstruction->getHeight() == m_pReconstructionGeometry->getGridRowCount(), "SIRT_CUDA", "Reconstruction data object size mismatch."); // check restrictions // TODO: check restrictions built into cuda code // success m_bIsInitialized = true; return true; } void CCudaForwardProjectionAlgorithm3D::setGPUIndex(int _iGPUIndex) { m_iGPUIndex = _iGPUIndex; } //---------------------------------------------------------------------------------------- // Run bool CCudaForwardProjectionAlgorithm3D::run(int) { // check initialized assert(m_bIsInitialized); CCompositeGeometryManager cgm; return cgm.doFP(m_pProjector, m_pVolume, m_pProjections); } } #endif astra-toolbox-2.3.0/src/CudaProjector2D.cpp000066400000000000000000000074571475635207100205660ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/CudaProjector2D.h" #include "astra/Logging.h" namespace astra { //---------------------------------------------------------------------------------------- // Default constructor CCudaProjector2D::CCudaProjector2D() { _clear(); } //---------------------------------------------------------------------------------------- // Destructor CCudaProjector2D::~CCudaProjector2D() { if (m_bIsInitialized) clear(); } //---------------------------------------------------------------------------------------- // Clear for constructors void CCudaProjector2D::_clear() { m_pProjectionGeometry = NULL; m_pVolumeGeometry = NULL; m_bIsInitialized = false; m_projectionKernel = ker2d_default; m_iVoxelSuperSampling = 1; m_iDetectorSuperSampling = 1; m_iGPUIndex = -1; } //---------------------------------------------------------------------------------------- // Clear void CCudaProjector2D::clear() { ASTRA_DELETE(m_pProjectionGeometry); ASTRA_DELETE(m_pVolumeGeometry); m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Check bool CCudaProjector2D::_check() { // projection geometry ASTRA_CONFIG_CHECK(m_pProjectionGeometry, "CudaProjector2D", "ProjectionGeometry2D not initialized."); ASTRA_CONFIG_CHECK(m_pProjectionGeometry->isInitialized(), "CudaProjector2D", "ProjectionGeometry2D not initialized."); // volume geometry ASTRA_CONFIG_CHECK(m_pVolumeGeometry, "CudaProjector2D", "VolumeGeometry2D not initialized."); ASTRA_CONFIG_CHECK(m_pVolumeGeometry->isInitialized(), "CudaProjector2D", "VolumeGeometry2D not initialized."); return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CCudaProjector2D::initialize(const Config& _cfg) { ConfigReader CR("CudaProjector2D", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CProjector2D::initialize(_cfg)) { return false; } // TODO: Check the projection geometry is a supported type std::string sProjKernel; CR.getString("ProjectionKernel", sProjKernel, "default"); if (sProjKernel == "default") { m_projectionKernel = ker2d_default; } else { ASTRA_ERROR("Unknown ProjectionKernel \"%s\"", sProjKernel.c_str()); return false; } bool ok = true; ok &= CR.getOptionInt("VoxelSuperSampling", m_iVoxelSuperSampling, 1); ok &= CR.getOptionInt("DetectorSuperSampling", m_iDetectorSuperSampling, 1); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, -1); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, -1); if (!ok) return false; m_bIsInitialized = _check(); return m_bIsInitialized; } std::string CCudaProjector2D::description() const { return CCudaProjector2D::type; } } // end namespace astra-toolbox-2.3.0/src/CudaProjector3D.cpp000066400000000000000000000101531475635207100205520ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/CudaProjector3D.h" #include "astra/VolumeGeometry3D.h" #include "astra/ProjectionGeometry3D.h" #include "astra/ConeProjectionGeometry3D.h" #include "astra/ConeVecProjectionGeometry3D.h" #include "astra/Logging.h" namespace astra { //---------------------------------------------------------------------------------------- // Default constructor CCudaProjector3D::CCudaProjector3D() { _clear(); } //---------------------------------------------------------------------------------------- // Destructor CCudaProjector3D::~CCudaProjector3D() { if (m_bIsInitialized) clear(); } //---------------------------------------------------------------------------------------- // Clear for constructors void CCudaProjector3D::_clear() { m_pProjectionGeometry.reset(); m_pVolumeGeometry.reset(); m_bIsInitialized = false; m_projectionKernel = ker3d_default; m_iVoxelSuperSampling = 1; m_iDetectorSuperSampling = 1; m_iGPUIndex = -1; } //---------------------------------------------------------------------------------------- // Clear void CCudaProjector3D::clear() { m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Check bool CCudaProjector3D::_check() { // projection geometry ASTRA_CONFIG_CHECK(m_pProjectionGeometry, "CudaProjector3D", "ProjectionGeometry3D not initialized."); ASTRA_CONFIG_CHECK(m_pProjectionGeometry->isInitialized(), "CudaProjector3D", "ProjectionGeometry3D not initialized."); // volume geometry ASTRA_CONFIG_CHECK(m_pVolumeGeometry, "CudaProjector3D", "VolumeGeometry3D not initialized."); ASTRA_CONFIG_CHECK(m_pVolumeGeometry->isInitialized(), "CudaProjector3D", "VolumeGeometry3D not initialized."); return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CCudaProjector3D::initialize(const Config& _cfg) { ConfigReader CR("CudaProjector3D", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CProjector3D::initialize(_cfg)) { return false; } std::string sProjKernel; CR.getString("ProjectionKernel", sProjKernel, "default"); if (sProjKernel == "default") { m_projectionKernel = ker3d_default; } else if (sProjKernel == "sum_square_weights") { m_projectionKernel = ker3d_sum_square_weights; } else if (sProjKernel == "2d_weighting") { m_projectionKernel = ker3d_2d_weighting; } else { ASTRA_ERROR("Unknown ProjectionKernel"); return false; } bool ok = true; ok &= CR.getOptionInt("VoxelSuperSampling", m_iVoxelSuperSampling, 1); ok &= CR.getOptionInt("DetectorSuperSampling", m_iDetectorSuperSampling, 1); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, -1); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, -1); if (!ok) return false; m_bIsInitialized = _check(); return m_bIsInitialized; } /* bool CProjector3D::initialize(astra::CProjectionGeometry3D *, astra::CVolumeGeometry3D *) { ASTRA_ASSERT(false); return false; } */ std::string CCudaProjector3D::description() const { return ""; } } // end namespace astra-toolbox-2.3.0/src/CudaReconstructionAlgorithm2D.cpp000066400000000000000000000167441475635207100235060ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_CUDA #include "astra/CudaReconstructionAlgorithm2D.h" #include "astra/AstraObjectManager.h" #include "astra/FanFlatProjectionGeometry2D.h" #include "astra/FanFlatVecProjectionGeometry2D.h" #include "astra/CudaProjector2D.h" #include "astra/Logging.h" #include "astra/cuda/2d/algo.h" #include using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaReconstructionAlgorithm2D::CCudaReconstructionAlgorithm2D() { _clear(); } //---------------------------------------------------------------------------------------- // Destructor CCudaReconstructionAlgorithm2D::~CCudaReconstructionAlgorithm2D() { delete m_pAlgo; m_pAlgo = 0; m_bAlgoInit = false; } void CCudaReconstructionAlgorithm2D::clear() { delete m_pAlgo; _clear(); } void CCudaReconstructionAlgorithm2D::_clear() { m_bIsInitialized = false; m_pAlgo = 0; m_bAlgoInit = false; CReconstructionAlgorithm2D::_clear(); m_iGPUIndex = -1; m_iDetectorSuperSampling = 1; m_iPixelSuperSampling = 1; } //--------------------------------------------------------------------------------------- void CCudaReconstructionAlgorithm2D::initializeFromProjector() { m_iPixelSuperSampling = 1; m_iDetectorSuperSampling = 1; m_iGPUIndex = -1; // Projector CCudaProjector2D* pCudaProjector = dynamic_cast(m_pProjector); if (!pCudaProjector) { if (m_pProjector) { ASTRA_WARN("non-CUDA Projector2D passed"); } } else { m_iDetectorSuperSampling = pCudaProjector->getDetectorSuperSampling(); m_iPixelSuperSampling = pCudaProjector->getVoxelSuperSampling(); m_iGPUIndex = pCudaProjector->getGPUIndex(); } } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaReconstructionAlgorithm2D::initialize(const Config& _cfg) { ConfigReader CR("CudaReconstructionAlgorithm2D", this, _cfg); m_bIsInitialized = CReconstructionAlgorithm2D::initialize(_cfg); if (!m_bIsInitialized) return false; initializeFromProjector(); bool ok = true; // Deprecated options ok &= CR.getOptionInt("PixelSuperSampling", m_iPixelSuperSampling, m_iPixelSuperSampling); ok &= CR.getOptionInt("DetectorSuperSampling", m_iDetectorSuperSampling, m_iDetectorSuperSampling); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, -1); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, -1); if (!ok) return false; return _check(); } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaReconstructionAlgorithm2D::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } m_pProjector = _pProjector; // required classes m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; initializeFromProjector(); return _check(); } //---------------------------------------------------------------------------------------- // Check bool CCudaReconstructionAlgorithm2D::_check() { if (!CReconstructionAlgorithm2D::_check()) return false; ASTRA_CONFIG_CHECK(m_iDetectorSuperSampling >= 1, "CudaReconstructionAlgorithm2D", "DetectorSuperSampling must be a positive integer."); ASTRA_CONFIG_CHECK(m_iPixelSuperSampling >= 1, "CudaReconstructionAlgorithm2D", "PixelSuperSampling must be a positive integer."); ASTRA_CONFIG_CHECK(m_iGPUIndex >= -1, "CudaReconstructionAlgorithm2D", "GPUIndex must be a non-negative integer or -1."); // check restrictions // TODO: check restrictions built into cuda code // success m_bIsInitialized = true; return true; } void CCudaReconstructionAlgorithm2D::setGPUIndex(int _iGPUIndex) { m_iGPUIndex = _iGPUIndex; } bool CCudaReconstructionAlgorithm2D::setupGeometry() { ASTRA_ASSERT(m_bIsInitialized); ASTRA_ASSERT(!m_bAlgoInit); bool ok; // TODO: Probably not the best place for this... ok = m_pAlgo->setGPUIndex(m_iGPUIndex); if (!ok) return false; const CVolumeGeometry2D& volgeom = m_pReconstruction->getGeometry(); const CProjectionGeometry2D& projgeom = m_pSinogram->getGeometry(); ok = m_pAlgo->setGeometry(&volgeom, &projgeom); if (!ok) return false; ok = m_pAlgo->setSuperSampling(m_iDetectorSuperSampling, m_iPixelSuperSampling); if (!ok) return false; if (m_bUseReconstructionMask) ok &= m_pAlgo->enableVolumeMask(); if (!ok) return false; if (m_bUseSinogramMask) ok &= m_pAlgo->enableSinogramMask(); if (!ok) return false; ok &= m_pAlgo->init(); if (!ok) return false; return true; } //---------------------------------------------------------------------------------------- void CCudaReconstructionAlgorithm2D::initCUDAAlgorithm() { bool ok; ok = setupGeometry(); ASTRA_ASSERT(ok); ok = m_pAlgo->allocateBuffers(); ASTRA_ASSERT(ok); } //---------------------------------------------------------------------------------------- // Iterate bool CCudaReconstructionAlgorithm2D::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); bool ok = true; const CVolumeGeometry2D& volgeom = m_pReconstruction->getGeometry(); if (!m_bAlgoInit) { initCUDAAlgorithm(); m_bAlgoInit = true; } ok = m_pAlgo->copyDataToGPU(m_pSinogram->getDataConst(), m_pSinogram->getGeometry().getDetectorCount(), m_pReconstruction->getDataConst(), volgeom.getGridColCount(), m_bUseReconstructionMask ? m_pReconstructionMask->getDataConst() : 0, volgeom.getGridColCount(), m_bUseSinogramMask ? m_pSinogramMask->getDataConst() : 0, m_pSinogram->getGeometry().getDetectorCount()); ASTRA_ASSERT(ok); if (m_bUseMinConstraint) { bool ret = m_pAlgo->setMinConstraint(m_fMinValue); if (!ret) { ASTRA_WARN("This algorithm ignores MinConstraint"); } } if (m_bUseMaxConstraint) { bool ret= m_pAlgo->setMaxConstraint(m_fMaxValue); if (!ret) { ASTRA_WARN("This algorithm ignores MaxConstraint"); } } ok &= m_pAlgo->iterate(_iNrIterations); ASTRA_ASSERT(ok); ok &= m_pAlgo->getReconstruction(m_pReconstruction->getData(), volgeom.getGridColCount()); ASTRA_ASSERT(ok); return ok; } bool CCudaReconstructionAlgorithm2D::getResidualNorm(float32& _fNorm) { if (!m_bIsInitialized || !m_pAlgo) return false; _fNorm = m_pAlgo->computeDiffNorm(); return true; } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaRoiSelectAlgorithm.cpp000066400000000000000000000064771475635207100221720ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_CUDA #include "astra/CudaRoiSelectAlgorithm.h" #include "astra/cuda/2d/astra.h" #include "astra/cuda/2d/darthelper.h" #include "astra/cuda/2d/algo.h" #include "astra/AstraObjectManager.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaRoiSelectAlgorithm::CCudaRoiSelectAlgorithm() { m_fRadius = 0.0f; m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Destructor CCudaRoiSelectAlgorithm::~CCudaRoiSelectAlgorithm() { } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaRoiSelectAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("CudaDartRoiSelectAlgorithm", this, _cfg); bool ok = true; int id = -1; ok &= CR.getRequiredID("DataId", id); m_pData = dynamic_cast(CData2DManager::getSingleton().get(id)); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, -1); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, -1); ok &= CR.getOptionNumerical("Radius", m_fRadius, 0.0f); if (!ok) return false; _check(); if (!m_bIsInitialized) return false; return true; } //--------------------------------------------------------------------------------------- // Initialize - C++ //bool CCudaDartMaskAlgorithm::initialize(CFloat32VolumeData2D* _pSegmentation, int _iConn) //{ // return false; //} //---------------------------------------------------------------------------------------- // Iterate bool CCudaRoiSelectAlgorithm::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); const CVolumeGeometry2D& volgeom = m_pData->getGeometry(); unsigned int width = volgeom.getGridColCount(); unsigned int height = volgeom.getGridRowCount(); if (m_fRadius == 0){ m_fRadius = (width < height) ? width : height; } astraCUDA::setGPUIndex(m_iGPUIndex); astraCUDA::roiSelect(m_pData->getData(), m_fRadius, width, height); return true; } //---------------------------------------------------------------------------------------- // Check bool CCudaRoiSelectAlgorithm::_check() { // success m_bIsInitialized = true; return true; } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaSartAlgorithm.cpp000066400000000000000000000103361475635207100211770ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_CUDA #include "astra/CudaSartAlgorithm.h" #include "astra/cuda/2d/sart.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaSartAlgorithm::CCudaSartAlgorithm() { m_bIsInitialized = false; CCudaReconstructionAlgorithm2D::_clear(); } //---------------------------------------------------------------------------------------- // Destructor CCudaSartAlgorithm::~CCudaSartAlgorithm() { // The actual work is done by ~CCudaReconstructionAlgorithm2D } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaSartAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("CudaSartAlgorithm", this, _cfg); m_bIsInitialized = CCudaReconstructionAlgorithm2D::initialize(_cfg); if (!m_bIsInitialized) return false; astraCUDA::SART *sart = new astraCUDA::SART(); m_pAlgo = sart; m_bAlgoInit = false; if (CR.hasOption("SinogramMaskId")) { ASTRA_CONFIG_CHECK(false, "SART_CUDA", "Sinogram mask option is not supported."); } // projection order int projectionCount = m_pSinogram->getGeometry().getProjectionAngleCount(); std::vector projectionOrder; std::string projOrder; if (!CR.getOptionString("ProjectionOrder", projOrder, "random")) return false; if (projOrder == "sequential") { projectionOrder.resize(projectionCount); for (int i = 0; i < projectionCount; i++) { projectionOrder[i] = i; } sart->setProjectionOrder(&projectionOrder[0], projectionCount); } else if (projOrder == "random") { projectionOrder.resize(projectionCount); for (int i = 0; i < projectionCount; i++) { projectionOrder[i] = i; } for (int i = 0; i < projectionCount-1; i++) { int k = (rand() % (projectionCount - i)); int t = projectionOrder[i]; projectionOrder[i] = projectionOrder[i + k]; projectionOrder[i + k] = t; } sart->setProjectionOrder(&projectionOrder[0], projectionCount); } else if (projOrder == "custom") { if (!CR.getOptionIntArray("ProjectionOrderList", projectionOrder)) return false; sart->setProjectionOrder(&projectionOrder[0], projectionOrder.size()); } else { ASTRA_ERROR("Unknown ProjectionOrder"); return false; } if (!CR.getOptionNumerical("Relaxation", m_fLambda, 1.0f)) return false; return true; } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaSartAlgorithm::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { m_bIsInitialized = CCudaReconstructionAlgorithm2D::initialize(_pProjector, _pSinogram, _pReconstruction); if (!m_bIsInitialized) return false; m_fLambda = 1.0f; m_pAlgo = new astraCUDA::SART(); m_bAlgoInit = false; return true; } //---------------------------------------------------------------------------------------- void CCudaSartAlgorithm::initCUDAAlgorithm() { CCudaReconstructionAlgorithm2D::initCUDAAlgorithm(); astraCUDA::SART* pSart = dynamic_cast(m_pAlgo); pSart->setRelaxation(m_fLambda); } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaSirtAlgorithm.cpp000066400000000000000000000074141475635207100212120ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #ifdef ASTRA_CUDA #include "astra/CudaSirtAlgorithm.h" #include "astra/AstraObjectManager.h" #include "astra/cuda/2d/sirt.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaSirtAlgorithm::CCudaSirtAlgorithm() { m_bIsInitialized = false; CCudaReconstructionAlgorithm2D::_clear(); m_pMinMask = 0; m_pMaxMask = 0; m_fLambda = 1.0f; } //---------------------------------------------------------------------------------------- // Destructor CCudaSirtAlgorithm::~CCudaSirtAlgorithm() { // The actual work is done by ~CCudaReconstructionAlgorithm2D m_pMinMask = 0; m_pMaxMask = 0; } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaSirtAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("CudaSirtAlgorithm", this, _cfg); m_bIsInitialized = CCudaReconstructionAlgorithm2D::initialize(_cfg); if (!m_bIsInitialized) return false; // min/max masks int id = -1; if (CR.getOptionID("MinMaskId", id)) { m_pMinMask = dynamic_cast(CData2DManager::getSingleton().get(id)); } if (CR.getOptionID("MaxMaskId", id)) { m_pMaxMask = dynamic_cast(CData2DManager::getSingleton().get(id)); } bool ok = true; ok &= CR.getOptionNumerical("Relaxation", m_fLambda, 1.0f); if (!ok) return false; m_pAlgo = new astraCUDA::SIRT(); m_bAlgoInit = false; return true; } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaSirtAlgorithm::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { m_bIsInitialized = CCudaReconstructionAlgorithm2D::initialize(_pProjector, _pSinogram, _pReconstruction); if (!m_bIsInitialized) return false; m_pAlgo = new astraCUDA::SIRT(); m_bAlgoInit = false; m_fLambda = 1.0f; return true; } //---------------------------------------------------------------------------------------- void CCudaSirtAlgorithm::initCUDAAlgorithm() { astraCUDA::SIRT* pSirt = dynamic_cast(m_pAlgo); pSirt->setRelaxation(m_fLambda); CCudaReconstructionAlgorithm2D::initCUDAAlgorithm(); if (m_pMinMask || m_pMaxMask) { const CVolumeGeometry2D& volgeom = m_pReconstruction->getGeometry(); const float *pfMinMaskData = 0; const float *pfMaxMaskData = 0; if (m_pMinMask) pfMinMaskData = m_pMinMask->getDataConst(); if (m_pMaxMask) pfMaxMaskData = m_pMaxMask->getDataConst(); bool ok = pSirt->uploadMinMaxMasks(pfMinMaskData, pfMaxMaskData, volgeom.getGridColCount()); ASTRA_ASSERT(ok); } } } // namespace astra #endif // ASTRA_CUDA astra-toolbox-2.3.0/src/CudaSirtAlgorithm3D.cpp000066400000000000000000000161661475635207100214050ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/CudaSirtAlgorithm3D.h" #include "astra/AstraObjectManager.h" #include "astra/ConeProjectionGeometry3D.h" #include "astra/ParallelProjectionGeometry3D.h" #include "astra/ParallelVecProjectionGeometry3D.h" #include "astra/ConeVecProjectionGeometry3D.h" #include "astra/VolumeGeometry3D.h" #include "astra/CudaProjector3D.h" #include "astra/Logging.h" #include "astra/cuda/3d/astra3d.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaSirtAlgorithm3D::CCudaSirtAlgorithm3D() { m_bIsInitialized = false; m_pSirt = 0; m_iGPUIndex = -1; m_iVoxelSuperSampling = 1; m_iDetectorSuperSampling = 1; m_fLambda = 1.0f; } //---------------------------------------------------------------------------------------- // Constructor with initialization CCudaSirtAlgorithm3D::CCudaSirtAlgorithm3D(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pProjectionData, CFloat32VolumeData3D* _pReconstruction) { _clear(); initialize(_pProjector, _pProjectionData, _pReconstruction); } //---------------------------------------------------------------------------------------- // Destructor CCudaSirtAlgorithm3D::~CCudaSirtAlgorithm3D() { delete m_pSirt; m_pSirt = 0; CReconstructionAlgorithm3D::_clear(); } //--------------------------------------------------------------------------------------- // Check bool CCudaSirtAlgorithm3D::_check() { // check base class ASTRA_CONFIG_CHECK(CReconstructionAlgorithm3D::_check(), "SIRT3D", "Error in ReconstructionAlgorithm3D initialization"); return true; } //---------------------------------------------------------------------------------------- void CCudaSirtAlgorithm3D::initializeFromProjector() { m_iVoxelSuperSampling = 1; m_iDetectorSuperSampling = 1; m_iGPUIndex = -1; CCudaProjector3D* pCudaProjector = dynamic_cast(m_pProjector); if (!pCudaProjector) { if (m_pProjector) { ASTRA_WARN("non-CUDA Projector3D passed to SIRT3D_CUDA"); } } else { m_iVoxelSuperSampling = pCudaProjector->getVoxelSuperSampling(); m_iDetectorSuperSampling = pCudaProjector->getDetectorSuperSampling(); m_iGPUIndex = pCudaProjector->getGPUIndex(); } } //-------------------------------------------------------------------------------------- // Initialize - Config bool CCudaSirtAlgorithm3D::initialize(const Config& _cfg) { ConfigReader CR("CudaSirtAlgorithm3D", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CReconstructionAlgorithm3D::initialize(_cfg)) { return false; } bool ok = true; ok &= CR.getOptionNumerical("Relaxation", m_fLambda, 1.0f); initializeFromProjector(); // Deprecated options ok &= CR.getOptionInt("VoxelSuperSampling", m_iVoxelSuperSampling, m_iVoxelSuperSampling); ok &= CR.getOptionInt("DetectorSuperSampling", m_iDetectorSuperSampling, m_iDetectorSuperSampling); if (CR.hasOption("GPUIndex")) ok &= CR.getOptionInt("GPUIndex", m_iGPUIndex, m_iGPUIndex); else ok &= CR.getOptionInt("GPUindex", m_iGPUIndex, m_iGPUIndex); if (!ok) return false; m_pSirt = new AstraSIRT3d(); m_bAstraSIRTInit = false; // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaSirtAlgorithm3D::initialize(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pSinogram, CFloat32VolumeData3D* _pReconstruction) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } m_fLambda = 1.0f; // required classes m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; m_pSirt = new AstraSIRT3d; m_bAstraSIRTInit = false; // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Iterate bool CCudaSirtAlgorithm3D::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); const CProjectionGeometry3D &projgeom = m_pSinogram->getGeometry(); const CVolumeGeometry3D &volgeom = m_pReconstruction->getGeometry(); bool ok = true; if (!m_bAstraSIRTInit) { ok &= m_pSirt->setGPUIndex(m_iGPUIndex); ok &= m_pSirt->setGeometry(&volgeom, &projgeom); ok &= m_pSirt->enableSuperSampling(m_iVoxelSuperSampling, m_iDetectorSuperSampling); if (m_bUseReconstructionMask) ok &= m_pSirt->enableVolumeMask(); if (m_bUseSinogramMask) ok &= m_pSirt->enableSinogramMask(); ok &= m_pSirt->setRelaxation(m_fLambda); ASTRA_ASSERT(ok); ok &= m_pSirt->init(); ASTRA_ASSERT(ok); m_bAstraSIRTInit = true; } ASTRA_ASSERT(m_pSinogram->isFloat32Memory()); ok = m_pSirt->setSinogram(m_pSinogram->getFloat32Memory(), m_pSinogram->getGeometry().getDetectorColCount()); ASTRA_ASSERT(ok); if (m_bUseReconstructionMask) { ASTRA_ASSERT(m_pReconstructionMask->isFloat32Memory()); ok &= m_pSirt->setVolumeMask(m_pReconstructionMask->getFloat32Memory(), volgeom.getGridColCount()); } if (m_bUseSinogramMask) { ASTRA_ASSERT(m_pSinogramMask->isFloat32Memory()); ok &= m_pSirt->setSinogramMask(m_pSinogramMask->getFloat32Memory(), m_pSinogramMask->getGeometry().getDetectorColCount()); } ASTRA_ASSERT(m_pReconstruction->isFloat32Memory()); ok &= m_pSirt->setStartReconstruction(m_pReconstruction->getFloat32Memory(), volgeom.getGridColCount()); ASTRA_ASSERT(ok); if (m_bUseMinConstraint) ok &= m_pSirt->setMinConstraint(m_fMinValue); if (m_bUseMaxConstraint) ok &= m_pSirt->setMaxConstraint(m_fMaxValue); ok &= m_pSirt->iterate(_iNrIterations); ASTRA_ASSERT(ok); ok &= m_pSirt->getReconstruction(m_pReconstruction->getFloat32Memory(), volgeom.getGridColCount()); ASTRA_ASSERT(ok); return ok; } //---------------------------------------------------------------------------------------- bool CCudaSirtAlgorithm3D::getResidualNorm(float32& _fNorm) { if (!m_bIsInitialized || !m_pSirt) return false; _fNorm = m_pSirt->computeDiffNorm(); return true; } } // namespace astra astra-toolbox-2.3.0/src/Data3D.cpp000066400000000000000000000057611475635207100166700ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include #include "astra/Data3D.h" namespace astra { std::string CData3D::description() const { std::stringstream res; res << m_iDims[0] << "x" << m_iDims[1] << "x" << m_iDims[2]; if (getType() == CData3D::PROJECTION) res << " sinogram data \t"; if (getType() == CData3D::VOLUME) res << " volume data \t"; return res.str(); } template void CDataMemory::_allocateData(size_t size) { ASTRA_ASSERT(m_pfData == NULL); // allocate contiguous block #ifdef _MSC_VER m_pfData = (T*)_aligned_malloc(size * sizeof(T), 16); #else int ret = posix_memalign((void**)&m_pfData, 16, size * sizeof(T)); ASTRA_ASSERT(ret == 0); #endif ASTRA_ASSERT(((size_t)m_pfData & 15) == 0); } template void CDataMemory::_freeData() { // free memory for data block #ifdef _MSC_VER _aligned_free(m_pfData); #else free(m_pfData); #endif m_pfData = nullptr; } CFloat32ProjectionData3D *createCFloat32ProjectionData3DMemory(const CProjectionGeometry3D &geom) { size_t size = geom.getProjectionCount(); size *= geom.getDetectorTotCount(); CDataStorage *storage = new CDataMemory(size); if (!storage) return 0; return new CFloat32ProjectionData3D(geom, storage); } CFloat32ProjectionData3D *createCFloat32ProjectionData3DMemory(std::unique_ptr &&geom) { size_t size = geom->getProjectionCount(); size *= geom->getDetectorTotCount(); CDataStorage *storage = new CDataMemory(size); if (!storage) return 0; return new CFloat32ProjectionData3D(std::move(geom), storage); } CFloat32VolumeData3D *createCFloat32VolumeData3DMemory(const CVolumeGeometry3D &geom) { CDataStorage *storage = new CDataMemory(geom.getGridTotCount()); if (!storage) return 0; return new CFloat32VolumeData3D(geom, storage); } CFloat32VolumeData3D *createCFloat32VolumeData3DMemory(std::unique_ptr &&geom) { CDataStorage *storage = new CDataMemory(geom->getGridTotCount()); if (!storage) return 0; return new CFloat32VolumeData3D(std::move(geom), storage); } } astra-toolbox-2.3.0/src/DataProjector.cpp000066400000000000000000000020711475635207100203600ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/DataProjector.h" namespace astra { // EMPTY } // end namespace astra astra-toolbox-2.3.0/src/DataProjectorPolicies.cpp000066400000000000000000000021011475635207100220420ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/DataProjectorPolicies.h" namespace astra { // EMPTY } // end namespace astra astra-toolbox-2.3.0/src/FanFlatBeamLineKernelProjector2D.cpp000066400000000000000000000127451475635207100237570ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/FanFlatBeamLineKernelProjector2D.h" #include #include #include #include "astra/DataProjectorPolicies.h" #include "astra/Logging.h" using namespace std; using namespace astra; #include "astra/FanFlatBeamLineKernelProjector2D.inl" //---------------------------------------------------------------------------------------- // default constructor CFanFlatBeamLineKernelProjector2D::CFanFlatBeamLineKernelProjector2D() { _clear(); } //---------------------------------------------------------------------------------------- // constructor CFanFlatBeamLineKernelProjector2D::CFanFlatBeamLineKernelProjector2D(const CFanFlatProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry) { _clear(); initialize(_pProjectionGeometry, _pReconstructionGeometry); } //---------------------------------------------------------------------------------------- // destructor CFanFlatBeamLineKernelProjector2D::~CFanFlatBeamLineKernelProjector2D() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CFanFlatBeamLineKernelProjector2D::_clear() { CProjector2D::_clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CFanFlatBeamLineKernelProjector2D::clear() { CProjector2D::clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Check bool CFanFlatBeamLineKernelProjector2D::_check() { // check base class ASTRA_CONFIG_CHECK(CProjector2D::_check(), "FanFlatBeamLineKernelProjector2D", "Error in Projector2D initialization"); ASTRA_CONFIG_CHECK(dynamic_cast(m_pProjectionGeometry) || dynamic_cast(m_pProjectionGeometry), "FanFlatBeamLineKernelProjector2D", "Unsupported projection geometry"); ASTRA_CONFIG_CHECK(abs(m_pVolumeGeometry->getPixelLengthX() / m_pVolumeGeometry->getPixelLengthY()) - 1 < eps, "FanFlatBeamLineKernelProjector2D", "Pixel height must equal pixel width."); // success return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CFanFlatBeamLineKernelProjector2D::initialize(const Config& _cfg) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CProjector2D::initialize(_cfg)) { return false; } // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize bool CFanFlatBeamLineKernelProjector2D::initialize(const CFanFlatProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pVolumeGeometry) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // hardcopy geometries m_pProjectionGeometry = _pProjectionGeometry.clone(); m_pVolumeGeometry = _pVolumeGeometry.clone(); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Get maximum amount of weights on a single ray int CFanFlatBeamLineKernelProjector2D::getProjectionWeightsCount(int _iProjectionIndex) { int maxDim = max(m_pVolumeGeometry->getGridRowCount(), m_pVolumeGeometry->getGridColCount()); return maxDim * 2 + 1; } //---------------------------------------------------------------------------------------- // Single Ray Weights void CFanFlatBeamLineKernelProjector2D::computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount) { ASTRA_ASSERT(m_bIsInitialized); StorePixelWeightsPolicy p(_pWeightedPixels, _iMaxPixelCount); projectSingleRay(_iProjectionIndex, _iDetectorIndex, p); _iStoredPixelCount = p.getStoredPixelCount(); } //---------------------------------------------------------------------------------------- //Result is always in [-PI/2; PI/2] float32 CFanFlatBeamLineKernelProjector2D::angleBetweenVectors(float32 _fAX, float32 _fAY, float32 _fBX, float32 _fBY) { float32 sinAB = (_fAX*_fBY - _fAY*_fBX)/sqrt((_fAX*_fAX + _fAY*_fAY)*(_fBX*_fBX + _fBY*_fBY)); return asin(sinAB); } //---------------------------------------------------------------------------------------- astra-toolbox-2.3.0/src/FanFlatBeamStripKernelProjector2D.cpp000066400000000000000000000117251475635207100241660ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/FanFlatBeamStripKernelProjector2D.h" #include #include #include "astra/DataProjectorPolicies.h" #include "astra/Logging.h" using namespace std; using namespace astra; #include "astra/FanFlatBeamStripKernelProjector2D.inl" //---------------------------------------------------------------------------------------- // default constructor CFanFlatBeamStripKernelProjector2D::CFanFlatBeamStripKernelProjector2D() { _clear(); } //---------------------------------------------------------------------------------------- // constructor CFanFlatBeamStripKernelProjector2D::CFanFlatBeamStripKernelProjector2D(const CFanFlatProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry) { _clear(); initialize(_pProjectionGeometry, _pReconstructionGeometry); } //---------------------------------------------------------------------------------------- // destructor CFanFlatBeamStripKernelProjector2D::~CFanFlatBeamStripKernelProjector2D() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CFanFlatBeamStripKernelProjector2D::_clear() { CProjector2D::_clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CFanFlatBeamStripKernelProjector2D::clear() { CProjector2D::clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Check bool CFanFlatBeamStripKernelProjector2D::_check() { // check base class ASTRA_CONFIG_CHECK(CProjector2D::_check(), "FanFlatBeamStripKernelProjector2D", "Error in Projector2D initialization"); ASTRA_CONFIG_CHECK(dynamic_cast(m_pProjectionGeometry), "FanFlatBeamLineKernelProjector2D", "Unsupported projection geometry"); ASTRA_CONFIG_CHECK(abs(m_pVolumeGeometry->getPixelLengthX() / m_pVolumeGeometry->getPixelLengthY()) - 1 < eps, "FanFlatBeamStripKernelProjector2D", "Pixel height must equal pixel width."); // success return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CFanFlatBeamStripKernelProjector2D::initialize(const Config& _cfg) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CProjector2D::initialize(_cfg)) { return false; } // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize bool CFanFlatBeamStripKernelProjector2D::initialize(const CFanFlatProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pVolumeGeometry) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // hardcopy geometries m_pProjectionGeometry = _pProjectionGeometry.clone(); m_pVolumeGeometry = _pVolumeGeometry.clone(); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Get maximum amount of weights on a single ray int CFanFlatBeamStripKernelProjector2D::getProjectionWeightsCount(int _iProjectionIndex) { int maxDim = max(m_pVolumeGeometry->getGridRowCount(), m_pVolumeGeometry->getGridColCount()); return maxDim * 10 + 1; } //---------------------------------------------------------------------------------------- // Single Ray Weights void CFanFlatBeamStripKernelProjector2D::computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount) { ASTRA_ASSERT(m_bIsInitialized); StorePixelWeightsPolicy p(_pWeightedPixels, _iMaxPixelCount); projectSingleRay(_iProjectionIndex, _iDetectorIndex, p); _iStoredPixelCount = p.getStoredPixelCount(); } astra-toolbox-2.3.0/src/FanFlatProjectionGeometry2D.cpp000066400000000000000000000172171475635207100231010ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/FanFlatProjectionGeometry2D.h" #include "astra/GeometryUtil2D.h" #include "astra/XMLConfig.h" #include "astra/Logging.h" #include #include using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Default constructor. Sets all variables to zero. CFanFlatProjectionGeometry2D::CFanFlatProjectionGeometry2D() { _clear(); m_fOriginSourceDistance = 0.0f; m_fOriginDetectorDistance = 0.0f; } //---------------------------------------------------------------------------------------- // Constructor. CFanFlatProjectionGeometry2D::CFanFlatProjectionGeometry2D(int _iProjectionAngleCount, int _iDetectorCount, float32 _fDetectorWidth, const float32* _pfProjectionAngles, float32 _fOriginSourceDistance, float32 _fOriginDetectorDistance) { this->initialize(_iProjectionAngleCount, _iDetectorCount, _fDetectorWidth, _pfProjectionAngles, _fOriginSourceDistance, _fOriginDetectorDistance); } //---------------------------------------------------------------------------------------- // Copy Constructor CFanFlatProjectionGeometry2D::CFanFlatProjectionGeometry2D(const CFanFlatProjectionGeometry2D& _projGeom) { _clear(); this->initialize(_projGeom.m_iProjectionAngleCount, _projGeom.m_iDetectorCount, _projGeom.m_fDetectorWidth, _projGeom.m_pfProjectionAngles, _projGeom.m_fOriginSourceDistance, _projGeom.m_fOriginDetectorDistance); } //---------------------------------------------------------------------------------------- // Assignment operator. CFanFlatProjectionGeometry2D& CFanFlatProjectionGeometry2D::operator=(const CFanFlatProjectionGeometry2D& _other) { if (m_bInitialized) delete[] m_pfProjectionAngles; m_bInitialized = _other.m_bInitialized; if (m_bInitialized) { m_iProjectionAngleCount = _other.m_iProjectionAngleCount; m_iDetectorCount = _other.m_iDetectorCount; m_fDetectorWidth = _other.m_fDetectorWidth; m_pfProjectionAngles = new float32[m_iProjectionAngleCount]; memcpy(m_pfProjectionAngles, _other.m_pfProjectionAngles, sizeof(float32)*m_iProjectionAngleCount); m_fOriginSourceDistance = _other.m_fOriginSourceDistance; m_fOriginDetectorDistance = _other.m_fOriginDetectorDistance; } return *this; } //---------------------------------------------------------------------------------------- // Destructor. CFanFlatProjectionGeometry2D::~CFanFlatProjectionGeometry2D() { } //---------------------------------------------------------------------------------------- // Initialization. bool CFanFlatProjectionGeometry2D::initialize(int _iProjectionAngleCount, int _iDetectorCount, float32 _fDetectorWidth, const float32* _pfProjectionAngles, float32 _fOriginSourceDistance, float32 _fOriginDetectorDistance) { m_fOriginSourceDistance = _fOriginSourceDistance; m_fOriginDetectorDistance = _fOriginDetectorDistance; _initialize(_iProjectionAngleCount, _iDetectorCount, _fDetectorWidth, _pfProjectionAngles); // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization with a Config object bool CFanFlatProjectionGeometry2D::initialize(const Config& _cfg) { ConfigReader CR("FanFlatProjectionGeometry2D", this, _cfg); // initialization of parent class if (!CProjectionGeometry2D::initialize(_cfg)) return false; bool ok = true; ok &= CR.getRequiredNumerical("DistanceOriginDetector", m_fOriginDetectorDistance); ok &= CR.getRequiredNumerical("DistanceOriginSource", m_fOriginSourceDistance); if (!ok) return false; // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Clone CProjectionGeometry2D* CFanFlatProjectionGeometry2D::clone() const { return new CFanFlatProjectionGeometry2D(*this); } //---------------------------------------------------------------------------------------- // is equal bool CFanFlatProjectionGeometry2D::isEqual(const CProjectionGeometry2D &_pGeom2) const { // try to cast argument to CFanFlatProjectionGeometry2D const CFanFlatProjectionGeometry2D* pGeom2 = dynamic_cast(&_pGeom2); if (pGeom2 == NULL) return false; // both objects must be initialized if (!m_bInitialized || !pGeom2->m_bInitialized) return false; // check all values if (m_iProjectionAngleCount != pGeom2->m_iProjectionAngleCount) return false; if (m_iDetectorCount != pGeom2->m_iDetectorCount) return false; if (m_fDetectorWidth != pGeom2->m_fDetectorWidth) return false; if (m_fOriginSourceDistance != pGeom2->m_fOriginSourceDistance) return false; if (m_fOriginDetectorDistance != pGeom2->m_fOriginDetectorDistance) return false; for (int i = 0; i < m_iProjectionAngleCount; ++i) { if (m_pfProjectionAngles[i] != pGeom2->m_pfProjectionAngles[i]) return false; } return true; } //---------------------------------------------------------------------------------------- // Is of type bool CFanFlatProjectionGeometry2D::isOfType(const std::string& _sType) { return (_sType == "fanflat"); } //---------------------------------------------------------------------------------------- // Get the configuration object Config* CFanFlatProjectionGeometry2D::getConfiguration() const { ConfigWriter CW("ProjectionGeometry2D", "fanflat"); CW.addInt("DetectorCount", getDetectorCount()); CW.addNumerical("DetectorWidth", getDetectorWidth()); CW.addNumerical("DistanceOriginSource", getOriginSourceDistance()); CW.addNumerical("DistanceOriginDetector", getOriginDetectorDistance()); CW.addNumericalArray("ProjectionAngles", m_pfProjectionAngles, m_iProjectionAngleCount); return CW.getConfig(); } //---------------------------------------------------------------------------------------- CFanFlatVecProjectionGeometry2D* CFanFlatProjectionGeometry2D::toVectorGeometry() { SFanProjection* vectors = genFanProjections(m_iProjectionAngleCount, m_iDetectorCount, m_fOriginSourceDistance, m_fOriginDetectorDistance, m_fDetectorWidth, m_pfProjectionAngles); CFanFlatVecProjectionGeometry2D* vecGeom = new CFanFlatVecProjectionGeometry2D(); vecGeom->initialize(m_iProjectionAngleCount, m_iDetectorCount, vectors); delete[] vectors; return vecGeom; } } // namespace astra astra-toolbox-2.3.0/src/FanFlatVecProjectionGeometry2D.cpp000066400000000000000000000171121475635207100235310ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/FanFlatVecProjectionGeometry2D.h" #include "astra/XMLConfig.h" #include "astra/Logging.h" #include #include using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Default constructor. Sets all variables to zero. CFanFlatVecProjectionGeometry2D::CFanFlatVecProjectionGeometry2D() { _clear(); m_pProjectionAngles = 0; } //---------------------------------------------------------------------------------------- // Constructor. CFanFlatVecProjectionGeometry2D::CFanFlatVecProjectionGeometry2D(int _iProjectionAngleCount, int _iDetectorCount, const SFanProjection* _pProjectionAngles) { this->initialize(_iProjectionAngleCount, _iDetectorCount, _pProjectionAngles); } //---------------------------------------------------------------------------------------- // Copy Constructor CFanFlatVecProjectionGeometry2D::CFanFlatVecProjectionGeometry2D(const CFanFlatVecProjectionGeometry2D& _projGeom) { _clear(); this->initialize(_projGeom.m_iProjectionAngleCount, _projGeom.m_iDetectorCount, _projGeom.m_pProjectionAngles); } //---------------------------------------------------------------------------------------- // Assignment operator. CFanFlatVecProjectionGeometry2D& CFanFlatVecProjectionGeometry2D::operator=(const CFanFlatVecProjectionGeometry2D& _other) { if (m_bInitialized) delete[] m_pProjectionAngles; m_bInitialized = _other.m_bInitialized; if (m_bInitialized) { m_iProjectionAngleCount = _other.m_iProjectionAngleCount; m_iDetectorCount = _other.m_iDetectorCount; m_pProjectionAngles = new SFanProjection[m_iProjectionAngleCount]; memcpy(m_pProjectionAngles, _other.m_pProjectionAngles, sizeof(m_pProjectionAngles[0])*m_iProjectionAngleCount); } return *this; } //---------------------------------------------------------------------------------------- // Destructor. CFanFlatVecProjectionGeometry2D::~CFanFlatVecProjectionGeometry2D() { // TODO delete[] m_pProjectionAngles; } //---------------------------------------------------------------------------------------- // Initialization. bool CFanFlatVecProjectionGeometry2D::initialize(int _iProjectionAngleCount, int _iDetectorCount, const SFanProjection* _pProjectionAngles) { m_iProjectionAngleCount = _iProjectionAngleCount; m_iDetectorCount = _iDetectorCount; m_pProjectionAngles = new SFanProjection[m_iProjectionAngleCount]; for (int i = 0; i < m_iProjectionAngleCount; ++i) m_pProjectionAngles[i] = _pProjectionAngles[i]; // TODO: check? // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization with a Config object bool CFanFlatVecProjectionGeometry2D::initialize(const Config& _cfg) { ConfigReader CR("FanFlatVecProjectionGeometry2D", this, _cfg); XMLNode node; // initialization of parent class if (!CProjectionGeometry2D::initialize(_cfg)) return false; // success m_bInitialized = _check(); return m_bInitialized; } bool CFanFlatVecProjectionGeometry2D::initializeAngles(const Config& _cfg) { ConfigReader CR("FanFlatVecProjectionGeometry2D", this, _cfg); // Required: Vectors vector data; if (!CR.getRequiredNumericalArray("Vectors", data)) return false; ASTRA_CONFIG_CHECK(data.size() % 6 == 0, "FanFlatVecProjectionGeometry2D", "Vectors doesn't consist of 6-tuples."); m_iProjectionAngleCount = data.size() / 6; m_pProjectionAngles = new SFanProjection[m_iProjectionAngleCount]; for (int i = 0; i < m_iProjectionAngleCount; ++i) { SFanProjection& p = m_pProjectionAngles[i]; p.fSrcX = data[6*i + 0]; p.fSrcY = data[6*i + 1]; p.fDetUX = data[6*i + 4]; p.fDetUY = data[6*i + 5]; // The backend code currently expects the corner of the detector, while // the matlab interface supplies the center p.fDetSX = data[6*i + 2] - 0.5 * m_iDetectorCount * p.fDetUX; p.fDetSY = data[6*i + 3] - 0.5 * m_iDetectorCount * p.fDetUY; } return true; } //---------------------------------------------------------------------------------------- // Clone CProjectionGeometry2D* CFanFlatVecProjectionGeometry2D::clone() const { return new CFanFlatVecProjectionGeometry2D(*this); } //---------------------------------------------------------------------------------------- // is equal bool CFanFlatVecProjectionGeometry2D::isEqual(const CProjectionGeometry2D &_pGeom2) const { // try to cast argument to CFanFlatVecProjectionGeometry2D const CFanFlatVecProjectionGeometry2D* pGeom2 = dynamic_cast(&_pGeom2); if (pGeom2 == NULL) return false; // both objects must be initialized if (!m_bInitialized || !pGeom2->m_bInitialized) return false; // check all values if (m_iProjectionAngleCount != pGeom2->m_iProjectionAngleCount) return false; if (m_iDetectorCount != pGeom2->m_iDetectorCount) return false; for (int i = 0; i < m_iProjectionAngleCount; ++i) { if (memcmp(&m_pProjectionAngles[i], &pGeom2->m_pProjectionAngles[i], sizeof(m_pProjectionAngles[i])) != 0) return false; } return true; } //---------------------------------------------------------------------------------------- // Is of type bool CFanFlatVecProjectionGeometry2D::isOfType(const std::string& _sType) { return (_sType == "fanflat_vec"); } //---------------------------------------------------------------------------------------- bool CFanFlatVecProjectionGeometry2D::_check() { // TODO return true; } //---------------------------------------------------------------------------------------- // Get the configuration object Config* CFanFlatVecProjectionGeometry2D::getConfiguration() const { ConfigWriter CW("ProjectionGeometry2D", "fanflat_vec"); CW.addInt("DetectorCount", getDetectorCount()); std::vector vectors; vectors.resize(6 * m_iProjectionAngleCount); for (int i = 0; i < m_iProjectionAngleCount; ++i) { SFanProjection& p = m_pProjectionAngles[i]; vectors[6*i + 0] = p.fSrcX; vectors[6*i + 1] = p.fSrcY; vectors[6*i + 2] = p.fDetSX + 0.5 * m_iDetectorCount * p.fDetUX; vectors[6*i + 3] = p.fDetSY + 0.5 * m_iDetectorCount * p.fDetUY; vectors[6*i + 4] = p.fDetUX; vectors[6*i + 5] = p.fDetUY; } CW.addNumericalMatrix("Vectors", &vectors[0], m_iProjectionAngleCount, 6); return CW.getConfig(); } //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/Features.cpp000066400000000000000000000025631475635207100174030ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Features.h" #include "astra/Globals.h" namespace astra { _AstraExport bool hasFeature(const std::string &flag) { if (flag == "cuda") { return cudaEnabled(); } if (flag == "projectors_scaled_as_line_integrals") { return true; } if (flag == "fan_cone_BP_density_weighting_by_default") { return true; } if (flag == "unpadded_GPULink") { return true; } return false; } } astra-toolbox-2.3.0/src/FilteredBackProjectionAlgorithm.cpp000066400000000000000000000257331475635207100240540ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/FilteredBackProjectionAlgorithm.h" #include #include #include #include "astra/AstraObjectManager.h" #include "astra/ParallelBeamLineKernelProjector2D.h" #include "astra/Fourier.h" #include "astra/DataProjector.h" #include "astra/Logging.h" using namespace std; namespace astra { #include "astra/Projector2DImpl.inl" const int FFT = 1; const int IFFT = -1; //---------------------------------------------------------------------------------------- // Constructor CFilteredBackProjectionAlgorithm::CFilteredBackProjectionAlgorithm() { _clear(); } //---------------------------------------------------------------------------------------- // Destructor CFilteredBackProjectionAlgorithm::~CFilteredBackProjectionAlgorithm() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CFilteredBackProjectionAlgorithm::_clear() { m_pProjector = NULL; m_pSinogram = NULL; m_pReconstruction = NULL; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CFilteredBackProjectionAlgorithm::clear() { m_pProjector = NULL; m_pSinogram = NULL; m_pReconstruction = NULL; m_bIsInitialized = false; m_filterConfig.m_pfCustomFilter.clear(); } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CFilteredBackProjectionAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("FilteredBackProjectionAlgorithm", this, _cfg); bool ok = true; int id = -1; ok &= CR.getRequiredID("ProjectorId", id); m_pProjector = CProjector2DManager::getSingleton().get(id); ok &= CR.getRequiredID("ProjectionDataId", id); m_pSinogram = dynamic_cast(CData2DManager::getSingleton().get(id)); ok &= CR.getRequiredID("ReconstructionDataId", id); m_pReconstruction = dynamic_cast(CData2DManager::getSingleton().get(id)); if (CR.has("ProjectionIndex")) { ASTRA_ERROR("ProjectionIndex is no longer available. Manually adjust the sinogram instead."); return false; } m_filterConfig = getFilterConfigForAlgorithm(_cfg, this); const CParallelProjectionGeometry2D* parprojgeom = dynamic_cast(&m_pSinogram->getGeometry()); if (!parprojgeom) { ASTRA_ERROR("FBP currently only supports parallel projection geometries."); return false; } // TODO: check that the angles are linearly spaced between 0 and pi // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Initialize bool CFilteredBackProjectionAlgorithm::initialize(CProjector2D* _pProjector, CFloat32VolumeData2D* _pVolume, CFloat32ProjectionData2D* _pSinogram) { clear(); // store classes m_pProjector = _pProjector; m_pReconstruction = _pVolume; m_pSinogram = _pSinogram; m_filterConfig = SFilterConfig(); // TODO: check that the angles are linearly spaced between 0 and pi // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Check bool CFilteredBackProjectionAlgorithm::_check() { ASTRA_CONFIG_CHECK(CReconstructionAlgorithm2D::_check(), "FBP", "Error in ReconstructionAlgorithm2D initialization"); ASTRA_CONFIG_CHECK(m_filterConfig.m_eType != FILTER_ERROR, "FBP", "Invalid filter name."); if((m_filterConfig.m_eType == FILTER_PROJECTION) || (m_filterConfig.m_eType == FILTER_SINOGRAM) || (m_filterConfig.m_eType == FILTER_RPROJECTION) || (m_filterConfig.m_eType == FILTER_RSINOGRAM)) { ASTRA_CONFIG_CHECK(!m_filterConfig.m_pfCustomFilter.empty(), "FBP", "Invalid filter pointer."); } ASTRA_CONFIG_CHECK(checkCustomFilterSize(m_filterConfig, m_pSinogram->getGeometry()), "FBP", "Filter size mismatch"); // success return true; } //---------------------------------------------------------------------------------------- // Iterate bool CFilteredBackProjectionAlgorithm::run(int _iNrIterations) { ASTRA_ASSERT(m_bIsInitialized); // Filter sinogram CFloat32ProjectionData2D filteredSinogram(m_pSinogram->getGeometry(), m_pSinogram->getData()); performFiltering(&filteredSinogram); // Back project m_pReconstruction->setData(0.0f); projectData(m_pProjector, DefaultBPPolicy(m_pReconstruction, &filteredSinogram)); // Scale data const CVolumeGeometry2D& volGeom = m_pProjector->getVolumeGeometry(); const CProjectionGeometry2D& projGeom = m_pProjector->getProjectionGeometry(); int iAngleCount = projGeom.getProjectionAngleCount(); float fPixelArea = volGeom.getPixelArea(); (*m_pReconstruction) *= PI/(2*iAngleCount*fPixelArea); m_pReconstruction->updateStatistics(); return true; } //---------------------------------------------------------------------------------------- void CFilteredBackProjectionAlgorithm::performFiltering(CFloat32ProjectionData2D * _pFilteredSinogram) { ASTRA_ASSERT(_pFilteredSinogram != NULL); ASTRA_ASSERT(_pFilteredSinogram->getAngleCount() == m_pSinogram->getAngleCount()); ASTRA_ASSERT(_pFilteredSinogram->getDetectorCount() == m_pSinogram->getDetectorCount()); ASTRA_ASSERT(m_filterConfig.m_eType != FILTER_ERROR); if (m_filterConfig.m_eType == FILTER_NONE) return; int iAngleCount = m_pProjector->getProjectionGeometry().getProjectionAngleCount(); int iDetectorCount = m_pProjector->getProjectionGeometry().getDetectorCount(); int zpDetector = calcNextPowerOfTwo(2 * m_pSinogram->getDetectorCount()); int iHalfFFTSize = astra::calcFFTFourierSize(zpDetector); // cdft setup int *ip = new int[int(2+sqrt((float)zpDetector)+1)]; ip[0] = 0; float32 *w = new float32[zpDetector/2]; // Create filter bool bFilterMultiAngle = false; bool bFilterComplex = false; float *pfFilter = 0; float *pfFilter_delete = 0; switch (m_filterConfig.m_eType) { case FILTER_ERROR: case FILTER_NONE: // Should have been handled before ASTRA_ASSERT(false); return; case FILTER_PROJECTION: // Fourier space, real, half the coefficients (because symmetric) // 1 x iHalfFFTSize pfFilter = &m_filterConfig.m_pfCustomFilter[0]; break; case FILTER_SINOGRAM: bFilterMultiAngle = true; pfFilter = &m_filterConfig.m_pfCustomFilter[0]; break; case FILTER_RSINOGRAM: bFilterMultiAngle = true; // fall-through case FILTER_RPROJECTION: { bFilterComplex = true; int count = bFilterMultiAngle ? iAngleCount : 1; // Spatial, real, full convolution kernel // Center in center (or right-of-center for even sized.) // I.e., 0 1 0 and 0 0 1 0 both correspond to the identity pfFilter = new float[2 * zpDetector * count]; pfFilter_delete = pfFilter; int iUsedFilterWidth = min(m_filterConfig.m_iCustomFilterWidth, zpDetector); int iStartFilterIndex = (m_filterConfig.m_iCustomFilterWidth - iUsedFilterWidth) / 2; int iMaxFilterIndex = iStartFilterIndex + iUsedFilterWidth; int iFilterShiftSize = m_filterConfig.m_iCustomFilterWidth / 2; for (int i = 0; i < count; ++i) { float *rOut = pfFilter + i * 2 * zpDetector; float *rIn = &m_filterConfig.m_pfCustomFilter[i * m_filterConfig.m_iCustomFilterWidth]; memset(rOut, 0, sizeof(float) * 2 * zpDetector); for(int j = iStartFilterIndex; j < iMaxFilterIndex; j++) { int iFFTInFilterIndex = (j + zpDetector - iFilterShiftSize) % zpDetector; rOut[2 * iFFTInFilterIndex] = rIn[j]; } cdft(2*zpDetector, -1, rOut, ip, w); } break; } default: pfFilter = genFilter(m_filterConfig, zpDetector, iHalfFFTSize); pfFilter_delete = pfFilter; } float32* pf = new float32[2 * iAngleCount * zpDetector]; // Copy and zero-pad data for (int iAngle = 0; iAngle < iAngleCount; ++iAngle) { float32* pfRow = pf + iAngle * 2 * zpDetector; float32* pfDataRow = _pFilteredSinogram->getData() + iAngle * iDetectorCount; for (int iDetector = 0; iDetector < iDetectorCount; ++iDetector) { pfRow[2*iDetector] = pfDataRow[iDetector]; pfRow[2*iDetector+1] = 0.0f; } for (int iDetector = iDetectorCount; iDetector < zpDetector; ++iDetector) { pfRow[2*iDetector] = 0.0f; pfRow[2*iDetector+1] = 0.0f; } } // in-place FFT for (int iAngle = 0; iAngle < iAngleCount; ++iAngle) { float32* pfRow = pf + iAngle * 2 * zpDetector; cdft(2*zpDetector, -1, pfRow, ip, w); } // Filter if (bFilterComplex) { for (int iAngle = 0; iAngle < iAngleCount; ++iAngle) { float32* pfRow = pf + iAngle * 2 * zpDetector; float *pfFilterRow = pfFilter; if (bFilterMultiAngle) pfFilterRow += iAngle * 2 * zpDetector; for (int i = 0; i < zpDetector; ++i) { float re = pfRow[2*i] * pfFilterRow[2*i] - pfRow[2*i+1] * pfFilterRow[2*i+1]; float im = pfRow[2*i] * pfFilterRow[2*i+1] + pfRow[2*i+1] * pfFilterRow[2*i]; pfRow[2*i] = re; pfRow[2*i+1] = im; } } } else { for (int iAngle = 0; iAngle < iAngleCount; ++iAngle) { float32* pfRow = pf + iAngle * 2 * zpDetector; float *pfFilterRow = pfFilter; if (bFilterMultiAngle) pfFilterRow += iAngle * iHalfFFTSize; for (int iDetector = 0; iDetector < iHalfFFTSize; ++iDetector) { pfRow[2*iDetector] *= pfFilterRow[iDetector]; pfRow[2*iDetector+1] *= pfFilterRow[iDetector]; } for (int iDetector = iHalfFFTSize; iDetector < zpDetector; ++iDetector) { pfRow[2*iDetector] *= pfFilterRow[zpDetector - iDetector]; pfRow[2*iDetector+1] *= pfFilterRow[zpDetector - iDetector]; } } } // in-place inverse FFT for (int iAngle = 0; iAngle < iAngleCount; ++iAngle) { float32* pfRow = pf + iAngle * 2 * zpDetector; cdft(2*zpDetector, 1, pfRow, ip, w); } // Copy data back for (int iAngle = 0; iAngle < iAngleCount; ++iAngle) { float32* pfRow = pf + iAngle * 2 * zpDetector; float32* pfDataRow = _pFilteredSinogram->getData() + iAngle * iDetectorCount; for (int iDetector = 0; iDetector < iDetectorCount; ++iDetector) pfDataRow[iDetector] = pfRow[2*iDetector] / zpDetector; } delete[] pf; delete[] w; delete[] ip; delete[] pfFilter_delete; } } astra-toolbox-2.3.0/src/Filters.cpp000066400000000000000000000443171475635207100172400ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Globals.h" #include "astra/Logging.h" #include "astra/Fourier.h" #include "astra/Filters.h" #include "astra/Config.h" #include "astra/Float32ProjectionData2D.h" #include "astra/AstraObjectManager.h" #include #include namespace astra { float *genFilter(const SFilterConfig &_cfg, int _iFFTRealDetectorCount, int _iFFTFourierDetectorCount) { float * pfFilt = new float[_iFFTFourierDetectorCount]; float * pfW = new float[_iFFTFourierDetectorCount]; // We cache one Fourier transform for repeated FBP's of the same size static float *pfData = 0; static int iFilterCacheSize = 0; if (!pfData || iFilterCacheSize != _iFFTRealDetectorCount) { // Compute filter in spatial domain delete[] pfData; pfData = new float[2*_iFFTRealDetectorCount]; int *ip = new int[int(2+sqrt(_iFFTRealDetectorCount)+1)]; ip[0] = 0; float32 *w = new float32[_iFFTRealDetectorCount/2]; for (int i = 0; i < _iFFTRealDetectorCount; ++i) { pfData[2*i+1] = 0.0f; if (i & 1) { int j = i; if (2*j > _iFFTRealDetectorCount) j = _iFFTRealDetectorCount - j; float f = PI * j; pfData[2*i] = -1 / (f*f); } else { pfData[2*i] = 0.0f; } } pfData[0] = 0.25f; cdft(2*_iFFTRealDetectorCount, -1, pfData, ip, w); delete[] ip; delete[] w; iFilterCacheSize = _iFFTRealDetectorCount; } for(int iDetectorIndex = 0; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fRelIndex = (float)iDetectorIndex / (float)_iFFTRealDetectorCount; pfFilt[iDetectorIndex] = 2.0f * pfData[2*iDetectorIndex]; pfW[iDetectorIndex] = PI * 2.0f * fRelIndex; } switch(_cfg.m_eType) { case FILTER_RAMLAK: { // do nothing break; } case FILTER_SHEPPLOGAN: { // filt(2:end) = filt(2:end) .* (sin(w(2:end)/(2*d))./(w(2:end)/(2*d))) for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { pfFilt[iDetectorIndex] = pfFilt[iDetectorIndex] * (sinf(pfW[iDetectorIndex] / 2.0f / _cfg.m_fD) / (pfW[iDetectorIndex] / 2.0f / _cfg.m_fD)); } break; } case FILTER_COSINE: { // filt(2:end) = filt(2:end) .* cos(w(2:end)/(2*d)) for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { pfFilt[iDetectorIndex] = pfFilt[iDetectorIndex] * cosf(pfW[iDetectorIndex] / 2.0f / _cfg.m_fD); } break; } case FILTER_HAMMING: { // filt(2:end) = filt(2:end) .* (.54 + .46 * cos(w(2:end)/d)) for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { pfFilt[iDetectorIndex] = pfFilt[iDetectorIndex] * ( 0.54f + 0.46f * cosf(pfW[iDetectorIndex] / _cfg.m_fD)); } break; } case FILTER_HANN: { // filt(2:end) = filt(2:end) .*(1+cos(w(2:end)./d)) / 2 for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { pfFilt[iDetectorIndex] = pfFilt[iDetectorIndex] * (1.0f + cosf(pfW[iDetectorIndex] / _cfg.m_fD)) / 2.0f; } break; } case FILTER_TUKEY: { float fAlpha = _cfg.m_fParameter; if(_cfg.m_fParameter < 0.0f) fAlpha = 0.5f; float fN = (float)_iFFTFourierDetectorCount; float fHalfN = fN / 2.0f; float fEnumTerm = fAlpha * fHalfN; float fDenom = (1.0f - fAlpha) * fHalfN; float fBlockStart = fHalfN - fEnumTerm; float fBlockEnd = fHalfN + fEnumTerm; for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fAbsSmallN = fabs((float)iDetectorIndex); float fStoredValue = 0.0f; if((fBlockStart <= fAbsSmallN) && (fAbsSmallN <= fBlockEnd)) { fStoredValue = 1.0f; } else { float fEnum = fAbsSmallN - fEnumTerm; float fCosInput = PI * fEnum / fDenom; fStoredValue = 0.5f * (1.0f + cosf(fCosInput)); } pfFilt[iDetectorIndex] *= fStoredValue; } break; } case FILTER_LANCZOS: { float fDenum = (float)(_iFFTFourierDetectorCount - 1); for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fSmallN = (float)iDetectorIndex; float fX = 2.0f * fSmallN / fDenum - 1.0f; float fSinInput = PI * fX; float fStoredValue = 0.0f; if(fabsf(fSinInput) > 0.001f) { fStoredValue = sin(fSinInput)/fSinInput; } else { fStoredValue = 1.0f; } pfFilt[iDetectorIndex] *= fStoredValue; } break; } case FILTER_TRIANGULAR: { float fNMinusOne = (float)(_iFFTFourierDetectorCount - 1); for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fSmallN = (float)iDetectorIndex; float fAbsInput = fSmallN - fNMinusOne / 2.0f; float fParenInput = fNMinusOne / 2.0f - fabsf(fAbsInput); float fStoredValue = 2.0f / fNMinusOne * fParenInput; pfFilt[iDetectorIndex] *= fStoredValue; } break; } case FILTER_GAUSSIAN: { float fSigma = _cfg.m_fParameter; if(_cfg.m_fParameter < 0.0f) fSigma = 0.4f; float fN = (float)_iFFTFourierDetectorCount; float fQuotient = (fN - 1.0f) / 2.0f; for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fSmallN = (float)iDetectorIndex; float fEnum = fSmallN - fQuotient; float fDenom = fSigma * fQuotient; float fPower = -0.5f * (fEnum / fDenom) * (fEnum / fDenom); float fStoredValue = expf(fPower); pfFilt[iDetectorIndex] *= fStoredValue; } break; } case FILTER_BARTLETTHANN: { const float fA0 = 0.62f; const float fA1 = 0.48f; const float fA2 = 0.38f; float fNMinusOne = (float)(_iFFTFourierDetectorCount) - 1.0f; for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fSmallN = (float)iDetectorIndex; float fAbsInput = fSmallN / fNMinusOne - 0.5f; float fFirstTerm = fA1 * fabsf(fAbsInput); float fCosInput = 2.0f * PI * fSmallN / fNMinusOne; float fSecondTerm = fA2 * cosf(fCosInput); float fStoredValue = fA0 - fFirstTerm - fSecondTerm; pfFilt[iDetectorIndex] *= fStoredValue; } break; } case FILTER_BLACKMAN: { float fAlpha = _cfg.m_fParameter; if(_cfg.m_fParameter < 0.0f) fAlpha = 0.16f; float fA0 = (1.0f - fAlpha) / 2.0f; float fA1 = 0.5f; float fA2 = fAlpha / 2.0f; float fNMinusOne = (float)(_iFFTFourierDetectorCount - 1); for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fSmallN = (float)iDetectorIndex; float fCosInput1 = 2.0f * PI * 0.5f * fSmallN / fNMinusOne; float fCosInput2 = 4.0f * PI * 0.5f * fSmallN / fNMinusOne; float fStoredValue = fA0 - fA1 * cosf(fCosInput1) + fA2 * cosf(fCosInput2); pfFilt[iDetectorIndex] *= fStoredValue; } break; } case FILTER_NUTTALL: { const float fA0 = 0.355768f; const float fA1 = 0.487396f; const float fA2 = 0.144232f; const float fA3 = 0.012604f; float fNMinusOne = (float)(_iFFTFourierDetectorCount) - 1.0f; for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fSmallN = (float)iDetectorIndex; float fBaseCosInput = PI * fSmallN / fNMinusOne; float fFirstTerm = fA1 * cosf(2.0f * fBaseCosInput); float fSecondTerm = fA2 * cosf(4.0f * fBaseCosInput); float fThirdTerm = fA3 * cosf(6.0f * fBaseCosInput); float fStoredValue = fA0 - fFirstTerm + fSecondTerm - fThirdTerm; pfFilt[iDetectorIndex] *= fStoredValue; } break; } case FILTER_BLACKMANHARRIS: { const float fA0 = 0.35875f; const float fA1 = 0.48829f; const float fA2 = 0.14128f; const float fA3 = 0.01168f; float fNMinusOne = (float)(_iFFTFourierDetectorCount) - 1.0f; for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fSmallN = (float)iDetectorIndex; float fBaseCosInput = PI * fSmallN / fNMinusOne; float fFirstTerm = fA1 * cosf(2.0f * fBaseCosInput); float fSecondTerm = fA2 * cosf(4.0f * fBaseCosInput); float fThirdTerm = fA3 * cosf(6.0f * fBaseCosInput); float fStoredValue = fA0 - fFirstTerm + fSecondTerm - fThirdTerm; pfFilt[iDetectorIndex] *= fStoredValue; } break; } case FILTER_BLACKMANNUTTALL: { const float fA0 = 0.3635819f; const float fA1 = 0.4891775f; const float fA2 = 0.1365995f; const float fA3 = 0.0106411f; float fNMinusOne = (float)(_iFFTFourierDetectorCount) - 1.0f; for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fSmallN = (float)iDetectorIndex; float fBaseCosInput = PI * fSmallN / fNMinusOne; float fFirstTerm = fA1 * cosf(2.0f * fBaseCosInput); float fSecondTerm = fA2 * cosf(4.0f * fBaseCosInput); float fThirdTerm = fA3 * cosf(6.0f * fBaseCosInput); float fStoredValue = fA0 - fFirstTerm + fSecondTerm - fThirdTerm; pfFilt[iDetectorIndex] *= fStoredValue; } break; } case FILTER_FLATTOP: { const float fA0 = 1.0f; const float fA1 = 1.93f; const float fA2 = 1.29f; const float fA3 = 0.388f; const float fA4 = 0.032f; float fNMinusOne = (float)(_iFFTFourierDetectorCount) - 1.0f; for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fSmallN = (float)iDetectorIndex; float fBaseCosInput = PI * fSmallN / fNMinusOne; float fFirstTerm = fA1 * cosf(2.0f * fBaseCosInput); float fSecondTerm = fA2 * cosf(4.0f * fBaseCosInput); float fThirdTerm = fA3 * cosf(6.0f * fBaseCosInput); float fFourthTerm = fA4 * cosf(8.0f * fBaseCosInput); float fStoredValue = fA0 - fFirstTerm + fSecondTerm - fThirdTerm + fFourthTerm; pfFilt[iDetectorIndex] *= fStoredValue; } break; } case FILTER_KAISER: { float fAlpha = _cfg.m_fParameter; if(_cfg.m_fParameter < 0.0f) fAlpha = 3.0f; float fPiTimesAlpha = PI * fAlpha; float fNMinusOne = (float)(_iFFTFourierDetectorCount - 1); float fDenom = (float)j0((double)fPiTimesAlpha); for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fSmallN = (float)iDetectorIndex; float fSquareInput = 2.0f * fSmallN / fNMinusOne - 1; float fSqrtInput = 1.0f - fSquareInput * fSquareInput; float fBesselInput = fPiTimesAlpha * sqrt(fSqrtInput); float fEnum = (float)j0((double)fBesselInput); float fStoredValue = fEnum / fDenom; pfFilt[iDetectorIndex] *= fStoredValue; } break; } case FILTER_PARZEN: { for(int iDetectorIndex = 1; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fSmallN = (float)iDetectorIndex; float fQ = fSmallN / (float)(_iFFTFourierDetectorCount - 1); float fStoredValue = 0.0f; if(fQ <= 0.5f) { fStoredValue = 1.0f - 6.0f * fQ * fQ * (1.0f - fQ); } else { float fCubedValue = 1.0f - fQ; fStoredValue = 2.0f * fCubedValue * fCubedValue * fCubedValue; } pfFilt[iDetectorIndex] *= fStoredValue; } break; } default: { ASTRA_ERROR("Cannot serve requested filter"); return NULL; } } // filt(w>pi*d) = 0; float fPiTimesD = PI * _cfg.m_fD; for(int iDetectorIndex = 0; iDetectorIndex < _iFFTFourierDetectorCount; iDetectorIndex++) { float fWValue = pfW[iDetectorIndex]; if(fWValue > fPiTimesD) { pfFilt[iDetectorIndex] = 0.0f; } } delete[] pfW; return pfFilt; } static bool stringCompareLowerCase(const char * _stringA, const char * _stringB) { int iCmpReturn = 0; #ifdef _MSC_VER iCmpReturn = _stricmp(_stringA, _stringB); #else iCmpReturn = strcasecmp(_stringA, _stringB); #endif return (iCmpReturn == 0); } struct FilterNameMapEntry { const char *m_name; E_FBPFILTER m_type; }; E_FBPFILTER convertStringToFilter(const std::string &_filterType) { static const FilterNameMapEntry map[] = { { "ram-lak", FILTER_RAMLAK }, { "shepp-logan", FILTER_SHEPPLOGAN }, { "cosine", FILTER_COSINE }, { "hamming", FILTER_HAMMING}, { "hann", FILTER_HANN}, { "tukey", FILTER_TUKEY }, { "lanczos", FILTER_LANCZOS}, { "triangular", FILTER_TRIANGULAR}, { "gaussian", FILTER_GAUSSIAN}, { "barlett-hann", FILTER_BARTLETTHANN }, { "blackman", FILTER_BLACKMAN}, { "nuttall", FILTER_NUTTALL }, { "blackman-harris", FILTER_BLACKMANHARRIS }, { "blackman-nuttall", FILTER_BLACKMANNUTTALL }, { "flat-top", FILTER_FLATTOP }, { "kaiser", FILTER_KAISER }, { "parzen", FILTER_PARZEN }, { "projection", FILTER_PROJECTION }, { "sinogram", FILTER_SINOGRAM }, { "rprojection", FILTER_RPROJECTION }, { "rsinogram", FILTER_RSINOGRAM }, { "none", FILTER_NONE }, { 0, FILTER_ERROR } }; const FilterNameMapEntry *i; for (i = &map[0]; i->m_name; ++i) if (stringCompareLowerCase(_filterType.c_str(), i->m_name)) return i->m_type; ASTRA_ERROR("Failed to convert \"%s\" into a filter.",_filterType.c_str()); return FILTER_ERROR; } SFilterConfig getFilterConfigForAlgorithm(const Config& _cfg, CAlgorithm *_alg) { ConfigReader CR("getFilterConfig", _alg, _cfg); SFilterConfig c; // filter type const char *nodeName = "FilterType"; if (CR.hasOption(nodeName)) { std::string s; CR.getOptionString(nodeName, s, ""); c.m_eType = convertStringToFilter(s); } else if (CR.has(nodeName)) { // Fallback: check cfg.FilterType (instead of cfg.option.FilterType) std::string s; CR.getString(nodeName, s, ""); c.m_eType = convertStringToFilter(s); } else { c.m_eType = FILTER_RAMLAK; } // filter nodeName = "FilterSinogramId"; int id = -1; switch (c.m_eType) { case FILTER_PROJECTION: case FILTER_SINOGRAM: case FILTER_RPROJECTION: case FILTER_RSINOGRAM: { bool ok = true; if (CR.hasOption(nodeName)) { ok &= CR.getOptionID(nodeName, id); } else if (CR.has(nodeName)) { ok &= CR.getID(nodeName, id); } if (!ok) { c.m_eType = FILTER_ERROR; return c; } } break; default: break; } if (id != -1) { const CFloat32ProjectionData2D * pFilterData = dynamic_cast(CData2DManager::getSingleton().get(id)); c.m_iCustomFilterWidth = pFilterData->getGeometry().getDetectorCount(); c.m_iCustomFilterHeight = pFilterData->getGeometry().getProjectionAngleCount(); c.m_pfCustomFilter.resize(c.m_iCustomFilterWidth * c.m_iCustomFilterHeight); memcpy(&c.m_pfCustomFilter[0], pFilterData->getDataConst(), sizeof(float) * c.m_iCustomFilterWidth * c.m_iCustomFilterHeight); } else { c.m_iCustomFilterWidth = 0; c.m_iCustomFilterHeight = 0; c.m_pfCustomFilter.clear(); } // filter parameter nodeName = "FilterParameter"; c.m_fParameter = -1.0f; switch (c.m_eType) { case FILTER_TUKEY: case FILTER_GAUSSIAN: case FILTER_BLACKMAN: case FILTER_KAISER: { bool ok = true; if (CR.hasOption(nodeName)) { ok &= CR.getOptionNumerical(nodeName, c.m_fParameter, c.m_fParameter); } else if (CR.has(nodeName)) { ok &= CR.getRequiredNumerical(nodeName, c.m_fParameter); } if (!ok) { c.m_eType = FILTER_ERROR; return c; } } break; default: break; } // D value nodeName = "FilterD"; c.m_fD = 1.0f; switch (c.m_eType) { case FILTER_PROJECTION: case FILTER_SINOGRAM: case FILTER_RPROJECTION: case FILTER_RSINOGRAM: break; case FILTER_NONE: case FILTER_ERROR: break; default: { bool ok = true; if (CR.hasOption(nodeName)) { ok &= CR.getOptionNumerical(nodeName, c.m_fD, c.m_fD); } else if (CR.has(nodeName)) { ok &= CR.getRequiredNumerical(nodeName, c.m_fD); } if (!ok) { c.m_eType = FILTER_ERROR; return c; } } break; } return c; } int calcNextPowerOfTwo(int n) { int x = 1; while (x < n && x > 0) x *= 2; return x; } // Because the input is real, the Fourier transform is symmetric. // CUFFT only outputs the first half (ignoring the redundant second half), // and expects the same as input for the IFFT. int calcFFTFourierSize(int _iFFTRealSize) { int iFFTFourierSize = _iFFTRealSize / 2 + 1; return iFFTFourierSize; } bool checkCustomFilterSize(const SFilterConfig &_cfg, const CProjectionGeometry2D &_geom) { int iExpectedWidth = -1, iExpectedHeight = 1; switch (_cfg.m_eType) { case FILTER_ERROR: ASTRA_ERROR("checkCustomFilterSize: internal error; FILTER_ERROR passed"); return false; case FILTER_NONE: return true; case FILTER_SINOGRAM: iExpectedHeight = _geom.getProjectionAngleCount(); // fallthrough case FILTER_PROJECTION: { int iPaddedDetCount = calcNextPowerOfTwo(2 * _geom.getDetectorCount()); iExpectedWidth = calcFFTFourierSize(iPaddedDetCount); } if (_cfg.m_iCustomFilterWidth != iExpectedWidth || _cfg.m_iCustomFilterHeight != iExpectedHeight) { ASTRA_ERROR("filter size mismatch: %dx%d (received) is not %dx%d (expected)", _cfg.m_iCustomFilterHeight, _cfg.m_iCustomFilterWidth, iExpectedHeight, iExpectedWidth); return false; } return true; case FILTER_RSINOGRAM: iExpectedHeight = _geom.getProjectionAngleCount(); // fallthrough case FILTER_RPROJECTION: if (_cfg.m_iCustomFilterHeight != iExpectedHeight) { ASTRA_ERROR("filter size mismatch: %dx%d (received) is not %dxX (expected)", _cfg.m_iCustomFilterHeight, _cfg.m_iCustomFilterWidth, iExpectedHeight); return false; } return true; default: // Non-custom filters; nothing to check. return true; } } } astra-toolbox-2.3.0/src/Float32Data.cpp000066400000000000000000000025421475635207100176260ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Float32Data.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Default constructor. CFloat32Data::CFloat32Data() { } //---------------------------------------------------------------------------------------- // Destructor. CFloat32Data::~CFloat32Data() { } } // end namespace astra-toolbox-2.3.0/src/Float32Data2D.cpp000066400000000000000000000360661475635207100200240ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Float32Data2D.h" #include #include #include #ifdef _MSC_VER #include #else #include #endif namespace astra { template CCustomMemory::~CCustomMemory() { } //---------------------------------------------------------------------------------------- // Constructors //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // Default constructor. CFloat32Data2D::CFloat32Data2D() { _clear(); m_bInitialized = false; } //---------------------------------------------------------------------------------------- // Create an instance of the CFloat32Data2D class, allocating (but not initializing) the data block. CFloat32Data2D::CFloat32Data2D(int _iWidth, int _iHeight) { m_bInitialized = false; _initialize(_iWidth, _iHeight); } //---------------------------------------------------------------------------------------- // Create an instance of the CFloat32Data2D class with initialization of the data block. CFloat32Data2D::CFloat32Data2D(int _iWidth, int _iHeight, const float32* _pfData) { m_bInitialized = false; _initialize(_iWidth, _iHeight, _pfData); } //---------------------------------------------------------------------------------------- // Create an instance of the CFloat32Data2D class with initialization of the data block. CFloat32Data2D::CFloat32Data2D(int _iWidth, int _iHeight, float32 _fScalar) { m_bInitialized = false; _initialize(_iWidth, _iHeight, _fScalar); } //---------------------------------------------------------------------------------------- // Create an instance of the CFloat32Data2D class with pre-allocated memory. CFloat32Data2D::CFloat32Data2D(int _iWidth, int _iHeight, CFloat32CustomMemory *_pCustomMemory) { m_bInitialized = false; _initialize(_iWidth, _iHeight, _pCustomMemory); } //---------------------------------------------------------------------------------------- // Copy constructor CFloat32Data2D::CFloat32Data2D(const CFloat32Data2D& _other) { m_bInitialized = false; *this = _other; } //---------------------------------------------------------------------------------------- // Assignment operator CFloat32Data2D& CFloat32Data2D::operator=(const CFloat32Data2D& _dataIn) { ASTRA_ASSERT(_dataIn.m_bInitialized); if (m_bInitialized) { if (m_iWidth == _dataIn.m_iWidth && m_iHeight == _dataIn.m_iHeight) { // Same dimensions, so no need to re-allocate memory m_fGlobalMin = _dataIn.m_fGlobalMin; m_fGlobalMax = _dataIn.m_fGlobalMax; m_fGlobalMean = _dataIn.m_fGlobalMean; ASTRA_ASSERT(m_iSize == (size_t)m_iWidth * m_iHeight); ASTRA_ASSERT(m_pfData); memcpy(m_pfData, _dataIn.m_pfData, m_iSize * sizeof(float32)); } else { if (m_pCustomMemory) { // Can't re-allocate custom data ASTRA_ASSERT(false); return *(CFloat32Data2D*)0; } // Re-allocate data _unInit(); _initialize(_dataIn.getWidth(), _dataIn.getHeight(), _dataIn.getDataConst()); } } else { _initialize(_dataIn.getWidth(), _dataIn.getHeight(), _dataIn.getDataConst()); } return (*this); } //---------------------------------------------------------------------------------------- // Destructor. Free allocated memory CFloat32Data2D::~CFloat32Data2D() { if (m_bInitialized) { _unInit(); } } //---------------------------------------------------------------------------------------- // Initializes an instance of the CFloat32Data2D class, allocating (but not initializing) the data block. bool CFloat32Data2D::_initialize(int _iWidth, int _iHeight) { // basic checks ASTRA_ASSERT(_iWidth > 0); ASTRA_ASSERT(_iHeight > 0); if (m_bInitialized) { _unInit(); } // calculate size m_iWidth = _iWidth; m_iHeight = _iHeight; m_iSize = (size_t)m_iWidth * m_iHeight; // allocate memory for the data, but do not fill it m_pfData = 0; m_ppfData2D = 0; m_pCustomMemory = 0; _allocateData(); // set minmax to default values m_fGlobalMin = 0.0; m_fGlobalMax = 0.0; m_fGlobalMean = 0.0; // initialization complete return true; } //---------------------------------------------------------------------------------------- // Initializes an instance of the CFloat32Data2D class with initialization of the data block. bool CFloat32Data2D::_initialize(int _iWidth, int _iHeight, const float32 *_pfData) { // basic checks ASTRA_ASSERT(_iWidth > 0); ASTRA_ASSERT(_iHeight > 0); ASTRA_ASSERT(_pfData != NULL); if (m_bInitialized) { _unInit(); } // calculate size m_iWidth = _iWidth; m_iHeight = _iHeight; m_iSize = (size_t)m_iWidth * m_iHeight; // allocate memory for the data m_pfData = 0; m_ppfData2D = 0; m_pCustomMemory = 0; _allocateData(); // fill the data block with a copy of the input data size_t i; for (i = 0; i < m_iSize; ++i) { m_pfData[i] = _pfData[i]; } // initialization complete return true; } //---------------------------------------------------------------------------------------- // Initializes an instance of the CFloat32Data2D class with a scalar initialization of the data block. bool CFloat32Data2D::_initialize(int _iWidth, int _iHeight, float32 _fScalar) { // basic checks ASTRA_ASSERT(_iWidth > 0); ASTRA_ASSERT(_iHeight > 0); if (m_bInitialized) { _unInit(); } // calculate size m_iWidth = _iWidth; m_iHeight = _iHeight; m_iSize = (size_t)m_iWidth * m_iHeight; // allocate memory for the data m_pfData = 0; m_ppfData2D = 0; m_pCustomMemory = 0; _allocateData(); // fill the data block with a copy of the input data size_t i; for (i = 0; i < m_iSize; ++i) { m_pfData[i] = _fScalar; } // initialization complete return true; } //---------------------------------------------------------------------------------------- // Initializes an instance of the CFloat32Data2D class with pre-allocated memory bool CFloat32Data2D::_initialize(int _iWidth, int _iHeight, CFloat32CustomMemory* _pCustomMemory) { // basic checks ASTRA_ASSERT(_iWidth > 0); ASTRA_ASSERT(_iHeight > 0); ASTRA_ASSERT(_pCustomMemory != NULL); if (m_bInitialized) { _unInit(); } // calculate size m_iWidth = _iWidth; m_iHeight = _iHeight; m_iSize = (size_t)m_iWidth * m_iHeight; // initialize the data pointers m_pCustomMemory = _pCustomMemory; m_pfData = 0; m_ppfData2D = 0; _allocateData(); // initialization complete return true; } //---------------------------------------------------------------------------------------- // Memory Allocation //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // Allocate memory for m_pfData and m_ppfData2D arrays. void CFloat32Data2D::_allocateData() { // basic checks ASTRA_ASSERT(!m_bInitialized); ASTRA_ASSERT(m_iSize > 0); ASTRA_ASSERT((size_t)m_iSize == (size_t)m_iWidth * m_iHeight); ASTRA_ASSERT(m_pfData == NULL); ASTRA_ASSERT(m_ppfData2D == NULL); if (!m_pCustomMemory) { // allocate contiguous block #ifdef _MSC_VER m_pfData = (float32*)_aligned_malloc(m_iSize * sizeof(float32), 16); #else int ret = posix_memalign((void**)&m_pfData, 16, m_iSize * sizeof(float32)); ASTRA_ASSERT(ret == 0); #endif } else { m_pfData = m_pCustomMemory->m_fPtr; } // create array of pointers to each row of the data block m_ppfData2D = new float32*[m_iHeight]; for (int iy = 0; iy < m_iHeight; iy++) { m_ppfData2D[iy] = &(m_pfData[iy * m_iWidth]); } } //---------------------------------------------------------------------------------------- // Free memory for m_pfData and m_ppfData2D arrays. void CFloat32Data2D::_freeData() { // basic checks ASTRA_ASSERT(m_pfData != NULL); ASTRA_ASSERT(m_ppfData2D != NULL); // free memory for index table delete[] m_ppfData2D; if (!m_pCustomMemory) { // free memory for data block #ifdef _MSC_VER _aligned_free(m_pfData); #else free(m_pfData); #endif } else { delete m_pCustomMemory; m_pCustomMemory = 0; } } //---------------------------------------------------------------------------------------- // Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. void CFloat32Data2D::_clear() { m_iWidth = 0; m_iHeight = 0; m_iSize = 0; m_pfData = NULL; m_ppfData2D = NULL; m_pCustomMemory = NULL; m_fGlobalMin = 0.0f; m_fGlobalMax = 0.0f; } //---------------------------------------------------------------------------------------- // Un-initialize the object, bringing it back in the unitialized state. void CFloat32Data2D::_unInit() { ASTRA_ASSERT(m_bInitialized); _freeData(); _clear(); m_bInitialized = false; } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // Data Operations //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // Copy the data block pointed to by _pfData to the data block pointed to by m_pfData. void CFloat32Data2D::copyData(const float32* _pfData) { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(_pfData != NULL); ASTRA_ASSERT(m_pfData != NULL); ASTRA_ASSERT(m_iSize > 0); // copy data size_t i; for (i = 0; i < m_iSize; ++i) { m_pfData[i] = _pfData[i]; } } //---------------------------------------------------------------------------------------- // scale m_pfData from 0 to 255. void CFloat32Data2D::scale() { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(m_pfData != NULL); ASTRA_ASSERT(m_iSize > 0); _computeGlobalMinMax(); for (size_t i = 0; i < m_iSize; i++) { // do checks m_pfData[i]= (m_pfData[i] - m_fGlobalMin) / (m_fGlobalMax - m_fGlobalMin) * 255; ; } } //---------------------------------------------------------------------------------------- // Set each element of the data to a specific scalar value void CFloat32Data2D::setData(float32 _fScalar) { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(m_pfData != NULL); ASTRA_ASSERT(m_iSize > 0); // copy data size_t i; for (i = 0; i < m_iSize; ++i) { m_pfData[i] = _fScalar; } } //---------------------------------------------------------------------------------------- // Clear Data void CFloat32Data2D::clearData() { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(m_pfData != NULL); ASTRA_ASSERT(m_iSize > 0); // set data size_t i; for (i = 0; i < m_iSize; ++i) { m_pfData[i] = 0.0f; } } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // Statistics Operations //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // Update data statistics, such as minimum and maximum value, after the data has been modified. void CFloat32Data2D::updateStatistics() { _computeGlobalMinMax(); } //---------------------------------------------------------------------------------------- // Find the minimum and maximum data value. void CFloat32Data2D::_computeGlobalMinMax() { // basic checks ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(m_pfData != NULL); ASTRA_ASSERT(m_iSize > 0); // initial values m_fGlobalMin = m_pfData[0]; m_fGlobalMax = m_pfData[0]; m_fGlobalMean = 0.0f; // loop for (size_t i = 0; i < m_iSize; i++) { // do checks float32 v = m_pfData[i]; if (v < m_fGlobalMin) { m_fGlobalMin = v; } if (v > m_fGlobalMax) { m_fGlobalMax = v; } m_fGlobalMean +=v; } m_fGlobalMean /= m_iSize; } //---------------------------------------------------------------------------------------- CFloat32Data2D& CFloat32Data2D::clampMin(float32& _fMin) { ASTRA_ASSERT(m_bInitialized); for (size_t i = 0; i < m_iSize; i++) { if (m_pfData[i] < _fMin) m_pfData[i] = _fMin; } return (*this); } CFloat32Data2D& CFloat32Data2D::clampMax(float32& _fMax) { ASTRA_ASSERT(m_bInitialized); for (size_t i = 0; i < m_iSize; i++) { if (m_pfData[i] > _fMax) m_pfData[i] = _fMax; } return (*this); } //---------------------------------------------------------------------------------------- // Operator Overloading //---------------------------------------------------------------------------------------- CFloat32Data2D& CFloat32Data2D::operator+=(const CFloat32Data2D& v) { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(v.m_bInitialized); ASTRA_ASSERT(getSize() == v.getSize()); for (size_t i = 0; i < m_iSize; i++) { m_pfData[i] += v.m_pfData[i]; } return (*this); } CFloat32Data2D& CFloat32Data2D::operator-=(const CFloat32Data2D& v) { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(v.m_bInitialized); ASTRA_ASSERT(getSize() == v.getSize()); for (size_t i = 0; i < m_iSize; i++) { m_pfData[i] -= v.m_pfData[i]; } return (*this); } CFloat32Data2D& CFloat32Data2D::operator*=(const CFloat32Data2D& v) { ASTRA_ASSERT(m_bInitialized); ASTRA_ASSERT(v.m_bInitialized); ASTRA_ASSERT(getSize() == v.getSize()); for (size_t i = 0; i < m_iSize; i++) { m_pfData[i] *= v.m_pfData[i]; } return (*this); } CFloat32Data2D& CFloat32Data2D::operator*=(const float32& f) { ASTRA_ASSERT(m_bInitialized); for (size_t i = 0; i < m_iSize; i++) { m_pfData[i] *= f; } return (*this); } CFloat32Data2D& CFloat32Data2D::operator/=(const float32& f) { ASTRA_ASSERT(m_bInitialized); for (size_t i = 0; i < m_iSize; i++) { m_pfData[i] /= f; } return (*this); } CFloat32Data2D& CFloat32Data2D::operator+=(const float32& f) { ASTRA_ASSERT(m_bInitialized); for (size_t i = 0; i < m_iSize; i++) { m_pfData[i] += f; } return (*this); } CFloat32Data2D& CFloat32Data2D::operator-=(const float32& f) { ASTRA_ASSERT(m_bInitialized); for (size_t i = 0; i < m_iSize; i++) { m_pfData[i] -= f; } return (*this); } std::string CFloat32Data2D::description() const { std::stringstream res; res << m_iWidth << "x" << m_iHeight; if (getType() == CFloat32Data2D::PROJECTION) res << " sinogram data \t"; if (getType() == CFloat32Data2D::VOLUME) res << " volume data \t"; return res.str(); } } // end namespace astra astra-toolbox-2.3.0/src/Float32ProjectionData2D.cpp000066400000000000000000000127021475635207100220500ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Float32ProjectionData2D.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Default constructor CFloat32ProjectionData2D::CFloat32ProjectionData2D() : CFloat32Data2D() { m_bInitialized = false; m_pGeometry = NULL; } //---------------------------------------------------------------------------------------- // Create an instance of the CFloat32ProjectionData2D class, allocating (but not initializing) the data block. CFloat32ProjectionData2D::CFloat32ProjectionData2D(const CProjectionGeometry2D &_pGeometry) { m_bInitialized = false; initialize(_pGeometry); } //---------------------------------------------------------------------------------------- // Create an instance of the CFloat32ProjectionData2D class with initialization of the data. CFloat32ProjectionData2D::CFloat32ProjectionData2D(const CProjectionGeometry2D &_pGeometry, float32* _pfData) { m_bInitialized = false; m_bInitialized = initialize(_pGeometry, _pfData); } //---------------------------------------------------------------------------------------- // Create an instance of the CFloat32ProjectionData2D class with scalar initialization of the data. CFloat32ProjectionData2D::CFloat32ProjectionData2D(const CProjectionGeometry2D &_pGeometry, float32 _fScalar) { m_bInitialized = false; m_bInitialized = initialize(_pGeometry, _fScalar); } //---------------------------------------------------------------------------------------- // Copy constructor CFloat32ProjectionData2D::CFloat32ProjectionData2D(const CFloat32ProjectionData2D& _other) : CFloat32Data2D(_other) { // Data is copied by parent constructor m_pGeometry = _other.m_pGeometry->clone(); m_bInitialized = true; } //---------------------------------------------------------------------------------------- // Create an instance of the CFloat32ProjectionData2D class with pre-allocated data CFloat32ProjectionData2D::CFloat32ProjectionData2D(const CProjectionGeometry2D &_pGeometry, CFloat32CustomMemory* _pCustomMemory) { m_bInitialized = false; m_bInitialized = initialize(_pGeometry, _pCustomMemory); } // Assignment operator CFloat32ProjectionData2D& CFloat32ProjectionData2D::operator=(const CFloat32ProjectionData2D& _other) { ASTRA_ASSERT(_other.m_bInitialized); if (m_bInitialized) delete m_pGeometry; *((CFloat32Data2D*)this) = _other; m_pGeometry = _other.m_pGeometry->clone(); m_bInitialized = true; return *this; } //---------------------------------------------------------------------------------------- // Initialization bool CFloat32ProjectionData2D::initialize(const CProjectionGeometry2D &_pGeometry) { m_pGeometry = _pGeometry.clone(); m_bInitialized = _initialize(m_pGeometry->getDetectorCount(), m_pGeometry->getProjectionAngleCount()); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization bool CFloat32ProjectionData2D::initialize(const CProjectionGeometry2D &_pGeometry, const float32* _pfData) { m_pGeometry = _pGeometry.clone(); m_bInitialized = _initialize(m_pGeometry->getDetectorCount(), m_pGeometry->getProjectionAngleCount(), _pfData); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization bool CFloat32ProjectionData2D::initialize(const CProjectionGeometry2D &_pGeometry, float32 _fScalar) { m_pGeometry = _pGeometry.clone(); m_bInitialized = _initialize(m_pGeometry->getDetectorCount(), m_pGeometry->getProjectionAngleCount(), _fScalar); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization bool CFloat32ProjectionData2D::initialize(const CProjectionGeometry2D &_pGeometry, CFloat32CustomMemory* _pCustomMemory) { m_pGeometry = _pGeometry.clone(); m_bInitialized = _initialize(m_pGeometry->getDetectorCount(), m_pGeometry->getProjectionAngleCount(), _pCustomMemory); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Destructor CFloat32ProjectionData2D::~CFloat32ProjectionData2D() { if (m_bInitialized) delete m_pGeometry; m_pGeometry = 0; } //---------------------------------------------------------------------------------------- void CFloat32ProjectionData2D::changeGeometry(const CProjectionGeometry2D &_pGeometry) { if (!m_bInitialized) return; delete m_pGeometry; m_pGeometry = _pGeometry.clone(); } } // end namespace astra astra-toolbox-2.3.0/src/Float32VolumeData2D.cpp000066400000000000000000000123231475635207100212020ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Float32VolumeData2D.h" namespace astra { //---------------------------------------------------------------------------------------- // Default constructor CFloat32VolumeData2D::CFloat32VolumeData2D() : CFloat32Data2D() { m_pGeometry = NULL; m_bInitialized = false; } //---------------------------------------------------------------------------------------- // Create an instance of the CFloat32VolumeData2D class, allocating (but not initializing) the data block. CFloat32VolumeData2D::CFloat32VolumeData2D(const CVolumeGeometry2D& _pGeometry) { m_bInitialized = false; m_bInitialized = initialize(_pGeometry); } //---------------------------------------------------------------------------------------- // Create an instance of the CFloat32VolumeData2D class with initialization of the data. CFloat32VolumeData2D::CFloat32VolumeData2D(const CVolumeGeometry2D& _pGeometry, float32* _pfData) { m_bInitialized = false; m_bInitialized = initialize(_pGeometry, _pfData); } //---------------------------------------------------------------------------------------- // Create an instance of the CFloat32VolumeData2D class with initialization of the data. CFloat32VolumeData2D::CFloat32VolumeData2D(const CVolumeGeometry2D& _pGeometry, float32 _fScalar) { m_bInitialized = false; m_bInitialized = initialize(_pGeometry, _fScalar); } //---------------------------------------------------------------------------------------- // Copy constructor CFloat32VolumeData2D::CFloat32VolumeData2D(const CFloat32VolumeData2D& _other) : CFloat32Data2D(_other) { m_pGeometry = _other.m_pGeometry->clone(); m_bInitialized = true; } //---------------------------------------------------------------------------------------- // Create an instance of the CFloat32VolumeData2D class with pre-allocated data CFloat32VolumeData2D::CFloat32VolumeData2D(const CVolumeGeometry2D& _pGeometry, CFloat32CustomMemory* _pCustomMemory) { m_bInitialized = false; m_bInitialized = initialize(_pGeometry, _pCustomMemory); } // Assignment operator CFloat32VolumeData2D& CFloat32VolumeData2D::operator=(const CFloat32VolumeData2D& _other) { ASTRA_ASSERT(_other.m_bInitialized); if (m_bInitialized) delete m_pGeometry; *((CFloat32Data2D*)this) = _other; m_pGeometry = _other.m_pGeometry->clone(); m_bInitialized = true; return *this; } //---------------------------------------------------------------------------------------- // Destructor CFloat32VolumeData2D::~CFloat32VolumeData2D() { if (m_bInitialized) delete m_pGeometry; m_pGeometry = 0; } //---------------------------------------------------------------------------------------- // Initialization bool CFloat32VolumeData2D::initialize(const CVolumeGeometry2D& _pGeometry) { m_pGeometry = _pGeometry.clone(); m_bInitialized = _initialize(m_pGeometry->getGridColCount(), m_pGeometry->getGridRowCount()); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization bool CFloat32VolumeData2D::initialize(const CVolumeGeometry2D& _pGeometry, const float32* _pfData) { m_pGeometry = _pGeometry.clone(); m_bInitialized = _initialize(m_pGeometry->getGridColCount(), m_pGeometry->getGridRowCount(), _pfData); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization bool CFloat32VolumeData2D::initialize(const CVolumeGeometry2D& _pGeometry, float32 _fScalar) { m_pGeometry = _pGeometry.clone(); m_bInitialized = _initialize(m_pGeometry->getGridColCount(), m_pGeometry->getGridRowCount(), _fScalar); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization bool CFloat32VolumeData2D::initialize(const CVolumeGeometry2D& _pGeometry, CFloat32CustomMemory* _pCustomMemory) { m_pGeometry = _pGeometry.clone(); m_bInitialized = _initialize(m_pGeometry->getGridColCount(), m_pGeometry->getGridRowCount(), _pCustomMemory); return m_bInitialized; } //---------------------------------------------------------------------------------------- void CFloat32VolumeData2D::changeGeometry(const CVolumeGeometry2D &_pGeometry) { if (!m_bInitialized) return; delete m_pGeometry; m_pGeometry = _pGeometry.clone(); } } // end namespace astra astra-toolbox-2.3.0/src/ForwardProjectionAlgorithm.cpp000066400000000000000000000167041475635207100231370ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ForwardProjectionAlgorithm.h" #include "astra/AstraObjectManager.h" #include "astra/DataProjectorPolicies.h" #include "astra/Logging.h" using namespace std; namespace astra { #include "astra/Projector2DImpl.inl" //---------------------------------------------------------------------------------------- // Constructor - Default CForwardProjectionAlgorithm::CForwardProjectionAlgorithm() { _clear(); } //---------------------------------------------------------------------------------------- // Constructor CForwardProjectionAlgorithm::CForwardProjectionAlgorithm(CProjector2D* _pProjector, CFloat32VolumeData2D* _pVolume, CFloat32ProjectionData2D* _pSinogram) { _clear(); initialize(_pProjector, _pVolume, _pSinogram); } //---------------------------------------------------------------------------------------- // Destructor CForwardProjectionAlgorithm::~CForwardProjectionAlgorithm() { delete m_pForwardProjector; clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CForwardProjectionAlgorithm::_clear() { m_pProjector = NULL; m_pSinogram = NULL; m_pVolume = NULL; m_pForwardProjector = NULL; m_bUseSinogramMask = false; m_bUseVolumeMask = false; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CForwardProjectionAlgorithm::clear() { m_pProjector = NULL; m_pSinogram = NULL; m_pVolume = NULL; m_bUseSinogramMask = false; m_bUseVolumeMask = false; m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Check bool CForwardProjectionAlgorithm::_check() { // check pointers ASTRA_CONFIG_CHECK(m_pProjector, "ForwardProjection", "Invalid Projector Object."); ASTRA_CONFIG_CHECK(m_pSinogram, "ForwardProjection", "Invalid Projection Data Object."); ASTRA_CONFIG_CHECK(m_pVolume, "ForwardProjection", "Invalid Volume Data Object."); // check initializations ASTRA_CONFIG_CHECK(m_pProjector->isInitialized(), "ForwardProjection", "Projector Object Not Initialized."); ASTRA_CONFIG_CHECK(m_pSinogram->isInitialized(), "ForwardProjection", "Projection Data Object Not Initialized."); ASTRA_CONFIG_CHECK(m_pVolume->isInitialized(), "ForwardProjection", "Volume Data Object Not Initialized."); // check compatibility between projector and data classes ASTRA_CONFIG_CHECK(m_pSinogram->getGeometry().isEqual(m_pProjector->getProjectionGeometry()), "ForwardProjection", "Projection Data not compatible with the specified Projector."); ASTRA_CONFIG_CHECK(m_pVolume->getGeometry().isEqual(m_pProjector->getVolumeGeometry()), "ForwardProjection", "Volume Data not compatible with the specified Projector."); ASTRA_CONFIG_CHECK(m_pForwardProjector, "ForwardProjection", "Invalid FP Policy"); // success return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CForwardProjectionAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("ForwardProjectionAlgorithm", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } bool ok = true; int id = -1; ok &= CR.getRequiredID("ProjectorId", id); m_pProjector = CProjector2DManager::getSingleton().get(id); if (!m_pProjector) { ASTRA_ERROR("ProjectorId is not a valid id"); return false; } ok &= CR.getRequiredID("ProjectionDataId", id); m_pSinogram = dynamic_cast(CData2DManager::getSingleton().get(id)); ok &= CR.getRequiredID("VolumeDataId", id); m_pVolume = dynamic_cast(CData2DManager::getSingleton().get(id)); if (CR.getOptionID("VolumeMaskId", id)) { m_bUseVolumeMask = true; m_pVolumeMask = dynamic_cast(CData2DManager::getSingleton().get(id)); } if (CR.getOptionID("SinogramMaskId", id)) { m_bUseSinogramMask = true; m_pSinogramMask = dynamic_cast(CData2DManager::getSingleton().get(id)); } // ray or voxel-driven projector? //m_bUseVoxelProjector = _cfg.self->getOptionBool("VoxelDriven", false); // init data projector _init(); // return success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Initialize bool CForwardProjectionAlgorithm::initialize(CProjector2D* _pProjector, CFloat32VolumeData2D* _pVolume, CFloat32ProjectionData2D* _pSinogram) { // store classes m_pProjector = _pProjector; m_pVolume = _pVolume; m_pSinogram = _pSinogram; // init data projector _init(); // return success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize Data Projectors - private void CForwardProjectionAlgorithm::_init() { // forward projection data projector m_pForwardProjector = dispatchDataProjector( m_pProjector, SinogramMaskPolicy(m_pSinogramMask), // sinogram mask ReconstructionMaskPolicy(m_pVolumeMask), // reconstruction mask DefaultFPPolicy(m_pVolume, m_pSinogram), // forward projection m_bUseSinogramMask, m_bUseVolumeMask, true // options on/off ); } //---------------------------------------------------------------------------------------- // Set Fixed Reconstruction Mask void CForwardProjectionAlgorithm::setVolumeMask(CFloat32VolumeData2D* _pMask, bool _bEnable) { // TODO: check geometry matches volume m_bUseVolumeMask = _bEnable; m_pVolumeMask = _pMask; if (m_pVolumeMask == NULL) { m_bUseVolumeMask = false; } } //---------------------------------------------------------------------------------------- // Set Fixed Sinogram Mask void CForwardProjectionAlgorithm::setSinogramMask(CFloat32ProjectionData2D* _pMask, bool _bEnable) { // TODO: check geometry matches sinogram m_bUseSinogramMask = _bEnable; m_pSinogramMask = _pMask; if (m_pSinogramMask == NULL) { m_bUseSinogramMask = false; } } //---------------------------------------------------------------------------------------- // Iterate bool CForwardProjectionAlgorithm::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); m_pSinogram->setData(0.0f); // if (m_bUseVoxelProjector) { // m_pForwardProjector->projectAllVoxels(); // } else { m_pForwardProjector->project(); // } return true; } //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/Fourier.cpp000066400000000000000000002545631475635207100172510ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Fourier.h" #include namespace astra { /* Copyright Takuya OOURA, 1996-2001 You may use, copy, modify and distribute this code for any purpose (include commercial use) and without fee. Source: http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html Fast Fourier/Cosine/Sine Transform dimension :one data length :power of 2 decimation :frequency radix :split-radix data :inplace table :use functions cdft: Complex Discrete Fourier Transform rdft: Real Discrete Fourier Transform ddct: Discrete Cosine Transform ddst: Discrete Sine Transform dfct: Cosine Transform of RDFT (Real Symmetric DFT) dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) function prototypes void cdft(int, int, float32 *, int *, float32 *); void rdft(int, int, float32 *, int *, float32 *); void ddct(int, int, float32 *, int *, float32 *); void ddst(int, int, float32 *, int *, float32 *); void dfct(int, float32 *, float32 *, int *, float32 *); void dfst(int, float32 *, float32 *, int *, float32 *); macro definitions USE_CDFT_PTHREADS : default=not defined CDFT_THREADS_BEGIN_N : must be >= 512, default=8192 CDFT_4THREADS_BEGIN_N : must be >= 512, default=65536 USE_CDFT_WINTHREADS : default=not defined CDFT_THREADS_BEGIN_N : must be >= 512, default=32768 CDFT_4THREADS_BEGIN_N : must be >= 512, default=524288 -------- Complex DFT (Discrete Fourier Transform) -------- [definition] X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k ip[0] = 0; // first time only cdft(2*n, 1, a, ip, w); ip[0] = 0; // first time only cdft(2*n, -1, a, ip, w); [parameters] 2*n :data length (int) n >= 1, n = power of 2 a[0...2*n-1] :input/output data (float32 *) input data a[2*j] = Re(x[j]), a[2*j+1] = Im(x[j]), 0<=j= 2+sqrt(n) strictly, length of ip >= 2+(1<<(int)(log(n+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n/2-1] :cos/sin table (float32 *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of cdft(2*n, -1, a, ip, w); is cdft(2*n, 1, a, ip, w); for (j = 0; j <= 2 * n - 1; j++) { a[j] *= 1.0 / n; } . -------- Real DFT / Inverse of Real DFT -------- [definition] RDFT R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0 IRDFT (excluding scale) a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k ip[0] = 0; // first time only rdft(n, 1, a, ip, w); ip[0] = 0; // first time only rdft(n, -1, a, ip, w); [parameters] n :data length (int) n >= 2, n = power of 2 a[0...n-1] :input/output data (float32 *) output data a[2*k] = R[k], 0<=k input data a[2*j] = R[j], 0<=j= 2+sqrt(n/2) strictly, length of ip >= 2+(1<<(int)(log(n/2+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n/2-1] :cos/sin table (float32 *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of rdft(n, 1, a, ip, w); is rdft(n, -1, a, ip, w); for (j = 0; j <= n - 1; j++) { a[j] *= 2.0 / n; } . -------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- [definition] IDCT (excluding scale) C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k DCT C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k ip[0] = 0; // first time only ddct(n, 1, a, ip, w); ip[0] = 0; // first time only ddct(n, -1, a, ip, w); [parameters] n :data length (int) n >= 2, n = power of 2 a[0...n-1] :input/output data (float32 *) output data a[k] = C[k], 0<=k= 2+sqrt(n/2) strictly, length of ip >= 2+(1<<(int)(log(n/2+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n*5/4-1] :cos/sin table (float32 *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of ddct(n, -1, a, ip, w); is a[0] *= 0.5; ddct(n, 1, a, ip, w); for (j = 0; j <= n - 1; j++) { a[j] *= 2.0 / n; } . -------- DST (Discrete Sine Transform) / Inverse of DST -------- [definition] IDST (excluding scale) S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k DST S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0 ip[0] = 0; // first time only ddst(n, 1, a, ip, w); ip[0] = 0; // first time only ddst(n, -1, a, ip, w); [parameters] n :data length (int) n >= 2, n = power of 2 a[0...n-1] :input/output data (float32 *) input data a[j] = A[j], 0 output data a[k] = S[k], 0= 2+sqrt(n/2) strictly, length of ip >= 2+(1<<(int)(log(n/2+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n*5/4-1] :cos/sin table (float32 *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of ddst(n, -1, a, ip, w); is a[0] *= 0.5; ddst(n, 1, a, ip, w); for (j = 0; j <= n - 1; j++) { a[j] *= 2.0 / n; } . -------- Cosine Transform of RDFT (Real Symmetric DFT) -------- [definition] C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n [usage] ip[0] = 0; // first time only dfct(n, a, t, ip, w); [parameters] n :data length - 1 (int) n >= 2, n = power of 2 a[0...n] :input/output data (float32 *) output data a[k] = C[k], 0<=k<=n t[0...n/2] :work area (float32 *) ip[0...*] :work area for bit reversal (int *) length of ip >= 2+sqrt(n/4) strictly, length of ip >= 2+(1<<(int)(log(n/4+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n*5/8-1] :cos/sin table (float32 *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of a[0] *= 0.5; a[n] *= 0.5; dfct(n, a, t, ip, w); is a[0] *= 0.5; a[n] *= 0.5; dfct(n, a, t, ip, w); for (j = 0; j <= n; j++) { a[j] *= 2.0 / n; } . -------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- [definition] S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0= 2, n = power of 2 a[0...n-1] :input/output data (float32 *) output data a[k] = S[k], 0= 2+sqrt(n/4) strictly, length of ip >= 2+(1<<(int)(log(n/4+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n*5/8-1] :cos/sin table (float32 *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of dfst(n, a, t, ip, w); is dfst(n, a, t, ip, w); for (j = 1; j <= n - 1; j++) { a[j] *= 2.0 / n; } . Appendix : The cos/sin table is recalculated when the larger table required. w[] and ip[] are compatible with all routines. */ static int cfttree(int n, int j, int k, float32 *a, int nw, float32 *w); static void bitrv208(float32 *a); static void bitrv208neg(float32 *a); static void bitrv216(float32 *a); static void bitrv216neg(float32 *a); static void bitrv2conj(int n, int *ip, float32 *a); static void bitrv2(int n, int *ip, float32 *a); static void cftb040(float32 *a); static void cftb1st(int n, float32 *a, float32 *w); static void cftbsub(int n, float32 *a, int *ip, int nw, float32 *w); static void cftf040(float32 *a); static void cftf081(float32 *a, float32 *w); static void cftf082(float32 *a, float32 *w); static void cftf161(float32 *a, float32 *w); static void cftf162(float32 *a, float32 *w); static void cftf1st(int n, float32 *a, float32 *w); static void cftfsub(int n, float32 *a, int *ip, int nw, float32 *w); static void cftfx41(int n, float32 *a, int nw, float32 *w); static void cftleaf(int n, int isplt, float32 *a, int nw, float32 *w); static void cftmdl1(int n, float32 *a, float32 *w); static void cftmdl2(int n, float32 *a, float32 *w); #ifdef USE_CDFT_THREADS static void *cftrec1_th(void *p); static void *cftrec2_th(void *p); #endif static void cftrec4(int n, float32 *a, int nw, float32 *w); static void cftx020(float32 *a); static void dctsub(int n, float32 *a, int nc, float32 *c); static void dstsub(int n, float32 *a, int nc, float32 *c); static void makect(int nc, int *ip, float32 *c); static void makeipt(int nw, int *ip); static void makewt(int nw, int *ip, float32 *w); static void rftbsub(int n, float32 *a, int nc, float32 *c); static void rftfsub(int n, float32 *a, int nc, float32 *c); #ifdef USE_CDFT_THREADS static void cftrec4_th(int n, float32 *a, int nw, float32 *w); #endif /* USE_CDFT_THREADS */ _AstraExport void cdft(int n, int isgn, float32 *a, int *ip, float32 *w) { int nw; nw = ip[0]; if (n > (nw << 2)) { nw = n >> 2; makewt(nw, ip, w); } if (isgn >= 0) { cftfsub(n, a, ip, nw, w); } else { cftbsub(n, a, ip, nw, w); } } _AstraExport void rdft(int n, int isgn, float32 *a, int *ip, float32 *w) { int nw, nc; float32 xi; nw = ip[0]; if (n > (nw << 2)) { nw = n >> 2; makewt(nw, ip, w); } nc = ip[1]; if (n > (nc << 2)) { nc = n >> 2; makect(nc, ip, w + nw); } if (isgn >= 0) { if (n > 4) { cftfsub(n, a, ip, nw, w); rftfsub(n, a, nc, w + nw); } else if (n == 4) { cftfsub(n, a, ip, nw, w); } xi = a[0] - a[1]; a[0] += a[1]; a[1] = xi; } else { a[1] = 0.5 * (a[0] - a[1]); a[0] -= a[1]; if (n > 4) { rftbsub(n, a, nc, w + nw); cftbsub(n, a, ip, nw, w); } else if (n == 4) { cftbsub(n, a, ip, nw, w); } } } _AstraExport void ddct(int n, int isgn, float32 *a, int *ip, float32 *w) { int j, nw, nc; float32 xr; nw = ip[0]; if (n > (nw << 2)) { nw = n >> 2; makewt(nw, ip, w); } nc = ip[1]; if (n > nc) { nc = n; makect(nc, ip, w + nw); } if (isgn < 0) { xr = a[n - 1]; for (j = n - 2; j >= 2; j -= 2) { a[j + 1] = a[j] - a[j - 1]; a[j] += a[j - 1]; } a[1] = a[0] - xr; a[0] += xr; if (n > 4) { rftbsub(n, a, nc, w + nw); cftbsub(n, a, ip, nw, w); } else if (n == 4) { cftbsub(n, a, ip, nw, w); } } dctsub(n, a, nc, w + nw); if (isgn >= 0) { if (n > 4) { cftfsub(n, a, ip, nw, w); rftfsub(n, a, nc, w + nw); } else if (n == 4) { cftfsub(n, a, ip, nw, w); } xr = a[0] - a[1]; a[0] += a[1]; for (j = 2; j < n; j += 2) { a[j - 1] = a[j] - a[j + 1]; a[j] += a[j + 1]; } a[n - 1] = xr; } } _AstraExport void ddst(int n, int isgn, float32 *a, int *ip, float32 *w) { int j, nw, nc; float32 xr; nw = ip[0]; if (n > (nw << 2)) { nw = n >> 2; makewt(nw, ip, w); } nc = ip[1]; if (n > nc) { nc = n; makect(nc, ip, w + nw); } if (isgn < 0) { xr = a[n - 1]; for (j = n - 2; j >= 2; j -= 2) { a[j + 1] = -a[j] - a[j - 1]; a[j] -= a[j - 1]; } a[1] = a[0] + xr; a[0] -= xr; if (n > 4) { rftbsub(n, a, nc, w + nw); cftbsub(n, a, ip, nw, w); } else if (n == 4) { cftbsub(n, a, ip, nw, w); } } dstsub(n, a, nc, w + nw); if (isgn >= 0) { if (n > 4) { cftfsub(n, a, ip, nw, w); rftfsub(n, a, nc, w + nw); } else if (n == 4) { cftfsub(n, a, ip, nw, w); } xr = a[0] - a[1]; a[0] += a[1]; for (j = 2; j < n; j += 2) { a[j - 1] = -a[j] - a[j + 1]; a[j] -= a[j + 1]; } a[n - 1] = -xr; } } _AstraExport void dfct(int n, float32 *a, float32 *t, int *ip, float32 *w) { int j, k, l, m, mh, nw, nc; float32 xr, xi, yr, yi; nw = ip[0]; if (n > (nw << 3)) { nw = n >> 3; makewt(nw, ip, w); } nc = ip[1]; if (n > (nc << 1)) { nc = n >> 1; makect(nc, ip, w + nw); } m = n >> 1; yi = a[m]; xi = a[0] + a[n]; a[0] -= a[n]; t[0] = xi - yi; t[m] = xi + yi; if (n > 2) { mh = m >> 1; for (j = 1; j < mh; j++) { k = m - j; xr = a[j] - a[n - j]; xi = a[j] + a[n - j]; yr = a[k] - a[n - k]; yi = a[k] + a[n - k]; a[j] = xr; a[k] = yr; t[j] = xi - yi; t[k] = xi + yi; } t[mh] = a[mh] + a[n - mh]; a[mh] -= a[n - mh]; dctsub(m, a, nc, w + nw); if (m > 4) { cftfsub(m, a, ip, nw, w); rftfsub(m, a, nc, w + nw); } else if (m == 4) { cftfsub(m, a, ip, nw, w); } a[n - 1] = a[0] - a[1]; a[1] = a[0] + a[1]; for (j = m - 2; j >= 2; j -= 2) { a[2 * j + 1] = a[j] + a[j + 1]; a[2 * j - 1] = a[j] - a[j + 1]; } l = 2; m = mh; while (m >= 2) { dctsub(m, t, nc, w + nw); if (m > 4) { cftfsub(m, t, ip, nw, w); rftfsub(m, t, nc, w + nw); } else if (m == 4) { cftfsub(m, t, ip, nw, w); } a[n - l] = t[0] - t[1]; a[l] = t[0] + t[1]; k = 0; for (j = 2; j < m; j += 2) { k += l << 2; a[k - l] = t[j] - t[j + 1]; a[k + l] = t[j] + t[j + 1]; } l <<= 1; mh = m >> 1; for (j = 0; j < mh; j++) { k = m - j; t[j] = t[m + k] - t[m + j]; t[k] = t[m + k] + t[m + j]; } t[mh] = t[m + mh]; m = mh; } a[l] = t[0]; a[n] = t[2] - t[1]; a[0] = t[2] + t[1]; } else { a[1] = a[0]; a[2] = t[0]; a[0] = t[1]; } } _AstraExport void dfst(int n, float32 *a, float32 *t, int *ip, float32 *w) { int j, k, l, m, mh, nw, nc; float32 xr, xi, yr, yi; nw = ip[0]; if (n > (nw << 3)) { nw = n >> 3; makewt(nw, ip, w); } nc = ip[1]; if (n > (nc << 1)) { nc = n >> 1; makect(nc, ip, w + nw); } if (n > 2) { m = n >> 1; mh = m >> 1; for (j = 1; j < mh; j++) { k = m - j; xr = a[j] + a[n - j]; xi = a[j] - a[n - j]; yr = a[k] + a[n - k]; yi = a[k] - a[n - k]; a[j] = xr; a[k] = yr; t[j] = xi + yi; t[k] = xi - yi; } t[0] = a[mh] - a[n - mh]; a[mh] += a[n - mh]; a[0] = a[m]; dstsub(m, a, nc, w + nw); if (m > 4) { cftfsub(m, a, ip, nw, w); rftfsub(m, a, nc, w + nw); } else if (m == 4) { cftfsub(m, a, ip, nw, w); } a[n - 1] = a[1] - a[0]; a[1] = a[0] + a[1]; for (j = m - 2; j >= 2; j -= 2) { a[2 * j + 1] = a[j] - a[j + 1]; a[2 * j - 1] = -a[j] - a[j + 1]; } l = 2; m = mh; while (m >= 2) { dstsub(m, t, nc, w + nw); if (m > 4) { cftfsub(m, t, ip, nw, w); rftfsub(m, t, nc, w + nw); } else if (m == 4) { cftfsub(m, t, ip, nw, w); } a[n - l] = t[1] - t[0]; a[l] = t[0] + t[1]; k = 0; for (j = 2; j < m; j += 2) { k += l << 2; a[k - l] = -t[j] - t[j + 1]; a[k + l] = t[j] - t[j + 1]; } l <<= 1; mh = m >> 1; for (j = 1; j < mh; j++) { k = m - j; t[j] = t[m + k] + t[m + j]; t[k] = t[m + k] - t[m + j]; } t[0] = t[m + mh]; m = mh; } a[l] = t[0]; } a[0] = 0; } /* -------- initializing routines -------- */ static void makewt(int nw, int *ip, float32 *w) { int j, nwh, nw0, nw1; float32 delta, wn4r, wk1r, wk1i, wk3r, wk3i; ip[0] = nw; ip[1] = 1; if (nw > 2) { nwh = nw >> 1; delta = atan(1.0) / nwh; wn4r = cos(delta * nwh); w[0] = 1; w[1] = wn4r; if (nwh == 4) { w[2] = cos(delta * 2); w[3] = sin(delta * 2); } else if (nwh > 4) { makeipt(nw, ip); w[2] = 0.5 / cos(delta * 2); w[3] = 0.5 / cos(delta * 6); for (j = 4; j < nwh; j += 4) { w[j] = cos(delta * j); w[j + 1] = sin(delta * j); w[j + 2] = cos(3 * delta * j); w[j + 3] = -sin(3 * delta * j); } } nw0 = 0; while (nwh > 2) { nw1 = nw0 + nwh; nwh >>= 1; w[nw1] = 1; w[nw1 + 1] = wn4r; if (nwh == 4) { wk1r = w[nw0 + 4]; wk1i = w[nw0 + 5]; w[nw1 + 2] = wk1r; w[nw1 + 3] = wk1i; } else if (nwh > 4) { wk1r = w[nw0 + 4]; wk3r = w[nw0 + 6]; w[nw1 + 2] = 0.5 / wk1r; w[nw1 + 3] = 0.5 / wk3r; for (j = 4; j < nwh; j += 4) { wk1r = w[nw0 + 2 * j]; wk1i = w[nw0 + 2 * j + 1]; wk3r = w[nw0 + 2 * j + 2]; wk3i = w[nw0 + 2 * j + 3]; w[nw1 + j] = wk1r; w[nw1 + j + 1] = wk1i; w[nw1 + j + 2] = wk3r; w[nw1 + j + 3] = wk3i; } } nw0 = nw1; } } } static void makeipt(int nw, int *ip) { int j, l, m, m2, p, q; ip[2] = 0; ip[3] = 16; m = 2; for (l = nw; l > 32; l >>= 2) { m2 = m << 1; q = m2 << 3; for (j = m; j < m2; j++) { p = ip[j] << 2; ip[m + j] = p; ip[m2 + j] = p + q; } m = m2; } } static void makect(int nc, int *ip, float32 *c) { int j, nch; float32 delta; ip[1] = nc; if (nc > 1) { nch = nc >> 1; delta = atan(1.0) / nch; c[0] = cos(delta * nch); c[nch] = 0.5 * c[0]; for (j = 1; j < nch; j++) { c[j] = 0.5 * cos(delta * j); c[nc - j] = 0.5 * sin(delta * j); } } } /* -------- child routines -------- */ #ifdef USE_CDFT_PTHREADS #define USE_CDFT_THREADS #ifndef CDFT_THREADS_BEGIN_N #define CDFT_THREADS_BEGIN_N 8192 #endif #ifndef CDFT_4THREADS_BEGIN_N #define CDFT_4THREADS_BEGIN_N 65536 #endif #include #include #include #define cdft_thread_t pthread_t #define cdft_thread_create(thp,func,argp) { \ if (pthread_create(thp, NULL, func, (void *) argp) != 0) { \ fprintf(stderr, "cdft thread error\n"); \ exit(1); \ } \ } #define cdft_thread_wait(th) { \ if (pthread_join(th, NULL) != 0) { \ fprintf(stderr, "cdft thread error\n"); \ exit(1); \ } \ } #endif /* USE_CDFT_PTHREADS */ #ifdef USE_CDFT_WINTHREADS #define USE_CDFT_THREADS #ifndef CDFT_THREADS_BEGIN_N #define CDFT_THREADS_BEGIN_N 32768 #endif #ifndef CDFT_4THREADS_BEGIN_N #define CDFT_4THREADS_BEGIN_N 524288 #endif #include #include #include #define cdft_thread_t HANDLE #define cdft_thread_create(thp,func,argp) { \ DWORD thid; \ *(thp) = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func, (LPVOID) argp, 0, &thid); \ if (*(thp) == 0) { \ fprintf(stderr, "cdft thread error\n"); \ exit(1); \ } \ } #define cdft_thread_wait(th) { \ WaitForSingleObject(th, INFINITE); \ CloseHandle(th); \ } #endif /* USE_CDFT_WINTHREADS */ static void cftfsub(int n, float32 *a, int *ip, int nw, float32 *w) { if (n > 8) { if (n > 32) { cftf1st(n, a, &w[nw - (n >> 2)]); #ifdef USE_CDFT_THREADS if (n > CDFT_THREADS_BEGIN_N) { cftrec4_th(n, a, nw, w); } else #endif /* USE_CDFT_THREADS */ if (n > 512) { cftrec4(n, a, nw, w); } else if (n > 128) { cftleaf(n, 1, a, nw, w); } else { cftfx41(n, a, nw, w); } bitrv2(n, ip, a); } else if (n == 32) { cftf161(a, &w[nw - 8]); bitrv216(a); } else { cftf081(a, w); bitrv208(a); } } else if (n == 8) { cftf040(a); } else if (n == 4) { cftx020(a); } } static void cftbsub(int n, float32 *a, int *ip, int nw, float32 *w) { if (n > 8) { if (n > 32) { cftb1st(n, a, &w[nw - (n >> 2)]); #ifdef USE_CDFT_THREADS if (n > CDFT_THREADS_BEGIN_N) { cftrec4_th(n, a, nw, w); } else #endif /* USE_CDFT_THREADS */ if (n > 512) { cftrec4(n, a, nw, w); } else if (n > 128) { cftleaf(n, 1, a, nw, w); } else { cftfx41(n, a, nw, w); } bitrv2conj(n, ip, a); } else if (n == 32) { cftf161(a, &w[nw - 8]); bitrv216neg(a); } else { cftf081(a, w); bitrv208neg(a); } } else if (n == 8) { cftb040(a); } else if (n == 4) { cftx020(a); } } static void bitrv2(int n, int *ip, float32 *a) { int j, j1, k, k1, l, m, nh, nm; float32 xr, xi, yr, yi; m = 1; for (l = n >> 2; l > 8; l >>= 2) { m <<= 1; } nh = n >> 1; nm = 4 * m; if (l == 8) { for (k = 0; k < m; k++) { for (j = 0; j < k; j++) { j1 = 4 * j + 2 * ip[m + k]; k1 = 4 * k + 2 * ip[m + j]; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 -= nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nh; k1 += 2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 += nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += 2; k1 += nh; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 -= nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nh; k1 -= 2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 += nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } k1 = 4 * k + 2 * ip[m + k]; j1 = k1 + 2; k1 += nh; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 -= nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= 2; k1 -= nh; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nh + 2; k1 += nh + 2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nh - nm; k1 += 2 * nm - 2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } } else { for (k = 0; k < m; k++) { for (j = 0; j < k; j++) { j1 = 4 * j + ip[m + k]; k1 = 4 * k + ip[m + j]; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nh; k1 += 2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += 2; k1 += nh; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nh; k1 -= 2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } k1 = 4 * k + ip[m + k]; j1 = k1 + 2; k1 += nh; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } } } static void bitrv2conj(int n, int *ip, float32 *a) { int j, j1, k, k1, l, m, nh, nm; float32 xr, xi, yr, yi; m = 1; for (l = n >> 2; l > 8; l >>= 2) { m <<= 1; } nh = n >> 1; nm = 4 * m; if (l == 8) { for (k = 0; k < m; k++) { for (j = 0; j < k; j++) { j1 = 4 * j + 2 * ip[m + k]; k1 = 4 * k + 2 * ip[m + j]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 -= nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nh; k1 += 2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 += nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += 2; k1 += nh; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 -= nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nh; k1 -= 2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 += nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } k1 = 4 * k + 2 * ip[m + k]; j1 = k1 + 2; k1 += nh; a[j1 - 1] = -a[j1 - 1]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; a[k1 + 3] = -a[k1 + 3]; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 -= nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= 2; k1 -= nh; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nh + 2; k1 += nh + 2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nh - nm; k1 += 2 * nm - 2; a[j1 - 1] = -a[j1 - 1]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; a[k1 + 3] = -a[k1 + 3]; } } else { for (k = 0; k < m; k++) { for (j = 0; j < k; j++) { j1 = 4 * j + ip[m + k]; k1 = 4 * k + ip[m + j]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nh; k1 += 2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += 2; k1 += nh; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nh; k1 -= 2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } k1 = 4 * k + ip[m + k]; j1 = k1 + 2; k1 += nh; a[j1 - 1] = -a[j1 - 1]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; a[k1 + 3] = -a[k1 + 3]; j1 += nm; k1 += nm; a[j1 - 1] = -a[j1 - 1]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; a[k1 + 3] = -a[k1 + 3]; } } } static void bitrv216(float32 *a) { float32 x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i, x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i; x1r = a[2]; x1i = a[3]; x2r = a[4]; x2i = a[5]; x3r = a[6]; x3i = a[7]; x4r = a[8]; x4i = a[9]; x5r = a[10]; x5i = a[11]; x7r = a[14]; x7i = a[15]; x8r = a[16]; x8i = a[17]; x10r = a[20]; x10i = a[21]; x11r = a[22]; x11i = a[23]; x12r = a[24]; x12i = a[25]; x13r = a[26]; x13i = a[27]; x14r = a[28]; x14i = a[29]; a[2] = x8r; a[3] = x8i; a[4] = x4r; a[5] = x4i; a[6] = x12r; a[7] = x12i; a[8] = x2r; a[9] = x2i; a[10] = x10r; a[11] = x10i; a[14] = x14r; a[15] = x14i; a[16] = x1r; a[17] = x1i; a[20] = x5r; a[21] = x5i; a[22] = x13r; a[23] = x13i; a[24] = x3r; a[25] = x3i; a[26] = x11r; a[27] = x11i; a[28] = x7r; a[29] = x7i; } static void bitrv216neg(float32 *a) { float32 x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i, x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i, x15r, x15i; x1r = a[2]; x1i = a[3]; x2r = a[4]; x2i = a[5]; x3r = a[6]; x3i = a[7]; x4r = a[8]; x4i = a[9]; x5r = a[10]; x5i = a[11]; x6r = a[12]; x6i = a[13]; x7r = a[14]; x7i = a[15]; x8r = a[16]; x8i = a[17]; x9r = a[18]; x9i = a[19]; x10r = a[20]; x10i = a[21]; x11r = a[22]; x11i = a[23]; x12r = a[24]; x12i = a[25]; x13r = a[26]; x13i = a[27]; x14r = a[28]; x14i = a[29]; x15r = a[30]; x15i = a[31]; a[2] = x15r; a[3] = x15i; a[4] = x7r; a[5] = x7i; a[6] = x11r; a[7] = x11i; a[8] = x3r; a[9] = x3i; a[10] = x13r; a[11] = x13i; a[12] = x5r; a[13] = x5i; a[14] = x9r; a[15] = x9i; a[16] = x1r; a[17] = x1i; a[18] = x14r; a[19] = x14i; a[20] = x6r; a[21] = x6i; a[22] = x10r; a[23] = x10i; a[24] = x2r; a[25] = x2i; a[26] = x12r; a[27] = x12i; a[28] = x4r; a[29] = x4i; a[30] = x8r; a[31] = x8i; } static void bitrv208(float32 *a) { float32 x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i; x1r = a[2]; x1i = a[3]; x3r = a[6]; x3i = a[7]; x4r = a[8]; x4i = a[9]; x6r = a[12]; x6i = a[13]; a[2] = x4r; a[3] = x4i; a[6] = x6r; a[7] = x6i; a[8] = x1r; a[9] = x1i; a[12] = x3r; a[13] = x3i; } static void bitrv208neg(float32 *a) { float32 x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x6r, x6i, x7r, x7i; x1r = a[2]; x1i = a[3]; x2r = a[4]; x2i = a[5]; x3r = a[6]; x3i = a[7]; x4r = a[8]; x4i = a[9]; x5r = a[10]; x5i = a[11]; x6r = a[12]; x6i = a[13]; x7r = a[14]; x7i = a[15]; a[2] = x7r; a[3] = x7i; a[4] = x3r; a[5] = x3i; a[6] = x5r; a[7] = x5i; a[8] = x1r; a[9] = x1i; a[10] = x6r; a[11] = x6i; a[12] = x2r; a[13] = x2i; a[14] = x4r; a[15] = x4i; } static void cftf1st(int n, float32 *a, float32 *w) { int j, j0, j1, j2, j3, k, m, mh; float32 wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i; float32 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i; mh = n >> 3; m = 2 * mh; j1 = m; j2 = j1 + m; j3 = j2 + m; x0r = a[0] + a[j2]; x0i = a[1] + a[j2 + 1]; x1r = a[0] - a[j2]; x1i = a[1] - a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[0] = x0r + x2r; a[1] = x0i + x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; a[j2] = x1r - x3i; a[j2 + 1] = x1i + x3r; a[j3] = x1r + x3i; a[j3 + 1] = x1i - x3r; wn4r = w[1]; csc1 = w[2]; csc3 = w[3]; wd1r = 1; wd1i = 0; wd3r = 1; wd3i = 0; k = 0; for (j = 2; j < mh - 2; j += 4) { k += 4; wk1r = csc1 * (wd1r + w[k]); wk1i = csc1 * (wd1i + w[k + 1]); wk3r = csc3 * (wd3r + w[k + 2]); wk3i = csc3 * (wd3i + w[k + 3]); wd1r = w[k]; wd1i = w[k + 1]; wd3r = w[k + 2]; wd3i = w[k + 3]; j1 = j + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j] + a[j2]; x0i = a[j + 1] + a[j2 + 1]; x1r = a[j] - a[j2]; x1i = a[j + 1] - a[j2 + 1]; y0r = a[j + 2] + a[j2 + 2]; y0i = a[j + 3] + a[j2 + 3]; y1r = a[j + 2] - a[j2 + 2]; y1i = a[j + 3] - a[j2 + 3]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; y2r = a[j1 + 2] + a[j3 + 2]; y2i = a[j1 + 3] + a[j3 + 3]; y3r = a[j1 + 2] - a[j3 + 2]; y3i = a[j1 + 3] - a[j3 + 3]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; a[j + 2] = y0r + y2r; a[j + 3] = y0i + y2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; a[j1 + 2] = y0r - y2r; a[j1 + 3] = y0i - y2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2] = wk1r * x0r - wk1i * x0i; a[j2 + 1] = wk1r * x0i + wk1i * x0r; x0r = y1r - y3i; x0i = y1i + y3r; a[j2 + 2] = wd1r * x0r - wd1i * x0i; a[j2 + 3] = wd1r * x0i + wd1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = wk3r * x0r + wk3i * x0i; a[j3 + 1] = wk3r * x0i - wk3i * x0r; x0r = y1r + y3i; x0i = y1i - y3r; a[j3 + 2] = wd3r * x0r + wd3i * x0i; a[j3 + 3] = wd3r * x0i - wd3i * x0r; j0 = m - j; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0] + a[j2]; x0i = a[j0 + 1] + a[j2 + 1]; x1r = a[j0] - a[j2]; x1i = a[j0 + 1] - a[j2 + 1]; y0r = a[j0 - 2] + a[j2 - 2]; y0i = a[j0 - 1] + a[j2 - 1]; y1r = a[j0 - 2] - a[j2 - 2]; y1i = a[j0 - 1] - a[j2 - 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; y2r = a[j1 - 2] + a[j3 - 2]; y2i = a[j1 - 1] + a[j3 - 1]; y3r = a[j1 - 2] - a[j3 - 2]; y3i = a[j1 - 1] - a[j3 - 1]; a[j0] = x0r + x2r; a[j0 + 1] = x0i + x2i; a[j0 - 2] = y0r + y2r; a[j0 - 1] = y0i + y2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; a[j1 - 2] = y0r - y2r; a[j1 - 1] = y0i - y2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2] = wk1i * x0r - wk1r * x0i; a[j2 + 1] = wk1i * x0i + wk1r * x0r; x0r = y1r - y3i; x0i = y1i + y3r; a[j2 - 2] = wd1i * x0r - wd1r * x0i; a[j2 - 1] = wd1i * x0i + wd1r * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = wk3i * x0r + wk3r * x0i; a[j3 + 1] = wk3i * x0i - wk3r * x0r; x0r = y1r + y3i; x0i = y1i - y3r; a[j3 - 2] = wd3i * x0r + wd3r * x0i; a[j3 - 1] = wd3i * x0i - wd3r * x0r; } wk1r = csc1 * (wd1r + wn4r); wk1i = csc1 * (wd1i + wn4r); wk3r = csc3 * (wd3r - wn4r); wk3i = csc3 * (wd3i - wn4r); j0 = mh; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0 - 2] + a[j2 - 2]; x0i = a[j0 - 1] + a[j2 - 1]; x1r = a[j0 - 2] - a[j2 - 2]; x1i = a[j0 - 1] - a[j2 - 1]; x2r = a[j1 - 2] + a[j3 - 2]; x2i = a[j1 - 1] + a[j3 - 1]; x3r = a[j1 - 2] - a[j3 - 2]; x3i = a[j1 - 1] - a[j3 - 1]; a[j0 - 2] = x0r + x2r; a[j0 - 1] = x0i + x2i; a[j1 - 2] = x0r - x2r; a[j1 - 1] = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2 - 2] = wk1r * x0r - wk1i * x0i; a[j2 - 1] = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3 - 2] = wk3r * x0r + wk3i * x0i; a[j3 - 1] = wk3r * x0i - wk3i * x0r; x0r = a[j0] + a[j2]; x0i = a[j0 + 1] + a[j2 + 1]; x1r = a[j0] - a[j2]; x1i = a[j0 + 1] - a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[j0] = x0r + x2r; a[j0 + 1] = x0i + x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2] = wn4r * (x0r - x0i); a[j2 + 1] = wn4r * (x0i + x0r); x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = -wn4r * (x0r + x0i); a[j3 + 1] = -wn4r * (x0i - x0r); x0r = a[j0 + 2] + a[j2 + 2]; x0i = a[j0 + 3] + a[j2 + 3]; x1r = a[j0 + 2] - a[j2 + 2]; x1i = a[j0 + 3] - a[j2 + 3]; x2r = a[j1 + 2] + a[j3 + 2]; x2i = a[j1 + 3] + a[j3 + 3]; x3r = a[j1 + 2] - a[j3 + 2]; x3i = a[j1 + 3] - a[j3 + 3]; a[j0 + 2] = x0r + x2r; a[j0 + 3] = x0i + x2i; a[j1 + 2] = x0r - x2r; a[j1 + 3] = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2 + 2] = wk1i * x0r - wk1r * x0i; a[j2 + 3] = wk1i * x0i + wk1r * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3 + 2] = wk3i * x0r + wk3r * x0i; a[j3 + 3] = wk3i * x0i - wk3r * x0r; } static void cftb1st(int n, float32 *a, float32 *w) { int j, j0, j1, j2, j3, k, m, mh; float32 wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i; float32 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i; mh = n >> 3; m = 2 * mh; j1 = m; j2 = j1 + m; j3 = j2 + m; x0r = a[0] + a[j2]; x0i = -a[1] - a[j2 + 1]; x1r = a[0] - a[j2]; x1i = -a[1] + a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[0] = x0r + x2r; a[1] = x0i - x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i + x2i; a[j2] = x1r + x3i; a[j2 + 1] = x1i + x3r; a[j3] = x1r - x3i; a[j3 + 1] = x1i - x3r; wn4r = w[1]; csc1 = w[2]; csc3 = w[3]; wd1r = 1; wd1i = 0; wd3r = 1; wd3i = 0; k = 0; for (j = 2; j < mh - 2; j += 4) { k += 4; wk1r = csc1 * (wd1r + w[k]); wk1i = csc1 * (wd1i + w[k + 1]); wk3r = csc3 * (wd3r + w[k + 2]); wk3i = csc3 * (wd3i + w[k + 3]); wd1r = w[k]; wd1i = w[k + 1]; wd3r = w[k + 2]; wd3i = w[k + 3]; j1 = j + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j] + a[j2]; x0i = -a[j + 1] - a[j2 + 1]; x1r = a[j] - a[j2]; x1i = -a[j + 1] + a[j2 + 1]; y0r = a[j + 2] + a[j2 + 2]; y0i = -a[j + 3] - a[j2 + 3]; y1r = a[j + 2] - a[j2 + 2]; y1i = -a[j + 3] + a[j2 + 3]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; y2r = a[j1 + 2] + a[j3 + 2]; y2i = a[j1 + 3] + a[j3 + 3]; y3r = a[j1 + 2] - a[j3 + 2]; y3i = a[j1 + 3] - a[j3 + 3]; a[j] = x0r + x2r; a[j + 1] = x0i - x2i; a[j + 2] = y0r + y2r; a[j + 3] = y0i - y2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i + x2i; a[j1 + 2] = y0r - y2r; a[j1 + 3] = y0i + y2i; x0r = x1r + x3i; x0i = x1i + x3r; a[j2] = wk1r * x0r - wk1i * x0i; a[j2 + 1] = wk1r * x0i + wk1i * x0r; x0r = y1r + y3i; x0i = y1i + y3r; a[j2 + 2] = wd1r * x0r - wd1i * x0i; a[j2 + 3] = wd1r * x0i + wd1i * x0r; x0r = x1r - x3i; x0i = x1i - x3r; a[j3] = wk3r * x0r + wk3i * x0i; a[j3 + 1] = wk3r * x0i - wk3i * x0r; x0r = y1r - y3i; x0i = y1i - y3r; a[j3 + 2] = wd3r * x0r + wd3i * x0i; a[j3 + 3] = wd3r * x0i - wd3i * x0r; j0 = m - j; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0] + a[j2]; x0i = -a[j0 + 1] - a[j2 + 1]; x1r = a[j0] - a[j2]; x1i = -a[j0 + 1] + a[j2 + 1]; y0r = a[j0 - 2] + a[j2 - 2]; y0i = -a[j0 - 1] - a[j2 - 1]; y1r = a[j0 - 2] - a[j2 - 2]; y1i = -a[j0 - 1] + a[j2 - 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; y2r = a[j1 - 2] + a[j3 - 2]; y2i = a[j1 - 1] + a[j3 - 1]; y3r = a[j1 - 2] - a[j3 - 2]; y3i = a[j1 - 1] - a[j3 - 1]; a[j0] = x0r + x2r; a[j0 + 1] = x0i - x2i; a[j0 - 2] = y0r + y2r; a[j0 - 1] = y0i - y2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i + x2i; a[j1 - 2] = y0r - y2r; a[j1 - 1] = y0i + y2i; x0r = x1r + x3i; x0i = x1i + x3r; a[j2] = wk1i * x0r - wk1r * x0i; a[j2 + 1] = wk1i * x0i + wk1r * x0r; x0r = y1r + y3i; x0i = y1i + y3r; a[j2 - 2] = wd1i * x0r - wd1r * x0i; a[j2 - 1] = wd1i * x0i + wd1r * x0r; x0r = x1r - x3i; x0i = x1i - x3r; a[j3] = wk3i * x0r + wk3r * x0i; a[j3 + 1] = wk3i * x0i - wk3r * x0r; x0r = y1r - y3i; x0i = y1i - y3r; a[j3 - 2] = wd3i * x0r + wd3r * x0i; a[j3 - 1] = wd3i * x0i - wd3r * x0r; } wk1r = csc1 * (wd1r + wn4r); wk1i = csc1 * (wd1i + wn4r); wk3r = csc3 * (wd3r - wn4r); wk3i = csc3 * (wd3i - wn4r); j0 = mh; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0 - 2] + a[j2 - 2]; x0i = -a[j0 - 1] - a[j2 - 1]; x1r = a[j0 - 2] - a[j2 - 2]; x1i = -a[j0 - 1] + a[j2 - 1]; x2r = a[j1 - 2] + a[j3 - 2]; x2i = a[j1 - 1] + a[j3 - 1]; x3r = a[j1 - 2] - a[j3 - 2]; x3i = a[j1 - 1] - a[j3 - 1]; a[j0 - 2] = x0r + x2r; a[j0 - 1] = x0i - x2i; a[j1 - 2] = x0r - x2r; a[j1 - 1] = x0i + x2i; x0r = x1r + x3i; x0i = x1i + x3r; a[j2 - 2] = wk1r * x0r - wk1i * x0i; a[j2 - 1] = wk1r * x0i + wk1i * x0r; x0r = x1r - x3i; x0i = x1i - x3r; a[j3 - 2] = wk3r * x0r + wk3i * x0i; a[j3 - 1] = wk3r * x0i - wk3i * x0r; x0r = a[j0] + a[j2]; x0i = -a[j0 + 1] - a[j2 + 1]; x1r = a[j0] - a[j2]; x1i = -a[j0 + 1] + a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[j0] = x0r + x2r; a[j0 + 1] = x0i - x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i + x2i; x0r = x1r + x3i; x0i = x1i + x3r; a[j2] = wn4r * (x0r - x0i); a[j2 + 1] = wn4r * (x0i + x0r); x0r = x1r - x3i; x0i = x1i - x3r; a[j3] = -wn4r * (x0r + x0i); a[j3 + 1] = -wn4r * (x0i - x0r); x0r = a[j0 + 2] + a[j2 + 2]; x0i = -a[j0 + 3] - a[j2 + 3]; x1r = a[j0 + 2] - a[j2 + 2]; x1i = -a[j0 + 3] + a[j2 + 3]; x2r = a[j1 + 2] + a[j3 + 2]; x2i = a[j1 + 3] + a[j3 + 3]; x3r = a[j1 + 2] - a[j3 + 2]; x3i = a[j1 + 3] - a[j3 + 3]; a[j0 + 2] = x0r + x2r; a[j0 + 3] = x0i - x2i; a[j1 + 2] = x0r - x2r; a[j1 + 3] = x0i + x2i; x0r = x1r + x3i; x0i = x1i + x3r; a[j2 + 2] = wk1i * x0r - wk1r * x0i; a[j2 + 3] = wk1i * x0i + wk1r * x0r; x0r = x1r - x3i; x0i = x1i - x3r; a[j3 + 2] = wk3i * x0r + wk3r * x0i; a[j3 + 3] = wk3i * x0i - wk3r * x0r; } #ifdef USE_CDFT_THREADS struct cdft_arg_st { int n0; int n; float32 *a; int nw; float32 *w; }; typedef struct cdft_arg_st cdft_arg_t; static void cftrec4_th(int n, float32 *a, int nw, float32 *w) { int i, idiv4, m, nthread; cdft_thread_t th[4]; cdft_arg_t ag[4]; nthread = 2; idiv4 = 0; m = n >> 1; if (n > CDFT_4THREADS_BEGIN_N) { nthread = 4; idiv4 = 1; m >>= 1; } for (i = 0; i < nthread; i++) { ag[i].n0 = n; ag[i].n = m; ag[i].a = &a[i * m]; ag[i].nw = nw; ag[i].w = w; if (i != idiv4) { cdft_thread_create(&th[i], cftrec1_th, &ag[i]); } else { cdft_thread_create(&th[i], cftrec2_th, &ag[i]); } } for (i = 0; i < nthread; i++) { cdft_thread_wait(th[i]); } } static void *cftrec1_th(void *p) { int isplt, j, k, m, n, n0, nw; float32 *a, *w; n0 = ((cdft_arg_t *) p)->n0; n = ((cdft_arg_t *) p)->n; a = ((cdft_arg_t *) p)->a; nw = ((cdft_arg_t *) p)->nw; w = ((cdft_arg_t *) p)->w; m = n0; while (m > 512) { m >>= 2; cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]); } cftleaf(m, 1, &a[n - m], nw, w); k = 0; for (j = n - m; j > 0; j -= m) { k++; isplt = cfttree(m, j, k, a, nw, w); cftleaf(m, isplt, &a[j - m], nw, w); } return (void *) 0; } static void *cftrec2_th(void *p) { int isplt, j, k, m, n, n0, nw; float32 *a, *w; n0 = ((cdft_arg_t *) p)->n0; n = ((cdft_arg_t *) p)->n; a = ((cdft_arg_t *) p)->a; nw = ((cdft_arg_t *) p)->nw; w = ((cdft_arg_t *) p)->w; k = 1; m = n0; while (m > 512) { m >>= 2; k <<= 2; cftmdl2(m, &a[n - m], &w[nw - m]); } cftleaf(m, 0, &a[n - m], nw, w); k >>= 1; for (j = n - m; j > 0; j -= m) { k++; isplt = cfttree(m, j, k, a, nw, w); cftleaf(m, isplt, &a[j - m], nw, w); } return (void *) 0; } #endif /* USE_CDFT_THREADS */ static void cftrec4(int n, float32 *a, int nw, float32 *w) { int isplt, j, k, m; m = n; while (m > 512) { m >>= 2; cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]); } cftleaf(m, 1, &a[n - m], nw, w); k = 0; for (j = n - m; j > 0; j -= m) { k++; isplt = cfttree(m, j, k, a, nw, w); cftleaf(m, isplt, &a[j - m], nw, w); } } int cfttree(int n, int j, int k, float32 *a, int nw, float32 *w) { int i, isplt, m; if ((k & 3) != 0) { isplt = k & 1; if (isplt != 0) { cftmdl1(n, &a[j - n], &w[nw - (n >> 1)]); } else { cftmdl2(n, &a[j - n], &w[nw - n]); } } else { m = n; for (i = k; (i & 3) == 0; i >>= 2) { m <<= 2; } isplt = i & 1; if (isplt != 0) { while (m > 128) { cftmdl1(m, &a[j - m], &w[nw - (m >> 1)]); m >>= 2; } } else { while (m > 128) { cftmdl2(m, &a[j - m], &w[nw - m]); m >>= 2; } } } return isplt; } static void cftleaf(int n, int isplt, float32 *a, int nw, float32 *w) { if (n == 512) { cftmdl1(128, a, &w[nw - 64]); cftf161(a, &w[nw - 8]); cftf162(&a[32], &w[nw - 32]); cftf161(&a[64], &w[nw - 8]); cftf161(&a[96], &w[nw - 8]); cftmdl2(128, &a[128], &w[nw - 128]); cftf161(&a[128], &w[nw - 8]); cftf162(&a[160], &w[nw - 32]); cftf161(&a[192], &w[nw - 8]); cftf162(&a[224], &w[nw - 32]); cftmdl1(128, &a[256], &w[nw - 64]); cftf161(&a[256], &w[nw - 8]); cftf162(&a[288], &w[nw - 32]); cftf161(&a[320], &w[nw - 8]); cftf161(&a[352], &w[nw - 8]); if (isplt != 0) { cftmdl1(128, &a[384], &w[nw - 64]); cftf161(&a[480], &w[nw - 8]); } else { cftmdl2(128, &a[384], &w[nw - 128]); cftf162(&a[480], &w[nw - 32]); } cftf161(&a[384], &w[nw - 8]); cftf162(&a[416], &w[nw - 32]); cftf161(&a[448], &w[nw - 8]); } else { cftmdl1(64, a, &w[nw - 32]); cftf081(a, &w[nw - 8]); cftf082(&a[16], &w[nw - 8]); cftf081(&a[32], &w[nw - 8]); cftf081(&a[48], &w[nw - 8]); cftmdl2(64, &a[64], &w[nw - 64]); cftf081(&a[64], &w[nw - 8]); cftf082(&a[80], &w[nw - 8]); cftf081(&a[96], &w[nw - 8]); cftf082(&a[112], &w[nw - 8]); cftmdl1(64, &a[128], &w[nw - 32]); cftf081(&a[128], &w[nw - 8]); cftf082(&a[144], &w[nw - 8]); cftf081(&a[160], &w[nw - 8]); cftf081(&a[176], &w[nw - 8]); if (isplt != 0) { cftmdl1(64, &a[192], &w[nw - 32]); cftf081(&a[240], &w[nw - 8]); } else { cftmdl2(64, &a[192], &w[nw - 64]); cftf082(&a[240], &w[nw - 8]); } cftf081(&a[192], &w[nw - 8]); cftf082(&a[208], &w[nw - 8]); cftf081(&a[224], &w[nw - 8]); } } static void cftmdl1(int n, float32 *a, float32 *w) { int j, j0, j1, j2, j3, k, m, mh; float32 wn4r, wk1r, wk1i, wk3r, wk3i; float32 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; mh = n >> 3; m = 2 * mh; j1 = m; j2 = j1 + m; j3 = j2 + m; x0r = a[0] + a[j2]; x0i = a[1] + a[j2 + 1]; x1r = a[0] - a[j2]; x1i = a[1] - a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[0] = x0r + x2r; a[1] = x0i + x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; a[j2] = x1r - x3i; a[j2 + 1] = x1i + x3r; a[j3] = x1r + x3i; a[j3 + 1] = x1i - x3r; wn4r = w[1]; k = 0; for (j = 2; j < mh; j += 2) { k += 4; wk1r = w[k]; wk1i = w[k + 1]; wk3r = w[k + 2]; wk3i = w[k + 3]; j1 = j + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j] + a[j2]; x0i = a[j + 1] + a[j2 + 1]; x1r = a[j] - a[j2]; x1i = a[j + 1] - a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2] = wk1r * x0r - wk1i * x0i; a[j2 + 1] = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = wk3r * x0r + wk3i * x0i; a[j3 + 1] = wk3r * x0i - wk3i * x0r; j0 = m - j; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0] + a[j2]; x0i = a[j0 + 1] + a[j2 + 1]; x1r = a[j0] - a[j2]; x1i = a[j0 + 1] - a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[j0] = x0r + x2r; a[j0 + 1] = x0i + x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2] = wk1i * x0r - wk1r * x0i; a[j2 + 1] = wk1i * x0i + wk1r * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = wk3i * x0r + wk3r * x0i; a[j3 + 1] = wk3i * x0i - wk3r * x0r; } j0 = mh; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0] + a[j2]; x0i = a[j0 + 1] + a[j2 + 1]; x1r = a[j0] - a[j2]; x1i = a[j0 + 1] - a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[j0] = x0r + x2r; a[j0 + 1] = x0i + x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2] = wn4r * (x0r - x0i); a[j2 + 1] = wn4r * (x0i + x0r); x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = -wn4r * (x0r + x0i); a[j3 + 1] = -wn4r * (x0i - x0r); } static void cftmdl2(int n, float32 *a, float32 *w) { int j, j0, j1, j2, j3, k, kr, m, mh; float32 wn4r, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i; float32 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y2r, y2i; mh = n >> 3; m = 2 * mh; wn4r = w[1]; j1 = m; j2 = j1 + m; j3 = j2 + m; x0r = a[0] - a[j2 + 1]; x0i = a[1] + a[j2]; x1r = a[0] + a[j2 + 1]; x1i = a[1] - a[j2]; x2r = a[j1] - a[j3 + 1]; x2i = a[j1 + 1] + a[j3]; x3r = a[j1] + a[j3 + 1]; x3i = a[j1 + 1] - a[j3]; y0r = wn4r * (x2r - x2i); y0i = wn4r * (x2i + x2r); a[0] = x0r + y0r; a[1] = x0i + y0i; a[j1] = x0r - y0r; a[j1 + 1] = x0i - y0i; y0r = wn4r * (x3r - x3i); y0i = wn4r * (x3i + x3r); a[j2] = x1r - y0i; a[j2 + 1] = x1i + y0r; a[j3] = x1r + y0i; a[j3 + 1] = x1i - y0r; k = 0; kr = 2 * m; for (j = 2; j < mh; j += 2) { k += 4; wk1r = w[k]; wk1i = w[k + 1]; wk3r = w[k + 2]; wk3i = w[k + 3]; kr -= 4; wd1i = w[kr]; wd1r = w[kr + 1]; wd3i = w[kr + 2]; wd3r = w[kr + 3]; j1 = j + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j] - a[j2 + 1]; x0i = a[j + 1] + a[j2]; x1r = a[j] + a[j2 + 1]; x1i = a[j + 1] - a[j2]; x2r = a[j1] - a[j3 + 1]; x2i = a[j1 + 1] + a[j3]; x3r = a[j1] + a[j3 + 1]; x3i = a[j1 + 1] - a[j3]; y0r = wk1r * x0r - wk1i * x0i; y0i = wk1r * x0i + wk1i * x0r; y2r = wd1r * x2r - wd1i * x2i; y2i = wd1r * x2i + wd1i * x2r; a[j] = y0r + y2r; a[j + 1] = y0i + y2i; a[j1] = y0r - y2r; a[j1 + 1] = y0i - y2i; y0r = wk3r * x1r + wk3i * x1i; y0i = wk3r * x1i - wk3i * x1r; y2r = wd3r * x3r + wd3i * x3i; y2i = wd3r * x3i - wd3i * x3r; a[j2] = y0r + y2r; a[j2 + 1] = y0i + y2i; a[j3] = y0r - y2r; a[j3 + 1] = y0i - y2i; j0 = m - j; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0] - a[j2 + 1]; x0i = a[j0 + 1] + a[j2]; x1r = a[j0] + a[j2 + 1]; x1i = a[j0 + 1] - a[j2]; x2r = a[j1] - a[j3 + 1]; x2i = a[j1 + 1] + a[j3]; x3r = a[j1] + a[j3 + 1]; x3i = a[j1 + 1] - a[j3]; y0r = wd1i * x0r - wd1r * x0i; y0i = wd1i * x0i + wd1r * x0r; y2r = wk1i * x2r - wk1r * x2i; y2i = wk1i * x2i + wk1r * x2r; a[j0] = y0r + y2r; a[j0 + 1] = y0i + y2i; a[j1] = y0r - y2r; a[j1 + 1] = y0i - y2i; y0r = wd3i * x1r + wd3r * x1i; y0i = wd3i * x1i - wd3r * x1r; y2r = wk3i * x3r + wk3r * x3i; y2i = wk3i * x3i - wk3r * x3r; a[j2] = y0r + y2r; a[j2 + 1] = y0i + y2i; a[j3] = y0r - y2r; a[j3 + 1] = y0i - y2i; } wk1r = w[m]; wk1i = w[m + 1]; j0 = mh; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0] - a[j2 + 1]; x0i = a[j0 + 1] + a[j2]; x1r = a[j0] + a[j2 + 1]; x1i = a[j0 + 1] - a[j2]; x2r = a[j1] - a[j3 + 1]; x2i = a[j1 + 1] + a[j3]; x3r = a[j1] + a[j3 + 1]; x3i = a[j1 + 1] - a[j3]; y0r = wk1r * x0r - wk1i * x0i; y0i = wk1r * x0i + wk1i * x0r; y2r = wk1i * x2r - wk1r * x2i; y2i = wk1i * x2i + wk1r * x2r; a[j0] = y0r + y2r; a[j0 + 1] = y0i + y2i; a[j1] = y0r - y2r; a[j1 + 1] = y0i - y2i; y0r = wk1i * x1r - wk1r * x1i; y0i = wk1i * x1i + wk1r * x1r; y2r = wk1r * x3r - wk1i * x3i; y2i = wk1r * x3i + wk1i * x3r; a[j2] = y0r - y2r; a[j2 + 1] = y0i - y2i; a[j3] = y0r + y2r; a[j3 + 1] = y0i + y2i; } static void cftfx41(int n, float32 *a, int nw, float32 *w) { if (n == 128) { cftf161(a, &w[nw - 8]); cftf162(&a[32], &w[nw - 32]); cftf161(&a[64], &w[nw - 8]); cftf161(&a[96], &w[nw - 8]); } else { cftf081(a, &w[nw - 8]); cftf082(&a[16], &w[nw - 8]); cftf081(&a[32], &w[nw - 8]); cftf081(&a[48], &w[nw - 8]); } } static void cftf161(float32 *a, float32 *w) { float32 wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i; wn4r = w[1]; wk1r = w[2]; wk1i = w[3]; x0r = a[0] + a[16]; x0i = a[1] + a[17]; x1r = a[0] - a[16]; x1i = a[1] - a[17]; x2r = a[8] + a[24]; x2i = a[9] + a[25]; x3r = a[8] - a[24]; x3i = a[9] - a[25]; y0r = x0r + x2r; y0i = x0i + x2i; y4r = x0r - x2r; y4i = x0i - x2i; y8r = x1r - x3i; y8i = x1i + x3r; y12r = x1r + x3i; y12i = x1i - x3r; x0r = a[2] + a[18]; x0i = a[3] + a[19]; x1r = a[2] - a[18]; x1i = a[3] - a[19]; x2r = a[10] + a[26]; x2i = a[11] + a[27]; x3r = a[10] - a[26]; x3i = a[11] - a[27]; y1r = x0r + x2r; y1i = x0i + x2i; y5r = x0r - x2r; y5i = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; y9r = wk1r * x0r - wk1i * x0i; y9i = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; y13r = wk1i * x0r - wk1r * x0i; y13i = wk1i * x0i + wk1r * x0r; x0r = a[4] + a[20]; x0i = a[5] + a[21]; x1r = a[4] - a[20]; x1i = a[5] - a[21]; x2r = a[12] + a[28]; x2i = a[13] + a[29]; x3r = a[12] - a[28]; x3i = a[13] - a[29]; y2r = x0r + x2r; y2i = x0i + x2i; y6r = x0r - x2r; y6i = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; y10r = wn4r * (x0r - x0i); y10i = wn4r * (x0i + x0r); x0r = x1r + x3i; x0i = x1i - x3r; y14r = wn4r * (x0r + x0i); y14i = wn4r * (x0i - x0r); x0r = a[6] + a[22]; x0i = a[7] + a[23]; x1r = a[6] - a[22]; x1i = a[7] - a[23]; x2r = a[14] + a[30]; x2i = a[15] + a[31]; x3r = a[14] - a[30]; x3i = a[15] - a[31]; y3r = x0r + x2r; y3i = x0i + x2i; y7r = x0r - x2r; y7i = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; y11r = wk1i * x0r - wk1r * x0i; y11i = wk1i * x0i + wk1r * x0r; x0r = x1r + x3i; x0i = x1i - x3r; y15r = wk1r * x0r - wk1i * x0i; y15i = wk1r * x0i + wk1i * x0r; x0r = y12r - y14r; x0i = y12i - y14i; x1r = y12r + y14r; x1i = y12i + y14i; x2r = y13r - y15r; x2i = y13i - y15i; x3r = y13r + y15r; x3i = y13i + y15i; a[24] = x0r + x2r; a[25] = x0i + x2i; a[26] = x0r - x2r; a[27] = x0i - x2i; a[28] = x1r - x3i; a[29] = x1i + x3r; a[30] = x1r + x3i; a[31] = x1i - x3r; x0r = y8r + y10r; x0i = y8i + y10i; x1r = y8r - y10r; x1i = y8i - y10i; x2r = y9r + y11r; x2i = y9i + y11i; x3r = y9r - y11r; x3i = y9i - y11i; a[16] = x0r + x2r; a[17] = x0i + x2i; a[18] = x0r - x2r; a[19] = x0i - x2i; a[20] = x1r - x3i; a[21] = x1i + x3r; a[22] = x1r + x3i; a[23] = x1i - x3r; x0r = y5r - y7i; x0i = y5i + y7r; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); x0r = y5r + y7i; x0i = y5i - y7r; x3r = wn4r * (x0r - x0i); x3i = wn4r * (x0i + x0r); x0r = y4r - y6i; x0i = y4i + y6r; x1r = y4r + y6i; x1i = y4i - y6r; a[8] = x0r + x2r; a[9] = x0i + x2i; a[10] = x0r - x2r; a[11] = x0i - x2i; a[12] = x1r - x3i; a[13] = x1i + x3r; a[14] = x1r + x3i; a[15] = x1i - x3r; x0r = y0r + y2r; x0i = y0i + y2i; x1r = y0r - y2r; x1i = y0i - y2i; x2r = y1r + y3r; x2i = y1i + y3i; x3r = y1r - y3r; x3i = y1i - y3i; a[0] = x0r + x2r; a[1] = x0i + x2i; a[2] = x0r - x2r; a[3] = x0i - x2i; a[4] = x1r - x3i; a[5] = x1i + x3r; a[6] = x1r + x3i; a[7] = x1i - x3r; } static void cftf162(float32 *a, float32 *w) { float32 wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, x0r, x0i, x1r, x1i, x2r, x2i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i; wn4r = w[1]; wk1r = w[4]; wk1i = w[5]; wk3r = w[6]; wk3i = -w[7]; wk2r = w[8]; wk2i = w[9]; x1r = a[0] - a[17]; x1i = a[1] + a[16]; x0r = a[8] - a[25]; x0i = a[9] + a[24]; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); y0r = x1r + x2r; y0i = x1i + x2i; y4r = x1r - x2r; y4i = x1i - x2i; x1r = a[0] + a[17]; x1i = a[1] - a[16]; x0r = a[8] + a[25]; x0i = a[9] - a[24]; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); y8r = x1r - x2i; y8i = x1i + x2r; y12r = x1r + x2i; y12i = x1i - x2r; x0r = a[2] - a[19]; x0i = a[3] + a[18]; x1r = wk1r * x0r - wk1i * x0i; x1i = wk1r * x0i + wk1i * x0r; x0r = a[10] - a[27]; x0i = a[11] + a[26]; x2r = wk3i * x0r - wk3r * x0i; x2i = wk3i * x0i + wk3r * x0r; y1r = x1r + x2r; y1i = x1i + x2i; y5r = x1r - x2r; y5i = x1i - x2i; x0r = a[2] + a[19]; x0i = a[3] - a[18]; x1r = wk3r * x0r - wk3i * x0i; x1i = wk3r * x0i + wk3i * x0r; x0r = a[10] + a[27]; x0i = a[11] - a[26]; x2r = wk1r * x0r + wk1i * x0i; x2i = wk1r * x0i - wk1i * x0r; y9r = x1r - x2r; y9i = x1i - x2i; y13r = x1r + x2r; y13i = x1i + x2i; x0r = a[4] - a[21]; x0i = a[5] + a[20]; x1r = wk2r * x0r - wk2i * x0i; x1i = wk2r * x0i + wk2i * x0r; x0r = a[12] - a[29]; x0i = a[13] + a[28]; x2r = wk2i * x0r - wk2r * x0i; x2i = wk2i * x0i + wk2r * x0r; y2r = x1r + x2r; y2i = x1i + x2i; y6r = x1r - x2r; y6i = x1i - x2i; x0r = a[4] + a[21]; x0i = a[5] - a[20]; x1r = wk2i * x0r - wk2r * x0i; x1i = wk2i * x0i + wk2r * x0r; x0r = a[12] + a[29]; x0i = a[13] - a[28]; x2r = wk2r * x0r - wk2i * x0i; x2i = wk2r * x0i + wk2i * x0r; y10r = x1r - x2r; y10i = x1i - x2i; y14r = x1r + x2r; y14i = x1i + x2i; x0r = a[6] - a[23]; x0i = a[7] + a[22]; x1r = wk3r * x0r - wk3i * x0i; x1i = wk3r * x0i + wk3i * x0r; x0r = a[14] - a[31]; x0i = a[15] + a[30]; x2r = wk1i * x0r - wk1r * x0i; x2i = wk1i * x0i + wk1r * x0r; y3r = x1r + x2r; y3i = x1i + x2i; y7r = x1r - x2r; y7i = x1i - x2i; x0r = a[6] + a[23]; x0i = a[7] - a[22]; x1r = wk1i * x0r + wk1r * x0i; x1i = wk1i * x0i - wk1r * x0r; x0r = a[14] + a[31]; x0i = a[15] - a[30]; x2r = wk3i * x0r - wk3r * x0i; x2i = wk3i * x0i + wk3r * x0r; y11r = x1r + x2r; y11i = x1i + x2i; y15r = x1r - x2r; y15i = x1i - x2i; x1r = y0r + y2r; x1i = y0i + y2i; x2r = y1r + y3r; x2i = y1i + y3i; a[0] = x1r + x2r; a[1] = x1i + x2i; a[2] = x1r - x2r; a[3] = x1i - x2i; x1r = y0r - y2r; x1i = y0i - y2i; x2r = y1r - y3r; x2i = y1i - y3i; a[4] = x1r - x2i; a[5] = x1i + x2r; a[6] = x1r + x2i; a[7] = x1i - x2r; x1r = y4r - y6i; x1i = y4i + y6r; x0r = y5r - y7i; x0i = y5i + y7r; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); a[8] = x1r + x2r; a[9] = x1i + x2i; a[10] = x1r - x2r; a[11] = x1i - x2i; x1r = y4r + y6i; x1i = y4i - y6r; x0r = y5r + y7i; x0i = y5i - y7r; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); a[12] = x1r - x2i; a[13] = x1i + x2r; a[14] = x1r + x2i; a[15] = x1i - x2r; x1r = y8r + y10r; x1i = y8i + y10i; x2r = y9r - y11r; x2i = y9i - y11i; a[16] = x1r + x2r; a[17] = x1i + x2i; a[18] = x1r - x2r; a[19] = x1i - x2i; x1r = y8r - y10r; x1i = y8i - y10i; x2r = y9r + y11r; x2i = y9i + y11i; a[20] = x1r - x2i; a[21] = x1i + x2r; a[22] = x1r + x2i; a[23] = x1i - x2r; x1r = y12r - y14i; x1i = y12i + y14r; x0r = y13r + y15i; x0i = y13i - y15r; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); a[24] = x1r + x2r; a[25] = x1i + x2i; a[26] = x1r - x2r; a[27] = x1i - x2i; x1r = y12r + y14i; x1i = y12i - y14r; x0r = y13r - y15i; x0i = y13i + y15r; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); a[28] = x1r - x2i; a[29] = x1i + x2r; a[30] = x1r + x2i; a[31] = x1i - x2r; } static void cftf081(float32 *a, float32 *w) { float32 wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; wn4r = w[1]; x0r = a[0] + a[8]; x0i = a[1] + a[9]; x1r = a[0] - a[8]; x1i = a[1] - a[9]; x2r = a[4] + a[12]; x2i = a[5] + a[13]; x3r = a[4] - a[12]; x3i = a[5] - a[13]; y0r = x0r + x2r; y0i = x0i + x2i; y2r = x0r - x2r; y2i = x0i - x2i; y1r = x1r - x3i; y1i = x1i + x3r; y3r = x1r + x3i; y3i = x1i - x3r; x0r = a[2] + a[10]; x0i = a[3] + a[11]; x1r = a[2] - a[10]; x1i = a[3] - a[11]; x2r = a[6] + a[14]; x2i = a[7] + a[15]; x3r = a[6] - a[14]; x3i = a[7] - a[15]; y4r = x0r + x2r; y4i = x0i + x2i; y6r = x0r - x2r; y6i = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; x2r = x1r + x3i; x2i = x1i - x3r; y5r = wn4r * (x0r - x0i); y5i = wn4r * (x0r + x0i); y7r = wn4r * (x2r - x2i); y7i = wn4r * (x2r + x2i); a[8] = y1r + y5r; a[9] = y1i + y5i; a[10] = y1r - y5r; a[11] = y1i - y5i; a[12] = y3r - y7i; a[13] = y3i + y7r; a[14] = y3r + y7i; a[15] = y3i - y7r; a[0] = y0r + y4r; a[1] = y0i + y4i; a[2] = y0r - y4r; a[3] = y0i - y4i; a[4] = y2r - y6i; a[5] = y2i + y6r; a[6] = y2r + y6i; a[7] = y2i - y6r; } static void cftf082(float32 *a, float32 *w) { float32 wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; wn4r = w[1]; wk1r = w[2]; wk1i = w[3]; y0r = a[0] - a[9]; y0i = a[1] + a[8]; y1r = a[0] + a[9]; y1i = a[1] - a[8]; x0r = a[4] - a[13]; x0i = a[5] + a[12]; y2r = wn4r * (x0r - x0i); y2i = wn4r * (x0i + x0r); x0r = a[4] + a[13]; x0i = a[5] - a[12]; y3r = wn4r * (x0r - x0i); y3i = wn4r * (x0i + x0r); x0r = a[2] - a[11]; x0i = a[3] + a[10]; y4r = wk1r * x0r - wk1i * x0i; y4i = wk1r * x0i + wk1i * x0r; x0r = a[2] + a[11]; x0i = a[3] - a[10]; y5r = wk1i * x0r - wk1r * x0i; y5i = wk1i * x0i + wk1r * x0r; x0r = a[6] - a[15]; x0i = a[7] + a[14]; y6r = wk1i * x0r - wk1r * x0i; y6i = wk1i * x0i + wk1r * x0r; x0r = a[6] + a[15]; x0i = a[7] - a[14]; y7r = wk1r * x0r - wk1i * x0i; y7i = wk1r * x0i + wk1i * x0r; x0r = y0r + y2r; x0i = y0i + y2i; x1r = y4r + y6r; x1i = y4i + y6i; a[0] = x0r + x1r; a[1] = x0i + x1i; a[2] = x0r - x1r; a[3] = x0i - x1i; x0r = y0r - y2r; x0i = y0i - y2i; x1r = y4r - y6r; x1i = y4i - y6i; a[4] = x0r - x1i; a[5] = x0i + x1r; a[6] = x0r + x1i; a[7] = x0i - x1r; x0r = y1r - y3i; x0i = y1i + y3r; x1r = y5r - y7r; x1i = y5i - y7i; a[8] = x0r + x1r; a[9] = x0i + x1i; a[10] = x0r - x1r; a[11] = x0i - x1i; x0r = y1r + y3i; x0i = y1i - y3r; x1r = y5r + y7r; x1i = y5i + y7i; a[12] = x0r - x1i; a[13] = x0i + x1r; a[14] = x0r + x1i; a[15] = x0i - x1r; } static void cftf040(float32 *a) { float32 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; x0r = a[0] + a[4]; x0i = a[1] + a[5]; x1r = a[0] - a[4]; x1i = a[1] - a[5]; x2r = a[2] + a[6]; x2i = a[3] + a[7]; x3r = a[2] - a[6]; x3i = a[3] - a[7]; a[0] = x0r + x2r; a[1] = x0i + x2i; a[2] = x1r - x3i; a[3] = x1i + x3r; a[4] = x0r - x2r; a[5] = x0i - x2i; a[6] = x1r + x3i; a[7] = x1i - x3r; } static void cftb040(float32 *a) { float32 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; x0r = a[0] + a[4]; x0i = a[1] + a[5]; x1r = a[0] - a[4]; x1i = a[1] - a[5]; x2r = a[2] + a[6]; x2i = a[3] + a[7]; x3r = a[2] - a[6]; x3i = a[3] - a[7]; a[0] = x0r + x2r; a[1] = x0i + x2i; a[2] = x1r + x3i; a[3] = x1i - x3r; a[4] = x0r - x2r; a[5] = x0i - x2i; a[6] = x1r - x3i; a[7] = x1i + x3r; } static void cftx020(float32 *a) { float32 x0r, x0i; x0r = a[0] - a[2]; x0i = a[1] - a[3]; a[0] += a[2]; a[1] += a[3]; a[2] = x0r; a[3] = x0i; } static void rftfsub(int n, float32 *a, int nc, float32 *c) { int j, k, kk, ks, m; float32 wkr, wki, xr, xi, yr, yi; m = n >> 1; ks = 2 * nc / m; kk = 0; for (j = 2; j < m; j += 2) { k = n - j; kk += ks; wkr = 0.5 - c[nc - kk]; wki = c[kk]; xr = a[j] - a[k]; xi = a[j + 1] + a[k + 1]; yr = wkr * xr - wki * xi; yi = wkr * xi + wki * xr; a[j] -= yr; a[j + 1] -= yi; a[k] += yr; a[k + 1] -= yi; } } static void rftbsub(int n, float32 *a, int nc, float32 *c) { int j, k, kk, ks, m; float32 wkr, wki, xr, xi, yr, yi; m = n >> 1; ks = 2 * nc / m; kk = 0; for (j = 2; j < m; j += 2) { k = n - j; kk += ks; wkr = 0.5 - c[nc - kk]; wki = c[kk]; xr = a[j] - a[k]; xi = a[j + 1] + a[k + 1]; yr = wkr * xr + wki * xi; yi = wkr * xi - wki * xr; a[j] -= yr; a[j + 1] -= yi; a[k] += yr; a[k + 1] -= yi; } } static void dctsub(int n, float32 *a, int nc, float32 *c) { int j, k, kk, ks, m; float32 wkr, wki, xr; m = n >> 1; ks = nc / n; kk = 0; for (j = 1; j < m; j++) { k = n - j; kk += ks; wkr = c[kk] - c[nc - kk]; wki = c[kk] + c[nc - kk]; xr = wki * a[j] - wkr * a[k]; a[j] = wkr * a[j] + wki * a[k]; a[k] = xr; } a[m] *= c[0]; } static void dstsub(int n, float32 *a, int nc, float32 *c) { int j, k, kk, ks, m; float32 wkr, wki, xr; m = n >> 1; ks = nc / n; kk = 0; for (j = 1; j < m; j++) { k = n - j; kk += ks; wkr = c[kk] - c[nc - kk]; wki = c[kk] + c[nc - kk]; xr = wki * a[k] - wkr * a[j]; a[k] = wkr * a[k] + wki * a[j]; a[j] = xr; } a[m] *= c[0]; } } astra-toolbox-2.3.0/src/GeometryUtil2D.cpp000066400000000000000000000130501475635207100204350ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/GeometryUtil2D.h" #include #include namespace astra { SParProjection* genParProjections(unsigned int iProjAngles, unsigned int iProjDets, double fDetSize, const float *pfAngles, const float *pfExtraOffsets) { SParProjection base; base.fRayX = 0.0f; base.fRayY = 1.0f; base.fDetSX = iProjDets * fDetSize * -0.5f; base.fDetSY = 0.0f; base.fDetUX = fDetSize; base.fDetUY = 0.0f; SParProjection* p = new SParProjection[iProjAngles]; #define ROTATE0(name,i,alpha) do { p[i].f##name##X = base.f##name##X * cos(alpha) - base.f##name##Y * sin(alpha); p[i].f##name##Y = base.f##name##X * sin(alpha) + base.f##name##Y * cos(alpha); } while(0) for (unsigned int i = 0; i < iProjAngles; ++i) { if (pfExtraOffsets) { // TODO } ROTATE0(Ray, i, pfAngles[i]); ROTATE0(DetS, i, pfAngles[i]); ROTATE0(DetU, i, pfAngles[i]); if (pfExtraOffsets) { float d = pfExtraOffsets[i]; p[i].fDetSX -= d * p[i].fDetUX; p[i].fDetSY -= d * p[i].fDetUY; } } #undef ROTATE0 return p; } SFanProjection* genFanProjections(unsigned int iProjAngles, unsigned int iProjDets, double fOriginSource, double fOriginDetector, double fDetSize, const float *pfAngles) // const float *pfExtraOffsets) { SFanProjection *pProjs = new SFanProjection[iProjAngles]; float fSrcX0 = 0.0f; float fSrcY0 = -fOriginSource; float fDetUX0 = fDetSize; float fDetUY0 = 0.0f; float fDetSX0 = iProjDets * fDetUX0 / -2.0f; float fDetSY0 = fOriginDetector; #define ROTATE0(name,i,alpha) do { pProjs[i].f##name##X = f##name##X0 * cos(alpha) - f##name##Y0 * sin(alpha); pProjs[i].f##name##Y = f##name##X0 * sin(alpha) + f##name##Y0 * cos(alpha); } while(0) for (unsigned int i = 0; i < iProjAngles; ++i) { ROTATE0(Src, i, pfAngles[i]); ROTATE0(DetS, i, pfAngles[i]); ROTATE0(DetU, i, pfAngles[i]); } #undef ROTATE0 return pProjs; } // Convert a SParProjection back into its set of "standard" circular parallel // beam parameters. This is always possible. bool getParParameters(const SParProjection &proj, unsigned int iProjDets, float &fAngle, float &fDetSize, float &fOffset) { // Take part of DetU orthogonal to Ray double ux = proj.fDetUX; double uy = proj.fDetUY; double t = (ux * proj.fRayX + uy * proj.fRayY) / (proj.fRayX * proj.fRayX + proj.fRayY * proj.fRayY); ux -= t * proj.fRayX; uy -= t * proj.fRayY; double angle = atan2(uy, ux); fAngle = (float)angle; double norm2 = uy * uy + ux * ux; fDetSize = (float)sqrt(norm2); // CHECKME: SIGNS? fOffset = (float)(-0.5*iProjDets - (proj.fDetSY*uy + proj.fDetSX*ux) / norm2); return true; } // Convert a SFanProjection back into its set of "standard" circular fan beam // parameters. This will return false if it can not be represented in this way. bool getFanParameters(const SFanProjection &proj, unsigned int iProjDets, float &fAngle, float &fOriginSource, float &fOriginDetector, float &fDetSize, float &fOffset) { // angle // det size // offset // origin-source // origin-detector // Need to check if line source-origin is orthogonal to vector ux,uy // (including the case source==origin) // (equivalent: source and origin project to same point on detector) double dp = proj.fSrcX * proj.fDetUX + proj.fSrcY * proj.fDetUY; double rel = (proj.fSrcX*proj.fSrcX + proj.fSrcY*proj.fSrcY) * (proj.fDetUX*proj.fDetUX + proj.fDetUY*proj.fDetUY); rel = sqrt(rel); if (std::abs(dp) > rel * 0.0001) return false; fOriginSource = sqrt(proj.fSrcX*proj.fSrcX + proj.fSrcY*proj.fSrcY); fDetSize = sqrt(proj.fDetUX*proj.fDetUX + proj.fDetUY*proj.fDetUY); // project origin on detector line ( == project source on detector line) double t = (- proj.fDetSX) * proj.fDetUX + (- proj.fDetSY) * proj.fDetUY; t /= (proj.fDetUX * proj.fDetUX + proj.fDetUY * proj.fDetUY); fOffset = (float)t - 0.5*iProjDets; fOriginDetector = sqrt((proj.fDetSX + t * proj.fDetUX)*(proj.fDetSX + t * proj.fDetUX) + (proj.fDetSY + t * proj.fDetUY)*(proj.fDetSY + t * proj.fDetUY)); fAngle = atan2(proj.fDetUY, proj.fDetUX); //fprintf(stderr, "getFanParams: s = (%f,%f) d = (%f,%f) u = (%f,%f)\n", proj.fSrcX, proj.fSrcY, proj.fDetSX, proj.fDetSY, proj.fDetUX, proj.fDetUY); //fprintf(stderr, "getFanParams: fOS = %f, fOD = %f, detsize = %f, offset = %f (t = %f), angle = %f\n", fOriginSource, fOriginDetector, fDetSize, fOffset, t, fAngle); return true; } } astra-toolbox-2.3.0/src/GeometryUtil3D.cpp000066400000000000000000000360421475635207100204440ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/GeometryUtil3D.h" #include "astra/ParallelProjectionGeometry3D.h" #include "astra/ParallelVecProjectionGeometry3D.h" #include "astra/ConeProjectionGeometry3D.h" #include "astra/ConeVecProjectionGeometry3D.h" #include namespace astra { std::vector genConeProjections(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, double fOriginSourceDistance, double fOriginDetectorDistance, double fDetUSize, double fDetVSize, const float *pfAngles) { SConeProjection base; base.fSrcX = 0.0f; base.fSrcY = -fOriginSourceDistance; base.fSrcZ = 0.0f; base.fDetSX = iProjU * fDetUSize * -0.5f; base.fDetSY = fOriginDetectorDistance; base.fDetSZ = iProjV * fDetVSize * -0.5f; base.fDetUX = fDetUSize; base.fDetUY = 0.0f; base.fDetUZ = 0.0f; base.fDetVX = 0.0f; base.fDetVY = 0.0f; base.fDetVZ = fDetVSize; std::vector p; p.resize(iProjAngles); #define ROTATE0(name,i,alpha) do { p[i].f##name##X = base.f##name##X * cos(alpha) - base.f##name##Y * sin(alpha); p[i].f##name##Y = base.f##name##X * sin(alpha) + base.f##name##Y * cos(alpha); p[i].f##name##Z = base.f##name##Z; } while(0) for (unsigned int i = 0; i < iProjAngles; ++i) { ROTATE0(Src, i, pfAngles[i]); ROTATE0(DetS, i, pfAngles[i]); ROTATE0(DetU, i, pfAngles[i]); ROTATE0(DetV, i, pfAngles[i]); } #undef ROTATE0 return p; } std::vector genPar3DProjections(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, double fDetUSize, double fDetVSize, const float *pfAngles) { SPar3DProjection base; base.fRayX = 0.0f; base.fRayY = 1.0f; base.fRayZ = 0.0f; base.fDetSX = iProjU * fDetUSize * -0.5f; base.fDetSY = 0.0f; base.fDetSZ = iProjV * fDetVSize * -0.5f; base.fDetUX = fDetUSize; base.fDetUY = 0.0f; base.fDetUZ = 0.0f; base.fDetVX = 0.0f; base.fDetVY = 0.0f; base.fDetVZ = fDetVSize; std::vector p; p.resize(iProjAngles); #define ROTATE0(name,i,alpha) do { p[i].f##name##X = base.f##name##X * cos(alpha) - base.f##name##Y * sin(alpha); p[i].f##name##Y = base.f##name##X * sin(alpha) + base.f##name##Y * cos(alpha); p[i].f##name##Z = base.f##name##Z; } while(0) for (unsigned int i = 0; i < iProjAngles; ++i) { ROTATE0(Ray, i, pfAngles[i]); ROTATE0(DetS, i, pfAngles[i]); ROTATE0(DetU, i, pfAngles[i]); ROTATE0(DetV, i, pfAngles[i]); } #undef ROTATE0 return p; } // Utility function to get newly allocated copy of projection vectors for any geometry template static std::vector getProjectionVectors(const P* geom); template<> std::vector getProjectionVectors(const CConeProjectionGeometry3D* pProjGeom) { return genConeProjections(pProjGeom->getProjectionCount(), pProjGeom->getDetectorColCount(), pProjGeom->getDetectorRowCount(), pProjGeom->getOriginSourceDistance(), pProjGeom->getOriginDetectorDistance(), pProjGeom->getDetectorSpacingX(), pProjGeom->getDetectorSpacingY(), pProjGeom->getProjectionAngles()); } template<> std::vector getProjectionVectors(const CConeVecProjectionGeometry3D* pProjGeom) { int nth = pProjGeom->getProjectionCount(); std::vector p; p.resize(nth); for (int i = 0; i < nth; ++i) p[i] = pProjGeom->getProjectionVectors()[i]; return p; } template<> std::vector getProjectionVectors(const CParallelProjectionGeometry3D* pProjGeom) { return genPar3DProjections(pProjGeom->getProjectionCount(), pProjGeom->getDetectorColCount(), pProjGeom->getDetectorRowCount(), pProjGeom->getDetectorSpacingX(), pProjGeom->getDetectorSpacingY(), pProjGeom->getProjectionAngles()); } template<> std::vector getProjectionVectors(const CParallelVecProjectionGeometry3D* pProjGeom) { int nth = pProjGeom->getProjectionCount(); std::vector p; p.resize(nth); for (int i = 0; i < nth; ++i) p[i] = pProjGeom->getProjectionVectors()[i]; return p; } // Translate detector location along u axis template static void translateDetectorVectorsU(std::vector &projs, double du) { for (auto &p : projs) { p.fDetSX += du * p.fDetUX; p.fDetSY += du * p.fDetUY; p.fDetSZ += du * p.fDetUZ; } } // Translate detector location along v axis template static void translateDetectorVectorsV(std::vector &projs, double dv) { for (auto &p : projs) { p.fDetSX += dv * p.fDetVX; p.fDetSY += dv * p.fDetVY; p.fDetSZ += dv * p.fDetVZ; } } CProjectionGeometry3D* getSubProjectionGeometry_U(const CProjectionGeometry3D* pProjGeom, int u, int size) { // First convert to vectors, then translate, then convert into new object const CConeProjectionGeometry3D* conegeom = dynamic_cast(pProjGeom); const CParallelProjectionGeometry3D* par3dgeom = dynamic_cast(pProjGeom); const CParallelVecProjectionGeometry3D* parvec3dgeom = dynamic_cast(pProjGeom); const CConeVecProjectionGeometry3D* conevec3dgeom = dynamic_cast(pProjGeom); if (conegeom || conevec3dgeom) { std::vector coneProjs; if (conegeom) { coneProjs = getProjectionVectors(conegeom); } else { coneProjs = getProjectionVectors(conevec3dgeom); } translateDetectorVectorsU(coneProjs, u); CProjectionGeometry3D* ret = new CConeVecProjectionGeometry3D(pProjGeom->getProjectionCount(), pProjGeom->getDetectorRowCount(), size, std::move(coneProjs)); return ret; } else if (par3dgeom || parvec3dgeom) { std::vector parProjs; if (par3dgeom) { parProjs = getProjectionVectors(par3dgeom); } else { parProjs = getProjectionVectors(parvec3dgeom); } translateDetectorVectorsU(parProjs, u); CProjectionGeometry3D* ret = new CParallelVecProjectionGeometry3D(pProjGeom->getProjectionCount(), pProjGeom->getDetectorRowCount(), size, std::move(parProjs)); return ret; } else { assert(false); return nullptr; } } CProjectionGeometry3D* getSubProjectionGeometry_V(const CProjectionGeometry3D* pProjGeom, int v, int size) { // First convert to vectors, then translate, then convert into new object const CConeProjectionGeometry3D* conegeom = dynamic_cast(pProjGeom); const CParallelProjectionGeometry3D* par3dgeom = dynamic_cast(pProjGeom); const CParallelVecProjectionGeometry3D* parvec3dgeom = dynamic_cast(pProjGeom); const CConeVecProjectionGeometry3D* conevec3dgeom = dynamic_cast(pProjGeom); if (conegeom || conevec3dgeom) { std::vector coneProjs; if (conegeom) { coneProjs = getProjectionVectors(conegeom); } else { coneProjs = getProjectionVectors(conevec3dgeom); } translateDetectorVectorsV(coneProjs, v); CProjectionGeometry3D* ret = new CConeVecProjectionGeometry3D(pProjGeom->getProjectionCount(), size, pProjGeom->getDetectorColCount(), std::move(coneProjs)); return ret; } else if (par3dgeom || parvec3dgeom) { std::vector parProjs; if (par3dgeom) { parProjs = getProjectionVectors(par3dgeom); } else { parProjs = getProjectionVectors(parvec3dgeom); } translateDetectorVectorsV(parProjs, v); CProjectionGeometry3D* ret = new CParallelVecProjectionGeometry3D(pProjGeom->getProjectionCount(), size, pProjGeom->getDetectorColCount(), std::move(parProjs)); return ret; } else { assert(false); return nullptr; } } CProjectionGeometry3D* getSubProjectionGeometry_Angle(const CProjectionGeometry3D* pProjGeom, int th, int size) { // First convert to vectors, then convert into new object const CConeProjectionGeometry3D* conegeom = dynamic_cast(pProjGeom); const CParallelProjectionGeometry3D* par3dgeom = dynamic_cast(pProjGeom); const CParallelVecProjectionGeometry3D* parvec3dgeom = dynamic_cast(pProjGeom); const CConeVecProjectionGeometry3D* conevec3dgeom = dynamic_cast(pProjGeom); if (conegeom || conevec3dgeom) { std::vector coneProjs; if (conegeom) { coneProjs = getProjectionVectors(conegeom); } else { coneProjs = getProjectionVectors(conevec3dgeom); } CProjectionGeometry3D* ret = new CConeVecProjectionGeometry3D(size, pProjGeom->getDetectorRowCount(), pProjGeom->getDetectorColCount(), std::vector(coneProjs.begin() + th, coneProjs.begin() + th+size)); return ret; } else { assert(par3dgeom || parvec3dgeom); std::vector parProjs; if (par3dgeom) { parProjs = getProjectionVectors(par3dgeom); } else { parProjs = getProjectionVectors(parvec3dgeom); } CProjectionGeometry3D* ret = new CParallelVecProjectionGeometry3D(size, pProjGeom->getDetectorRowCount(), pProjGeom->getDetectorColCount(), std::vector(parProjs.begin() + th, parProjs.begin() + th+size)); return ret; } } // (See declaration in header for (mathematical) description of these functions) void computeBP_UV_Coeffs(const SPar3DProjection& proj, double &fUX, double &fUY, double &fUZ, double &fUC, double &fVX, double &fVY, double &fVZ, double &fVC) { double denom = (proj.fRayX*proj.fDetUY*proj.fDetVZ - proj.fRayX*proj.fDetUZ*proj.fDetVY - proj.fRayY*proj.fDetUX*proj.fDetVZ + proj.fRayY*proj.fDetUZ*proj.fDetVX + proj.fRayZ*proj.fDetUX*proj.fDetVY - proj.fRayZ*proj.fDetUY*proj.fDetVX); fUX = ( - (proj.fRayY*proj.fDetVZ - proj.fRayZ*proj.fDetVY)) / denom; fUY = ( (proj.fRayX*proj.fDetVZ - proj.fRayZ*proj.fDetVX)) / denom; fUZ = (- (proj.fRayX*proj.fDetVY - proj.fRayY*proj.fDetVX) ) / denom; fUC = (-(proj.fDetSY*proj.fDetVZ - proj.fDetSZ*proj.fDetVY)*proj.fRayX + (proj.fRayY*proj.fDetVZ - proj.fRayZ*proj.fDetVY)*proj.fDetSX - (proj.fRayY*proj.fDetSZ - proj.fRayZ*proj.fDetSY)*proj.fDetVX) / denom; fVX = ((proj.fRayY*proj.fDetUZ - proj.fRayZ*proj.fDetUY) ) / denom; fVY = (- (proj.fRayX*proj.fDetUZ - proj.fRayZ*proj.fDetUX) ) / denom; fVZ = ((proj.fRayX*proj.fDetUY - proj.fRayY*proj.fDetUX) ) / denom; fVC = ((proj.fDetSY*proj.fDetUZ - proj.fDetSZ*proj.fDetUY)*proj.fRayX - (proj.fRayY*proj.fDetUZ - proj.fRayZ*proj.fDetUY)*proj.fDetSX + (proj.fRayY*proj.fDetSZ - proj.fRayZ*proj.fDetSY)*proj.fDetUX ) / denom; } void computeBP_UV_Coeffs(const SConeProjection& proj, double &fUX, double &fUY, double &fUZ, double &fUC, double &fVX, double &fVY, double &fVZ, double &fVC, double &fDX, double &fDY, double &fDZ, double &fDC) { fUX = (proj.fDetSZ - proj.fSrcZ)*proj.fDetVY - (proj.fDetSY - proj.fSrcY)*proj.fDetVZ; fUY = (proj.fDetSX - proj.fSrcX)*proj.fDetVZ -(proj.fDetSZ - proj.fSrcZ)*proj.fDetVX; fUZ = (proj.fDetSY - proj.fSrcY)*proj.fDetVX - (proj.fDetSX - proj.fSrcX)*proj.fDetVY; fUC = (proj.fDetSY*proj.fDetVZ - proj.fDetSZ*proj.fDetVY)*proj.fSrcX - (proj.fDetSX*proj.fDetVZ - proj.fDetSZ*proj.fDetVX)*proj.fSrcY + (proj.fDetSX*proj.fDetVY - proj.fDetSY*proj.fDetVX)*proj.fSrcZ; fVX = (proj.fDetSY - proj.fSrcY)*proj.fDetUZ-(proj.fDetSZ - proj.fSrcZ)*proj.fDetUY; fVY = (proj.fDetSZ - proj.fSrcZ)*proj.fDetUX - (proj.fDetSX - proj.fSrcX)*proj.fDetUZ; fVZ = (proj.fDetSX - proj.fSrcX)*proj.fDetUY-(proj.fDetSY - proj.fSrcY)*proj.fDetUX; fVC = -(proj.fDetSY*proj.fDetUZ - proj.fDetSZ*proj.fDetUY)*proj.fSrcX + (proj.fDetSX*proj.fDetUZ - proj.fDetSZ*proj.fDetUX)*proj.fSrcY - (proj.fDetSX*proj.fDetUY - proj.fDetSY*proj.fDetUX)*proj.fSrcZ; fDX = proj.fDetUY*proj.fDetVZ - proj.fDetUZ*proj.fDetVY; fDY = proj.fDetUZ*proj.fDetVX - proj.fDetUX*proj.fDetVZ; fDZ = proj.fDetUX*proj.fDetVY - proj.fDetUY*proj.fDetVX; fDC = -proj.fSrcX * (proj.fDetUY*proj.fDetVZ - proj.fDetUZ*proj.fDetVY) - proj.fSrcY * (proj.fDetUZ*proj.fDetVX - proj.fDetUX*proj.fDetVZ) - proj.fSrcZ * (proj.fDetUX*proj.fDetVY - proj.fDetUY*proj.fDetVX); } } astra-toolbox-2.3.0/src/Globals.cpp000066400000000000000000000027461475635207100172130ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Globals.h" #include "astra/cuda/2d/astra.h" namespace astra { bool running_in_matlab=false; _AstraExport bool cudaAvailable() { #ifdef ASTRA_CUDA return astraCUDA::availableGPUMemory() > 0; #else return false; #endif } static bool (*pShouldAbortHook)(void) = 0; _AstraExport void setShouldAbortHook(bool (*_pShouldAbortHook)(void)) { pShouldAbortHook = _pShouldAbortHook; } _AstraExport bool shouldAbort() { if (pShouldAbortHook && (*pShouldAbortHook)()) return true; return false; } } astra-toolbox-2.3.0/src/Logging.cpp000066400000000000000000000111201475635207100172000ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define CLOG_MAIN #include #include "astra/Logging.h" #include "astra/Utilities.h" #include using namespace astra; void CLogger::enableScreen() { m_bEnabledScreen = true; } void CLogger::enableFile() { m_bEnabledFile = true; } void CLogger::enable() { enableScreen(); enableFile(); } void CLogger::disableScreen() { m_bEnabledScreen = false; } void CLogger::disableFile() { m_bEnabledFile = false; } void CLogger::disable() { disableScreen(); disableFile(); } void CLogger::debug(const char *sfile, int sline, const char *fmt, ...) { _assureIsInitialized(); va_list ap, apf; if(m_bEnabledScreen){ va_start(ap, fmt); clog_debug(sfile,sline,0,fmt,ap); va_end(ap); } if(m_bEnabledFile && m_bFileProvided){ va_start(apf, fmt); clog_debug(sfile,sline,1,fmt,apf); va_end(apf); } } void CLogger::info(const char *sfile, int sline, const char *fmt, ...) { _assureIsInitialized(); va_list ap, apf; if(m_bEnabledScreen){ va_start(ap, fmt); clog_info(sfile,sline,0,fmt,ap); va_end(ap); } if(m_bEnabledFile && m_bFileProvided){ va_start(apf, fmt); clog_info(sfile,sline,1,fmt,apf); va_end(apf); } } void CLogger::warn(const char *sfile, int sline, const char *fmt, ...) { _assureIsInitialized(); va_list ap, apf; if(m_bEnabledScreen){ va_start(ap, fmt); clog_warn(sfile,sline,0,fmt,ap); va_end(ap); } if(m_bEnabledFile && m_bFileProvided){ va_start(apf, fmt); clog_warn(sfile,sline,1,fmt,apf); va_end(apf); } } void CLogger::error(const char *sfile, int sline, const char *fmt, ...) { _assureIsInitialized(); va_list ap, apf, apl; if(m_bEnabledScreen){ va_start(ap, fmt); clog_error(sfile,sline,0,fmt,ap); va_end(ap); } if(m_bEnabledFile && m_bFileProvided){ va_start(apf, fmt); clog_error(sfile,sline,1,fmt,apf); va_end(apf); } va_start(apl, fmt); _setLastErrMsg(sfile,sline,fmt,apl); va_end(apl); } void CLogger::_setLevel(int id, log_level m_eLevel) { switch(m_eLevel){ case LOG_DEBUG: clog_set_level(id,CLOG_DEBUG); break; case LOG_INFO: clog_set_level(id,CLOG_INFO); break; case LOG_WARN: clog_set_level(id,CLOG_WARN); break; case LOG_ERROR: clog_set_level(id,CLOG_ERROR); break; } } void CLogger::setOutputScreen(int fd, log_level m_eLevel) { _assureIsInitialized(); if(fd==1||fd==2){ clog_set_fd(0, fd); }else{ error(__FILE__,__LINE__,"Invalid file descriptor"); } _setLevel(0,m_eLevel); } void CLogger::setOutputFile(const char *filename, log_level m_eLevel) { if(m_bFileProvided){ clog_free(1); m_bFileProvided=false; } if(!clog_init_path(1,filename)){ m_bFileProvided=true; _setLevel(1,m_eLevel); } } void CLogger::_assureIsInitialized() { if(!m_bInitialized) { clog_init_fd(0, 2); clog_set_level(0, CLOG_INFO); clog_set_fmt(0, "%l: %m\n"); m_bInitialized = true; } } void CLogger::_setLastErrMsg(const char *sfile, int sline, const char *fmt, va_list ap) { m_sLastErrMsg = StringUtil::vformat(fmt, ap); } std::string CLogger::getLastErrMsg() { std::string err_msg = m_sLastErrMsg; m_sLastErrMsg.clear(); return err_msg; } void CLogger::setFormatFile(const char *fmt) { if(m_bFileProvided){ clog_set_fmt(1,fmt); }else{ error(__FILE__,__LINE__,"No log file specified"); } } void CLogger::setFormatScreen(const char *fmt) { clog_set_fmt(0,fmt); } CLogger::CLogger() { ; } bool CLogger::setCallbackScreen(void (*cb)(const char *msg, size_t len)){ _assureIsInitialized(); return clog_set_cb(0,cb)==0; } bool CLogger::m_bEnabledScreen = true; bool CLogger::m_bEnabledFile = true; bool CLogger::m_bFileProvided = false; bool CLogger::m_bInitialized = false; std::string CLogger::m_sLastErrMsg; astra-toolbox-2.3.0/src/ParallelBeamBlobKernelProjector2D.cpp000066400000000000000000000150251475635207100241610ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ParallelBeamBlobKernelProjector2D.h" #include #include #include "astra/DataProjectorPolicies.h" #include "astra/Logging.h" using namespace std; using namespace astra; #include "astra/ParallelBeamBlobKernelProjector2D.inl" //---------------------------------------------------------------------------------------- // default constructor CParallelBeamBlobKernelProjector2D::CParallelBeamBlobKernelProjector2D() { _clear(); } //---------------------------------------------------------------------------------------- // constructor CParallelBeamBlobKernelProjector2D::CParallelBeamBlobKernelProjector2D(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry, float32 _fBlobSize, float32 _fBlobSampleRate, int _iBlobSampleCount, float32* _pfBlobValues) { _clear(); initialize(_pProjectionGeometry, _pReconstructionGeometry, _fBlobSize, _fBlobSampleRate, _iBlobSampleCount, _pfBlobValues); } //---------------------------------------------------------------------------------------- // destructor CParallelBeamBlobKernelProjector2D::~CParallelBeamBlobKernelProjector2D() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CParallelBeamBlobKernelProjector2D::_clear() { CProjector2D::_clear(); m_pfBlobValues.clear(); m_iBlobSampleCount = 0; m_fBlobSize = 0; m_fBlobSampleRate = 0; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CParallelBeamBlobKernelProjector2D::clear() { CProjector2D::clear(); m_pfBlobValues.clear(); m_iBlobSampleCount = 0; m_fBlobSize = 0; m_fBlobSampleRate = 0; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Check bool CParallelBeamBlobKernelProjector2D::_check() { // check base class ASTRA_CONFIG_CHECK(CProjector2D::_check(), "ParallelBeamBlobKernelProjector2D", "Error in Projector2D initialization"); ASTRA_CONFIG_CHECK(dynamic_cast(m_pProjectionGeometry) || dynamic_cast(m_pProjectionGeometry), "ParallelBeamBlobKernelProjector2D", "Unsupported projection geometry"); ASTRA_CONFIG_CHECK(m_iBlobSampleCount > 0, "ParallelBeamBlobKernelProjector2D", "m_iBlobSampleCount should be strictly positive."); // success return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CParallelBeamBlobKernelProjector2D::initialize(const Config& _cfg) { ConfigReader CR("ParallelBeamBlobKernelProjector2D", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CProjector2D::initialize(_cfg)) { return false; } Config *subcfg; std::string _type; if (!CR.getRequiredSubConfig("Kernel", subcfg, _type)) return false; { ConfigReader SCR("ParallelBeamBlobKernelProjector2D::Kernel", this, *subcfg); bool ok = true; ok &= SCR.getRequiredNumerical("KernelSize", m_fBlobSize); ok &= SCR.getRequiredNumerical("SampleRate", m_fBlobSampleRate); ok &= SCR.getRequiredInt("SampleCount", m_iBlobSampleCount); ok &= SCR.getRequiredNumericalArray("KernelValues", m_pfBlobValues); delete subcfg; if (!ok) return false; ASTRA_CONFIG_CHECK(m_pfBlobValues.size() == (unsigned int)m_iBlobSampleCount, "BlobProjector", "Number of specified values doesn't match SampleCount."); } // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // initialize bool CParallelBeamBlobKernelProjector2D::initialize(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pVolumeGeometry, float32 _fBlobSize, float32 _fBlobSampleRate, int _iBlobSampleCount, float32* _pfBlobValues) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } m_pProjectionGeometry = _pProjectionGeometry.clone(); m_pVolumeGeometry = _pVolumeGeometry.clone(); m_fBlobSize = _fBlobSize; m_fBlobSampleRate = _fBlobSampleRate; m_iBlobSampleCount = _iBlobSampleCount; m_pfBlobValues.resize(_iBlobSampleCount); for (int i = 0; i <_iBlobSampleCount; i++) { m_pfBlobValues[i] = _pfBlobValues[i]; } // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Get maximum amount of weights on a single ray int CParallelBeamBlobKernelProjector2D::getProjectionWeightsCount(int _iProjectionIndex) { int maxDim = max(m_pVolumeGeometry->getGridRowCount(), m_pVolumeGeometry->getGridColCount()); return (int)(maxDim * 2 * (m_fBlobSize+2) + 1); } //---------------------------------------------------------------------------------------- // Single Ray Weights void CParallelBeamBlobKernelProjector2D::computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount) { ASTRA_ASSERT(m_bIsInitialized); StorePixelWeightsPolicy p(_pWeightedPixels, _iMaxPixelCount); projectSingleRay(_iProjectionIndex, _iDetectorIndex, p); _iStoredPixelCount = p.getStoredPixelCount(); } astra-toolbox-2.3.0/src/ParallelBeamDistanceDrivenProjector2D.cpp000066400000000000000000000124601475635207100250440ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ParallelBeamDistanceDrivenProjector2D.h" #include #include #include "astra/DataProjectorPolicies.h" #include "astra/Logging.h" namespace astra { #include "astra/ParallelBeamDistanceDrivenProjector2D.inl" //---------------------------------------------------------------------------------------- // default constructor CParallelBeamDistanceDrivenProjector2D::CParallelBeamDistanceDrivenProjector2D() { _clear(); } //---------------------------------------------------------------------------------------- // constructor CParallelBeamDistanceDrivenProjector2D::CParallelBeamDistanceDrivenProjector2D(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry) { _clear(); initialize(_pProjectionGeometry, _pReconstructionGeometry); } //---------------------------------------------------------------------------------------- // destructor CParallelBeamDistanceDrivenProjector2D::~CParallelBeamDistanceDrivenProjector2D() { clear(); } //--------------------------------------------------------------------------------------- // Clear - CParallelBeamDistanceDrivenProjector2D void CParallelBeamDistanceDrivenProjector2D::_clear() { CProjector2D::_clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CParallelBeamDistanceDrivenProjector2D::clear() { CProjector2D::clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Check bool CParallelBeamDistanceDrivenProjector2D::_check() { // check base class ASTRA_CONFIG_CHECK(CProjector2D::_check(), "ParallelBeamDistanceDrivenProjector2D", "Error in Projector2D initialization"); ASTRA_CONFIG_CHECK(dynamic_cast(m_pProjectionGeometry) || dynamic_cast(m_pProjectionGeometry), "ParallelBeamDistanceDrivenProjector2D", "Unsupported projection geometry"); ASTRA_CONFIG_CHECK(abs(m_pVolumeGeometry->getPixelLengthX() / m_pVolumeGeometry->getPixelLengthY()) - 1 < eps, "ParallelBeamDistanceDrivenProjector2D", "Pixel height must equal pixel width."); // success return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CParallelBeamDistanceDrivenProjector2D::initialize(const Config& _cfg) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CProjector2D::initialize(_cfg)) { return false; } // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize bool CParallelBeamDistanceDrivenProjector2D::initialize(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pVolumeGeometry) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // hardcopy geometries m_pProjectionGeometry = _pProjectionGeometry.clone(); m_pVolumeGeometry = _pVolumeGeometry.clone(); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Get maximum amount of weights on a single ray int CParallelBeamDistanceDrivenProjector2D::getProjectionWeightsCount(int _iProjectionIndex) { int maxDim = std::max(m_pVolumeGeometry->getGridRowCount(), m_pVolumeGeometry->getGridColCount()); int scale = m_pProjectionGeometry->getDetectorWidth() / std::min(m_pVolumeGeometry->getPixelLengthX(), m_pVolumeGeometry->getPixelLengthY()); return maxDim * scale * 10 + 1; } //---------------------------------------------------------------------------------------- // Single Ray Weights void CParallelBeamDistanceDrivenProjector2D::computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount) { ASTRA_ASSERT(m_bIsInitialized); StorePixelWeightsPolicy p(_pWeightedPixels, _iMaxPixelCount); projectSingleRay(_iProjectionIndex, _iDetectorIndex, p); _iStoredPixelCount = p.getStoredPixelCount(); } } astra-toolbox-2.3.0/src/ParallelBeamLineKernelProjector2D.cpp000066400000000000000000000120341475635207100241670ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ParallelBeamLineKernelProjector2D.h" #include #include #include "astra/DataProjectorPolicies.h" #include "astra/Logging.h" using namespace std; using namespace astra; #include "astra/ParallelBeamLineKernelProjector2D.inl" //---------------------------------------------------------------------------------------- // default constructor CParallelBeamLineKernelProjector2D::CParallelBeamLineKernelProjector2D() { _clear(); } //---------------------------------------------------------------------------------------- // constructor CParallelBeamLineKernelProjector2D::CParallelBeamLineKernelProjector2D(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry) { _clear(); initialize(_pProjectionGeometry, _pReconstructionGeometry); } //---------------------------------------------------------------------------------------- // destructor CParallelBeamLineKernelProjector2D::~CParallelBeamLineKernelProjector2D() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CParallelBeamLineKernelProjector2D::_clear() { CProjector2D::_clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CParallelBeamLineKernelProjector2D::clear() { CProjector2D::clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Check bool CParallelBeamLineKernelProjector2D::_check() { // check base class ASTRA_CONFIG_CHECK(CProjector2D::_check(), "ParallelBeamLineKernelProjector2D", "Error in Projector2D initialization"); ASTRA_CONFIG_CHECK(dynamic_cast(m_pProjectionGeometry) || dynamic_cast(m_pProjectionGeometry), "ParallelBeamLineKernelProjector2D", "Unsupported projection geometry"); ASTRA_CONFIG_CHECK(abs(m_pVolumeGeometry->getPixelLengthX() / m_pVolumeGeometry->getPixelLengthY()) - 1 < eps, "ParallelBeamLineKernelProjector2D", "Pixel height must equal pixel width."); // success return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CParallelBeamLineKernelProjector2D::initialize(const Config& _cfg) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CProjector2D::initialize(_cfg)) { return false; } // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize bool CParallelBeamLineKernelProjector2D::initialize(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pVolumeGeometry) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // hardcopy geometries m_pProjectionGeometry = _pProjectionGeometry.clone(); m_pVolumeGeometry = _pVolumeGeometry.clone(); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Get maximum amount of weights on a single ray int CParallelBeamLineKernelProjector2D::getProjectionWeightsCount(int _iProjectionIndex) { int maxDim = max(m_pVolumeGeometry->getGridRowCount(), m_pVolumeGeometry->getGridColCount()); return maxDim * 2 + 1; } //---------------------------------------------------------------------------------------- // Single Ray Weights void CParallelBeamLineKernelProjector2D::computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount) { ASTRA_ASSERT(m_bIsInitialized); StorePixelWeightsPolicy p(_pWeightedPixels, _iMaxPixelCount); projectSingleRay(_iProjectionIndex, _iDetectorIndex, p); _iStoredPixelCount = p.getStoredPixelCount(); } astra-toolbox-2.3.0/src/ParallelBeamLinearKernelProjector2D.cpp000066400000000000000000000122001475635207100245050ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ParallelBeamLinearKernelProjector2D.h" #include #include #include "astra/DataProjectorPolicies.h" #include "astra/Logging.h" using namespace std; using namespace astra; #include "astra/ParallelBeamLinearKernelProjector2D.inl" //---------------------------------------------------------------------------------------- // default constructor CParallelBeamLinearKernelProjector2D::CParallelBeamLinearKernelProjector2D() { _clear(); } //---------------------------------------------------------------------------------------- // constructor CParallelBeamLinearKernelProjector2D::CParallelBeamLinearKernelProjector2D(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry) { _clear(); initialize(_pProjectionGeometry, _pReconstructionGeometry); } //---------------------------------------------------------------------------------------- // destructor CParallelBeamLinearKernelProjector2D::~CParallelBeamLinearKernelProjector2D() { clear(); } //--------------------------------------------------------------------------------------- // Clear - CParallelBeamLinearKernelProjector2D void CParallelBeamLinearKernelProjector2D::_clear() { CProjector2D::_clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CParallelBeamLinearKernelProjector2D::clear() { CProjector2D::clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Check bool CParallelBeamLinearKernelProjector2D::_check() { // check base class ASTRA_CONFIG_CHECK(CProjector2D::_check(), "ParallelBeamLinearKernelProjector2D", "Error in Projector2D initialization"); ASTRA_CONFIG_CHECK(dynamic_cast(m_pProjectionGeometry) || dynamic_cast(m_pProjectionGeometry), "ParallelBeamLinearKernelProjector2D", "Unsupported projection geometry"); /// TODO: ADD PIXEL H/W LIMITATIONS ASTRA_CONFIG_CHECK(abs(m_pVolumeGeometry->getPixelLengthX() / m_pVolumeGeometry->getPixelLengthY()) - 1 < eps, "ParallelBeamLinearKernelProjector2D", "Pixel height must equal pixel width."); // success return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CParallelBeamLinearKernelProjector2D::initialize(const Config& _cfg) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CProjector2D::initialize(_cfg)) { return false; } // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize bool CParallelBeamLinearKernelProjector2D::initialize(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pVolumeGeometry) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // hardcopy geometries m_pProjectionGeometry = _pProjectionGeometry.clone(); m_pVolumeGeometry = _pVolumeGeometry.clone(); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Get maximum amount of weights on a single ray int CParallelBeamLinearKernelProjector2D::getProjectionWeightsCount(int _iProjectionIndex) { int maxDim = max(m_pVolumeGeometry->getGridRowCount(), m_pVolumeGeometry->getGridColCount()); return maxDim * 2 + 1; } //---------------------------------------------------------------------------------------- // Single Ray Weights void CParallelBeamLinearKernelProjector2D::computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount) { ASTRA_ASSERT(m_bIsInitialized); StorePixelWeightsPolicy p(_pWeightedPixels, _iMaxPixelCount); projectSingleRay(_iProjectionIndex, _iDetectorIndex, p); _iStoredPixelCount = p.getStoredPixelCount(); } astra-toolbox-2.3.0/src/ParallelBeamStripKernelProjector2D.cpp000066400000000000000000000124701475635207100244050ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ParallelBeamStripKernelProjector2D.h" #include #include #include "astra/DataProjectorPolicies.h" #include "astra/Logging.h" using namespace std; using namespace astra; #include "astra/ParallelBeamStripKernelProjector2D.inl" //---------------------------------------------------------------------------------------- // default constructor CParallelBeamStripKernelProjector2D::CParallelBeamStripKernelProjector2D() { _clear(); } //---------------------------------------------------------------------------------------- // constructor CParallelBeamStripKernelProjector2D::CParallelBeamStripKernelProjector2D(const CParallelProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry) { _clear(); initialize(_pProjectionGeometry, _pReconstructionGeometry); } //---------------------------------------------------------------------------------------- // destructor CParallelBeamStripKernelProjector2D::~CParallelBeamStripKernelProjector2D() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CParallelBeamStripKernelProjector2D::_clear() { CProjector2D::_clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CParallelBeamStripKernelProjector2D::clear() { CProjector2D::clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Check bool CParallelBeamStripKernelProjector2D::_check() { // check base class ASTRA_CONFIG_CHECK(CProjector2D::_check(), "ParallelBeamStripKernelProjector2D", "Error in Projector2D initialization"); ASTRA_CONFIG_CHECK(dynamic_cast(m_pProjectionGeometry) || dynamic_cast(m_pProjectionGeometry), "ParallelBeamStripKernelProjector2D", "Unsupported projection geometry"); ASTRA_CONFIG_CHECK(abs(m_pVolumeGeometry->getPixelLengthX() / m_pVolumeGeometry->getPixelLengthY()) - 1 < eps, "ParallelBeamStripKernelProjector2D", "Pixel height must equal pixel width."); // success return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CParallelBeamStripKernelProjector2D::initialize(const Config& _cfg) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CProjector2D::initialize(_cfg)) { return false; } // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize bool CParallelBeamStripKernelProjector2D::initialize(const CParallelProjectionGeometry2D& _pProjectionGeometry, const CVolumeGeometry2D& _pVolumeGeometry) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // hardcopy geometries m_pProjectionGeometry = _pProjectionGeometry.clone(); m_pVolumeGeometry = _pVolumeGeometry.clone(); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Get maximum amount of weights on a single ray int CParallelBeamStripKernelProjector2D::getProjectionWeightsCount(int _iProjectionIndex) { int maxDim = max(m_pVolumeGeometry->getGridRowCount(), m_pVolumeGeometry->getGridColCount()); double scale = max(m_pProjectionGeometry->getDetectorWidth() / min(m_pVolumeGeometry->getPixelLengthX(), m_pVolumeGeometry->getPixelLengthY()), 1.0f); return int(maxDim * scale * 10) + 1; } //---------------------------------------------------------------------------------------- // Single Ray Weights void CParallelBeamStripKernelProjector2D::computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount) { ASTRA_ASSERT(m_bIsInitialized); StorePixelWeightsPolicy p(_pWeightedPixels, _iMaxPixelCount); projectSingleRay(_iProjectionIndex, _iDetectorIndex, p); _iStoredPixelCount = p.getStoredPixelCount(); } //---------------------------------------------------------------------------------------- astra-toolbox-2.3.0/src/ParallelProjectionGeometry2D.cpp000066400000000000000000000144761475635207100233260ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ParallelProjectionGeometry2D.h" #include "astra/GeometryUtil2D.h" #include "astra/XMLConfig.h" #include using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Default constructor. CParallelProjectionGeometry2D::CParallelProjectionGeometry2D() : CProjectionGeometry2D() { } //---------------------------------------------------------------------------------------- // Constructor. CParallelProjectionGeometry2D::CParallelProjectionGeometry2D(int _iProjectionAngleCount, int _iDetectorCount, float32 _fDetectorWidth, const float32* _pfProjectionAngles) { _clear(); initialize(_iProjectionAngleCount, _iDetectorCount, _fDetectorWidth, _pfProjectionAngles); } //---------------------------------------------------------------------------------------- CParallelProjectionGeometry2D::CParallelProjectionGeometry2D(const CParallelProjectionGeometry2D& _projGeom) { _clear(); initialize(_projGeom.m_iProjectionAngleCount, _projGeom.m_iDetectorCount, _projGeom.m_fDetectorWidth, _projGeom.m_pfProjectionAngles); } //---------------------------------------------------------------------------------------- CParallelProjectionGeometry2D& CParallelProjectionGeometry2D::operator=(const CParallelProjectionGeometry2D& _other) { if (m_bInitialized) delete[] m_pfProjectionAngles; m_bInitialized = _other.m_bInitialized; if (_other.m_bInitialized) { m_iProjectionAngleCount = _other.m_iProjectionAngleCount; m_iDetectorCount = _other.m_iDetectorCount; m_fDetectorWidth = _other.m_fDetectorWidth; m_pfProjectionAngles = new float32[m_iProjectionAngleCount]; memcpy(m_pfProjectionAngles, _other.m_pfProjectionAngles, sizeof(float32)*m_iProjectionAngleCount); } return *this; } //---------------------------------------------------------------------------------------- // Destructor. CParallelProjectionGeometry2D::~CParallelProjectionGeometry2D() { } //--------------------------------------------------------------------------------------- // Initialize - Config bool CParallelProjectionGeometry2D::initialize(const Config& _cfg) { ConfigReader CR("ParallelProjectionGeometry2D", this, _cfg); // initialization of parent class if (!CProjectionGeometry2D::initialize(_cfg)) return false; // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization. bool CParallelProjectionGeometry2D::initialize(int _iProjectionAngleCount, int _iDetectorCount, float32 _fDetectorWidth, const float32* _pfProjectionAngles) { _initialize(_iProjectionAngleCount, _iDetectorCount, _fDetectorWidth, _pfProjectionAngles); // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Clone CProjectionGeometry2D* CParallelProjectionGeometry2D::clone() const { return new CParallelProjectionGeometry2D(*this); } //---------------------------------------------------------------------------------------- // is equal bool CParallelProjectionGeometry2D::isEqual(const CProjectionGeometry2D &_pGeom2) const { // try to cast argument to CParallelProjectionGeometry2D const CParallelProjectionGeometry2D* pGeom2 = dynamic_cast(&_pGeom2); if (pGeom2 == NULL) return false; // both objects must be initialized if (!m_bInitialized || !pGeom2->m_bInitialized) return false; // check all values if (m_iProjectionAngleCount != pGeom2->m_iProjectionAngleCount) return false; if (m_iDetectorCount != pGeom2->m_iDetectorCount) return false; if (m_fDetectorWidth != pGeom2->m_fDetectorWidth) return false; for (int i = 0; i < m_iProjectionAngleCount; ++i) { // if (m_pfProjectionAngles[i] != pGeom2->m_pfProjectionAngles[i]) return false; } return true; } //---------------------------------------------------------------------------------------- // is of type bool CParallelProjectionGeometry2D::isOfType(const std::string& _sType) { return (_sType == "parallel"); } //---------------------------------------------------------------------------------------- // Get the configuration object Config* CParallelProjectionGeometry2D::getConfiguration() const { ConfigWriter CW("ProjectionGeometry2D", "parallel"); CW.addInt("DetectorCount", getDetectorCount()); CW.addNumerical("DetectorWidth", getDetectorWidth()); CW.addNumericalArray("ProjectionAngles", m_pfProjectionAngles, m_iProjectionAngleCount); return CW.getConfig(); } //---------------------------------------------------------------------------------------- CParallelVecProjectionGeometry2D* CParallelProjectionGeometry2D::toVectorGeometry() { SParProjection* vectors = genParProjections(m_iProjectionAngleCount, m_iDetectorCount, m_fDetectorWidth, m_pfProjectionAngles, 0); // TODO: ExtraOffsets? CParallelVecProjectionGeometry2D* vecGeom = new CParallelVecProjectionGeometry2D(); vecGeom->initialize(m_iProjectionAngleCount, m_iDetectorCount, vectors); delete[] vectors; return vecGeom; } } // end namespace astra astra-toolbox-2.3.0/src/ParallelProjectionGeometry3D.cpp000066400000000000000000000160021475635207100233120ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ParallelProjectionGeometry3D.h" #include "astra/XMLConfig.h" #include "astra/GeometryUtil3D.h" #include using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Default constructor. CParallelProjectionGeometry3D::CParallelProjectionGeometry3D() : CProjectionGeometry3D() { } //---------------------------------------------------------------------------------------- // Constructor. CParallelProjectionGeometry3D::CParallelProjectionGeometry3D(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, float32 _fDetectorWidth, float32 _fDetectorHeight, std::vector &&_pfProjectionAngles) : CProjectionGeometry3D() { initialize(_iProjectionAngleCount, _iDetectorRowCount, _iDetectorColCount, _fDetectorWidth, _fDetectorHeight, std::move(_pfProjectionAngles)); } //---------------------------------------------------------------------------------------- // Destructor. CParallelProjectionGeometry3D::~CParallelProjectionGeometry3D() { } //--------------------------------------------------------------------------------------- // Initialize - Config bool CParallelProjectionGeometry3D::initialize(const Config& _cfg) { ConfigReader CR("ParallelProjectionGeometry3D", this, _cfg); // initialization of parent class if (!CProjectionGeometry3D::initialize(_cfg)) return false; // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization. bool CParallelProjectionGeometry3D::initialize(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, float32 _fDetectorWidth, float32 _fDetectorHeight, std::vector &&_pfProjectionAngles) { _initialize(_iProjectionAngleCount, _iDetectorRowCount, _iDetectorColCount, _fDetectorWidth, _fDetectorHeight, std::move(_pfProjectionAngles)); // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Clone CProjectionGeometry3D* CParallelProjectionGeometry3D::clone() const { CParallelProjectionGeometry3D* res = new CParallelProjectionGeometry3D(); res->m_bInitialized = m_bInitialized; res->m_iProjectionAngleCount = m_iProjectionAngleCount; res->m_iDetectorRowCount = m_iDetectorRowCount; res->m_iDetectorColCount = m_iDetectorColCount; res->m_iDetectorTotCount = m_iDetectorTotCount; res->m_fDetectorSpacingX = m_fDetectorSpacingX; res->m_fDetectorSpacingY = m_fDetectorSpacingY; res->m_pfProjectionAngles = m_pfProjectionAngles; return res; } //---------------------------------------------------------------------------------------- // is equal bool CParallelProjectionGeometry3D::isEqual(const CProjectionGeometry3D * _pGeom2) const { if (_pGeom2 == NULL) return false; // try to cast argument to CParallelProjectionGeometry3D const CParallelProjectionGeometry3D* pGeom2 = dynamic_cast(_pGeom2); if (pGeom2 == NULL) return false; // both objects must be initialized if (!m_bInitialized || !pGeom2->m_bInitialized) return false; // check all values if (m_iProjectionAngleCount != pGeom2->m_iProjectionAngleCount) return false; if (m_iDetectorRowCount != pGeom2->m_iDetectorRowCount) return false; if (m_iDetectorColCount != pGeom2->m_iDetectorColCount) return false; if (m_iDetectorTotCount != pGeom2->m_iDetectorTotCount) return false; if (m_fDetectorSpacingX != pGeom2->m_fDetectorSpacingX) return false; if (m_fDetectorSpacingY != pGeom2->m_fDetectorSpacingY) return false; for (int i = 0; i < m_iProjectionAngleCount; ++i) { if (m_pfProjectionAngles[i] != pGeom2->m_pfProjectionAngles[i]) return false; } return true; } //---------------------------------------------------------------------------------------- // is of type bool CParallelProjectionGeometry3D::isOfType(const std::string& _sType) const { return (_sType == "parallel3d"); } //---------------------------------------------------------------------------------------- // Get the configuration object Config* CParallelProjectionGeometry3D::getConfiguration() const { ConfigWriter CW("ProjectionGeometry3D", "parallel3d"); CW.addInt("DetectorRowCount", m_iDetectorRowCount); CW.addInt("DetectorColCount", m_iDetectorColCount); CW.addNumerical("DetectorSpacingX", m_fDetectorSpacingX); CW.addNumerical("DetectorSpacingY", m_fDetectorSpacingY); CW.addNumericalArray("ProjectionAngles", &m_pfProjectionAngles[0], m_iProjectionAngleCount); return CW.getConfig(); } //---------------------------------------------------------------------------------------- void CParallelProjectionGeometry3D::projectPoint(double fX, double fY, double fZ, int iAngleIndex, double &fU, double &fV) const { ASTRA_ASSERT(iAngleIndex >= 0); ASTRA_ASSERT(iAngleIndex < m_iProjectionAngleCount); // V (detector row) fV = detectorOffsetYToRowIndexFloat(fZ); // U (detector column) float alpha = m_pfProjectionAngles[iAngleIndex]; // projector direction is (cos(alpha), sin(alpha)) fU = detectorOffsetXToColIndexFloat(cos(alpha) * fX + sin(alpha) * fY); } CParallelProjectionGeometry2D * CParallelProjectionGeometry3D::createProjectionGeometry2D() const { const float32 * pfProjectionAngles = getProjectionAngles(); //new float32[getProjectionCount()]; //getProjectionAngles(pfProjectionAngles); CParallelProjectionGeometry2D * pOutput = new CParallelProjectionGeometry2D(getProjectionCount(), getDetectorColCount(), getDetectorSpacingX(), pfProjectionAngles); //delete [] pfProjectionAngles; return pOutput; } //---------------------------------------------------------------------------------------- } // end namespace astra astra-toolbox-2.3.0/src/ParallelVecProjectionGeometry2D.cpp000066400000000000000000000156721475635207100237630ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ParallelVecProjectionGeometry2D.h" #include "astra/XMLConfig.h" #include "astra/Logging.h" #include #include using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Default constructor. Sets all variables to zero. CParallelVecProjectionGeometry2D::CParallelVecProjectionGeometry2D() { _clear(); m_pProjectionAngles = 0; } //---------------------------------------------------------------------------------------- // Constructor. CParallelVecProjectionGeometry2D::CParallelVecProjectionGeometry2D(int _iProjectionAngleCount, int _iDetectorCount, const SParProjection* _pProjectionAngles) { this->initialize(_iProjectionAngleCount, _iDetectorCount, _pProjectionAngles); } //---------------------------------------------------------------------------------------- // Copy Constructor CParallelVecProjectionGeometry2D::CParallelVecProjectionGeometry2D(const CParallelVecProjectionGeometry2D& _projGeom) { _clear(); this->initialize(_projGeom.m_iProjectionAngleCount, _projGeom.m_iDetectorCount, _projGeom.m_pProjectionAngles); } //---------------------------------------------------------------------------------------- // Destructor. CParallelVecProjectionGeometry2D::~CParallelVecProjectionGeometry2D() { // TODO delete[] m_pProjectionAngles; } //---------------------------------------------------------------------------------------- // Initialization. bool CParallelVecProjectionGeometry2D::initialize(int _iProjectionAngleCount, int _iDetectorCount, const SParProjection* _pProjectionAngles) { m_iProjectionAngleCount = _iProjectionAngleCount; m_iDetectorCount = _iDetectorCount; m_pProjectionAngles = new SParProjection[m_iProjectionAngleCount]; for (int i = 0; i < m_iProjectionAngleCount; ++i) m_pProjectionAngles[i] = _pProjectionAngles[i]; // TODO: check? // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization with a Config object bool CParallelVecProjectionGeometry2D::initialize(const Config& _cfg) { ConfigReader CR("ParallelVecProjectionGeometry2D", this, _cfg); // initialization of parent class if (!CProjectionGeometry2D::initialize(_cfg)) return false; // success m_bInitialized = _check(); return m_bInitialized; } bool CParallelVecProjectionGeometry2D::initializeAngles(const Config& _cfg) { ConfigReader CR("ParallelVecProjectionGeometry2D", this, _cfg); // Required: Vectors vector data; if (!CR.getRequiredNumericalArray("Vectors", data)) return false; ASTRA_CONFIG_CHECK(data.size() % 6 == 0, "ParallelVecProjectionGeometry2D", "Vectors doesn't consist of 6-tuples."); m_iProjectionAngleCount = data.size() / 6; m_pProjectionAngles = new SParProjection[m_iProjectionAngleCount]; for (int i = 0; i < m_iProjectionAngleCount; ++i) { SParProjection& p = m_pProjectionAngles[i]; p.fRayX = data[6*i + 0]; p.fRayY = data[6*i + 1]; p.fDetUX = data[6*i + 4]; p.fDetUY = data[6*i + 5]; // The backend code currently expects the corner of the detector, while // the matlab interface supplies the center p.fDetSX = data[6*i + 2] - 0.5 * m_iDetectorCount * p.fDetUX; p.fDetSY = data[6*i + 3] - 0.5 * m_iDetectorCount * p.fDetUY; } return true; } //---------------------------------------------------------------------------------------- // Clone CProjectionGeometry2D* CParallelVecProjectionGeometry2D::clone() const { return new CParallelVecProjectionGeometry2D(*this); } //---------------------------------------------------------------------------------------- // is equal bool CParallelVecProjectionGeometry2D::isEqual(const CProjectionGeometry2D &_pGeom2) const { // try to cast argument to CParallelVecProjectionGeometry2D const CParallelVecProjectionGeometry2D* pGeom2 = dynamic_cast(&_pGeom2); if (pGeom2 == NULL) return false; // both objects must be initialized if (!m_bInitialized || !pGeom2->m_bInitialized) return false; // check all values if (m_iProjectionAngleCount != pGeom2->m_iProjectionAngleCount) return false; if (m_iDetectorCount != pGeom2->m_iDetectorCount) return false; for (int i = 0; i < m_iProjectionAngleCount; ++i) { if (memcmp(&m_pProjectionAngles[i], &pGeom2->m_pProjectionAngles[i], sizeof(m_pProjectionAngles[i])) != 0) return false; } return true; } //---------------------------------------------------------------------------------------- // Is of type bool CParallelVecProjectionGeometry2D::isOfType(const std::string& _sType) { return (_sType == "parallel_vec"); } //---------------------------------------------------------------------------------------- bool CParallelVecProjectionGeometry2D::_check() { // TODO return true; } //---------------------------------------------------------------------------------------- // Get the configuration object Config* CParallelVecProjectionGeometry2D::getConfiguration() const { ConfigWriter CW("ProjectionGeometry2D", "parallel_vec"); CW.addInt("DetectorCount", getDetectorCount()); std::vector vectors; vectors.resize(6 * m_iProjectionAngleCount); for (int i = 0; i < m_iProjectionAngleCount; ++i) { SParProjection& p = m_pProjectionAngles[i]; vectors[6*i + 0] = p.fRayX; vectors[6*i + 1] = p.fRayY; vectors[6*i + 2] = p.fDetSX + 0.5 * m_iDetectorCount * p.fDetUX; vectors[6*i + 3] = p.fDetSY + 0.5 * m_iDetectorCount * p.fDetUY; vectors[6*i + 4] = p.fDetUX; vectors[6*i + 5] = p.fDetUY; } CW.addNumericalMatrix("Vectors", &vectors[0], m_iProjectionAngleCount, 6); return CW.getConfig(); } //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/ParallelVecProjectionGeometry3D.cpp000066400000000000000000000220271475635207100237540ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ParallelVecProjectionGeometry3D.h" #include "astra/Utilities.h" #include "astra/XMLConfig.h" #include "astra/Logging.h" #include using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Default constructor. CParallelVecProjectionGeometry3D::CParallelVecProjectionGeometry3D() : CProjectionGeometry3D() { } //---------------------------------------------------------------------------------------- // Constructor. CParallelVecProjectionGeometry3D::CParallelVecProjectionGeometry3D(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, std::vector &&_ProjectionAngles) : CProjectionGeometry3D() { initialize(_iProjectionAngleCount, _iDetectorRowCount, _iDetectorColCount, std::move(_ProjectionAngles)); } //---------------------------------------------------------------------------------------- // Destructor. CParallelVecProjectionGeometry3D::~CParallelVecProjectionGeometry3D() { } //--------------------------------------------------------------------------------------- // Initialize - Config bool CParallelVecProjectionGeometry3D::initialize(const Config& _cfg) { ConfigReader CR("ParallelVecProjectionGeometry3D", this, _cfg); XMLNode node; // initialization of parent class if (!CProjectionGeometry3D::initialize(_cfg)) return false; // success m_bInitialized = _check(); return m_bInitialized; } bool CParallelVecProjectionGeometry3D::initializeAngles(const Config& _cfg) { ConfigReader CR("ParallelVecProjectionGeometry3D", this, _cfg); // Required: Vectors vector data; if (!CR.getRequiredNumericalArray("Vectors", data)) return false; ASTRA_CONFIG_CHECK(data.size() % 12 == 0, "ParallelVecProjectionGeometry3D", "Vectors doesn't consist of 12-tuples."); m_iProjectionAngleCount = data.size() / 12; m_ProjectionAngles.resize(m_iProjectionAngleCount); for (int i = 0; i < m_iProjectionAngleCount; ++i) { SPar3DProjection& p = m_ProjectionAngles[i]; p.fRayX = data[12*i + 0]; p.fRayY = data[12*i + 1]; p.fRayZ = data[12*i + 2]; p.fDetUX = data[12*i + 6]; p.fDetUY = data[12*i + 7]; p.fDetUZ = data[12*i + 8]; p.fDetVX = data[12*i + 9]; p.fDetVY = data[12*i + 10]; p.fDetVZ = data[12*i + 11]; // The backend code currently expects the corner of the detector, while // the matlab interface supplies the center p.fDetSX = data[12*i + 3] - 0.5 * m_iDetectorRowCount * p.fDetVX - 0.5 * m_iDetectorColCount * p.fDetUX; p.fDetSY = data[12*i + 4] - 0.5 * m_iDetectorRowCount * p.fDetVY - 0.5 * m_iDetectorColCount * p.fDetUY; p.fDetSZ = data[12*i + 5] - 0.5 * m_iDetectorRowCount * p.fDetVZ - 0.5 * m_iDetectorColCount * p.fDetUZ; } return true; } //---------------------------------------------------------------------------------------- // Initialization. bool CParallelVecProjectionGeometry3D::initialize(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, std::vector &&_ProjectionAngles) { m_iProjectionAngleCount = _iProjectionAngleCount; m_iDetectorRowCount = _iDetectorRowCount; m_iDetectorColCount = _iDetectorColCount; m_ProjectionAngles = std::move(_ProjectionAngles); // TODO: check? // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Clone CProjectionGeometry3D* CParallelVecProjectionGeometry3D::clone() const { CParallelVecProjectionGeometry3D* res = new CParallelVecProjectionGeometry3D(); res->m_bInitialized = m_bInitialized; res->m_iProjectionAngleCount = m_iProjectionAngleCount; res->m_iDetectorRowCount = m_iDetectorRowCount; res->m_iDetectorColCount = m_iDetectorColCount; res->m_iDetectorTotCount = m_iDetectorTotCount; res->m_fDetectorSpacingX = m_fDetectorSpacingX; res->m_fDetectorSpacingY = m_fDetectorSpacingY; res->m_ProjectionAngles = m_ProjectionAngles; return res; } //---------------------------------------------------------------------------------------- // is equal bool CParallelVecProjectionGeometry3D::isEqual(const CProjectionGeometry3D * _pGeom2) const { if (_pGeom2 == NULL) return false; // try to cast argument to CParallelProjectionGeometry3D const CParallelVecProjectionGeometry3D* pGeom2 = dynamic_cast(_pGeom2); if (pGeom2 == NULL) return false; // both objects must be initialized if (!m_bInitialized || !pGeom2->m_bInitialized) return false; // check all values if (m_iProjectionAngleCount != pGeom2->m_iProjectionAngleCount) return false; if (m_iDetectorRowCount != pGeom2->m_iDetectorRowCount) return false; if (m_iDetectorColCount != pGeom2->m_iDetectorColCount) return false; if (m_iDetectorTotCount != pGeom2->m_iDetectorTotCount) return false; //if (m_fDetectorSpacingX != pGeom2->m_fDetectorSpacingX) return false; //if (m_fDetectorSpacingY != pGeom2->m_fDetectorSpacingY) return false; for (int i = 0; i < m_iProjectionAngleCount; ++i) { if (memcmp(&m_ProjectionAngles[i], &pGeom2->m_ProjectionAngles[i], sizeof(m_ProjectionAngles[i])) != 0) return false; } return true; } //---------------------------------------------------------------------------------------- // is of type bool CParallelVecProjectionGeometry3D::isOfType(const std::string& _sType) const { return (_sType == "parallel3d_vec"); } //---------------------------------------------------------------------------------------- // Get the configuration object Config* CParallelVecProjectionGeometry3D::getConfiguration() const { ConfigWriter CW("ProjectionGeometry3D", "parallel3d_vec"); CW.addInt("DetectorRowCount", m_iDetectorRowCount); CW.addInt("DetectorColCount", m_iDetectorColCount); std::vector vectors; vectors.resize(12 * m_iProjectionAngleCount); for (int i = 0; i < m_iProjectionAngleCount; ++i) { const SPar3DProjection& p = m_ProjectionAngles[i]; vectors[12*i + 0] = p.fRayX; vectors[12*i + 1] = p.fRayY; vectors[12*i + 2] = p.fRayZ; vectors[12*i + 3] = p.fDetSX + 0.5*m_iDetectorRowCount*p.fDetVX + 0.5*m_iDetectorColCount*p.fDetUX; vectors[12*i + 4] = p.fDetSY + 0.5*m_iDetectorRowCount*p.fDetVY + 0.5*m_iDetectorColCount*p.fDetUY; vectors[12*i + 5] = p.fDetSZ + 0.5*m_iDetectorRowCount*p.fDetVZ + 0.5*m_iDetectorColCount*p.fDetUZ; vectors[12*i + 6] = p.fDetUX; vectors[12*i + 7] = p.fDetUY; vectors[12*i + 8] = p.fDetUZ; vectors[12*i + 9] = p.fDetVX; vectors[12*i + 10] = p.fDetVY; vectors[12*i + 11] = p.fDetVZ; } CW.addNumericalMatrix("Vectors", &vectors[0], m_iProjectionAngleCount, 12); return CW.getConfig(); } //---------------------------------------------------------------------------------------- void CParallelVecProjectionGeometry3D::projectPoint(double fX, double fY, double fZ, int iAngleIndex, double &fU, double &fV) const { ASTRA_ASSERT(iAngleIndex >= 0); ASTRA_ASSERT(iAngleIndex < m_iProjectionAngleCount); double fUX, fUY, fUZ, fUC; double fVX, fVY, fVZ, fVC; computeBP_UV_Coeffs(m_ProjectionAngles[iAngleIndex], fUX, fUY, fUZ, fUC, fVX, fVY, fVZ, fVC); // The -0.5f shifts from corner to center of detector pixels fU = (fUX*fX + fUY*fY + fUZ*fZ + fUC) - 0.5; fV = (fVX*fX + fVY*fY + fVZ*fZ + fVC) - 0.5; } //---------------------------------------------------------------------------------------- bool CParallelVecProjectionGeometry3D::_check() { ASTRA_CONFIG_CHECK(m_ProjectionAngles.size() == m_iProjectionAngleCount, "ParallelVecProjectionGeometry3D", "Number of vectors does not match number of angles"); // TODO return true; } } // end namespace astra astra-toolbox-2.3.0/src/PlatformDepSystemCode.cpp000066400000000000000000000035221475635207100220360ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/PlatformDepSystemCode.h" using namespace astra; #ifdef _WIN32 #include "windows.h" // windows API available unsigned long CPlatformDepSystemCode::getMSCount() { return ::GetTickCount(); } int CPlatformDepSystemCode::fseek64(FILE * _pStream, astra::int64 _iOffset, int _iOrigin) { return _fseeki64(_pStream, _iOffset, _iOrigin); } astra::int64 CPlatformDepSystemCode::ftell64(FILE * _pStream) { return _ftelli64(_pStream); } #else // linux, ... #include unsigned long CPlatformDepSystemCode::getMSCount() { struct timeval tv; gettimeofday(&tv, 0); return (tv.tv_sec * 1000) + (tv.tv_usec/1000); } int CPlatformDepSystemCode::fseek64(FILE * _pStream, astra::int64 _iOffset, int _iOrigin) { return fseeko(_pStream, _iOffset, _iOrigin); } astra::int64 CPlatformDepSystemCode::ftell64(FILE * _pStream) { return ftello(_pStream); } #endif astra-toolbox-2.3.0/src/PluginAlgorithmFactory.cpp000066400000000000000000000021421475635207100222530ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/PluginAlgorithmFactory.h" namespace astra { CPluginAlgorithmFactory *CPluginAlgorithmFactory::m_factory = 0; } astra-toolbox-2.3.0/src/ProjectionGeometry2D.cpp000066400000000000000000000140031475635207100216330ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ProjectionGeometry2D.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Default constructor. CProjectionGeometry2D::CProjectionGeometry2D() : configCheckData(0) { _clear(); } //---------------------------------------------------------------------------------------- // Constructor. CProjectionGeometry2D::CProjectionGeometry2D(int _iAngleCount, int _iDetectorCount, float32 _fDetectorWidth, const float32* _pfProjectionAngles) : configCheckData(0) { _clear(); _initialize(_iAngleCount, _iDetectorCount, _fDetectorWidth, _pfProjectionAngles); } //---------------------------------------------------------------------------------------- // Destructor. CProjectionGeometry2D::~CProjectionGeometry2D() { if (m_bInitialized) { clear(); } } //---------------------------------------------------------------------------------------- // Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. // Should only be used by constructors. Otherwise use the clear() function. void CProjectionGeometry2D::_clear() { m_iProjectionAngleCount = 0; m_iDetectorCount = 0; m_fDetectorWidth = 0.0f; m_pfProjectionAngles = NULL; m_bInitialized = false; } //---------------------------------------------------------------------------------------- // Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. void CProjectionGeometry2D::clear() { m_iProjectionAngleCount = 0; m_iDetectorCount = 0; m_fDetectorWidth = 0.0f; if (m_bInitialized){ delete[] m_pfProjectionAngles; } m_pfProjectionAngles = NULL; m_bInitialized = false; } //---------------------------------------------------------------------------------------- // Check all variable values. bool CProjectionGeometry2D::_check() { ASTRA_CONFIG_CHECK(m_iDetectorCount > 0, "ProjectionGeometry2D", "Detector Count should be positive."); ASTRA_CONFIG_CHECK(m_fDetectorWidth > 0.0f, "ProjectionGeometry2D", "Detector Width should be positive."); ASTRA_CONFIG_CHECK(m_iProjectionAngleCount > 0, "ProjectionGeometry2D", "ProjectionAngleCount should be positive."); ASTRA_CONFIG_CHECK(m_pfProjectionAngles != NULL, "ProjectionGeometry2D", "ProjectionAngles not initialized"); // autofix: angles in [0,2pi[ for (int i = 0; i < m_iProjectionAngleCount; i++) { while (2*PI <= m_pfProjectionAngles[i]) m_pfProjectionAngles[i] -= 2*PI; while (m_pfProjectionAngles[i] < 0) m_pfProjectionAngles[i] += 2*PI; } // success return true; } //---------------------------------------------------------------------------------------- // Initialization with a Config object bool CProjectionGeometry2D::initialize(const Config& _cfg) { ConfigReader CR("ProjectionGeometry2D", this, _cfg); // uninitialize if the object was initialized before if (m_bInitialized) { clear(); } bool ok = true; // Required: number of voxels ok &= CR.getRequiredInt("DetectorCount", m_iDetectorCount); if (!ok) return false; if (!initializeAngles(_cfg)) return false; // some checks ASTRA_CONFIG_CHECK(m_iDetectorCount > 0, "ProjectionGeometry2D", "DetectorCount should be positive."); return true; } bool CProjectionGeometry2D::initializeAngles(const Config& _cfg) { ConfigReader CR("ProjectionGeometry2D", this, _cfg); bool ok = true; // Required: Detector pixel dimensions ok &= CR.getRequiredNumerical("DetectorWidth", m_fDetectorWidth); if (!ok) return false; // Required: ProjectionAngles vector angles; if (!CR.getRequiredNumericalArray("ProjectionAngles", angles)) return false; m_iProjectionAngleCount = angles.size(); ASTRA_CONFIG_CHECK(m_iProjectionAngleCount > 0, "ProjectionGeometry2D", "Not enough ProjectionAngles specified."); m_pfProjectionAngles = new float32[m_iProjectionAngleCount]; for (int i = 0; i < m_iProjectionAngleCount; i++) { m_pfProjectionAngles[i] = (float)angles[i]; } ASTRA_CONFIG_CHECK(m_pfProjectionAngles != NULL, "ProjectionGeometry2D", "ProjectionAngles not initialized"); ASTRA_CONFIG_CHECK(m_fDetectorWidth > 0.0f, "ProjectionGeometry2D", "DetectorWidth should be positive."); return true; } //---------------------------------------------------------------------------------------- // Initialization. bool CProjectionGeometry2D::_initialize(int _iProjectionAngleCount, int _iDetectorCount, float32 _fDetectorWidth, const float32* _pfProjectionAngles) { if (m_bInitialized) { clear(); } // copy parameters m_iProjectionAngleCount = _iProjectionAngleCount; m_iDetectorCount = _iDetectorCount; m_fDetectorWidth = _fDetectorWidth; m_pfProjectionAngles = new float32[m_iProjectionAngleCount]; for (int i = 0; i < m_iProjectionAngleCount; i++) { m_pfProjectionAngles[i] = _pfProjectionAngles[i]; } // Interface class, so don't set m_bInitialized to true return true; } //--------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/ProjectionGeometry2DFactory.cpp000066400000000000000000000040051475635207100231640ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ProjectionGeometry2DFactory.h" #include "astra/ParallelProjectionGeometry2D.h" #include "astra/ParallelVecProjectionGeometry2D.h" #include "astra/FanFlatProjectionGeometry2D.h" #include "astra/FanFlatVecProjectionGeometry2D.h" #include "astra/SparseMatrixProjectionGeometry2D.h" #include "astra/Logging.h" namespace astra { _AstraExport std::unique_ptr constructProjectionGeometry2D(const std::string &type) { CProjectionGeometry2D* pProjGeometry = 0; if (type == "parallel") { pProjGeometry = new CParallelProjectionGeometry2D(); } else if (type == "parallel_vec") { pProjGeometry = new CParallelVecProjectionGeometry2D(); } else if (type == "fanflat") { pProjGeometry = new CFanFlatProjectionGeometry2D(); } else if (type == "fanflat_vec") { pProjGeometry = new CFanFlatVecProjectionGeometry2D(); } else if (type == "sparse_matrix") { pProjGeometry = new CSparseMatrixProjectionGeometry2D(); } else { // invalid geometry type } return std::unique_ptr(pProjGeometry); } } astra-toolbox-2.3.0/src/ProjectionGeometry3D.cpp000066400000000000000000000207161475635207100216440ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ProjectionGeometry3D.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Check all variable values. bool CProjectionGeometry3D::_check() { ASTRA_CONFIG_CHECK(m_iDetectorRowCount > 0, "ProjectionGeometry3D", "DetectorRowCount should be positive."); ASTRA_CONFIG_CHECK(m_iDetectorColCount > 0, "ProjectionGeometry3D", "DetectorColCount should be positive."); ASTRA_CONFIG_CHECK(m_fDetectorSpacingX > 0.0f, "ProjectionGeometry3D", "m_fDetectorSpacingX should be positive."); ASTRA_CONFIG_CHECK(m_fDetectorSpacingY > 0.0f, "ProjectionGeometry3D", "m_fDetectorSpacingY should be positive."); ASTRA_CONFIG_CHECK(m_iProjectionAngleCount > 0, "ProjectionGeometry3D", "ProjectionAngleCount should be positive."); ASTRA_CONFIG_CHECK(m_pfProjectionAngles.size() == m_iProjectionAngleCount, "ProjectionGeometry3D", "Number of angles does not match"); /* // autofix: angles in [0,2pi[ for (int i = 0; i < m_iProjectionAngleCount; i++) { while (2*PI <= m_pfProjectionAngles[i]) m_pfProjectionAngles[i] -= 2*PI; while (m_pfProjectionAngles[i] < 0) m_pfProjectionAngles[i] += 2*PI; } */ // succes return true; } //---------------------------------------------------------------------------------------- // Default constructor. CProjectionGeometry3D::CProjectionGeometry3D() : configCheckData(0) { _clear(); } //---------------------------------------------------------------------------------------- // Constructor. CProjectionGeometry3D::CProjectionGeometry3D(int _iAngleCount, int _iDetectorRowCount, int _iDetectorColCount, float32 _fDetectorSpacingX, float32 _fDetectorSpacingY, std::vector &&_pfProjectionAngles) : configCheckData(0) { _clear(); _initialize(_iAngleCount, _iDetectorRowCount, _iDetectorColCount, _fDetectorSpacingX, _fDetectorSpacingY, std::move(_pfProjectionAngles)); } //---------------------------------------------------------------------------------------- // Copy constructor. CProjectionGeometry3D::CProjectionGeometry3D(const CProjectionGeometry3D& _projGeom) { _clear(); _initialize(_projGeom.m_iProjectionAngleCount, _projGeom.m_iDetectorRowCount, _projGeom.m_iDetectorColCount, _projGeom.m_fDetectorSpacingX, _projGeom.m_fDetectorSpacingY, std::vector(_projGeom.m_pfProjectionAngles)); } //---------------------------------------------------------------------------------------- // Destructor. CProjectionGeometry3D::~CProjectionGeometry3D() { if (m_bInitialized) { clear(); } } //---------------------------------------------------------------------------------------- // Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. // Should only be used by constructors. Otherwise use the clear() function. void CProjectionGeometry3D::_clear() { m_iProjectionAngleCount = 0; m_iDetectorRowCount = 0; m_iDetectorColCount = 0; m_fDetectorSpacingX = 0.0f; m_fDetectorSpacingY = 0.0f; m_pfProjectionAngles.clear(); m_bInitialized = false; } //---------------------------------------------------------------------------------------- // Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. void CProjectionGeometry3D::clear() { m_iProjectionAngleCount = 0; m_iDetectorRowCount = 0; m_iDetectorColCount = 0; m_fDetectorSpacingX = 0.0f; m_fDetectorSpacingY = 0.0f; m_pfProjectionAngles.clear(); m_bInitialized = false; } //---------------------------------------------------------------------------------------- // Initialization with a Config object bool CProjectionGeometry3D::initialize(const Config& _cfg) { ConfigReader CR("ProjectionGeometry3D", this, _cfg); if (m_bInitialized) { clear(); } bool ok = true; // Required: number of voxels ok &= CR.getRequiredInt("DetectorColCount", m_iDetectorColCount); ok &= CR.getRequiredInt("DetectorRowCount", m_iDetectorRowCount); ok &= initializeAngles(_cfg); if (!ok) return false; m_iDetectorTotCount = m_iDetectorRowCount * m_iDetectorColCount; return true; } bool CProjectionGeometry3D::initializeAngles(const Config& _cfg) { ConfigReader CR("ProjectionGeometry3D", this, _cfg); bool ok = true; // Required: Detector pixel dimensions ok &= CR.getRequiredNumerical("DetectorSpacingX", m_fDetectorSpacingX); ok &= CR.getRequiredNumerical("DetectorSpacingY", m_fDetectorSpacingY); if (!ok) return false; // Required: ProjectionAngles vector angles; if (!CR.getRequiredNumericalArray("ProjectionAngles", angles)) return false; m_iProjectionAngleCount = angles.size(); ASTRA_CONFIG_CHECK(m_iProjectionAngleCount > 0, "ProjectionGeometry3D", "Not enough ProjectionAngles specified."); m_pfProjectionAngles.resize(m_iProjectionAngleCount); for (int i = 0; i < m_iProjectionAngleCount; i++) { m_pfProjectionAngles[i] = (float)angles[i]; } return true; } //---------------------------------------------------------------------------------------- // Initialization. bool CProjectionGeometry3D::_initialize(int _iProjectionAngleCount, int _iDetectorRowCount, int _iDetectorColCount, float32 _fDetectorSpacingX, float32 _fDetectorSpacingY, std::vector &&_pfProjectionAngles) { if (m_bInitialized) { clear(); } // copy parameters m_iProjectionAngleCount = _iProjectionAngleCount; m_iDetectorRowCount = _iDetectorRowCount; m_iDetectorColCount = _iDetectorColCount; m_iDetectorTotCount = _iDetectorRowCount * _iDetectorColCount; m_fDetectorSpacingX = _fDetectorSpacingX; m_fDetectorSpacingY = _fDetectorSpacingY; m_pfProjectionAngles = std::move(_pfProjectionAngles); return true; } void CProjectionGeometry3D::getProjectedBBox(double fXMin, double fXMax, double fYMin, double fYMax, double fZMin, double fZMax, double &fUMin, double &fUMax, double &fVMin, double &fVMax) const { double vmin_g, vmax_g; double umin_g, umax_g; // Default implementation, correct for flat panel detectors: // Project corners of volume, take bounding box assert(getProjectionCount() > 0); for (int i = 0; i < getProjectionCount(); ++i) { double vol_u[8]; double vol_v[8]; projectPoint(fXMin, fYMin, fZMin, i, vol_u[0], vol_v[0]); projectPoint(fXMin, fYMin, fZMax, i, vol_u[1], vol_v[1]); projectPoint(fXMin, fYMax, fZMin, i, vol_u[2], vol_v[2]); projectPoint(fXMin, fYMax, fZMax, i, vol_u[3], vol_v[3]); projectPoint(fXMax, fYMin, fZMin, i, vol_u[4], vol_v[4]); projectPoint(fXMax, fYMin, fZMax, i, vol_u[5], vol_v[5]); projectPoint(fXMax, fYMax, fZMin, i, vol_u[6], vol_v[6]); projectPoint(fXMax, fYMax, fZMax, i, vol_u[7], vol_v[7]); double umin = vol_u[0]; double umax = vol_u[0]; double vmin = vol_v[0]; double vmax = vol_v[0]; for (int j = 1; j < 8; ++j) { if (vol_u[j] < umin) umin = vol_u[j]; if (vol_u[j] > umax) umax = vol_u[j]; if (vol_v[j] < vmin) vmin = vol_v[j]; if (vol_v[j] > vmax) vmax = vol_v[j]; } if (i == 0 || umin < umin_g) umin_g = umin; if (i == 0 || umax > umax_g) umax_g = umax; if (i == 0 || vmin < vmin_g) vmin_g = vmin; if (i == 0 || vmax > vmax_g) vmax_g = vmax; } fUMin = umin_g; fUMax = umax_g; fVMin = vmin_g; fVMax = vmax_g; } } // namespace astra astra-toolbox-2.3.0/src/ProjectionGeometry3DFactory.cpp000066400000000000000000000035411475635207100231710ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ProjectionGeometry3DFactory.h" #include "astra/ParallelProjectionGeometry3D.h" #include "astra/ParallelVecProjectionGeometry3D.h" #include "astra/ConeProjectionGeometry3D.h" #include "astra/ConeVecProjectionGeometry3D.h" #include "astra/Logging.h" namespace astra { _AstraExport std::unique_ptr constructProjectionGeometry3D(const std::string &type) { CProjectionGeometry3D* pProjGeometry = 0; if (type == "parallel3d") { pProjGeometry = new CParallelProjectionGeometry3D(); } else if (type == "parallel3d_vec") { pProjGeometry = new CParallelVecProjectionGeometry3D(); } else if (type == "cone") { pProjGeometry = new CConeProjectionGeometry3D(); } else if (type == "cone_vec") { pProjGeometry = new CConeVecProjectionGeometry3D(); } else { // invalid geometry type } return std::unique_ptr(pProjGeometry); } } astra-toolbox-2.3.0/src/Projector2D.cpp000066400000000000000000000151151475635207100177570ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Projector2D.h" #include "astra/ProjectionGeometry2DFactory.h" #include "astra/SparseMatrix.h" #include "astra/Logging.h" namespace astra { //---------------------------------------------------------------------------------------- // constructor CProjector2D::CProjector2D() : configCheckData(0) { m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // constructor CProjector2D::CProjector2D(const CProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pVolumeGeometry) : configCheckData(0) { m_pProjectionGeometry = _pProjectionGeometry.clone(); m_pVolumeGeometry = _pVolumeGeometry.clone(); m_bIsInitialized = true; } //---------------------------------------------------------------------------------------- // destructor CProjector2D::~CProjector2D() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CProjector2D::_clear() { m_pProjectionGeometry = NULL; m_pVolumeGeometry = NULL; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CProjector2D::clear() { if (m_pProjectionGeometry) { delete m_pProjectionGeometry; m_pProjectionGeometry = NULL; } if (m_pVolumeGeometry) { delete m_pVolumeGeometry; m_pVolumeGeometry = NULL; } m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Check bool CProjector2D::_check() { // check pointers ASTRA_CONFIG_CHECK(m_pProjectionGeometry, "Projector2D", "Invalid Projection Geometry Object."); ASTRA_CONFIG_CHECK(m_pVolumeGeometry, "Projector2D", "Invalid Volume Geometry Object."); // check initializations ASTRA_CONFIG_CHECK(m_pProjectionGeometry->isInitialized(), "Projector2D", "Projection Geometry Object Not Initialized."); ASTRA_CONFIG_CHECK(m_pVolumeGeometry->isInitialized(), "Projector2D", "Volume Geometry Object Not Initialized."); // success return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CProjector2D::initialize(const Config& _cfg) { ConfigReader CR("Projector2D", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } Config *subcfg; std::string type; bool ok = true; ok = CR.getRequiredSubConfig("ProjectionGeometry", subcfg, type); if (!ok) return false; std::unique_ptr pProjGeometry = constructProjectionGeometry2D(type); if (!pProjGeometry) { delete subcfg; // Invalid geometry type ASTRA_CONFIG_CHECK(false, "Projector2D", "Invalid projection geometry type \"%s\" specified.", type.c_str()); } pProjGeometry->initialize(*subcfg); delete subcfg; m_pProjectionGeometry = pProjGeometry.release(); ASTRA_CONFIG_CHECK(m_pProjectionGeometry->isInitialized(), "Projector2D", "ProjectionGeometry not initialized."); ok = CR.getRequiredSubConfig("VolumeGeometry", subcfg, type); if (!ok) return false; m_pVolumeGeometry = new CVolumeGeometry2D(); m_pVolumeGeometry->initialize(*subcfg); delete subcfg; ASTRA_CONFIG_CHECK(m_pVolumeGeometry->isInitialized(), "Projector2D", "VolumeGeometry not initialized."); return true; } //---------------------------------------------------------------------------------------- // weights of each detector in a projection angle void CProjector2D::computeProjectionRayWeights(int _iProjection, SPixelWeight* _pfWeightedPixels, int* _piRayStoredPixelCount) { int iPixelBufferSize = getProjectionWeightsCount(_iProjection); int iDetector; for(iDetector = m_pProjectionGeometry->getDetectorCount()-1; iDetector >= 0; --iDetector) { computeSingleRayWeights(_iProjection, // projector index iDetector, // detector index &_pfWeightedPixels[iDetector*iPixelBufferSize], // pixel buffer iPixelBufferSize, // pixel buffer size _piRayStoredPixelCount[iDetector]); // stored pixel count } } //---------------------------------------------------------------------------------------- // explicit projection matrix CSparseMatrix* CProjector2D::getMatrix() { unsigned int iProjectionCount = m_pProjectionGeometry->getProjectionAngleCount(); unsigned int iDetectorCount = m_pProjectionGeometry->getDetectorCount(); unsigned int iRayCount = iProjectionCount * iDetectorCount; unsigned int iVolumeSize = m_pVolumeGeometry->getGridTotCount(); unsigned long lSize = 0; unsigned int iMaxRayLength = 0; for (unsigned int i = 0; i < iProjectionCount; ++i) { unsigned int iRayLength = getProjectionWeightsCount(i); lSize += iDetectorCount * iRayLength; if (iRayLength > iMaxRayLength) iMaxRayLength = iRayLength; } CSparseMatrix* pMatrix = new CSparseMatrix(iRayCount, iVolumeSize, lSize); if (!pMatrix || !pMatrix->isInitialized()) { delete pMatrix; return 0; } SPixelWeight* pEntries = new SPixelWeight[iMaxRayLength]; unsigned long lMatrixIndex = 0; for (unsigned int iRay = 0; iRay < iRayCount; ++iRay) { pMatrix->m_plRowStarts[iRay] = lMatrixIndex; int iPixelCount; int iProjIndex, iDetIndex; m_pProjectionGeometry->indexToAngleDetectorIndex(iRay, iProjIndex, iDetIndex); computeSingleRayWeights(iProjIndex, iDetIndex, pEntries, iMaxRayLength, iPixelCount); for (int i = 0; i < iPixelCount; ++i) { pMatrix->m_piColIndices[lMatrixIndex] = pEntries[i].m_iIndex; pMatrix->m_pfValues[lMatrixIndex] = pEntries[i].m_fWeight; ++lMatrixIndex; } } pMatrix->m_plRowStarts[iRayCount] = lMatrixIndex; delete[] pEntries; return pMatrix; } } // end namespace astra-toolbox-2.3.0/src/Projector3D.cpp000066400000000000000000000116071475635207100177620ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Projector3D.h" #include "astra/VolumeGeometry3D.h" #include "astra/ProjectionGeometry3D.h" #include "astra/ProjectionGeometry3DFactory.h" #include "astra/Logging.h" namespace astra { //---------------------------------------------------------------------------------------- // Default constructor CProjector3D::CProjector3D() : configCheckData(0) { _clear(); } //---------------------------------------------------------------------------------------- // Destructor CProjector3D::~CProjector3D() { if (m_bIsInitialized) clear(); } //---------------------------------------------------------------------------------------- // Clear for constructors void CProjector3D::_clear() { m_pProjectionGeometry.reset(); m_pVolumeGeometry .reset(); m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Clear void CProjector3D::clear() { m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Check bool CProjector3D::_check() { // projection geometry ASTRA_CONFIG_CHECK(m_pProjectionGeometry, "Projector3D", "ProjectionGeometry3D not initialized."); ASTRA_CONFIG_CHECK(m_pProjectionGeometry->isInitialized(), "Projector3D", "ProjectionGeometry3D not initialized."); // volume geometry ASTRA_CONFIG_CHECK(m_pVolumeGeometry, "Projector3D", "VolumeGeometry3D not initialized."); ASTRA_CONFIG_CHECK(m_pVolumeGeometry->isInitialized(), "Projector3D", "VolumeGeometry3D not initialized."); return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CProjector3D::initialize(const Config& _cfg) { ConfigReader CR("Projector3D", this, _cfg); Config *subcfg; std::string type; bool ok = true; ok = CR.getRequiredSubConfig("ProjectionGeometry", subcfg, type); if (!ok) return false; std::unique_ptr pProjGeometry = constructProjectionGeometry3D(type); if (!pProjGeometry) { delete subcfg; // Invalid geometry type ASTRA_CONFIG_CHECK(false, "Projector3D", "Invalid projection geometry type \"%s\" specified.", type.c_str()); } pProjGeometry->initialize(*subcfg); delete subcfg; m_pProjectionGeometry = std::move(pProjGeometry); ASTRA_CONFIG_CHECK(m_pProjectionGeometry->isInitialized(), "Projector3D", "ProjectionGeometry not initialized."); ok = CR.getRequiredSubConfig("VolumeGeometry", subcfg, type); if (!ok) return false; m_pVolumeGeometry = std::make_unique(); m_pVolumeGeometry->initialize(*subcfg); delete subcfg; ASTRA_CONFIG_CHECK(m_pVolumeGeometry->isInitialized(), "Projector3D", "VolumeGeometry not initialized."); return true; } /* bool CProjector3D::initialize(astra::CProjectionGeometry3D *, astra::CVolumeGeometry3D *) { ASTRA_ASSERT(false); return false; } */ //---------------------------------------------------------------------------------------- // Weights of each detector in a projection angle void CProjector3D::computeProjectionRayWeights(int _iProjection, SPixelWeight* _pfWeightedPixels, int* _piRayStoredPixelCount) { int iPixelBufferSize = getProjectionWeightsCount(_iProjection); int iDetector = 0; for(iDetector = m_pProjectionGeometry->getDetectorTotCount()-1; iDetector >= 0; --iDetector) { int iSliceIndex = iDetector / m_pProjectionGeometry->getDetectorColCount(); int iDetectorColIndex = iDetector % m_pProjectionGeometry->getDetectorColCount(); computeSingleRayWeights(_iProjection, // projector index iSliceIndex, // slice index iDetectorColIndex, // detector index &_pfWeightedPixels[iDetector*iPixelBufferSize], // pixel buffer iPixelBufferSize, // pixel buffer size _piRayStoredPixelCount[iDetector]); // stored pixel count } } //---------------------------------------------------------------------------------------- } // end namespace astra-toolbox-2.3.0/src/ReconstructionAlgorithm2D.cpp000066400000000000000000000165571475635207100227130ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ReconstructionAlgorithm2D.h" #include "astra/AstraObjectManager.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CReconstructionAlgorithm2D::CReconstructionAlgorithm2D() { _clear(); } //---------------------------------------------------------------------------------------- // Destructor CReconstructionAlgorithm2D::~CReconstructionAlgorithm2D() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CReconstructionAlgorithm2D::_clear() { m_pProjector = NULL; m_pSinogram = NULL; m_pReconstruction = NULL; m_bUseMinConstraint = false; m_fMinValue = 0.0f; m_bUseMaxConstraint = false; m_fMaxValue = 0.0f; m_bUseReconstructionMask = false; m_pReconstructionMask = NULL; m_bUseSinogramMask = false; m_pSinogramMask = NULL; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CReconstructionAlgorithm2D::clear() { // Nothing to delete, so just _clear() _clear(); } //--------------------------------------------------------------------------------------- // Initialize - Config bool CReconstructionAlgorithm2D::initialize(const Config& _cfg) { ConfigReader CR("ReconstructionAlgorithm2D", this, _cfg); bool ok = true; int id = -1; m_pProjector = 0; if (CR.getID("ProjectorId", id)) { m_pProjector = CProjector2DManager::getSingleton().get(id); if (!m_pProjector) { ASTRA_ERROR("ProjectorId is not a valid id"); return false; } } ok &= CR.getRequiredID("ProjectionDataId", id); m_pSinogram = dynamic_cast(CData2DManager::getSingleton().get(id)); ok &= CR.getRequiredID("ReconstructionDataId", id); m_pReconstruction = dynamic_cast(CData2DManager::getSingleton().get(id)); if (requiresProjector()) { ASTRA_CONFIG_CHECK(m_pProjector, "Reconstruction2D", "No projector specified."); } // fixed mask if (CR.getOptionID("ReconstructionMaskId", id)) { m_bUseReconstructionMask = true; m_pReconstructionMask = dynamic_cast(CData2DManager::getSingleton().get(id)); } // fixed mask if (CR.getOptionID("SinogramMaskId", id)) { m_bUseSinogramMask = true; m_pSinogramMask = dynamic_cast(CData2DManager::getSingleton().get(id)); } // Constraints - NEW if (CR.hasOption("MinConstraint")) { m_bUseMinConstraint = true; ok &= CR.getOptionNumerical("MinConstraint", m_fMinValue, 0.0f); } else { // Constraint - OLD ok &= CR.getOptionBool("UseMinConstraint", m_bUseMinConstraint, false); if (m_bUseMinConstraint) { ok &= CR.getOptionNumerical("MinConstraintValue", m_fMinValue, 0.0f); ASTRA_WARN("UseMinConstraint/MinConstraintValue are deprecated. Use \"MinConstraint\" instead."); } } if (CR.hasOption("MaxConstraint")) { m_bUseMaxConstraint = true; ok &= CR.getOptionNumerical("MaxConstraint", m_fMaxValue, 255.0f); } else { // Constraint - OLD ok &= CR.getOptionBool("UseMaxConstraint", m_bUseMaxConstraint, false); if (m_bUseMaxConstraint) { ok &= CR.getOptionNumerical("MaxConstraintValue", m_fMaxValue, 255.0f); ASTRA_WARN("UseMaxConstraint/MaxConstraintValue are deprecated. Use \"MaxConstraint\" instead."); } } if (!ok) return false; // return success return _check(); } //---------------------------------------------------------------------------------------- // Initialize - C++ bool CReconstructionAlgorithm2D::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; // return success return _check(); } //--------------------------------------------------------------------------------------- // Set Constraints void CReconstructionAlgorithm2D::setConstraints(bool _bUseMin, float32 _fMinValue, bool _bUseMax, float32 _fMaxValue) { m_bUseMinConstraint = _bUseMin; m_fMinValue = _fMinValue; m_bUseMaxConstraint = _bUseMax; m_fMaxValue = _fMaxValue; } //---------------------------------------------------------------------------------------- // Set Fixed Reconstruction Mask void CReconstructionAlgorithm2D::setReconstructionMask(CFloat32VolumeData2D* _pMask, bool _bEnable) { // TODO: check geometry matches volume m_bUseReconstructionMask = _bEnable; m_pReconstructionMask = _pMask; if (m_pReconstructionMask == NULL) { m_bUseReconstructionMask = false; } } //---------------------------------------------------------------------------------------- // Set Fixed Sinogram Mask void CReconstructionAlgorithm2D::setSinogramMask(CFloat32ProjectionData2D* _pMask, bool _bEnable) { // TODO: check geometry matches sinogram m_bUseSinogramMask = _bEnable; m_pSinogramMask = _pMask; if (m_pSinogramMask == NULL) { m_bUseSinogramMask = false; } }//---------------------------------------------------------------------------------------- // Check bool CReconstructionAlgorithm2D::_check() { // check pointers if (requiresProjector()) ASTRA_CONFIG_CHECK(m_pProjector, "Reconstruction2D", "Invalid Projector Object."); ASTRA_CONFIG_CHECK(m_pSinogram, "Reconstruction2D", "Invalid Projection Data Object."); ASTRA_CONFIG_CHECK(m_pReconstruction, "Reconstruction2D", "Invalid Reconstruction Data Object."); // check initializations if (requiresProjector()) ASTRA_CONFIG_CHECK(m_pProjector->isInitialized(), "Reconstruction2D", "Projector Object Not Initialized."); ASTRA_CONFIG_CHECK(m_pSinogram->isInitialized(), "Reconstruction2D", "Projection Data Object Not Initialized."); ASTRA_CONFIG_CHECK(m_pReconstruction->isInitialized(), "Reconstruction2D", "Reconstruction Data Object Not Initialized."); // check compatibility between projector and data classes if (requiresProjector()) { ASTRA_CONFIG_CHECK(m_pSinogram->getGeometry().isEqual(m_pProjector->getProjectionGeometry()), "Reconstruction2D", "Projection Data not compatible with the specified Projector."); ASTRA_CONFIG_CHECK(m_pReconstruction->getGeometry().isEqual(m_pProjector->getVolumeGeometry()), "Reconstruction2D", "Reconstruction Data not compatible with the specified Projector."); } // success return true; } //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/ReconstructionAlgorithm3D.cpp000066400000000000000000000175731475635207100227130ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ReconstructionAlgorithm3D.h" #include "astra/AstraObjectManager.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CReconstructionAlgorithm3D::CReconstructionAlgorithm3D() { m_pProjector = NULL; m_pSinogram = NULL; m_pReconstruction = NULL; m_bUseMinConstraint = false; m_fMinValue = 0.0f; m_bUseMaxConstraint = false; m_fMaxValue = 0.0f; m_bUseReconstructionMask = false; m_pReconstructionMask = NULL; m_bUseSinogramMask = false; m_pSinogramMask = NULL; m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Destructor CReconstructionAlgorithm3D::~CReconstructionAlgorithm3D() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CReconstructionAlgorithm3D::_clear() { m_pProjector = NULL; m_pSinogram = NULL; m_pReconstruction = NULL; m_bUseMinConstraint = false; m_fMinValue = 0.0f; m_bUseMaxConstraint = false; m_fMaxValue = 0.0f; m_bUseReconstructionMask = false; m_pReconstructionMask = NULL; m_bUseSinogramMask = false; m_pSinogramMask = NULL; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CReconstructionAlgorithm3D::clear() { m_pProjector = NULL; m_pSinogram = NULL; m_pReconstruction = NULL; m_bUseMinConstraint = false; m_fMinValue = 0.0f; m_bUseMaxConstraint = false; m_fMaxValue = 0.0f; m_bUseReconstructionMask = false; m_pReconstructionMask = NULL; m_bUseSinogramMask = false; m_pSinogramMask = NULL; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Initialize - Config bool CReconstructionAlgorithm3D::initialize(const Config& _cfg) { ConfigReader CR("ReconstructionAlgorithm3D", this, _cfg); // projector m_pProjector = 0; int id = -1; if (CR.has("ProjectorId")) { CR.getID("ProjectorId", id); m_pProjector = CProjector3DManager::getSingleton().get(id); if (!m_pProjector) { ASTRA_WARN("Optional parameter ProjectorId is not a valid id"); } } bool ok = true; // sinogram data ok &= CR.getRequiredID("ProjectionDataId", id); m_pSinogram = dynamic_cast(CData3DManager::getSingleton().get(id)); // reconstruction data ok &= CR.getRequiredID("ReconstructionDataId", id); m_pReconstruction = dynamic_cast(CData3DManager::getSingleton().get(id)); if (!ok) return false; // fixed mask if (CR.getOptionID("ReconstructionMaskId", id)) { m_bUseReconstructionMask = true; m_pReconstructionMask = dynamic_cast(CData3DManager::getSingleton().get(id)); } // fixed mask if (CR.getOptionID("SinogramMaskId", id)) { m_bUseSinogramMask = true; m_pSinogramMask = dynamic_cast(CData3DManager::getSingleton().get(id)); } // Constraints - NEW if (CR.hasOption("MinConstraint")) { m_bUseMinConstraint = true; ok &= CR.getOptionNumerical("MinConstraint", m_fMinValue, 0.0f); } else { // Constraint - OLD ok &= CR.getOptionBool("UseMinConstraint", m_bUseMinConstraint, false); if (m_bUseMinConstraint) { ok &= CR.getOptionNumerical("MinConstraintValue", m_fMinValue, 0.0f); ASTRA_WARN("UseMinConstraint/MinConstraintValue are deprecated. Use \"MinConstraint\" instead."); } } if (CR.hasOption("MaxConstraint")) { m_bUseMaxConstraint = true; ok &= CR.getOptionNumerical("MaxConstraint", m_fMaxValue, 255.0f); } else { // Constraint - OLD ok &= CR.getOptionBool("UseMaxConstraint", m_bUseMaxConstraint, false); if (m_bUseMaxConstraint) { ok &= CR.getOptionNumerical("MaxConstraintValue", m_fMaxValue, 255.0f); ASTRA_WARN("UseMaxConstraint/MaxConstraintValue are deprecated. Use \"MaxConstraint\" instead."); } } if (!ok) return false; // return success return _check(); } //---------------------------------------------------------------------------------------- // Initialize - C++ bool CReconstructionAlgorithm3D::initialize(CProjector3D* _pProjector, CFloat32ProjectionData3D* _pSinogram, CFloat32VolumeData3D* _pReconstruction) { m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; // return success return _check(); } //--------------------------------------------------------------------------------------- // Set Constraints void CReconstructionAlgorithm3D::setConstraints(bool _bUseMin, float32 _fMinValue, bool _bUseMax, float32 _fMaxValue) { m_bUseMinConstraint = _bUseMin; m_fMinValue = _fMinValue; m_bUseMaxConstraint = _bUseMax; m_fMaxValue = _fMaxValue; } //---------------------------------------------------------------------------------------- // Set Fixed Reconstruction Mask void CReconstructionAlgorithm3D::setReconstructionMask(CFloat32VolumeData3D* _pMask, bool _bEnable) { // TODO: check geometry matches volume m_bUseReconstructionMask = _bEnable; m_pReconstructionMask = _pMask; if (m_pReconstructionMask == NULL) { m_bUseReconstructionMask = false; } } //---------------------------------------------------------------------------------------- // Set Fixed Sinogram Mask void CReconstructionAlgorithm3D::setSinogramMask(CFloat32ProjectionData3D* _pMask, bool _bEnable) { // TODO: check geometry matches sinogram m_bUseSinogramMask = _bEnable; m_pSinogramMask = _pMask; if (m_pSinogramMask == NULL) { m_bUseSinogramMask = false; } }//---------------------------------------------------------------------------------------- // Check bool CReconstructionAlgorithm3D::_check() { // check pointers #if 0 ASTRA_CONFIG_CHECK(m_pProjector, "Reconstruction3D", "Invalid Projector Object."); #endif ASTRA_CONFIG_CHECK(m_pSinogram, "Reconstruction3D", "Invalid Projection Data Object."); ASTRA_CONFIG_CHECK(m_pReconstruction, "Reconstruction3D", "Invalid Reconstruction Data Object."); // check initializations #if 0 ASTRA_CONFIG_CHECK(m_pProjector->isInitialized(), "Reconstruction3D", "Projector Object Not Initialized."); #endif ASTRA_CONFIG_CHECK(m_pSinogram->isInitialized(), "Reconstruction3D", "Projection Data Object Not Initialized."); ASTRA_CONFIG_CHECK(m_pReconstruction->isInitialized(), "Reconstruction3D", "Reconstruction Data Object Not Initialized."); #if 0 // check compatibility between projector and data classes ASTRA_CONFIG_CHECK(m_pSinogram->getGeometry()->isEqual(m_pProjector->getProjectionGeometry()), "Reconstruction3D", "Projection Data not compatible with the specified Projector."); ASTRA_CONFIG_CHECK(m_pReconstruction->getGeometry()->isEqual(m_pProjector->getVolumeGeometry()), "Reconstruction3D", "Reconstruction Data not compatible with the specified Projector."); #endif // success return true; } //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/SartAlgorithm.cpp000066400000000000000000000251351475635207100204050ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/SartAlgorithm.h" #include "astra/AstraObjectManager.h" #include "astra/DataProjectorPolicies.h" #include "astra/Logging.h" using namespace std; namespace astra { #include "astra/Projector2DImpl.inl" //--------------------------------------------------------------------------------------- // Clear - Constructors void CSartAlgorithm::_clear() { CReconstructionAlgorithm2D::_clear(); m_piProjectionOrder.clear(); m_pTotalRayLength = NULL; m_pTotalPixelWeight = NULL; m_bIsInitialized = false; m_iIterationCount = 0; } //--------------------------------------------------------------------------------------- // Clear - Public void CSartAlgorithm::clear() { CReconstructionAlgorithm2D::clear(); m_piProjectionOrder.clear(); delete m_pTotalRayLength; m_pTotalRayLength = NULL; delete m_pTotalPixelWeight; m_pTotalPixelWeight = NULL; delete m_pDiffSinogram; m_pDiffSinogram = NULL; m_bIsInitialized = false; m_iIterationCount = 0; } //---------------------------------------------------------------------------------------- // Constructor CSartAlgorithm::CSartAlgorithm() { _clear(); } //---------------------------------------------------------------------------------------- // Constructor CSartAlgorithm::CSartAlgorithm(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { _clear(); initialize(_pProjector, _pSinogram, _pReconstruction); } //---------------------------------------------------------------------------------------- // Constructor CSartAlgorithm::CSartAlgorithm(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction, int* _piProjectionOrder, int _iProjectionCount) { _clear(); initialize(_pProjector, _pSinogram, _pReconstruction, _piProjectionOrder, _iProjectionCount); } //---------------------------------------------------------------------------------------- // Destructor CSartAlgorithm::~CSartAlgorithm() { clear(); } //--------------------------------------------------------------------------------------- // Initialize - Config bool CSartAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("SartAlgorithm", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CReconstructionAlgorithm2D::initialize(_cfg)) { return false; } // projection order int projectionCount = m_pProjector->getProjectionGeometry().getProjectionAngleCount(); std::string projOrder; if (!CR.getOptionString("ProjectionOrder", projOrder, "sequential")) return false; if (projOrder == "sequential") { m_piProjectionOrder.resize(projectionCount); for (int i = 0; i < projectionCount; i++) { m_piProjectionOrder[i] = i; } } else if (projOrder == "random") { m_piProjectionOrder.resize(projectionCount); for (int i = 0; i < projectionCount; i++) { m_piProjectionOrder[i] = i; } for (int i = 0; i < projectionCount-1; i++) { int k = (rand() % (projectionCount - i)); int t = m_piProjectionOrder[i]; m_piProjectionOrder[i] = m_piProjectionOrder[i + k]; m_piProjectionOrder[i + k] = t; } } else if (projOrder == "custom") { if (!CR.getOptionIntArray("ProjectionOrderList", m_piProjectionOrder)) return false; } else { ASTRA_ERROR("Unknown ProjectionOrder"); return false; } if (!CR.getOptionNumerical("Relaxation", m_fLambda, 1.0f)) return false; // create data objects m_pTotalRayLength = new CFloat32ProjectionData2D(m_pProjector->getProjectionGeometry()); m_pTotalPixelWeight = new CFloat32VolumeData2D(m_pProjector->getVolumeGeometry()); m_pDiffSinogram = new CFloat32ProjectionData2D(m_pProjector->getProjectionGeometry()); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CSartAlgorithm::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // required classes m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; // ray order int iProjectionCount = _pProjector->getProjectionGeometry().getProjectionAngleCount(); m_piProjectionOrder.resize(iProjectionCount); for (int i = 0; i < iProjectionCount; i++) { m_piProjectionOrder[i] = i; } // create data objects m_pTotalRayLength = new CFloat32ProjectionData2D(m_pProjector->getProjectionGeometry()); m_pTotalPixelWeight = new CFloat32VolumeData2D(m_pProjector->getVolumeGeometry()); m_pDiffSinogram = new CFloat32ProjectionData2D(m_pProjector->getProjectionGeometry()); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CSartAlgorithm::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction, int* _piProjectionOrder, int _iProjectionCount) { // required classes m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; // ray order m_piProjectionOrder.resize(_iProjectionCount); for (int i = 0; i < _iProjectionCount; i++) { m_piProjectionOrder[i] = _piProjectionOrder[i]; } // create data objects m_pTotalRayLength = new CFloat32ProjectionData2D(m_pProjector->getProjectionGeometry()); m_pTotalPixelWeight = new CFloat32VolumeData2D(m_pProjector->getVolumeGeometry()); m_pDiffSinogram = new CFloat32ProjectionData2D(m_pProjector->getProjectionGeometry()); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- bool CSartAlgorithm::_check() { // check base class ASTRA_CONFIG_CHECK(CReconstructionAlgorithm2D::_check(), "SART", "Error in ReconstructionAlgorithm2D initialization"); // check projection order all within range for (unsigned int i = 0; i < m_piProjectionOrder.size(); ++i) { ASTRA_CONFIG_CHECK(0 <= m_piProjectionOrder[i] && m_piProjectionOrder[i] < m_pProjector->getProjectionGeometry().getProjectionAngleCount(), "SART", "Projection Order out of range."); } return true; } //---------------------------------------------------------------------------------------- // Iterate bool CSartAlgorithm::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); // data projectors CDataProjectorInterface* pFirstForwardProjector; CDataProjectorInterface* pForwardProjector; CDataProjectorInterface* pBackProjector; m_pTotalRayLength->setData(0.0f); m_pTotalPixelWeight->setData(0.0f); // backprojection data projector pBackProjector = dispatchDataProjector( m_pProjector, SinogramMaskPolicy(m_pSinogramMask), // sinogram mask ReconstructionMaskPolicy(m_pReconstructionMask), // reconstruction mask SIRTBPPolicy(m_pReconstruction, m_pDiffSinogram, m_pTotalPixelWeight, m_pTotalRayLength, m_fLambda), // SIRT backprojection m_bUseSinogramMask, m_bUseReconstructionMask, true // options on/off ); // first time forward projection data projector, // also computes total pixel weight and total ray length pFirstForwardProjector = dispatchDataProjector( m_pProjector, SinogramMaskPolicy(m_pSinogramMask), // sinogram mask ReconstructionMaskPolicy(m_pReconstructionMask), // reconstruction mask Combine3Policy( // 3 basic operations DiffFPPolicy(m_pReconstruction, m_pDiffSinogram, m_pSinogram), // forward projection with difference calculation TotalPixelWeightPolicy(m_pTotalPixelWeight), // calculate the total pixel weights TotalRayLengthPolicy(m_pTotalRayLength)), // calculate the total ray lengths m_bUseSinogramMask, m_bUseReconstructionMask, true // options on/off ); // forward projection data projector pForwardProjector = dispatchDataProjector( m_pProjector, SinogramMaskPolicy(m_pSinogramMask), // sinogram mask ReconstructionMaskPolicy(m_pReconstructionMask), // reconstruction mask CombinePolicy( // 2 basic operations DiffFPPolicy(m_pReconstruction, m_pDiffSinogram, m_pSinogram), // forward projection with difference calculation TotalPixelWeightPolicy(m_pTotalPixelWeight)), // calculate the total pixel weights m_bUseSinogramMask, m_bUseReconstructionMask, true // options on/off ); // iteration loop for (int iIteration = 0; iIteration < _iNrIterations && !shouldAbort(); ++iIteration) { int iProjection = m_piProjectionOrder[m_iIterationCount % m_piProjectionOrder.size()]; // forward projection and difference calculation m_pTotalPixelWeight->setData(0.0f); if ((size_t) iIteration < m_piProjectionOrder.size()) pFirstForwardProjector->projectSingleProjection(iProjection); else pForwardProjector->projectSingleProjection(iProjection); // backprojection pBackProjector->projectSingleProjection(iProjection); // update iteration count m_iIterationCount++; if (m_bUseMinConstraint) m_pReconstruction->clampMin(m_fMinValue); if (m_bUseMaxConstraint) m_pReconstruction->clampMax(m_fMaxValue); } ASTRA_DELETE(pFirstForwardProjector); ASTRA_DELETE(pForwardProjector); ASTRA_DELETE(pBackProjector); return true; } //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/SheppLogan.cpp000066400000000000000000000170531475635207100176650ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/SheppLogan.h" #include "astra/Float32VolumeData2D.h" #include "astra/Data3D.h" namespace astra { struct Ellipse { double x; double y; double axisx; double axisy; double rotation; double value; }; struct Ellipsoid { double x; double y; double z; double axisx; double axisy; double axisz; double rotation; double value; }; template static void add_ellipse(T *data, unsigned int width, unsigned int height, const Ellipse &ell) { double a = (0.5 + 0.5 * ell.x) * width - 0.5; double b = (0.5 - 0.5 * ell.y) * height - 0.5; double c = 0.5 * ell.axisx * width; double d = 0.5 * ell.axisy * height; double th = ell.rotation * PI / 180.0; double costh = cos(th); double costh2 = costh * costh; double sinth = sin(th); double sinth2 = sinth * sinth; double a2 = a * a; double b2 = b * b; double c2 = c * c; double d2 = d * d; // Sage: // var('x y a b c d th') // f = ((x - a) * cos(th) - (y - b) * sin(th))^2 / c^2 + ((x - a)*sin(th) + (y - b) * cos(th))^2/d^2 - 1 // L = [ (s.coefficients(y),t) for s,t in f.expand().coefficients(x) ] double c20 = costh2/c2 + sinth2/d2; double c10 = -2*a*costh2/c2 + 2*b*costh*sinth/c2 - 2*b*costh*sinth/d2 - 2*a*sinth2/d2; double c11 = -2*costh*sinth/c2 + 2*costh*sinth/d2; double c00 = a2*costh2/c2 + b2*costh2/d2 - 2*a*b*costh*sinth/c2 + 2*a*b*costh*sinth/d2 + b2*sinth2/c2 + a2*sinth2/d2 - 1; double c01 = -2*b*costh2/d2 + 2*a*costh*sinth/c2 - 2*a*costh*sinth/d2 - 2*b*sinth2/c2; double c02 = costh2/d2 + sinth2/c2; for (unsigned int y = 0; y < height; ++y) { double A = c20; double B = y*c11 + c10; double C = y*y*c02 + y*c01 + c00; double D = B*B - 4*A*C; if (D < 0) continue; unsigned int xmin = ceil(0.5 * (-B - sqrt(D)) / A); unsigned int xmax = floor(0.5 * (-B + sqrt(D)) / A) + 1; if (xmin < 0) xmin = 0; if (xmax > width) xmax = width; if (xmin >= width) continue; if (xmax <= 0) continue; assert(xmax >= xmin); T *p = data + y * width + xmin; for (unsigned int x = 0; x < xmax - xmin; ++x) *p++ += (T)ell.value; } } template static void add_ellipsoid(T *data, unsigned int width, unsigned int height, unsigned int depth, const Ellipsoid &ell) { double a = (0.5 + 0.5 * ell.x) * width - 0.5; double b = (0.5 - 0.5 * ell.y) * height - 0.5; double c = 0.5 * ell.axisx * width; double d = 0.5 * ell.axisy * height; double e = (0.5 - 0.5 * ell.z) * depth - 0.5; double f = 0.5 * ell.axisz * depth; double th = ell.rotation * PI / 180.0; double costh = cos(th); double costh2 = costh * costh; double sinth = sin(th); double sinth2 = sinth * sinth; double a2 = a * a; double b2 = b * b; double c2 = c * c; double d2 = d * d; // Sage: // var('x y a b c d th') // f = ((x - a) * cos(th) - (y - b) * sin(th))^2 / c^2 + ((x - a)*sin(th) + (y - b) * cos(th))^2/d^2 - 1 // L = [ (s.coefficients(y),t) for s,t in f.expand().coefficients(x) ] double c20 = costh2/c2 + sinth2/d2; double c10 = -2*a*costh2/c2 + 2*b*costh*sinth/c2 - 2*b*costh*sinth/d2 - 2*a*sinth2/d2; double c11 = -2*costh*sinth/c2 + 2*costh*sinth/d2; double c00 = a2*costh2/c2 + b2*costh2/d2 - 2*a*b*costh*sinth/c2 + 2*a*b*costh*sinth/d2 + b2*sinth2/c2 + a2*sinth2/d2 - 1; double c01 = -2*b*costh2/d2 + 2*a*costh*sinth/c2 - 2*a*costh*sinth/d2 - 2*b*sinth2/c2; double c02 = costh2/d2 + sinth2/c2; for (unsigned int y = 0; y < height; ++y) { double A = c20; double B = y*c11 + c10; double C = y*y*c02 + y*c01 + c00; double D = B*B - 4*A*C; if (D < 0) continue; unsigned int xmin = ceil(0.5 * (-B - sqrt(D)) / A); unsigned int xmax = floor(0.5 * (-B + sqrt(D)) / A) + 1; if (xmin < 0) xmin = 0; if (xmax > width) xmax = width; if (xmin >= width) continue; if (xmax <= 0) continue; assert(xmax >= xmin); for (unsigned int x = 0; x < xmax - xmin; ++x) { double xy = A*(x + xmin)*(x + xmin) + B*(x + xmin) + C; unsigned int zmin = ceil(e - f * sqrt(-xy)); unsigned int zmax = floor(e + f * sqrt(-xy)) + 1; if (zmin < 0) zmin = 0; if (zmax > depth) zmax = depth; if (zmin >= depth) continue; if (zmax <= 0) continue; assert(zmax >= zmin); T *p = data + (zmin * height + y) * width + (xmin + x); for (unsigned int z = 0; z < zmax - zmin; ++z) { *p += (T)ell.value; p += width * height; } } } } void generateSheppLogan(CFloat32VolumeData2D *data, bool modified) { std::vector ells = { //x, y, axisx, axisy,rot, value { 0, 0, 0.69, 0.92, 0, 2 }, { 0, -0.0184, 0.6624, 0.874, 0, -0.98 }, { 0.22, 0, 0.11, 0.31, -18, -0.02 }, {-0.22, 0, 0.16, 0.41, 18, -0.02 }, { 0, 0.35, 0.21, 0.25, 0, 0.01 }, { 0, 0.1, 0.046, 0.046, 0, 0.01 }, { 0, -0.1, 0.046, 0.046, 0, 0.01 }, {-0.08,-0.605, 0.046, 0.023, 0, 0.01 }, { 0, -0.605, 0.023, 0.023, 0, 0.01 }, { 0.06,-0.605, 0.023, 0.046, 0, 0.01 } }; std::vector modvalues = { 1.0, -0.8, -0.2, -0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 }; if (modified) { assert(ells.size() == modvalues.size()); for (unsigned int i = 0; i < ells.size(); ++i) ells[i].value = modvalues[i]; } float32 *ptr = data->getData(); for (size_t i = 0; i < data->getSize(); ++i) ptr[i] = 0.0f; for (const Ellipse &ell: ells) add_ellipse(ptr, data->getWidth(), data->getHeight(), ell); for (size_t i = 0; i < data->getSize(); ++i) if (ptr[i] < 0) ptr[i] = 0.0f; } void generateSheppLogan3D(CFloat32VolumeData3D *data, bool modified) { ASTRA_ASSERT(data->isFloat32Memory()); std::vector ells = { //x, y, z, axisx, axisy,axisz,rot,value { 0, 0, 0, 0.69, 0.92, 0.81, 0, 2.00 }, { 0, -0.0184, 0, 0.6624, 0.874, 0.78, 0,-0.98 }, { 0.22, 0, 0, 0.11, 0.31, 0.22,-18,-0.02 }, {-0.22, 0, 0, 0.16, 0.41, 0.28, 18,-0.02 }, { 0, 0.35, 0, 0.21, 0.25, 0.41, 0, 0.01 }, { 0, 0.1, 0, 0.046, 0.046, 0.05, 0, 0.01 }, { 0, -0.1, 0, 0.046, 0.046, 0.05, 0, 0.01 }, {-0.08,-0.605, 0, 0.046, 0.023, 0.05, 0, 0.01 }, { 0, -0.605, 0, 0.023, 0.023, 0.02, 0, 0.01 }, { 0.06,-0.605, 0, 0.023, 0.046, 0.02, 0, 0.01 } }; std::vector modvalues = { 1.0, -0.8, -0.2, -0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 }; if (modified) { assert(ells.size() == modvalues.size()); for (unsigned int i = 0; i < ells.size(); ++i) ells[i].value = modvalues[i]; } float32 *ptr = data->getFloat32Memory(); for (size_t i = 0; i < data->getSize(); ++i) ptr[i] = 0.0f; for (const Ellipsoid &ell: ells) add_ellipsoid(ptr, data->getWidth(), data->getHeight(), data->getDepth(), ell); for (size_t i = 0; i < data->getSize(); ++i) if (ptr[i] < 0) ptr[i] = 0.0f; } } astra-toolbox-2.3.0/src/SirtAlgorithm.cpp000066400000000000000000000221541475635207100204130ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/SirtAlgorithm.h" #include "astra/AstraObjectManager.h" #include "astra/DataProjectorPolicies.h" #include "astra/Logging.h" using namespace std; namespace astra { #include "astra/Projector2DImpl.inl" //---------------------------------------------------------------------------------------- // Constructor CSirtAlgorithm::CSirtAlgorithm() { _clear(); } //--------------------------------------------------------------------------------------- // Initialize - C++ CSirtAlgorithm::CSirtAlgorithm(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { _clear(); initialize(_pProjector, _pSinogram, _pReconstruction); } //---------------------------------------------------------------------------------------- // Destructor CSirtAlgorithm::~CSirtAlgorithm() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CSirtAlgorithm::_clear() { CReconstructionAlgorithm2D::_clear(); m_bIsInitialized = false; m_pTotalRayLength = NULL; m_pTotalPixelWeight = NULL; m_pDiffSinogram = NULL; m_pTmpVolume = NULL; m_fLambda = 1.0f; m_iIterationCount = 0; } //--------------------------------------------------------------------------------------- // Clear - Public void CSirtAlgorithm::clear() { CReconstructionAlgorithm2D::_clear(); m_bIsInitialized = false; ASTRA_DELETE(m_pTotalRayLength); ASTRA_DELETE(m_pTotalPixelWeight); ASTRA_DELETE(m_pDiffSinogram); ASTRA_DELETE(m_pTmpVolume); m_fLambda = 1.0f; m_iIterationCount = 0; } //---------------------------------------------------------------------------------------- // Check bool CSirtAlgorithm::_check() { // check base class ASTRA_CONFIG_CHECK(CReconstructionAlgorithm2D::_check(), "SIRT", "Error in ReconstructionAlgorithm2D initialization"); ASTRA_CONFIG_CHECK(m_pTotalRayLength, "SIRT", "Invalid TotalRayLength Object"); ASTRA_CONFIG_CHECK(m_pTotalRayLength->isInitialized(), "SIRT", "Invalid TotalRayLength Object"); ASTRA_CONFIG_CHECK(m_pTotalPixelWeight, "SIRT", "Invalid TotalPixelWeight Object"); ASTRA_CONFIG_CHECK(m_pTotalPixelWeight->isInitialized(), "SIRT", "Invalid TotalPixelWeight Object"); ASTRA_CONFIG_CHECK(m_pDiffSinogram, "SIRT", "Invalid DiffSinogram Object"); ASTRA_CONFIG_CHECK(m_pDiffSinogram->isInitialized(), "SIRT", "Invalid DiffSinogram Object"); return true; } //--------------------------------------------------------------------------------------- // Initialize - Config bool CSirtAlgorithm::initialize(const Config& _cfg) { ConfigReader CR("SirtAlgorithm", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CReconstructionAlgorithm2D::initialize(_cfg)) { return false; } bool ok = true; ok &= CR.getOptionNumerical("Relaxation", m_fLambda, 1.0f); if (!ok) return false; // init data objects and data projectors _init(); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CSirtAlgorithm::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // required classes m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; m_fLambda = 1.0f; // init data objects and data projectors _init(); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize Data Projectors - private void CSirtAlgorithm::_init() { // create data objects m_pTotalRayLength = new CFloat32ProjectionData2D(m_pProjector->getProjectionGeometry()); m_pTotalPixelWeight = new CFloat32VolumeData2D(m_pProjector->getVolumeGeometry()); m_pDiffSinogram = new CFloat32ProjectionData2D(m_pProjector->getProjectionGeometry()); m_pTmpVolume = new CFloat32VolumeData2D(m_pProjector->getVolumeGeometry()); } //---------------------------------------------------------------------------------------- // Iterate bool CSirtAlgorithm::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); int iIteration = 0; // data projectors CDataProjectorInterface* pForwardProjector; CDataProjectorInterface* pBackProjector; CDataProjectorInterface* pFirstForwardProjector; m_pTotalRayLength->setData(0.0f); m_pTotalPixelWeight->setData(0.0f); // forward projection data projector pForwardProjector = dispatchDataProjector( m_pProjector, SinogramMaskPolicy(m_pSinogramMask), // sinogram mask ReconstructionMaskPolicy(m_pReconstructionMask), // reconstruction mask DiffFPPolicy(m_pReconstruction, m_pDiffSinogram, m_pSinogram), // forward projection with difference calculation m_bUseSinogramMask, m_bUseReconstructionMask, true // options on/off ); // backprojection data projector pBackProjector = dispatchDataProjector( m_pProjector, SinogramMaskPolicy(m_pSinogramMask), // sinogram mask ReconstructionMaskPolicy(m_pReconstructionMask), // reconstruction mask DefaultBPPolicy(m_pTmpVolume, m_pDiffSinogram), // backprojection m_bUseSinogramMask, m_bUseReconstructionMask, true // options on/off ); // first time forward projection data projector, // also computes total pixel weight and total ray length pFirstForwardProjector = dispatchDataProjector( m_pProjector, SinogramMaskPolicy(m_pSinogramMask), // sinogram mask ReconstructionMaskPolicy(m_pReconstructionMask), // reconstruction mask Combine3Policy( // 3 basic operations DiffFPPolicy(m_pReconstruction, m_pDiffSinogram, m_pSinogram), // forward projection with difference calculation TotalPixelWeightPolicy(m_pTotalPixelWeight), // calculate the total pixel weights TotalRayLengthPolicy(m_pTotalRayLength)), // calculate the total ray lengths m_bUseSinogramMask, m_bUseReconstructionMask, true // options on/off ); // forward projection, difference calculation and raylength/pixelweight computation pFirstForwardProjector->project(); float32* pfT = m_pTotalPixelWeight->getData(); for (size_t i = 0; i < m_pTotalPixelWeight->getSize(); ++i) { float32 x = pfT[i]; if (x < -eps || x > eps) x = 1.0f / x; else x = 0.0f; pfT[i] = m_fLambda * x; } pfT = m_pTotalRayLength->getData(); for (size_t i = 0; i < m_pTotalRayLength->getSize(); ++i) { float32 x = pfT[i]; if (x < -eps || x > eps) x = 1.0f / x; else x = 0.0f; pfT[i] = x; } // divide by line weights (*m_pDiffSinogram) *= (*m_pTotalRayLength); // backprojection m_pTmpVolume->setData(0.0f); pBackProjector->project(); // divide by pixel weights (*m_pTmpVolume) *= (*m_pTotalPixelWeight); (*m_pReconstruction) += (*m_pTmpVolume); if (m_bUseMinConstraint) m_pReconstruction->clampMin(m_fMinValue); if (m_bUseMaxConstraint) m_pReconstruction->clampMax(m_fMaxValue); // update iteration count m_iIterationCount++; iIteration++; // iteration loop for (; iIteration < _iNrIterations && !shouldAbort(); ++iIteration) { // forward projection and difference calculation pForwardProjector->project(); // divide by line weights (*m_pDiffSinogram) *= (*m_pTotalRayLength); // backprojection m_pTmpVolume->setData(0.0f); pBackProjector->project(); // multiply with relaxation factor divided by pixel weights (*m_pTmpVolume) *= (*m_pTotalPixelWeight); (*m_pReconstruction) += (*m_pTmpVolume); if (m_bUseMinConstraint) m_pReconstruction->clampMin(m_fMinValue); if (m_bUseMaxConstraint) m_pReconstruction->clampMax(m_fMaxValue); // update iteration count m_iIterationCount++; } ASTRA_DELETE(pForwardProjector); ASTRA_DELETE(pBackProjector); ASTRA_DELETE(pFirstForwardProjector); return true; } //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/SparseMatrix.cpp000066400000000000000000000045451475635207100202510ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include #include "astra/Globals.h" #include "astra/SparseMatrix.h" namespace astra { //---------------------------------------------------------------------------------------- // constructor CSparseMatrix::CSparseMatrix() { m_bInitialized = false; } //---------------------------------------------------------------------------------------- // constructor CSparseMatrix::CSparseMatrix(unsigned int _iHeight, unsigned int _iWidth, unsigned long _lSize) { initialize(_iHeight, _iWidth, _lSize); } //---------------------------------------------------------------------------------------- // destructor CSparseMatrix::~CSparseMatrix() { delete[] m_pfValues; delete[] m_piColIndices; delete[] m_plRowStarts; } //---------------------------------------------------------------------------------------- // initialize bool CSparseMatrix::initialize(unsigned int _iHeight, unsigned int _iWidth, unsigned long _lSize) { m_iHeight = _iHeight; m_iWidth = _iWidth; m_lSize = _lSize; m_pfValues = new float32[_lSize]; m_piColIndices = new unsigned int[_lSize]; m_plRowStarts = new unsigned long[_iHeight+1]; m_bInitialized = true; return m_bInitialized; } std::string CSparseMatrix::description() const { std::stringstream res; res << m_iHeight << "x" << m_iWidth << " sparse matrix"; return res.str(); } } // end namespace astra-toolbox-2.3.0/src/SparseMatrixProjectionGeometry2D.cpp000066400000000000000000000146731475635207100242130ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/SparseMatrixProjectionGeometry2D.h" #include "astra/AstraObjectManager.h" #include "astra/XMLConfig.h" #include "astra/Logging.h" using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Default constructor. CSparseMatrixProjectionGeometry2D::CSparseMatrixProjectionGeometry2D() : CProjectionGeometry2D() { m_pMatrix = 0; } //---------------------------------------------------------------------------------------- // Constructor. CSparseMatrixProjectionGeometry2D::CSparseMatrixProjectionGeometry2D(int _iProjectionAngleCount, int _iDetectorCount, const CSparseMatrix* _pMatrix) { _clear(); initialize(_iProjectionAngleCount, _iDetectorCount, _pMatrix); } //---------------------------------------------------------------------------------------- CSparseMatrixProjectionGeometry2D::CSparseMatrixProjectionGeometry2D(const CSparseMatrixProjectionGeometry2D& _projGeom) { _clear(); initialize(_projGeom.m_iProjectionAngleCount, _projGeom.m_iDetectorCount, _projGeom.m_pMatrix); } //---------------------------------------------------------------------------------------- CSparseMatrixProjectionGeometry2D& CSparseMatrixProjectionGeometry2D::operator=(const CSparseMatrixProjectionGeometry2D& _other) { m_bInitialized = _other.m_bInitialized; if (_other.m_bInitialized) { m_pMatrix = _other.m_pMatrix; m_iDetectorCount = _other.m_iDetectorCount; m_fDetectorWidth = _other.m_fDetectorWidth; } return *this; } //---------------------------------------------------------------------------------------- // Destructor. CSparseMatrixProjectionGeometry2D::~CSparseMatrixProjectionGeometry2D() { m_pMatrix = 0; } //--------------------------------------------------------------------------------------- // Initialize - Config bool CSparseMatrixProjectionGeometry2D::initialize(const Config& _cfg) { ConfigReader CR("SparseMatrixProjectionGeometry2D", this, _cfg); // initialization of parent class if (!CProjectionGeometry2D::initialize(_cfg)) return false; int id = -1; // get matrix if (!CR.getRequiredID("MatrixID", id)) return false; m_pMatrix = CMatrixManager::getSingleton().get(id); // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization. bool CSparseMatrixProjectionGeometry2D::initialize(int _iProjectionAngleCount, int _iDetectorCount, const CSparseMatrix* _pMatrix) { if (m_bInitialized) { clear(); } m_iProjectionAngleCount = _iProjectionAngleCount; m_iDetectorCount = _iDetectorCount; // FIXME: We should probably require these for consistency? m_fDetectorWidth = 1.0f; m_pfProjectionAngles = new float32[m_iProjectionAngleCount]; for (int i = 0; i < m_iProjectionAngleCount; ++i) m_pfProjectionAngles[i] = 0.0f; m_pMatrix = _pMatrix; // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Check. bool CSparseMatrixProjectionGeometry2D::_check() { // check base class ASTRA_CONFIG_CHECK(CProjectionGeometry2D::_check(), "SparseMatrixProjectionGeometry2D", "Error in ProjectionGeometry2D initialization"); ASTRA_CONFIG_CHECK(m_pMatrix, "SparseMatrixProjectionGeometry2D", "No matrix specified"); ASTRA_CONFIG_CHECK(m_pMatrix->m_iHeight == (unsigned int)(m_iProjectionAngleCount * m_iDetectorCount), "SparseMatrixProjectionGeometry2D", "Matrix height doesn't match projection geometry"); return true; } //---------------------------------------------------------------------------------------- // Clone CProjectionGeometry2D* CSparseMatrixProjectionGeometry2D::clone() const { return new CSparseMatrixProjectionGeometry2D(*this); } //---------------------------------------------------------------------------------------- // is equal bool CSparseMatrixProjectionGeometry2D::isEqual(const CProjectionGeometry2D &_pGeom2) const { // try to cast argument to CSparseMatrixProjectionGeometry2D const CSparseMatrixProjectionGeometry2D* pGeom2 = dynamic_cast(&_pGeom2); if (pGeom2 == NULL) return false; // both objects must be initialized if (!m_bInitialized || !pGeom2->m_bInitialized) return false; // check all values if (m_iProjectionAngleCount != pGeom2->m_iProjectionAngleCount) return false; if (m_iDetectorCount != pGeom2->m_iDetectorCount) return false; if (m_fDetectorWidth != pGeom2->m_fDetectorWidth) return false; // Maybe check equality of matrices by element? if (m_pMatrix != pGeom2->m_pMatrix) return false; return true; } //---------------------------------------------------------------------------------------- // is of type bool CSparseMatrixProjectionGeometry2D::isOfType(const std::string& _sType) { return (_sType == "sparse_matrix"); } //---------------------------------------------------------------------------------------- // Get the configuration object Config* CSparseMatrixProjectionGeometry2D::getConfiguration() const { ConfigWriter CW("ProjectionGeometry2D", "sparse matrix"); CW.addInt("DetectorCount", getDetectorCount()); CW.addNumerical("DetectorWidth", getDetectorWidth()); CW.addNumericalArray("ProjectionAngles", m_pfProjectionAngles, m_iProjectionAngleCount); CW.addID("MatrixID", CMatrixManager::getSingleton().getIndex(m_pMatrix)); return CW.getConfig(); } } // end namespace astra astra-toolbox-2.3.0/src/SparseMatrixProjector2D.cpp000066400000000000000000000131631475635207100223230ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/SparseMatrixProjector2D.h" #include #include "astra/DataProjectorPolicies.h" #include "astra/Logging.h" using namespace std; using namespace astra; #include "astra/SparseMatrixProjector2D.inl" //---------------------------------------------------------------------------------------- // default constructor CSparseMatrixProjector2D::CSparseMatrixProjector2D() { _clear(); } //---------------------------------------------------------------------------------------- // constructor CSparseMatrixProjector2D::CSparseMatrixProjector2D(const CSparseMatrixProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pReconstructionGeometry) { _clear(); initialize(_pProjectionGeometry, _pReconstructionGeometry); } //---------------------------------------------------------------------------------------- // destructor CSparseMatrixProjector2D::~CSparseMatrixProjector2D() { clear(); } //--------------------------------------------------------------------------------------- // Clear - Constructors void CSparseMatrixProjector2D::_clear() { CProjector2D::_clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CSparseMatrixProjector2D::clear() { CProjector2D::clear(); m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Check bool CSparseMatrixProjector2D::_check() { // check base class ASTRA_CONFIG_CHECK(CProjector2D::_check(), "SparseMatrixProjector2D", "Error in Projector2D initialization"); ASTRA_CONFIG_CHECK(m_pVolumeGeometry->isInitialized(), "SparseMatrixProjector2D", "Volume geometry not initialized"); ASTRA_CONFIG_CHECK(m_pProjectionGeometry->isInitialized(), "SparseMatrixProjector2D", "Projection geometry not initialized"); ASTRA_CONFIG_CHECK(dynamic_cast(m_pProjectionGeometry), "SparseMatrixProjector2D", "Unsupported projection geometry"); const CSparseMatrix* pMatrix = dynamic_cast(m_pProjectionGeometry)->getMatrix(); ASTRA_CONFIG_CHECK(pMatrix, "SparseMatrixProjector2D", "No matrix specified in projection geometry"); ASTRA_CONFIG_CHECK(pMatrix->m_iWidth == (unsigned int)m_pVolumeGeometry->getGridTotCount(), "SparseMatrixProjector2D", "Matrix width doesn't match volume geometry"); return true; } //--------------------------------------------------------------------------------------- // Initialize, use a Config object bool CSparseMatrixProjector2D::initialize(const Config& _cfg) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CProjector2D::initialize(_cfg)) { return false; } // success m_bIsInitialized = _check(); return m_bIsInitialized; } //--------------------------------------------------------------------------------------- // Initialize bool CSparseMatrixProjector2D::initialize(const CSparseMatrixProjectionGeometry2D &_pProjectionGeometry, const CVolumeGeometry2D &_pVolumeGeometry) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // hardcopy geometries m_pProjectionGeometry = _pProjectionGeometry.clone(); m_pVolumeGeometry = _pVolumeGeometry.clone(); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Get maximum amount of weights on a single ray int CSparseMatrixProjector2D::getProjectionWeightsCount(int _iProjectionIndex) { const CSparseMatrix* pMatrix = dynamic_cast(m_pProjectionGeometry)->getMatrix(); unsigned int iMax = 0; unsigned long lSize = m_pProjectionGeometry->getDetectorCount(); lSize *= m_pProjectionGeometry->getProjectionAngleCount(); for (unsigned long i = 0; i < lSize; ++i) { unsigned int iRowSize = pMatrix->getRowSize(i); if (iRowSize > iMax) iMax = pMatrix->getRowSize(i); } return iMax; } //---------------------------------------------------------------------------------------- // Single Ray Weights void CSparseMatrixProjector2D::computeSingleRayWeights(int _iProjectionIndex, int _iDetectorIndex, SPixelWeight* _pWeightedPixels, int _iMaxPixelCount, int& _iStoredPixelCount) { // TODO: Move this default implementation to Projector2D? ASTRA_ASSERT(m_bIsInitialized); StorePixelWeightsPolicy p(_pWeightedPixels, _iMaxPixelCount); projectSingleRay(_iProjectionIndex, _iDetectorIndex, p); _iStoredPixelCount = p.getStoredPixelCount(); } astra-toolbox-2.3.0/src/Utilities.cpp000066400000000000000000000107021475635207100175720ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/Utilities.h" #include #include #include namespace astra { namespace StringUtil { std::string vformat(const char *fmt, va_list ap) { const int N = 255; va_list apc; char buf[N+1]; va_copy(apc, ap); int len = vsnprintf(buf, N, fmt, apc); va_end(apc); // If output fits within our static buffer, we're done if (len <= N) return buf; // ... otherwise, allocate dynamic buffer char *dynbuf = new char[len + 1]; va_copy(apc, ap); vsnprintf(dynbuf, len, fmt, apc); va_end(apc); std::string ret = dynbuf; delete[] dynbuf; return ret; } std::string format(const char *fmt, ...) { va_list ap; va_start(ap, fmt); std::string ret = vformat(fmt, ap); va_end(ap); return ret; } int stringToInt(const std::string& s) { int i; std::istringstream iss(s); iss.imbue(std::locale::classic()); iss >> i; if (iss.fail() || !iss.eof()) throw bad_cast(); return i; } int stringToInt(const std::string& s, int fallback) { int i; std::istringstream iss(s); iss.imbue(std::locale::classic()); iss >> i; if (iss.fail() || !iss.eof()) return fallback; return i; } float stringToFloat(const std::string& s) { return (float)stringToDouble(s); } double stringToDouble(const std::string& s) { double f; std::istringstream iss(s); iss.imbue(std::locale::classic()); iss >> f; if (iss.fail() || !iss.eof()) throw bad_cast(); return f; } template<> float stringTo(const std::string& s) { return stringToFloat(s); } template<> double stringTo(const std::string& s) { return stringToDouble(s); } template std::vector stringToNumericVector(const std::string &s) { std::vector out; out.reserve(100); std::istringstream iss; iss.imbue(std::locale::classic()); size_t length = s.size(); size_t current = 0; size_t next; do { next = s.find_first_of(",;", current); std::string t = s.substr(current, next - current); iss.str(t); iss.clear(); T f; iss >> f; if (iss.fail() || !iss.eof()) throw bad_cast(); out.push_back(f); current = next + 1; } while (next != std::string::npos && current != length); return out; } std::vector stringToFloatVector(const std::string &s) { return stringToNumericVector(s); } std::vector stringToDoubleVector(const std::string &s) { return stringToNumericVector(s); } template std::vector stringToVector(const std::string& s) { std::vector out; size_t length = s.size(); size_t current = 0; size_t next; do { next = s.find_first_of(",;", current); std::string t = s.substr(current, next - current); out.push_back(stringTo(t)); current = next + 1; } while (next != std::string::npos && current != length); return out; } std::string floatToString(float f) { std::ostringstream s; s.imbue(std::locale::classic()); s << std::setprecision(9) << f; return s.str(); } std::string doubleToString(double f) { std::ostringstream s; s.imbue(std::locale::classic()); s << std::setprecision(17) << f; return s.str(); } template<> std::string toString(float f) { return floatToString(f); } template<> std::string toString(double f) { return doubleToString(f); } void splitString(std::vector &items, const std::string& s, const char *delim) { items.clear(); size_t length = s.size(); size_t current = 0; size_t next; do { next = s.find_first_of(delim, current); items.push_back(s.substr(current, next - current)); current = next + 1; } while (next != std::string::npos && current != length); } } } astra-toolbox-2.3.0/src/Vector3D.cpp000066400000000000000000000020061475635207100172460ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "../include/astra/Vector3D.h" astra-toolbox-2.3.0/src/VolumeGeometry2D.cpp000066400000000000000000000242461475635207100210000ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/VolumeGeometry2D.h" #include "astra/Logging.h" #include "astra/XMLConfig.h" #include namespace astra { //---------------------------------------------------------------------------------------- // Check all variable values bool CVolumeGeometry2D::_check() { ASTRA_CONFIG_CHECK(m_iGridColCount > 0, "VolumeGeometry2D", "GridColCount must be strictly positive."); ASTRA_CONFIG_CHECK(m_iGridRowCount > 0, "VolumeGeometry2D", "GridRowCount must be strictly positive."); ASTRA_CONFIG_CHECK(m_fWindowMinX < m_fWindowMaxX, "VolumeGeometry2D", "WindowMinX should be lower than WindowMaxX."); ASTRA_CONFIG_CHECK(m_fWindowMinY < m_fWindowMaxY, "VolumeGeometry2D", "WindowMinY should be lower than WindowMaxY."); ASTRA_CONFIG_CHECK(fabsf(m_iGridTotCount - (m_iGridColCount * m_iGridRowCount)) < eps, "VolumeGeometry2D", "Internal configuration error (m_iGridTotCount)."); ASTRA_CONFIG_CHECK(fabsf(m_fWindowLengthX - (m_fWindowMaxX - m_fWindowMinX)) < eps, "VolumeGeometry2D", "Internal configuration error (m_fWindowLengthX)."); ASTRA_CONFIG_CHECK(fabsf(m_fWindowLengthY - (m_fWindowMaxY - m_fWindowMinY)) < eps, "VolumeGeometry2D", "Internal configuration error (m_fWindowLengthY)."); ASTRA_CONFIG_CHECK(fabsf(m_fWindowArea - (m_fWindowLengthX * m_fWindowLengthY)) < eps, "VolumeGeometry2D", "Internal configuration error (m_fWindowArea)."); ASTRA_CONFIG_CHECK(fabsf(m_fPixelLengthX - (m_fWindowLengthX / (float32)m_iGridColCount)) < eps, "VolumeGeometry2D", "Internal configuration error (m_fPixelLengthX)."); ASTRA_CONFIG_CHECK(fabsf(m_fPixelLengthY - (m_fWindowLengthY / (float32)m_iGridRowCount)) < eps, "VolumeGeometry2D", "Internal configuration error (m_fPixelLengthY)."); ASTRA_CONFIG_CHECK(fabsf(m_fPixelArea - (m_fPixelLengthX * m_fPixelLengthY)) < eps, "VolumeGeometry2D", "Internal configuration error (m_fPixelArea)."); ASTRA_CONFIG_CHECK(fabsf(m_fDivPixelLengthX * m_fPixelLengthX - 1.0f) < eps, "VolumeGeometry2D", "Internal configuration error (m_fDivPixelLengthX)."); ASTRA_CONFIG_CHECK(fabsf(m_fDivPixelLengthY * m_fPixelLengthY - 1.0f) < eps, "VolumeGeometry2D", "Internal configuration error (m_fDivPixelLengthY)."); return true; } //---------------------------------------------------------------------------------------- // Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. void CVolumeGeometry2D::clear() { m_iGridColCount = 0; m_iGridRowCount = 0; m_iGridTotCount = 0; m_fWindowLengthX = 0.0f; m_fWindowLengthY = 0.0f; m_fWindowArea = 0.0f; m_fPixelLengthX = 0.0f; m_fPixelLengthY = 0.0f; m_fPixelArea = 0.0f; m_fDivPixelLengthX = 0.0f; m_fDivPixelLengthY = 0.0f; m_fWindowMinX = 0.0f; m_fWindowMinY = 0.0f; m_fWindowMaxX = 0.0f; m_fWindowMaxY = 0.0f; m_bInitialized = false; } //---------------------------------------------------------------------------------------- // Default constructor. CVolumeGeometry2D::CVolumeGeometry2D() : configCheckData(0) { clear(); } //---------------------------------------------------------------------------------------- // Default constructor CVolumeGeometry2D::CVolumeGeometry2D(int _iGridColCount, int _iGridRowCount) : configCheckData(0) { clear(); initialize(_iGridColCount, _iGridRowCount); } //---------------------------------------------------------------------------------------- // Constructor. CVolumeGeometry2D::CVolumeGeometry2D(int _iGridColCount, int _iGridRowCount, float32 _fWindowMinX, float32 _fWindowMinY, float32 _fWindowMaxX, float32 _fWindowMaxY) { clear(); initialize(_iGridColCount, _iGridRowCount, _fWindowMinX, _fWindowMinY, _fWindowMaxX, _fWindowMaxY); } //---------------------------------------------------------------------------------------- // Destructor. CVolumeGeometry2D::~CVolumeGeometry2D() { if (m_bInitialized) { clear(); } } //---------------------------------------------------------------------------------------- // Clone CVolumeGeometry2D* CVolumeGeometry2D::clone() const { CVolumeGeometry2D* res = new CVolumeGeometry2D(); res->m_bInitialized = m_bInitialized; res->m_iGridColCount = m_iGridColCount; res->m_iGridRowCount = m_iGridRowCount; res->m_iGridTotCount = m_iGridTotCount; res->m_fWindowLengthX = m_fWindowLengthX; res->m_fWindowLengthY = m_fWindowLengthY; res->m_fWindowArea = m_fWindowArea; res->m_fPixelLengthX = m_fPixelLengthX; res->m_fPixelLengthY = m_fPixelLengthY; res->m_fPixelArea = m_fPixelArea; res->m_fDivPixelLengthX = m_fDivPixelLengthX; res->m_fDivPixelLengthY = m_fDivPixelLengthY; res->m_fWindowMinX = m_fWindowMinX; res->m_fWindowMinY = m_fWindowMinY; res->m_fWindowMaxX = m_fWindowMaxX; res->m_fWindowMaxY = m_fWindowMaxY; return res; } //---------------------------------------------------------------------------------------- // Initialization witha Config object bool CVolumeGeometry2D::initialize(const Config& _cfg) { ConfigReader CR("VolumeGeometry2D", this, _cfg); // uninitialize if the object was initialized before if (m_bInitialized) { clear(); } bool ok = true; // Required: number of voxels ok &= CR.getRequiredInt("GridColCount", m_iGridColCount); ok &= CR.getRequiredInt("GridRowCount", m_iGridRowCount); // Optional: window minima and maxima ok &= CR.getOptionNumerical("WindowMinX", m_fWindowMinX, -m_iGridColCount/2.0f); ok &= CR.getOptionNumerical("WindowMaxX", m_fWindowMaxX, m_iGridColCount/2.0f); ok &= CR.getOptionNumerical("WindowMinY", m_fWindowMinY, -m_iGridRowCount/2.0f); ok &= CR.getOptionNumerical("WindowMaxY", m_fWindowMaxY, m_iGridRowCount/2.0f); if (!ok) return false; _calculateDependents(); // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization. bool CVolumeGeometry2D::initialize(int _iGridColCount, int _iGridRowCount) { return initialize(_iGridColCount, _iGridRowCount, -_iGridColCount/2.0f, -_iGridRowCount/2.0f, _iGridColCount/2.0f, _iGridRowCount/2.0f); } //---------------------------------------------------------------------------------------- // Initialization. bool CVolumeGeometry2D::initialize(int _iGridColCount, int _iGridRowCount, float32 _fWindowMinX, float32 _fWindowMinY, float32 _fWindowMaxX, float32 _fWindowMaxY) { if (m_bInitialized) { clear(); } m_iGridColCount = _iGridColCount; m_iGridRowCount = _iGridRowCount; m_fWindowMinX = _fWindowMinX; m_fWindowMinY = _fWindowMinY; m_fWindowMaxX = _fWindowMaxX; m_fWindowMaxY = _fWindowMaxY; _calculateDependents(); m_bInitialized = _check(); return m_bInitialized; } void CVolumeGeometry2D::_calculateDependents() { m_iGridTotCount = (m_iGridColCount * m_iGridRowCount); m_fWindowLengthX = (m_fWindowMaxX - m_fWindowMinX); m_fWindowLengthY = (m_fWindowMaxY - m_fWindowMinY); m_fWindowArea = (m_fWindowLengthX * m_fWindowLengthY); m_fPixelLengthX = (m_fWindowLengthX / (float32)m_iGridColCount); m_fPixelLengthY = (m_fWindowLengthY / (float32)m_iGridRowCount); m_fPixelArea = (m_fPixelLengthX * m_fPixelLengthY); m_fDivPixelLengthX = ((float32)m_iGridColCount / m_fWindowLengthX); // == (1.0f / m_fPixelLengthX); m_fDivPixelLengthY = ((float32)m_iGridRowCount / m_fWindowLengthY); // == (1.0f / m_fPixelLengthY); } //---------------------------------------------------------------------------------------- // is of type bool CVolumeGeometry2D::isEqual(const CVolumeGeometry2D &_pGeom2) const { // both objects must be initialized if (!m_bInitialized || !_pGeom2.m_bInitialized) return false; // check all values if (m_iGridColCount != _pGeom2.m_iGridColCount) return false; if (m_iGridRowCount != _pGeom2.m_iGridRowCount) return false; if (m_iGridTotCount != _pGeom2.m_iGridTotCount) return false; if (m_fWindowLengthX != _pGeom2.m_fWindowLengthX) return false; if (m_fWindowLengthY != _pGeom2.m_fWindowLengthY) return false; if (m_fWindowArea != _pGeom2.m_fWindowArea) return false; if (m_fPixelLengthX != _pGeom2.m_fPixelLengthX) return false; if (m_fPixelLengthY != _pGeom2.m_fPixelLengthY) return false; if (m_fPixelArea != _pGeom2.m_fPixelArea) return false; if (m_fDivPixelLengthX != _pGeom2.m_fDivPixelLengthX) return false; if (m_fDivPixelLengthY != _pGeom2.m_fDivPixelLengthY) return false; if (m_fWindowMinX != _pGeom2.m_fWindowMinX) return false; if (m_fWindowMinY != _pGeom2.m_fWindowMinY) return false; if (m_fWindowMaxX != _pGeom2.m_fWindowMaxX) return false; if (m_fWindowMaxY != _pGeom2.m_fWindowMaxY) return false; return true; } //---------------------------------------------------------------------------------------- // Get the configuration object Config* CVolumeGeometry2D::getConfiguration() const { ConfigWriter CW("VolumeGeometry2D"); CW.addInt("GridColCount", m_iGridColCount); CW.addInt("GridRowCount", m_iGridRowCount); CW.addOptionNumerical("WindowMinX", m_fWindowMinX); CW.addOptionNumerical("WindowMaxX", m_fWindowMaxX); CW.addOptionNumerical("WindowMinY", m_fWindowMinY); CW.addOptionNumerical("WindowMaxY", m_fWindowMaxY); return CW.getConfig(); } //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/VolumeGeometry3D.cpp000066400000000000000000000354061475635207100210010ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/VolumeGeometry3D.h" #include "astra/XMLConfig.h" #include "astra/Logging.h" namespace astra { //---------------------------------------------------------------------------------------- // Check all variable values bool CVolumeGeometry3D::_check() { ASTRA_CONFIG_CHECK(m_iGridColCount > 0, "VolumeGeometry3D", "GridColCount must be strictly positive."); ASTRA_CONFIG_CHECK(m_iGridRowCount > 0, "VolumeGeometry3D", "GridRowCount must be strictly positive."); ASTRA_CONFIG_CHECK(m_iGridSliceCount > 0, "VolumeGeometry3D", "GridSliceCount must be strictly positive."); ASTRA_CONFIG_CHECK(m_fWindowMinX < m_fWindowMaxX, "VolumeGeometry3D", "WindowMinX should be lower than WindowMaxX."); ASTRA_CONFIG_CHECK(m_fWindowMinY < m_fWindowMaxY, "VolumeGeometry3D", "WindowMinY should be lower than WindowMaxY."); ASTRA_CONFIG_CHECK(m_fWindowMinZ < m_fWindowMaxZ, "VolumeGeometry3D", "WindowMinZ should be lower than WindowMaxZ."); ASTRA_CONFIG_CHECK(m_iGridTotCount == ((size_t)m_iGridColCount * m_iGridRowCount * m_iGridSliceCount), "VolumeGeometry3D", "Internal configuration error."); #if 0 ASTRA_CONFIG_CHECK(m_fWindowLengthX == (m_fWindowMaxX - m_fWindowMinX), "VolumeGeometry3D", "Internal configuration error."); ASTRA_CONFIG_CHECK(m_fWindowLengthY == (m_fWindowMaxY - m_fWindowMinY), "VolumeGeometry3D", "Internal configuration error."); ASTRA_CONFIG_CHECK(m_fWindowLengthZ == (m_fWindowMaxZ - m_fWindowMinZ), "VolumeGeometry3D", "Internal configuration error."); ASTRA_CONFIG_CHECK(m_fWindowArea == (m_fWindowLengthX * m_fWindowLengthY * m_fWindowLengthZ), "VolumeGeometry3D", "Internal configuration error."); ASTRA_CONFIG_CHECK(m_fPixelLengthX == (m_fWindowLengthX / (float32)m_iGridColCount), "VolumeGeometry3D", "Internal configuration error."); ASTRA_CONFIG_CHECK(m_fPixelLengthY == (m_fWindowLengthY / (float32)m_iGridRowCount), "VolumeGeometry3D", "Internal configuration error."); ASTRA_CONFIG_CHECK(m_fPixelLengthZ == (m_fWindowLengthZ / (float32)m_iGridSliceCount), "VolumeGeometry3D", "Internal configuration error."); ASTRA_CONFIG_CHECK(m_fPixelArea == (m_fPixelLengthX * m_fPixelLengthY * m_fPixelLengthZ), "VolumeGeometry3D", "Internal configuration error."); ASTRA_CONFIG_CHECK(m_fDivPixelLengthX == (1.0f / m_fPixelLengthX), "VolumeGeometry3D", "Internal configuration error."); ASTRA_CONFIG_CHECK(m_fDivPixelLengthY == (1.0f / m_fPixelLengthY), "VolumeGeometry3D", "Internal configuration error."); ASTRA_CONFIG_CHECK(m_fDivPixelLengthZ == (1.0f / m_fPixelLengthZ), "VolumeGeometry3D", "Internal configuration error."); #endif return true; } //---------------------------------------------------------------------------------------- // Clear all member variables, setting all numeric variables to 0 and all pointers to NULL. void CVolumeGeometry3D::clear() { m_iGridColCount = 0; m_iGridRowCount = 0; m_iGridSliceCount = 0; m_iGridTotCount = 0; m_fWindowLengthX = 0.0f; m_fWindowLengthY = 0.0f; m_fWindowLengthZ = 0.0f; m_fWindowArea = 0.0f; m_fPixelLengthX = 0.0f; m_fPixelLengthY = 0.0f; m_fPixelLengthZ = 0.0f; m_fPixelArea = 0.0f; m_fDivPixelLengthX = 0.0f; m_fDivPixelLengthY = 0.0f; m_fDivPixelLengthZ = 0.0f; m_fWindowMinX = 0.0f; m_fWindowMinY = 0.0f; m_fWindowMinZ = 0.0f; m_fWindowMaxX = 0.0f; m_fWindowMaxY = 0.0f; m_fWindowMaxZ = 0.0f; m_bInitialized = false; } //---------------------------------------------------------------------------------------- // Default constructor. CVolumeGeometry3D::CVolumeGeometry3D() : configCheckData(0) { clear(); m_bInitialized = false; } //---------------------------------------------------------------------------------------- // Default constructor CVolumeGeometry3D::CVolumeGeometry3D(int _iGridColCount, int _iGridRowCount, int _iGridSliceCount) : configCheckData(0) { clear(); initialize(_iGridColCount, _iGridRowCount, _iGridSliceCount); } //---------------------------------------------------------------------------------------- // Constructor. CVolumeGeometry3D::CVolumeGeometry3D(int _iGridColCount, int _iGridRowCount, int _iGridSliceCount, float32 _fWindowMinX, float32 _fWindowMinY, float32 _fWindowMinZ, float32 _fWindowMaxX, float32 _fWindowMaxY, float32 _fWindowMaxZ) { clear(); initialize(_iGridColCount, _iGridRowCount, _iGridSliceCount, _fWindowMinX, _fWindowMinY, _fWindowMinZ, _fWindowMaxX, _fWindowMaxY, _fWindowMaxZ); } CVolumeGeometry3D::CVolumeGeometry3D(const CVolumeGeometry3D& _other) { *this = _other; } CVolumeGeometry3D& CVolumeGeometry3D::operator=(const CVolumeGeometry3D& _other) { m_bInitialized = _other.m_bInitialized; m_iGridColCount = _other.m_iGridColCount; m_iGridRowCount = _other.m_iGridRowCount; m_iGridSliceCount = _other.m_iGridSliceCount; m_fWindowLengthX = _other.m_fWindowLengthX; m_fWindowLengthY = _other.m_fWindowLengthY; m_fWindowLengthZ = _other.m_fWindowLengthZ; m_fWindowArea = _other.m_fWindowArea; m_fPixelLengthX = _other.m_fPixelLengthX; m_fPixelLengthY = _other.m_fPixelLengthY; m_fPixelLengthZ = _other.m_fPixelLengthZ; m_fDivPixelLengthX = _other.m_fDivPixelLengthX; m_fDivPixelLengthY = _other.m_fDivPixelLengthY; m_fDivPixelLengthZ = _other.m_fDivPixelLengthZ; m_fWindowMinX = _other.m_fWindowMinX; m_fWindowMinY = _other.m_fWindowMinY; m_fWindowMinZ = _other.m_fWindowMinZ; m_fWindowMaxX = _other.m_fWindowMaxX; m_fWindowMaxY = _other.m_fWindowMaxY; m_fWindowMaxZ = _other.m_fWindowMaxZ; m_iGridTotCount = _other.m_iGridTotCount; m_fPixelArea = _other.m_fPixelArea; return *this; } //---------------------------------------------------------------------------------------- // Destructor. CVolumeGeometry3D::~CVolumeGeometry3D() { if (m_bInitialized) { clear(); } } //---------------------------------------------------------------------------------------- // Initialization with a Config object bool CVolumeGeometry3D::initialize(const Config& _cfg) { ConfigReader CR("VolumeGeometry3D", this, _cfg); // uninitialize if the object was initialized before if (m_bInitialized) { clear(); } bool ok = true; // Required: number of voxels ok &= CR.getRequiredInt("GridColCount", m_iGridColCount); ok &= CR.getRequiredInt("GridRowCount", m_iGridRowCount); ok &= CR.getRequiredInt("GridSliceCount", m_iGridSliceCount); // Optional: window minima and maxima ok &= CR.getOptionNumerical("WindowMinX", m_fWindowMinX, -m_iGridColCount/2.0f); ok &= CR.getOptionNumerical("WindowMaxX", m_fWindowMaxX, m_iGridColCount/2.0f); ok &= CR.getOptionNumerical("WindowMinY", m_fWindowMinY, -m_iGridRowCount/2.0f); ok &= CR.getOptionNumerical("WindowMaxY", m_fWindowMaxY, m_iGridRowCount/2.0f); ok &= CR.getOptionNumerical("WindowMinZ", m_fWindowMinZ, -m_iGridSliceCount/2.0f); ok &= CR.getOptionNumerical("WindowMaxZ", m_fWindowMaxZ, m_iGridSliceCount/2.0f); if (!ok) return false; // calculate some other things m_iGridTotCount = ((size_t)m_iGridColCount * m_iGridRowCount * m_iGridSliceCount); m_fWindowLengthX = (m_fWindowMaxX - m_fWindowMinX); m_fWindowLengthY = (m_fWindowMaxY - m_fWindowMinY); m_fWindowLengthZ = (m_fWindowMaxZ - m_fWindowMinZ); m_fWindowArea = (m_fWindowLengthX * m_fWindowLengthY * m_fWindowLengthZ); m_fPixelLengthX = (m_fWindowLengthX / (float32)m_iGridColCount); m_fPixelLengthY = (m_fWindowLengthY / (float32)m_iGridRowCount); m_fPixelLengthZ = (m_fWindowLengthZ / (float32)m_iGridSliceCount); m_fPixelArea = (m_fPixelLengthX * m_fPixelLengthY * m_fPixelLengthZ); m_fDivPixelLengthX = ((float32)m_iGridColCount / m_fWindowLengthX); // == (1.0f / m_fPixelLengthX); m_fDivPixelLengthY = ((float32)m_iGridRowCount / m_fWindowLengthY); // == (1.0f / m_fPixelLengthY); m_fDivPixelLengthZ = ((float32)m_iGridSliceCount / m_fWindowLengthZ); // == (1.0f / m_fPixelLengthZ); // success m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Initialization. bool CVolumeGeometry3D::initialize(int _iGridColCount, int _iGridRowCount, int _iGridSliceCount) { return initialize(_iGridColCount, _iGridRowCount, _iGridSliceCount, -_iGridColCount/2.0f, -_iGridRowCount/2.0f, -_iGridSliceCount/2.0f, _iGridColCount/2.0f, _iGridRowCount/2.0f, _iGridSliceCount/2.0f); } //---------------------------------------------------------------------------------------- // Initialization. bool CVolumeGeometry3D::initialize(int _iGridColCount, int _iGridRowCount, int _iGridSliceCount, float32 _fWindowMinX, float32 _fWindowMinY, float32 _fWindowMinZ, float32 _fWindowMaxX, float32 _fWindowMaxY, float32 _fWindowMaxZ) { if (m_bInitialized) { clear(); } m_iGridColCount = _iGridColCount; m_iGridRowCount = _iGridRowCount; m_iGridSliceCount = _iGridSliceCount; m_iGridTotCount = ((size_t)m_iGridColCount * m_iGridRowCount * m_iGridSliceCount); m_fWindowMinX = _fWindowMinX; m_fWindowMinY = _fWindowMinY; m_fWindowMinZ = _fWindowMinZ; m_fWindowMaxX = _fWindowMaxX; m_fWindowMaxY = _fWindowMaxY; m_fWindowMaxZ = _fWindowMaxZ; m_fWindowLengthX = (m_fWindowMaxX - m_fWindowMinX); m_fWindowLengthY = (m_fWindowMaxY - m_fWindowMinY); m_fWindowLengthZ = (m_fWindowMaxZ - m_fWindowMinZ); m_fWindowArea = (m_fWindowLengthX * m_fWindowLengthY * m_fWindowLengthZ); m_fPixelLengthX = (m_fWindowLengthX / (float32)m_iGridColCount); m_fPixelLengthY = (m_fWindowLengthY / (float32)m_iGridRowCount); m_fPixelLengthZ = (m_fWindowLengthZ / (float32)m_iGridSliceCount); m_fPixelArea = (m_fPixelLengthX * m_fPixelLengthY); m_fDivPixelLengthX = ((float32)m_iGridColCount / m_fWindowLengthX); // == (1.0f / m_fPixelLengthX); m_fDivPixelLengthY = ((float32)m_iGridRowCount / m_fWindowLengthY); // == (1.0f / m_fPixelLengthY); m_fDivPixelLengthZ = ((float32)m_iGridSliceCount / m_fWindowLengthZ); // == (1.0f / m_fPixelLengthZ); m_bInitialized = _check(); return m_bInitialized; } //---------------------------------------------------------------------------------------- // Clone CVolumeGeometry3D* CVolumeGeometry3D::clone() const { CVolumeGeometry3D* res = new CVolumeGeometry3D(); res->m_bInitialized = m_bInitialized; res->m_iGridColCount = m_iGridColCount; res->m_iGridRowCount = m_iGridRowCount; res->m_iGridSliceCount = m_iGridSliceCount; res->m_iGridTotCount = m_iGridTotCount; res->m_fWindowLengthX = m_fWindowLengthX; res->m_fWindowLengthY = m_fWindowLengthY; res->m_fWindowLengthZ = m_fWindowLengthZ; res->m_fWindowArea = m_fWindowArea; res->m_fPixelLengthX = m_fPixelLengthX; res->m_fPixelLengthY = m_fPixelLengthY; res->m_fPixelLengthZ = m_fPixelLengthZ; res->m_fPixelArea = m_fPixelArea; res->m_fDivPixelLengthX = m_fDivPixelLengthX; res->m_fDivPixelLengthY = m_fDivPixelLengthY; res->m_fDivPixelLengthZ = m_fDivPixelLengthZ; res->m_fWindowMinX = m_fWindowMinX; res->m_fWindowMinY = m_fWindowMinY; res->m_fWindowMinZ = m_fWindowMinZ; res->m_fWindowMaxX = m_fWindowMaxX; res->m_fWindowMaxY = m_fWindowMaxY; res->m_fWindowMaxZ = m_fWindowMaxZ; return res; } //---------------------------------------------------------------------------------------- // is of type bool CVolumeGeometry3D::isEqual(const CVolumeGeometry3D* _pGeom2) const { if (_pGeom2 == NULL) return false; // both objects must be initialized if (!m_bInitialized || !_pGeom2->m_bInitialized) return false; // check all values if (m_iGridColCount != _pGeom2->m_iGridColCount) return false; if (m_iGridRowCount != _pGeom2->m_iGridRowCount) return false; if (m_iGridSliceCount != _pGeom2->m_iGridSliceCount) return false; if (m_iGridTotCount != _pGeom2->m_iGridTotCount) return false; if (m_fWindowLengthX != _pGeom2->m_fWindowLengthX) return false; if (m_fWindowLengthY != _pGeom2->m_fWindowLengthY) return false; if (m_fWindowLengthZ != _pGeom2->m_fWindowLengthZ) return false; if (m_fWindowArea != _pGeom2->m_fWindowArea) return false; if (m_fPixelLengthX != _pGeom2->m_fPixelLengthX) return false; if (m_fPixelLengthY != _pGeom2->m_fPixelLengthY) return false; if (m_fPixelLengthZ != _pGeom2->m_fPixelLengthZ) return false; if (m_fPixelArea != _pGeom2->m_fPixelArea) return false; if (m_fDivPixelLengthX != _pGeom2->m_fDivPixelLengthX) return false; if (m_fDivPixelLengthY != _pGeom2->m_fDivPixelLengthY) return false; if (m_fDivPixelLengthZ != _pGeom2->m_fDivPixelLengthZ) return false; if (m_fWindowMinX != _pGeom2->m_fWindowMinX) return false; if (m_fWindowMinY != _pGeom2->m_fWindowMinY) return false; if (m_fWindowMinZ != _pGeom2->m_fWindowMinZ) return false; if (m_fWindowMaxX != _pGeom2->m_fWindowMaxX) return false; if (m_fWindowMaxY != _pGeom2->m_fWindowMaxY) return false; if (m_fWindowMaxZ != _pGeom2->m_fWindowMaxZ) return false; return true; } CVolumeGeometry2D * CVolumeGeometry3D::createVolumeGeometry2D() const { CVolumeGeometry2D * pOutput = new CVolumeGeometry2D(); pOutput->initialize(getGridColCount(), getGridRowCount()); return pOutput; } //---------------------------------------------------------------------------------------- // Get the configuration object Config* CVolumeGeometry3D::getConfiguration() const { ConfigWriter CW("VolumeGeometry3D", "parallel"); CW.addInt("GridColCount", m_iGridColCount); CW.addInt("GridRowCount", m_iGridRowCount); CW.addInt("GridSliceCount", m_iGridSliceCount); CW.addOptionNumerical("WindowMinX", m_fWindowMinX); CW.addOptionNumerical("WindowMaxX", m_fWindowMaxX); CW.addOptionNumerical("WindowMinY", m_fWindowMinY); CW.addOptionNumerical("WindowMaxY", m_fWindowMaxY); CW.addOptionNumerical("WindowMinZ", m_fWindowMinZ); CW.addOptionNumerical("WindowMaxZ", m_fWindowMaxZ); return CW.getConfig(); } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- } // namespace astra astra-toolbox-2.3.0/src/XMLConfig.cpp000066400000000000000000000151631475635207100174130ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/XMLConfig.h" #include "astra/Logging.h" using namespace std; namespace astra { XMLConfig::XMLConfig(XMLNode _self) { self = _self; _doc = 0; } XMLConfig::XMLConfig(const std::string &rootname) { _doc = XMLDocument::createDocument(rootname); self = _doc->getRootNode(); } XMLConfig::~XMLConfig() { delete _doc; _doc = 0; } bool XMLConfig::has(const std::string &name) const { XMLNode node = self.getSingleNode(name); if (!node) return false; return true; } bool XMLConfig::hasOption(const std::string &name) const { return self.hasOption(name); } bool XMLConfig::getSubConfig(const std::string &name, Config *&_cfg, std::string &type) const { XMLNode node; node = self.getSingleNode(name); if (!node) return false; type = node.getAttribute("type", ""); _cfg = new XMLConfig(node); return true; } bool XMLConfig::getInt(const std::string &name, int &iValue) const { XMLNode node = self.getSingleNode(name); if (!node) return false; try { iValue = node.getContentInt(); } catch (const StringUtil::bad_cast &e) { return false; } return true; } bool XMLConfig::getFloat(const std::string &name, float &fValue) const { XMLNode node = self.getSingleNode(name); if (!node) return false; try { fValue = node.getContentNumerical(); } catch (const StringUtil::bad_cast &e) { return false; } return true; } bool XMLConfig::getDoubleArray(const std::string &name, std::vector &values) const { values.clear(); XMLNode node = self.getSingleNode(name); if (!node) return false; try { values = node.getContentNumericalArrayDouble(); } catch (const StringUtil::bad_cast &e) { return false; } return true; } bool XMLConfig::getIntArray(const std::string &name, std::vector &values) const { values.clear(); XMLNode node = self.getSingleNode(name); if (!node) return false; // TODO: Don't go via doubles std::vector tmp; try { tmp = node.getContentNumericalArrayDouble(); } catch (const StringUtil::bad_cast &e) { return false; } values.resize(tmp.size()); for (size_t i = 0; i < tmp.size(); ++i) { int t = static_cast(tmp[i]); if (t != tmp[i]) return false; values[i] = t; } return true; } bool XMLConfig::getString(const std::string &name, std::string &sValue) const { XMLNode node = self.getSingleNode(name); if (!node) return false; sValue = node.getContent(); return true; } bool XMLConfig::getOptionFloat(const std::string &name, float &fValue) const { try { fValue = self.getOptionNumerical(name); } catch (const StringUtil::bad_cast &e) { return false; } return true; } bool XMLConfig::getOptionInt(const std::string &name, int &iValue) const { try { iValue = self.getOptionInt(name); } catch (const StringUtil::bad_cast &e) { return false; } return true; } bool XMLConfig::getOptionUInt(const std::string &name, unsigned int &iValue) const { int tmp = 0; try { tmp = self.getOptionInt(name); } catch (const StringUtil::bad_cast &e) { return false; } if (tmp < 0) return false; iValue = (unsigned int)tmp; return true; } bool XMLConfig::getOptionBool(const std::string &name, bool &bValue) const { try { bValue = self.getOptionBool(name); } catch (const StringUtil::bad_cast &e) { return false; } return true; } bool XMLConfig::getOptionString(const std::string &name, std::string &sValue) const { sValue = self.getOption(name); return true; } bool XMLConfig::getOptionIntArray(const std::string &name, std::vector &values) const { values.clear(); std::list nodes = self.getNodes("Option"); for (XMLNode &it : nodes) { if (it.getAttribute("key") == name) { std::vector data = it.getContentArray(); values.resize(data.size()); try { for (size_t i = 0; i < data.size(); ++i) values[i] = StringUtil::stringToInt(data[i]); } catch (const StringUtil::bad_cast &e) { return false; } return true; } } return false; } std::list XMLConfig::checkUnparsed(const ConfigCheckData &data) const { std::list errors; for (XMLNode &i : self.getNodes()) { std::string nodeName = i.getName(); if (nodeName == "Option") { nodeName = i.getAttribute("key", ""); if (data.parsedOptions.find(nodeName) == data.parsedOptions.end()) { errors.push_back(nodeName); } } else { if (data.parsedNodes.find(nodeName) == data.parsedNodes.end()) { errors.push_back(nodeName); } } } return errors; } //----------------------------------------------------------------------------- ConfigWriter::ConfigWriter(const std::string &name) { cfg = new XMLConfig(name); } ConfigWriter::ConfigWriter(const std::string &name, const std::string &type) : ConfigWriter(name) { cfg->self.addAttribute("type", type); } ConfigWriter::~ConfigWriter() { delete cfg; } Config* ConfigWriter::getConfig() { Config *ret = cfg; cfg = nullptr; return ret; } void ConfigWriter::addInt(const std::string &name, int iValue) { cfg->self.addChildNode(name, iValue); } void ConfigWriter::addNumerical(const std::string &name, double fValue) { cfg->self.addChildNode(name, fValue); } void ConfigWriter::addNumericalArray(const std::string &name, const float* pfValues, int iCount) { XMLNode res = cfg->self.addChildNode(name); res.setContent(pfValues, iCount); } void ConfigWriter::addNumericalMatrix(const std::string &name, const double* pfValues, int iHeight, int iWidth) { XMLNode res = cfg->self.addChildNode(name); res.setContent(pfValues, iWidth, iHeight, false); } void ConfigWriter::addID(const std::string &name, int iValue) { cfg->self.addChildNode(name, iValue); } void ConfigWriter::addOptionNumerical(const std::string &name, double fValue) { cfg->self.addOption(name, fValue); } } astra-toolbox-2.3.0/src/XMLDocument.cpp000066400000000000000000000060711475635207100177620ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/XMLDocument.h" #include #include #include #include #include "rapidxml/rapidxml.hpp" #include "rapidxml/rapidxml_print.hpp" using namespace rapidxml; using namespace astra; using namespace std; //----------------------------------------------------------------------------- XMLDocument::XMLDocument() { fDOMDocument = 0; } //----------------------------------------------------------------------------- XMLDocument::~XMLDocument() { delete fDOMDocument; //parser->release(); } //----------------------------------------------------------------------------- XMLDocument* XMLDocument::readFromFile(string filename) { // create the document XMLDocument* res = new XMLDocument(); res->fDOMDocument = new xml_document<>(); std::ifstream file(filename.c_str()); std::stringstream reader; reader << file.rdbuf(); res->fBuf = reader.str(); res->fDOMDocument->parse<0>((char*)res->fBuf.c_str()); // return the document return res; } //----------------------------------------------------------------------------- // create an XML document with an empty root node XMLDocument* XMLDocument::createDocument(string sRootName) { XMLDocument* res = new XMLDocument(); res->fDOMDocument = new xml_document<>(); char *node_name = res->fDOMDocument->allocate_string(sRootName.c_str()); xml_node<> *node = res->fDOMDocument->allocate_node(node_element, node_name); res->fDOMDocument->append_node(node); return res; } //----------------------------------------------------------------------------- XMLNode XMLDocument::getRootNode() { return XMLNode(fDOMDocument->first_node()); } //----------------------------------------------------------------------------- void XMLDocument::saveToFile(string sFilename) { std::ofstream file(sFilename.c_str()); file << *fDOMDocument; } //----------------------------------------------------------------------------- std::string XMLDocument::toString() { std::stringstream ss; ss << *fDOMDocument->first_node(); return ss.str(); } //----------------------------------------------------------------------------- astra-toolbox-2.3.0/src/XMLNode.cpp000066400000000000000000000325711475635207100170750ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/XMLNode.h" #include "rapidxml/rapidxml.hpp" #include "rapidxml/rapidxml_print.hpp" #include #include using namespace rapidxml; using namespace astra; using namespace std; //----------------------------------------------------------------------------- // default constructor XMLNode::XMLNode() { fDOMElement = 0; } //----------------------------------------------------------------------------- // private constructor XMLNode::XMLNode(xml_node<>* node) { fDOMElement = node; } //----------------------------------------------------------------------------- // destructor XMLNode::~XMLNode() { } //----------------------------------------------------------------------------- // set DOM node (private) void XMLNode::setDOMNode(xml_node<>* n) { fDOMElement = n; } //----------------------------------------------------------------------------- // print XML Node void XMLNode::print() const { std::cout << fDOMElement; } //----------------------------------------------------------------------------- // print XML Node std::string XMLNode::toString() const { std::string s; ::print(std::back_inserter(s), *fDOMElement, 0); return s; } //----------------------------------------------------------------------------- // Get single node XMLNode XMLNode::getSingleNode(string name) const { xml_node<> *node = fDOMElement->first_node(name.c_str()); return XMLNode(node); } //----------------------------------------------------------------------------- // Get list of nodes list XMLNode::getNodes(string name) const { list result; xml_node<> *iter; for (iter = fDOMElement->first_node(name.c_str()); iter; iter = iter->next_sibling(name.c_str())) { result.push_back(XMLNode(iter)); } return result; } //----------------------------------------------------------------------------- // Get list of nodes list XMLNode::getNodes() const { list result; xml_node<> *iter; for (iter = fDOMElement->first_node(); iter; iter = iter->next_sibling()) { result.push_back(XMLNode(iter)); } return result; } //----------------------------------------------------------------------------- // Get name of this node std::string XMLNode::getName() const { return fDOMElement->name(); } //----------------------------------------------------------------------------- // Get node content - STRING string XMLNode::getContent() const { return fDOMElement->value(); } //----------------------------------------------------------------------------- // Get node content - NUMERICAL float32 XMLNode::getContentNumerical() const { return StringUtil::stringToFloat(getContent()); } int XMLNode::getContentInt() const { return StringUtil::stringToInt(getContent()); } //----------------------------------------------------------------------------- // Get node content - BOOLEAN bool XMLNode::getContentBool() const { string res = getContent(); return ((res == "1") || (res == "yes") || (res == "true") || (res == "on")); } //----------------------------------------------------------------------------- // Get node content - STRING LIST vector XMLNode::getContentArray() const { // get listsize int iSize = StringUtil::stringToInt(getAttribute("listsize")); // create result array vector res(iSize); // loop all list item nodes list nodes = getNodes("ListItem"); for (list::iterator it = nodes.begin(); it != nodes.end(); ++it) { int iIndex = it->getAttributeNumerical("index"); string sValue = it->getAttribute("value"); ASTRA_ASSERT(iIndex < iSize); res[iIndex] = sValue; } // return return res; } //----------------------------------------------------------------------------- // Get node content - NUMERICAL LIST // NB: A 2D matrix is returned as a linear list vector XMLNode::getContentNumericalArray() const { return StringUtil::stringToFloatVector(getContent()); } vector XMLNode::getContentNumericalArrayDouble() const { return StringUtil::stringToDoubleVector(getContent()); } //----------------------------------------------------------------------------- // Is attribute? bool XMLNode::hasAttribute(string _sName) const { xml_attribute<> *attr = fDOMElement->first_attribute(_sName.c_str()); return (attr != 0); } //----------------------------------------------------------------------------- // Get attribute - STRING string XMLNode::getAttribute(string _sName, string _sDefaultValue) const { xml_attribute<> *attr = fDOMElement->first_attribute(_sName.c_str()); if (!attr) return _sDefaultValue; return attr->value(); } //----------------------------------------------------------------------------- // Get attribute - NUMERICAL float32 XMLNode::getAttributeNumerical(string _sName, float32 _fDefaultValue) const { if (!hasAttribute(_sName)) return _fDefaultValue; return StringUtil::stringToFloat(getAttribute(_sName)); } double XMLNode::getAttributeNumericalDouble(string _sName, double _fDefaultValue) const { if (!hasAttribute(_sName)) return _fDefaultValue; return StringUtil::stringToDouble(getAttribute(_sName)); } int XMLNode::getAttributeInt(string _sName, int _iDefaultValue) const { if (!hasAttribute(_sName)) return _iDefaultValue; return StringUtil::stringToInt(getAttribute(_sName)); } //----------------------------------------------------------------------------- // Get attribute - BOOLEAN bool XMLNode::getAttributeBool(string _sName, bool _bDefaultValue) const { if (!hasAttribute(_sName)) return _bDefaultValue; string res = getAttribute(_sName); return ((res == "1") || (res == "yes") || (res == "true") || (res == "on")); } //----------------------------------------------------------------------------- // Has option? bool XMLNode::hasOption(string _sKey) const { xml_node<> *iter; for (iter = fDOMElement->first_node("Option"); iter; iter = iter->next_sibling("Option")) { xml_attribute<> *attr = iter->first_attribute("key"); if (attr && _sKey == attr->value()) return true; } return false; } //----------------------------------------------------------------------------- // Get option - STRING string XMLNode::getOption(string _sKey, string _sDefaultValue) const { xml_node<> *iter; for (iter = fDOMElement->first_node("Option"); iter; iter = iter->next_sibling("Option")) { xml_attribute<> *attr = iter->first_attribute("key"); if (attr && _sKey == attr->value()) { attr = iter->first_attribute("value"); if (!attr) return ""; return attr->value(); } } return _sDefaultValue; } //----------------------------------------------------------------------------- // Get option - NUMERICAL float32 XMLNode::getOptionNumerical(string _sKey, float32 _fDefaultValue) const { if (!hasOption(_sKey)) return _fDefaultValue; return StringUtil::stringToFloat(getOption(_sKey)); } int XMLNode::getOptionInt(string _sKey, int _iDefaultValue) const { if (!hasOption(_sKey)) return _iDefaultValue; return StringUtil::stringToInt(getOption(_sKey)); } //----------------------------------------------------------------------------- // Get option - BOOL bool XMLNode::getOptionBool(string _sKey, bool _bDefaultValue) const { bool bHasOption = hasOption(_sKey); if (!bHasOption) return _bDefaultValue; string res = getOption(_sKey); return ((res == "1") || (res == "yes") || (res == "true") || (res == "on")); } //----------------------------------------------------------------------------- // Get option - NUMERICAL ARRAY vector XMLNode::getOptionNumericalArray(string _sKey) const { if (!hasOption(_sKey)) return vector(); list nodes = getNodes("Option"); for (list::iterator it = nodes.begin(); it != nodes.end(); ++it) { if (it->getAttribute("key") == _sKey) { vector vals = it->getContentNumericalArray(); return vals; } } return vector(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // BUILD NODE //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Add child node - EMPTY XMLNode XMLNode::addChildNode(string _sNodeName) { xml_document<> *doc = fDOMElement->document(); char *node_name = doc->allocate_string(_sNodeName.c_str()); xml_node<> *node = doc->allocate_node(node_element, node_name); fDOMElement->append_node(node); return XMLNode(node); } //----------------------------------------------------------------------------- // Add child node - STRING XMLNode XMLNode::addChildNode(string _sNodeName, string _sText) { XMLNode res = addChildNode(_sNodeName); res.setContent(_sText); return res; } //----------------------------------------------------------------------------- // Add child node - FLOAT XMLNode XMLNode::addChildNode(string _sNodeName, float32 _fValue) { XMLNode res = addChildNode(_sNodeName); res.setContent(_fValue); return res; } //----------------------------------------------------------------------------- // Add child node - LIST XMLNode XMLNode::addChildNode(string _sNodeName, float32* _pfList, int _iSize) { XMLNode res = addChildNode(_sNodeName); res.setContent(_pfList, _iSize); return res; } //----------------------------------------------------------------------------- // Set content - STRING void XMLNode::setContent(string _sText) { xml_document<> *doc = fDOMElement->document(); char *text = doc->allocate_string(_sText.c_str()); fDOMElement->value(text); } //----------------------------------------------------------------------------- // Set content - FLOAT void XMLNode::setContent(float32 _fValue) { setContent(StringUtil::floatToString(_fValue)); } //----------------------------------------------------------------------------- // Set content - LIST template static std::string setContentList_internal(const T* pfList, int _iSize) { std::string str; for (int i = 0; i < _iSize; i++) { str += StringUtil::toString(pfList[i]) + ","; } return str; } void XMLNode::setContent(const float32* pfList, int _iSize) { setContent(setContentList_internal(pfList, _iSize)); } void XMLNode::setContent(const double* pfList, int _iSize) { setContent(setContentList_internal(pfList, _iSize)); } //----------------------------------------------------------------------------- // Set content - MATRIX template static std::string setContentMatrix_internal(const T* _pfMatrix, int _iWidth, int _iHeight, bool transposed) { int s1,s2; if (!transposed) { s1 = 1; s2 = _iWidth; } else { s1 = _iHeight; s2 = 1; } std::ostringstream s; s.imbue(std::locale::classic()); s << std::setprecision(17); for (int y = 0; y < _iHeight; ++y) { if (_iWidth > 0) s << _pfMatrix[0*s1 + y*s2]; for (int x = 1; x < _iWidth; x++) s << "," << _pfMatrix[x*s1 + y*s2]; s << ";"; } return s.str(); } void XMLNode::setContent(const float32* _pfMatrix, int _iWidth, int _iHeight, bool transposed) { setContent(setContentMatrix_internal(_pfMatrix, _iWidth, _iHeight, transposed)); } void XMLNode::setContent(const double* _pfMatrix, int _iWidth, int _iHeight, bool transposed) { setContent(setContentMatrix_internal(_pfMatrix, _iWidth, _iHeight, transposed)); } //----------------------------------------------------------------------------- // Add attribute - STRING void XMLNode::addAttribute(string _sName, string _sText) { xml_document<> *doc = fDOMElement->document(); char *name = doc->allocate_string(_sName.c_str()); char *text = doc->allocate_string(_sText.c_str()); xml_attribute<> *attr = doc->allocate_attribute(name, text); fDOMElement->append_attribute(attr); } //----------------------------------------------------------------------------- // Add attribute - FLOAT void XMLNode::addAttribute(string _sName, float32 _fValue) { addAttribute(_sName, StringUtil::floatToString(_fValue)); } //----------------------------------------------------------------------------- // Add option - STRING void XMLNode::addOption(string _sName, string _sText) { XMLNode node = addChildNode("Option"); node.addAttribute("key", _sName); node.addAttribute("value", _sText); } //----------------------------------------------------------------------------- // Add option - FLOAT void XMLNode::addOption(string _sName, float32 _sText) { XMLNode node = addChildNode("Option"); node.addAttribute("key", _sName); node.addAttribute("value", _sText); } //----------------------------------------------------------------------------- astra-toolbox-2.3.0/src/astra.def000066400000000000000000000002001475635207100166750ustar00rootroot00000000000000; MatlabDLL.def : Declares the module parameters for the DLL. LIBRARY "astra" EXPORTS ; Explicit exports can go here astra-toolbox-2.3.0/tests/000077500000000000000000000000001475635207100154665ustar00rootroot00000000000000astra-toolbox-2.3.0/tests/main.cpp000066400000000000000000000022571475635207100171240ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define BOOST_TEST_DYN_LINK // Generate main() #define BOOST_AUTO_TEST_MAIN #include #include #include astra-toolbox-2.3.0/tests/python/000077500000000000000000000000001475635207100170075ustar00rootroot00000000000000astra-toolbox-2.3.0/tests/python/functional/000077500000000000000000000000001475635207100211515ustar00rootroot00000000000000astra-toolbox-2.3.0/tests/python/functional/test_line2d.py000066400000000000000000000573631475635207100237550ustar00rootroot00000000000000import numpy as np import astra import math import pytest # Display sinograms with mismatch on test failure DISPLAY=False NONUNITDET=True OBLIQUE=True FLEXVOL=True NONSQUARE=False # non-square pixels not supported yet by most projectors # Round interpolation weight to 8 bits to emulate CUDA texture unit precision CUDA_8BIT_LINEAR=True CUDA_TOL=2e-2 nloops = 50 seed = 123 # KNOWN FAILURES: # fan/strip relatively high numerical errors around 45 degrees # return length of intersection of the line through points src = (x,y) # and det (x,y), and the rectangle defined by xmin, ymin, xmax, ymax def intersect_line_rectangle(src, det, xmin, xmax, ymin, ymax): EPS = 1e-5 if np.abs(src[0] - det[0]) < EPS: if src[0] >= xmin and src[0] < xmax: return ymax - ymin else: return 0.0 if np.abs(src[1] - det[1]) < EPS: if src[1] >= ymin and src[1] < ymax: return xmax - xmin else: return 0.0 n = np.sqrt((det[0] - src[0]) ** 2 + (det[1] - src[1]) ** 2) check = [ (-(xmin - src[0]), -(det[0] - src[0]) / n ), (xmax - src[0], (det[0] - src[0]) / n ), (-(ymin - src[1]), -(det[1] - src[1]) / n ), (ymax - src[1], (det[1] - src[1]) / n ) ] pre = [ -np.inf ] post = [ np.inf ] for p, q in check: r = p / (1.0 * q) if q > 0: post.append(r) # exiting half-plane else: pre.append(r) # entering half-plane end_r = np.min(post) start_r = np.max(pre) if end_r > start_r: return end_r - start_r else: return 0.0 def intersect_line_rectangle_feather(src, det, xmin, xmax, ymin, ymax, feather): return intersect_line_rectangle(src, det, xmin-feather, xmax+feather, ymin-feather, ymax+feather) def intersect_line_rectangle_interval(src, det, xmin, xmax, ymin, ymax, f): a = intersect_line_rectangle_feather(src, det, xmin, xmax, ymin, ymax, -f) b = intersect_line_rectangle(src, det, xmin, xmax, ymin, ymax) c = intersect_line_rectangle_feather(src, det, xmin, xmax, ymin, ymax, f) return (a,b,c) # x-coord of intersection of the line (src, det) with the horizontal line at y def intersect_line_horizontal(src, det, y): EPS = 1e-5 if np.abs(src[1] - det[1]) < EPS: return np.nan t = (y - src[1]) / (det[1] - src[1]) return src[0] + t * (det[0] - src[0]) # y-coord of intersection of the line (src, det) with the vertical line at x def intersect_line_vertical(src, det, x): src = ( src[1], src[0] ) det = ( det[1], det[0] ) return intersect_line_horizontal(src, det, x) # length of the intersection of the strip with boundaries edge1, edge2 with the horizontal # segment at y, with horizontal extent x_seg def intersect_ray_horizontal_segment(edge1, edge2, y, x_seg): e1 = intersect_line_horizontal(edge1[0], edge1[1], y) e2 = intersect_line_horizontal(edge2[0], edge2[1], y) if not (np.isfinite(e1) and np.isfinite(e2)): return np.nan (e1, e2) = np.sort([e1, e2]) (x1, x2) = np.sort(x_seg) l = np.max([e1, x1]) r = np.min([e2, x2]) return np.max([r-l, 0.0]) def intersect_ray_vertical_segment(edge1, edge2, x, y_seg): # mirror edge1 and edge2 edge1 = [ (a[1], a[0]) for a in edge1 ] edge2 = [ (a[1], a[0]) for a in edge2 ] return intersect_ray_horizontal_segment(edge1, edge2, x, y_seg) # weight of the intersection of line with the horizontal segment at y, with horizontal extent x_seg # using linear interpolation def intersect_line_horizontal_segment_linear(src, det, y, x_seg, inter_width): EPS = 1e-5 x = intersect_line_horizontal(src, det, y) assert(x_seg[1] - x_seg[0] + EPS >= inter_width) if x < x_seg[0] - 0.5*inter_width: return 0.0 elif x < x_seg[0] + 0.5*inter_width: return (x - (x_seg[0] - 0.5*inter_width)) / inter_width elif x < x_seg[1] - 0.5*inter_width: return 1.0 elif x < x_seg[1] + 0.5*inter_width: return (x_seg[1] + 0.5*inter_width - x) / inter_width else: return 0.0 def intersect_line_vertical_segment_linear(src, det, x, y_seg, inter_height): src = ( src[1], src[0] ) det = ( det[1], det[0] ) return intersect_line_horizontal_segment_linear(src, det, x, y_seg, inter_height) def area_signed(a, b): return a[0] * b[1] - a[1] * b[0] # is c to the left of ab def is_left_of(a, b, c): EPS = 1e-5 return area_signed( (b[0] - a[0], b[1] - a[1]), (c[0] - a[0], c[1] - a[1]) ) > EPS # compute area of rect on left side of line def halfarea_rect_line(src, det, xmin, xmax, ymin, ymax): pts = ( (xmin,ymin), (xmin,ymax), (xmax,ymin), (xmax,ymax) ) pts_left = list(filter( lambda p: is_left_of(src, det, p), pts )) npts_left = len(pts_left) if npts_left == 0: return 0.0 elif npts_left == 1: # triangle p = pts_left[0] xd = intersect_line_horizontal(src, det, p[1]) - p[0] yd = intersect_line_vertical(src, det, p[0]) - p[1] ret = 0.5 * abs(xd) * abs(yd) return ret elif npts_left == 2: p = pts_left[0] q = pts_left[1] if p[0] == q[0]: # vertical intersection x1 = intersect_line_horizontal(src, det, p[1]) - p[0] x2 = intersect_line_horizontal(src, det, q[1]) - q[0] ret = 0.5 * (ymax - ymin) * (abs(x1) + abs(x2)) return ret else: assert(p[1] == q[1]) # horizontal intersection y1 = intersect_line_vertical(src, det, p[0]) - p[1] y2 = intersect_line_vertical(src, det, q[0]) - q[1] ret = 0.5 * (xmax - xmin) * (abs(y1) + abs(y2)) return ret else: # mirror and invert ret = ((xmax - xmin) * (ymax - ymin)) - halfarea_rect_line(det, src, xmin, xmax, ymin, ymax) return ret # area of intersection of the strip with boundaries edge1, edge2 with rectangle def intersect_ray_rect(edge1, edge2, xmin, xmax, ymin, ymax): s1 = halfarea_rect_line(edge1[0], edge1[1], xmin, xmax, ymin, ymax) s2 = halfarea_rect_line(edge2[0], edge2[1], xmin, xmax, ymin, ymax) return abs(s1 - s2) # width of projection of detector orthogonal to ray direction # i.e., effective detector width def effective_detweight(src, det, u): ray = np.array(det) - np.array(src) ray = ray / np.linalg.norm(ray, ord=2) return abs(area_signed(ray, u)) # LINE GENERATORS # --------------- # # Per ray these yield three lines, at respectively the center and two edges of the detector pixel. # Each line is given by two points on the line. # ( ( (p0x, p0y), (q0x, q0y) ), ( (p1x, p1y), (q1x, q1y) ), ( (p2x, p2y), (q2x, q2y) ) ) def gen_lines_fanflat(proj_geom): angles = proj_geom['ProjectionAngles'] for theta in angles: #theta = -theta src = ( math.sin(theta) * proj_geom['DistanceOriginSource'], -math.cos(theta) * proj_geom['DistanceOriginSource'] ) detc= (-math.sin(theta) * proj_geom['DistanceOriginDetector'], math.cos(theta) * proj_geom['DistanceOriginDetector'] ) detu= ( math.cos(theta) * proj_geom['DetectorWidth'], math.sin(theta) * proj_geom['DetectorWidth'] ) src = np.array(src, dtype=np.float64) detc= np.array(detc, dtype=np.float64) detu= np.array(detu, dtype=np.float64) detb= detc + (0.5 - 0.5*proj_geom['DetectorCount']) * detu for i in range(proj_geom['DetectorCount']): yield ((src, detb + i * detu), (src, detb + (i - 0.5) * detu), (src, detb + (i + 0.5) * detu)) def gen_lines_fanflat_vec(proj_geom): v = proj_geom['Vectors'] for i in range(v.shape[0]): src = v[i,0:2] detc = v[i,2:4] detu = v[i,4:6] detb = detc + (0.5 - 0.5*proj_geom['DetectorCount']) * detu for i in range(proj_geom['DetectorCount']): yield ((src, detb + i * detu), (src, detb + (i - 0.5) * detu), (src, detb + (i + 0.5) * detu)) def gen_lines_parallel(proj_geom): angles = proj_geom['ProjectionAngles'] for theta in angles: ray = ( math.sin(theta), -math.cos(theta) ) detc= (0, 0 ) detu= ( math.cos(theta) * proj_geom['DetectorWidth'], math.sin(theta) * proj_geom['DetectorWidth'] ) ray = np.array(ray, dtype=np.float64) detc= np.array(detc, dtype=np.float64) detu= np.array(detu, dtype=np.float64) detb= detc + (0.5 - 0.5*proj_geom['DetectorCount']) * detu for i in range(proj_geom['DetectorCount']): yield ((detb + i * detu - ray, detb + i * detu), (detb + (i - 0.5) * detu - ray, detb + (i - 0.5) * detu), (detb + (i + 0.5) * detu - ray, detb + (i + 0.5) * detu)) def gen_lines_parallel_vec(proj_geom): v = proj_geom['Vectors'] for i in range(v.shape[0]): ray = v[i,0:2] detc = v[i,2:4] detu = v[i,4:6] detb = detc + (0.5 - 0.5*proj_geom['DetectorCount']) * detu for i in range(proj_geom['DetectorCount']): yield ((detb + i * detu - ray, detb + i * detu), (detb + (i - 0.5) * detu - ray, detb + (i - 0.5) * detu), (detb + (i + 0.5) * detu - ray, detb + (i + 0.5) * detu)) def gen_lines(proj_geom): g = { 'fanflat': gen_lines_fanflat, 'fanflat_vec': gen_lines_fanflat_vec, 'parallel': gen_lines_parallel, 'parallel_vec': gen_lines_parallel_vec } for l in g[proj_geom['type']](proj_geom): yield l range2d = ( 8, 64 ) def gen_random_geometry_fanflat(): if not NONUNITDET: w = 1.0 else: w = 0.6 + 0.8 * np.random.random() pg = astra.create_proj_geom('fanflat', w, np.random.randint(*range2d), np.linspace(0, 2*np.pi, np.random.randint(*range2d), endpoint=False), 256 * (0.5 + np.random.random()), 256 * np.random.random()) return pg def gen_random_geometry_parallel(): if not NONUNITDET: w = 1.0 else: w = 0.8 + 0.4 * np.random.random() pg = astra.create_proj_geom('parallel', w, np.random.randint(*range2d), np.linspace(0, 2*np.pi, np.random.randint(*range2d), endpoint=False)) return pg def gen_random_geometry_fanflat_vec(): Vectors = np.zeros([16,6]) # We assume constant detector width in these tests if not NONUNITDET: w = 1.0 else: w = 0.6 + 0.8 * np.random.random() for i in range(Vectors.shape[0]): angle1 = 2*np.pi*np.random.random() if OBLIQUE: angle2 = angle1 + 0.5 * np.random.random() else: angle2 = angle1 dist1 = 256 * (0.5 + np.random.random()) detc = 10 * np.random.random(size=2) detu = [ math.cos(angle1) * w, math.sin(angle1) * w ] src = [ math.sin(angle2) * dist1, -math.cos(angle2) * dist1 ] Vectors[i, :] = [ src[0], src[1], detc[0], detc[1], detu[0], detu[1] ] pg = astra.create_proj_geom('fanflat_vec', np.random.randint(*range2d), Vectors) return pg def gen_random_geometry_parallel_vec(): Vectors = np.zeros([16,6]) # We assume constant detector width in these tests if not NONUNITDET: w = 1.0 else: w = 0.6 + 0.8 * np.random.random() for i in range(Vectors.shape[0]): l = 0.6 + 0.8 * np.random.random() angle1 = 2*np.pi*np.random.random() if OBLIQUE: angle2 = angle1 + 0.5 * np.random.random() else: angle2 = angle1 detc = 10 * np.random.random(size=2) detu = [ math.cos(angle1) * w, math.sin(angle1) * w ] ray = [ math.sin(angle2) * l, -math.cos(angle2) * l ] Vectors[i, :] = [ ray[0], ray[1], detc[0], detc[1], detu[0], detu[1] ] pg = astra.create_proj_geom('parallel_vec', np.random.randint(*range2d), Vectors) return pg def proj_type_to_fan(t): if t == 'cuda': return t else: return t + '_fanflat' def display_mismatch(data, sinogram, a): import pylab pylab.gray() pylab.imshow(data) pylab.figure() pylab.imshow(sinogram) pylab.figure() pylab.imshow(a) pylab.figure() pylab.imshow(sinogram-a) pylab.show() def display_mismatch_triple(data, sinogram, a, b, c): import pylab pylab.gray() pylab.imshow(data) pylab.figure() pylab.imshow(sinogram) pylab.figure() pylab.imshow(b) pylab.figure() pylab.imshow(a) pylab.figure() pylab.imshow(c) pylab.figure() pylab.imshow(sinogram-a) pylab.figure() pylab.imshow(c-sinogram) pylab.show() @pytest.mark.slow class Test2DKernel: def single_test(self, type, proj_type): shape = np.random.randint(*range2d, size=2) # these rectangles are biased, but that shouldn't matter rect_min = [ np.random.randint(0, a) for a in shape ] rect_max = [ np.random.randint(rect_min[i]+1, shape[i]+1) for i in range(len(shape))] if FLEXVOL: if not NONSQUARE: pixsize = np.array([0.5, 0.5]) + np.random.random() else: pixsize = 0.5 + np.random.random(size=2) origin = 10 * np.random.random(size=2) else: pixsize = (1.,1.) origin = (0.,0.) vg = astra.create_vol_geom(shape[1], shape[0], origin[0] - 0.5 * shape[0] * pixsize[0], origin[0] + 0.5 * shape[0] * pixsize[0], origin[1] - 0.5 * shape[1] * pixsize[1], origin[1] + 0.5 * shape[1] * pixsize[1]) if type == 'parallel': pg = gen_random_geometry_parallel() projector_id = astra.create_projector(proj_type, pg, vg) elif type == 'parallel_vec': pg = gen_random_geometry_parallel_vec() projector_id = astra.create_projector(proj_type, pg, vg) elif type == 'fanflat': pg = gen_random_geometry_fanflat() projector_id = astra.create_projector(proj_type_to_fan(proj_type), pg, vg) elif type == 'fanflat_vec': pg = gen_random_geometry_fanflat_vec() projector_id = astra.create_projector(proj_type_to_fan(proj_type), pg, vg) data = np.zeros((shape[1], shape[0]), dtype=np.float32) data[rect_min[1]:rect_max[1],rect_min[0]:rect_max[0]] = 1 sinogram_id, sinogram = astra.create_sino(data, projector_id) assert np.all(np.isfinite(sinogram)) #print(pg) #print(vg) astra.data2d.delete(sinogram_id) astra.projector.delete(projector_id) # NB: Flipped y-axis here, since that is how astra interprets 2D volumes xmin = origin[0] + (-0.5 * shape[0] + rect_min[0]) * pixsize[0] xmax = origin[0] + (-0.5 * shape[0] + rect_max[0]) * pixsize[0] ymin = origin[1] + (+0.5 * shape[1] - rect_max[1]) * pixsize[1] ymax = origin[1] + (+0.5 * shape[1] - rect_min[1]) * pixsize[1] if proj_type == 'line': a = np.zeros(np.prod(astra.functions.geom_size(pg)), dtype=np.float32) b = np.zeros(np.prod(astra.functions.geom_size(pg)), dtype=np.float32) c = np.zeros(np.prod(astra.functions.geom_size(pg)), dtype=np.float32) for i, (center, edge1, edge2) in enumerate(gen_lines(pg)): (src, det) = center # We compute line intersections with slightly bigger (cw) and # smaller (aw) rectangles, and see if the kernel falls # between these two values. (aw,bw,cw) = intersect_line_rectangle_interval(src, det, xmin, xmax, ymin, ymax, 1e-3) a[i] = aw b[i] = bw c[i] = cw a = a.reshape(astra.functions.geom_size(pg)) b = b.reshape(astra.functions.geom_size(pg)) c = c.reshape(astra.functions.geom_size(pg)) if not np.all(np.isfinite(a)): raise RuntimeError("Invalid value in reference sinogram") if not np.all(np.isfinite(b)): raise RuntimeError("Invalid value in reference sinogram") if not np.all(np.isfinite(c)): raise RuntimeError("Invalid value in reference sinogram") assert np.all(np.isfinite(sinogram)) # Check if sinogram lies between a and c y = np.min(sinogram-a) z = np.min(c-sinogram) if DISPLAY and (z < 0 or y < 0): display_mismatch_triple(data, sinogram, a, b, c) assert not(z < 0 or y < 0) elif proj_type == 'linear' or proj_type == 'cuda': a = np.zeros(np.prod(astra.functions.geom_size(pg)), dtype=np.float32) for i, (center, edge1, edge2) in enumerate(gen_lines(pg)): (src, det) = center (xd, yd) = det - src l = 0.0 if np.abs(xd) > np.abs(yd): # horizontal ray length = math.sqrt(1.0 + abs(yd/xd)**2) * pixsize[0] y_seg = (ymin, ymax) for j in range(rect_min[0], rect_max[0]): x = origin[0] + (-0.5 * shape[0] + j + 0.5) * pixsize[0] w = intersect_line_vertical_segment_linear(center[0], center[1], x, y_seg, pixsize[1]) # limited interpolation precision with cuda if CUDA_8BIT_LINEAR and proj_type == 'cuda': w = np.round(w * 256.0) / 256.0 l += w * length else: length = math.sqrt(1.0 + abs(xd/yd)**2) * pixsize[1] x_seg = (xmin, xmax) for j in range(rect_min[1], rect_max[1]): y = origin[1] + (+0.5 * shape[1] - j - 0.5) * pixsize[1] w = intersect_line_horizontal_segment_linear(center[0], center[1], y, x_seg, pixsize[0]) # limited interpolation precision with cuda if CUDA_8BIT_LINEAR and proj_type == 'cuda': w = np.round(w * 256.0) / 256.0 l += w * length a[i] = l a = a.reshape(astra.functions.geom_size(pg)) if not np.all(np.isfinite(a)): raise RuntimeError("Invalid value in reference sinogram") x = np.max(np.abs(sinogram-a)) TOL = 2e-3 if proj_type != 'cuda' else CUDA_TOL if DISPLAY and x > TOL: display_mismatch(data, sinogram, a) assert not(x > TOL) elif proj_type == 'distance_driven' and 'par' in type: a = np.zeros(np.prod(astra.functions.geom_size(pg)), dtype=np.float32) for i, (center, edge1, edge2) in enumerate(gen_lines(pg)): (src, det) = center try: detweight = pg['DetectorWidth'] except KeyError: detweight = effective_detweight(src, det, pg['Vectors'][i//pg['DetectorCount'],4:6]) (xd, yd) = det - src l = 0.0 if np.abs(xd) > np.abs(yd): # horizontal ray y_seg = (ymin, ymax) for j in range(rect_min[0], rect_max[0]): x = origin[0] + (-0.5 * shape[0] + j + 0.5) * pixsize[0] l += intersect_ray_vertical_segment(edge1, edge2, x, y_seg) * pixsize[0] / detweight else: x_seg = (xmin, xmax) for j in range(rect_min[1], rect_max[1]): y = origin[1] + (+0.5 * shape[1] - j - 0.5) * pixsize[1] l += intersect_ray_horizontal_segment(edge1, edge2, y, x_seg) * pixsize[1] / detweight a[i] = l a = a.reshape(astra.functions.geom_size(pg)) if not np.all(np.isfinite(a)): raise RuntimeError("Invalid value in reference sinogram") x = np.max(np.abs(sinogram-a)) TOL = 2e-3 if DISPLAY and x > TOL: display_mismatch(data, sinogram, a) assert not(x > TOL) elif proj_type == 'strip' and 'fan' in type: a = np.zeros(np.prod(astra.functions.geom_size(pg)), dtype=np.float32) for i, (center, edge1, edge2) in enumerate(gen_lines(pg)): (src, det) = center detweight = effective_detweight(src, det, edge2[1] - edge1[1]) det_dist = np.linalg.norm(src-det, ord=2) l = 0.0 for j in range(rect_min[0], rect_max[0]): xmin = origin[0] + (-0.5 * shape[0] + j) * pixsize[0] xmax = origin[0] + (-0.5 * shape[0] + j + 1) * pixsize[0] xcen = 0.5 * (xmin + xmax) for k in range(rect_min[1], rect_max[1]): ymin = origin[1] + (+0.5 * shape[1] - k - 1) * pixsize[1] ymax = origin[1] + (+0.5 * shape[1] - k) * pixsize[1] ycen = 0.5 * (ymin + ymax) scale = det_dist / (np.linalg.norm( src - np.array((xcen,ycen)), ord=2 ) * detweight) w = intersect_ray_rect(edge1, edge2, xmin, xmax, ymin, ymax) l += w * scale a[i] = l a = a.reshape(astra.functions.geom_size(pg)) if not np.all(np.isfinite(a)): raise RuntimeError("Invalid value in reference sinogram") x = np.max(np.abs(sinogram-a)) # BUG: Known bug in fan/strip code around 45 degree projections causing larger errors than desirable TOL = 4e-2 if DISPLAY and x > TOL: display_mismatch(data, sinogram, a) assert not(x > TOL) elif proj_type == 'strip': a = np.zeros(np.prod(astra.functions.geom_size(pg)), dtype=np.float32) for i, (center, edge1, edge2) in enumerate(gen_lines(pg)): (src, det) = center try: detweight = pg['DetectorWidth'] except KeyError: detweight = effective_detweight(src, det, pg['Vectors'][i//pg['DetectorCount'],4:6]) a[i] = intersect_ray_rect(edge1, edge2, xmin, xmax, ymin, ymax) / detweight a = a.reshape(astra.functions.geom_size(pg)) if not np.all(np.isfinite(a)): raise RuntimeError("Invalid value in reference sinogram") x = np.max(np.abs(sinogram-a)) TOL = 8e-3 if DISPLAY and x > TOL: display_mismatch(data, sinogram, a) assert not(x > TOL) else: raise RuntimeError("Unsupported projector") def single_test_adjoint(self, type, proj_type): shape = np.random.randint(*range2d, size=2) if FLEXVOL: if not NONSQUARE: pixsize = np.array([0.5, 0.5]) + np.random.random() else: pixsize = 0.5 + np.random.random(size=2) origin = 10 * np.random.random(size=2) else: pixsize = (1.,1.) origin = (0.,0.) vg = astra.create_vol_geom(shape[1], shape[0], origin[0] - 0.5 * shape[0] * pixsize[0], origin[0] + 0.5 * shape[0] * pixsize[0], origin[1] - 0.5 * shape[1] * pixsize[1], origin[1] + 0.5 * shape[1] * pixsize[1]) if type == 'parallel': pg = gen_random_geometry_parallel() projector_id = astra.create_projector(proj_type, pg, vg) elif type == 'parallel_vec': pg = gen_random_geometry_parallel_vec() projector_id = astra.create_projector(proj_type, pg, vg) elif type == 'fanflat': pg = gen_random_geometry_fanflat() projector_id = astra.create_projector(proj_type_to_fan(proj_type), pg, vg) elif type == 'fanflat_vec': pg = gen_random_geometry_fanflat_vec() projector_id = astra.create_projector(proj_type_to_fan(proj_type), pg, vg) for i in range(5): X = np.random.random((shape[1], shape[0])) Y = np.random.random(astra.geom_size(pg)) sinogram_id, fX = astra.create_sino(X, projector_id) bp_id, fTY = astra.create_backprojection(Y, projector_id) astra.data2d.delete(sinogram_id) astra.data2d.delete(bp_id) da = np.dot(fX.ravel(), Y.ravel()) db = np.dot(X.ravel(), fTY.ravel()) m = np.abs(da - db) TOL = 1e-3 if 'cuda' not in proj_type else 1e-1 if m / da >= TOL: print(vg) print(pg) print(m/da, da/db, da, db) assert m / da < TOL astra.projector.delete(projector_id) def multi_test(self, type, proj_type): np.random.seed(seed) for _ in range(nloops): self.single_test(type, proj_type) def multi_test_adjoint(self, type, proj_type): np.random.seed(seed) for _ in range(nloops): self.single_test_adjoint(type, proj_type) __combinations = { 'parallel': [ 'line', 'linear', 'distance_driven', 'strip', 'cuda' ], 'parallel_vec': [ 'line', 'linear', 'distance_driven', 'strip', 'cuda' ], 'fanflat': [ 'line', 'strip', 'cuda' ], 'fanflat_vec': [ 'line', 'cuda' ] } for k, l in __combinations.items(): for v in l: def f(k,v): return lambda self: self.multi_test(k, v) def f_adj(k,v): return lambda self: self.multi_test_adjoint(k, v) setattr(Test2DKernel, 'test_' + k + '_' + v, f(k,v)) setattr(Test2DKernel, 'test_' + k + '_' + v + '_adjoint', f_adj(k,v)) astra-toolbox-2.3.0/tests/python/functional/test_multigpu_consistency.py000066400000000000000000000031471475635207100270560ustar00rootroot00000000000000import astra import numpy as np import pytest @pytest.mark.slow def test_multigpu_consistency(): def print_diff(a, b): x = np.abs((a-b).reshape(-1)) print(np.linalg.norm(x, ord=2), np.max(x), np.max(np.abs(a)), np.max(np.abs(b))) N = 1024 angles = np.linspace(0, 2*np.pi, N, endpoint=False) vg = astra.create_vol_geom(N, N, N) pg = astra.create_proj_geom('cone', 1.0, 1.0, N, N, angles, 10*N, 0) projector_id = astra.create_projector('cuda3d', pg, vg) W = astra.OpTomo(projector_id) phantom_id = astra.data3d.shepp_logan(vg, returnData=False) astra.set_gpu_index(0) pid, projdata_single = astra.create_sino3d_gpu(phantom_id, pg, vg, returnData=True) astra.data3d.delete(pid) rec_single = W.reconstruct('FDK_CUDA', projdata_single) gpu = 0 gpus = [0] while True: print("Now using GPUs " + ", ".join(str(i) for i in gpus)) for m in ( 0, 100000000 ): astra.set_gpu_index(gpus, memory=m) pid, projdata_multi = astra.create_sino3d_gpu(phantom_id, pg, vg, returnData=True) astra.data3d.delete(pid) print_diff(projdata_single, projdata_multi) assert(np.allclose(projdata_multi, projdata_single, rtol=1e-3, atol=1e-1)) rec_multi = W.reconstruct('FDK_CUDA', projdata_single) print_diff(rec_single, rec_multi) assert(np.allclose(rec_multi, rec_single, rtol=1e-3, atol=1e-3)) gpu = gpu + 1 if 'Invalid' in astra.get_gpu_info(gpu): print(f"No GPU #%s. Aborting." % (gpu,)) break gpus.append(gpu) astra-toolbox-2.3.0/tests/python/functional/test_rec_scaling.py000066400000000000000000000220711475635207100250350ustar00rootroot00000000000000import numpy as np import astra import astra.experimental import math import pytest DISPLAY=False def VolumeGeometries(is3D,noncube,singleslice): if not is3D: for s in [0.8, 1.0, 1.25]: yield astra.create_vol_geom(128, 128, -64*s, 64*s, -64*s, 64*s) elif noncube: for sx in [0.8, 1.0]: for sy in [0.8, 1.0]: for sz in [0.8, 1.0]: yield astra.create_vol_geom(64, 64, 64, -32*sx, 32*sx, -32*sy, 32*sy, -32*sz, 32*sz) if singleslice: yield astra.create_vol_geom(64, 64, 1, -32*sx, 32*sx, -32*sy, 32*sy, -0.5*sz, 0.5*sz) else: for s in [0.8, 1.0]: yield astra.create_vol_geom(64, 64, 64, -32*s, 32*s, -32*s, 32*s, -32*s, 32*s) def ProjectionGeometries(type,shortscan,singleslice): if type == 'parallel': for dU in [0.8, 1.0, 1.25]: yield astra.create_proj_geom('parallel', dU, 256, np.linspace(0,np.pi,180,False)) elif type == 'fanflat': for dU in [0.8, 1.0, 1.25]: for src in [500, 1000]: for det in [0, 250, 500]: yield astra.create_proj_geom('fanflat', dU, 256, np.linspace(0,2*np.pi,180,False), src, det) elif type == 'parallel3d': for dU in [0.8, 1.0]: for dV in [0.8, 1.0]: if singleslice: yield astra.create_proj_geom('parallel3d', dU, dV, 1, 128, np.linspace(0,np.pi,180,False)) else: yield astra.create_proj_geom('parallel3d', dU, dV, 128, 128, np.linspace(0,np.pi,180,False)) elif type == 'parallel3d_vec': for j in range(10): Vectors = np.zeros([180,12]) wu = 0.6 + 0.8 * np.random.random() wv = 0.6 + 0.8 * np.random.random() for i in range(Vectors.shape[0]): l = 0.6 + 0.8 * np.random.random() angle1 = 2*np.pi*np.random.random() angle2 = angle1 + 0.5 * np.random.random() angle3 = 0.1*np.pi*np.random.random() detc = 10 * np.random.random(size=3) detu = [ math.cos(angle1) * wu, math.sin(angle1) * wu, 0 ] detv = [ -math.sin(angle1) * math.sin(angle3) * wv, math.cos(angle1) * math.sin(angle3) * wv, math.cos(angle3) * wv ] ray = [ math.sin(angle2) * l, -math.cos(angle2) * l, 0 ] Vectors[i, :] = [ ray[0], ray[1], ray[2], detc[0], detc[1], detc[2], detu[0], detu[1], detu[2], detv[0], detv[1], detv[2] ] pg = astra.create_proj_geom('parallel3d_vec', 128, 128, Vectors) yield pg elif type == 'cone': A = [1.5, 2] if shortscan else [ 2 ] for dU in [0.8, 1.0]: for dV in [0.8, 1.0]: for src in [500, 1000]: for det in [0, 250]: for a in A: if singleslice: yield astra.create_proj_geom('cone', dU, dV, 1, 128, np.linspace(0,a*np.pi,180,False), src, det) else: yield astra.create_proj_geom('cone', dU, dV, 128, 128, np.linspace(0,a*np.pi,180,False), src, det) elif type == 'cone_vec': for j in range(10): Vectors = np.zeros([180,12]) wu = 0.6 + 0.8 * np.random.random() wv = 0.6 + 0.8 * np.random.random() for i in range(Vectors.shape[0]): l = 256 * (0.5 * np.random.random()) angle1 = 2*np.pi*np.random.random() angle2 = angle1 + 0.5 * np.random.random() angle3 = 0.1*np.pi*np.random.random() detc = 10 * np.random.random(size=3) detu = [ math.cos(angle1) * wu, math.sin(angle1) * wu, 0 ] detv = [ -math.sin(angle1) * math.sin(angle3) * wv, math.cos(angle1) * math.sin(angle3) * wv, math.cos(angle3) * wv ] src = [ math.sin(angle2) * l, -math.cos(angle2) * l, 0 ] Vectors[i, :] = [ src[0], src[1], src[2], detc[0], detc[1], detc[2], detu[0], detu[1], detu[2], detv[0], detv[1], detv[2] ] pg = astra.create_proj_geom('parallel3d_vec', 128, 128, Vectors) yield pg @pytest.mark.slow class TestRecScale: def single_test(self, geom_type, proj_type, alg, iters, vss, dss): if alg == 'FBP' and 'fanflat' in geom_type: pytest.skip('CPU FBP is parallel-beam only') is3D = (geom_type in ['parallel3d', 'cone']) for vg in VolumeGeometries(is3D, 'FDK' not in alg, False): for pg in ProjectionGeometries(geom_type, 'FDK' in alg, False): if not is3D: vol = np.zeros((128,128),dtype=np.float32) vol[50:70,50:70] = 1 else: vol = np.zeros((64,64,64),dtype=np.float32) vol[25:35,25:35,25:35] = 1 options = {} if vss > 1: options["VoxelSuperSampling"] = vss if dss > 1: options["DetectorSuperSampling"] = vss proj_id = astra.create_projector(proj_type, pg, vg, options=options) if not is3D: sino_id, sinogram = astra.create_sino(vol, proj_id) else: sino_id, sinogram = astra.create_sino3d_gpu(vol, pg, vg) if not is3D: DATA = astra.data2d else: DATA = astra.data3d rec_id = DATA.create('-vol', vg, 0.0 if 'EM' not in alg else 1.0) cfg = astra.astra_dict(alg) cfg['ReconstructionDataId'] = rec_id cfg['ProjectionDataId'] = sino_id cfg['ProjectorId'] = proj_id if 'FDK' in alg and geom_type == "cone" and pg["ProjectionAngles"][-1] < 1.8*np.pi: cfg['option'] = { 'ShortScan': True } alg_id = astra.algorithm.create(cfg) for i in range(iters): astra.algorithm.run(alg_id, 1) rec = DATA.get(rec_id) astra.astra.delete([sino_id, alg_id, alg_id, proj_id]) if not is3D: val = np.sum(rec[55:65,55:65]) / 100. else: val = np.sum(rec[27:32,27:32,27:32]) / 125. TOL = 5e-2 if DISPLAY and abs(val-1.0) >= TOL: import pylab print(geom_type, proj_type, alg, vg, pg) print(val) pylab.gray() if not is3D: pylab.imshow(rec) else: pylab.imshow(rec[:,32,:]) pylab.show() assert abs(val-1.0) < TOL def single_test_adjoint3D(self, geom_type, proj_type): for vg in VolumeGeometries(True, True, 'vec' not in geom_type): for pg in ProjectionGeometries(geom_type, False, vg['GridSliceCount'] == 1): for i in range(5): X = np.random.random(astra.geom_size(vg)).astype(np.float32) Y = np.random.random(astra.geom_size(pg)).astype(np.float32) projector_cfg = astra.astra_dict('cuda3d') projector_cfg['ProjectionGeometry'] = pg projector_cfg['VolumeGeometry'] = vg if vg['GridSliceCount'] == 1: projector_cfg['ProjectionKernel'] = '2d_weighting' projector_id = astra.projector3d.create(projector_cfg) fX = np.zeros(astra.geom_size(pg), dtype=np.float32) astra.experimental.direct_FP3D(projector_id, X, fX) fTY = np.zeros(astra.geom_size(vg), dtype=np.float32) astra.experimental.direct_BP3D(projector_id, fTY, Y) astra.projector3d.delete(projector_id) da = np.dot(fX.ravel(), Y.ravel()) db = np.dot(X.ravel(), fTY.ravel()) m = np.abs(da - db) TOL = 1e-1 if m / da >= TOL: print(vg) print(pg) print(m/da, da/db, da, db) assert m / da < TOL __combinations = { 'parallel': [ 'line', 'linear', 'distance_driven', 'strip', 'cuda' ], 'fanflat': [ 'line_fanflat', 'strip_fanflat', 'cuda' ], 'parallel3d': [ 'cuda3d' ], 'cone': [ 'cuda3d' ], } __combinations_adjoint = { 'parallel3d': [ 'cuda3d' ], 'cone': [ 'cuda3d' ], 'parallel3d_vec': [ 'cuda3d' ], 'cone_vec': [ 'cuda3d' ], } __algs = { 'SIRT': 50, 'SART': 10*180, 'CGLS': 30, 'FBP': 1 } __algs_CUDA = { 'SIRT_CUDA': 50, 'SART_CUDA': 10*180, 'CGLS_CUDA': 30, 'EM_CUDA': 50, 'FBP_CUDA': 1 } __algs_parallel3d = { 'SIRT3D_CUDA': 200, 'CGLS3D_CUDA': 20, } __algs_cone = { 'SIRT3D_CUDA': 200, 'CGLS3D_CUDA': 20, 'FDK_CUDA': 1 } __combinations_ss = { 'parallel': [ { 'projector': 'cuda', 'alg': 'SIRT_CUDA', 'iters': 50 } ], 'fanflat': [ { 'projector': 'cuda', 'alg': 'SIRT_CUDA', 'iters': 50 } ], 'parallel3d': [ { 'projector': 'cuda3d', 'alg': 'SIRT3D_CUDA', 'iters': 200 } ], 'cone': [ { 'projector': 'cuda3d', 'alg': 'SIRT3D_CUDA', 'iters': 200 } ] } for k, l in __combinations.items(): for v in l: is3D = (k in ['parallel3d', 'cone']) if k == 'parallel3d': A = __algs_parallel3d elif k == 'cone': A = __algs_cone elif v == 'cuda': A = __algs_CUDA else: A = __algs for a, i in A.items(): def f(k, v, a, i): return lambda self: self.single_test(k, v, a, i, 1, 1) setattr(TestRecScale, 'test_' + a + '_' + k + '_' + v, f(k,v,a,i)) for k, l in __combinations_adjoint.items(): for v in l: def g(k, v): return lambda self: self.single_test_adjoint3D(k, v) setattr(TestRecScale, 'test_adjoint_' + k + '_' + v, g(k,v)) for k, l in __combinations_ss.items(): for A in l: for vss in [1, 2]: for dss in [1, 2]: def h(k, v, a, i, vss, dss): return lambda self: self.single_test(k, v, a, i, vss, dss) setattr(TestRecScale, 'test_ss_' + a + '_' + k + '_' + v + '_' + str(vss) + '_' + str(dss), h(k, A['projector'], A['alg'], A['iters'], vss, dss)) astra-toolbox-2.3.0/tests/python/integration/000077500000000000000000000000001475635207100213325ustar00rootroot00000000000000astra-toolbox-2.3.0/tests/python/integration/conftest.py000066400000000000000000000025311475635207100235320ustar00rootroot00000000000000import pytest import astra cuda_present = astra.use_cuda() cupy_present = False if cuda_present: try: import cupy cupy_present = True except Exception: pass pytorch_present = False try: import torch pytorch_present = True except Exception: pass pytorch_cuda_present = False if pytorch_present: try: import torch if torch.cuda.is_available(): pytorch_cuda_present = True except Exception: pass jax_present = False try: import jax jax_present = True except Exception: pass jax_cuda_present = False if jax_present: try: import jax if len(jax.devices('cuda')) > 0: jax_cuda_present = True except Exception: pass backends_to_skip = [] if not cupy_present: backends_to_skip.append('cupy') if not jax_present: backends_to_skip.append('jax_cpu') if not jax_cuda_present: backends_to_skip.append('jax_cuda') if not pytorch_present: backends_to_skip.append('pytorch_cpu') if not pytorch_cuda_present: backends_to_skip.append('pytorch_cuda') def pytest_collection_modifyitems(config, items): for item in items: if hasattr(item, 'callspec') and item.callspec.params.get('backend') in backends_to_skip: item.add_marker(pytest.mark.skip('Backend skipped because it is unavailable')) astra-toolbox-2.3.0/tests/python/integration/test_dlpack.py000066400000000000000000000130021475635207100241750ustar00rootroot00000000000000import astra import astra.experimental import numpy as np import pytest DET_SPACING_X = 1.0 DET_SPACING_Y = 1.0 DET_ROW_COUNT = 20 DET_COL_COUNT = 45 N_ANGLES = 180 ANGLES = np.linspace(0, 2 * np.pi, N_ANGLES, endpoint=False) N_ROWS = 40 N_COLS = 30 N_SLICES = 50 VOL_GEOM = astra.create_vol_geom(N_ROWS, N_COLS, N_SLICES) PROJ_GEOM = astra.create_proj_geom('parallel3d', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES) DATA_INIT_VALUE = 1.0 def _convert_to_backend(data, backend): if backend == 'numpy': return data elif backend == 'pytorch_cpu': import torch return torch.tensor(data, device='cpu') elif backend == 'pytorch_cuda': import torch return torch.tensor(data, device='cuda') elif backend == 'cupy': import cupy as cp return cp.array(data) elif backend == 'jax_cpu': import jax return jax.device_put(data, device=jax.devices('cpu')[0]) elif backend == 'jax_cuda': import jax return jax.device_put(data, device=jax.devices('cuda')[0]) @pytest.fixture def projector(): projector_id = astra.create_projector('cuda3d', PROJ_GEOM, VOL_GEOM) yield projector_id astra.projector3d.delete(projector_id) @pytest.fixture def vol_data(backend): data = np.full([N_SLICES, N_ROWS, N_COLS], DATA_INIT_VALUE, dtype=np.float32) return _convert_to_backend(data, backend) @pytest.fixture def proj_data(backend): data = np.full([DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT], DATA_INIT_VALUE, dtype=np.float32) return _convert_to_backend(data, backend) @pytest.fixture def reference_fp(): vol_data_id = astra.data3d.create('-vol', VOL_GEOM, DATA_INIT_VALUE) data_id, data = astra.create_sino3d_gpu(vol_data_id, PROJ_GEOM, VOL_GEOM) astra.data3d.delete(data_id) return data @pytest.fixture def reference_bp(): proj_data_id = astra.data3d.create('-sino', PROJ_GEOM, DATA_INIT_VALUE) data_id, data = astra.create_backprojection3d_gpu(proj_data_id, PROJ_GEOM, VOL_GEOM) astra.data3d.delete(data_id) return data @pytest.fixture def proj_data_non_contiguous(backend): data = np.full([N_ANGLES, DET_ROW_COUNT, DET_COL_COUNT], DATA_INIT_VALUE, dtype=np.float32) return _convert_to_backend(data, backend).swapaxes(0, 1) @pytest.fixture def vol_geom_singular_dim(singular_dims): dims = [N_ROWS, N_COLS, N_SLICES] for dim in singular_dims.split('-'): if dim == 'rows': dims[0] = 1 elif dim == 'cols': dims[1] = 1 elif dim == 'slices': dims[2] = 1 return astra.create_vol_geom(*dims) @pytest.fixture def vol_data_singular_dim(singular_dims, backend): shape = [N_SLICES, N_ROWS, N_COLS] for dim in singular_dims.split('-'): if dim == 'rows': shape[1] = 1 elif dim == 'cols': shape[2] = 1 elif dim == 'slices': shape[0] = 1 data = np.full(shape, DATA_INIT_VALUE, dtype=np.float32) return _convert_to_backend(data, backend) @pytest.fixture def vol_data_slice(singular_dims, backend): data = np.full([N_SLICES, N_ROWS, N_COLS], DATA_INIT_VALUE, dtype=np.float32) data = _convert_to_backend(data, backend) for dim in singular_dims.split('-'): if dim == 'rows': data = data[:, 0:1, :] elif dim == 'cols': data = data[:, :, 0:1] elif dim == 'slices': data = data[0:1, :, :] return data @pytest.mark.parametrize('backend', ['numpy', 'pytorch_cpu', 'pytorch_cuda', 'cupy', 'jax_cpu', 'jax_cuda']) class TestAll: def test_backends_fp(self, backend, projector, vol_data, proj_data, reference_fp): astra.experimental.direct_FP3D(projector, vol_data, proj_data) if backend.startswith('pytorch'): proj_data = proj_data.cpu() assert np.allclose(proj_data, reference_fp) def test_backends_bp(self, backend, projector, vol_data, proj_data, reference_bp): astra.experimental.direct_BP3D(projector, vol_data, proj_data) if backend.startswith('pytorch'): vol_data = vol_data.cpu() assert np.allclose(vol_data, reference_bp) def test_non_contiguous(self, backend, proj_data_non_contiguous): if backend.startswith('jax'): # JAX should not produce non-contiguous tensors, so nothing to test return with pytest.raises(ValueError): astra.data3d.link('-sino', PROJ_GEOM, proj_data_non_contiguous) @pytest.mark.parametrize('singular_dims', [ 'rows', 'cols', 'slices', 'rows-cols', 'rows-slices', 'cols-slices', 'rows-cols-slices' ]) def test_singular_dimensions(self, backend, singular_dims, vol_geom_singular_dim, vol_data_singular_dim): astra.data3d.link('-vol', vol_geom_singular_dim, vol_data_singular_dim) @pytest.mark.parametrize('backend', ['pytorch_cuda', 'cupy']) @pytest.mark.parametrize('singular_dims', ['rows']) def test_allow_pitched(backend, singular_dims, vol_geom_singular_dim, vol_data_slice): astra.data3d.link('-vol', vol_geom_singular_dim, vol_data_slice) @pytest.mark.parametrize('backend', ['numpy']) def test_read_only(backend, vol_data): vol_data.flags['WRITEABLE'] = False # BufferError for numpy < 2 which doesn't support exporting read-only arrays # ValueError for numpy >= 2 where astra rejects the read-only array with pytest.raises((ValueError, BufferError)): astra.data3d.link('-vol', VOL_GEOM, vol_data) astra-toolbox-2.3.0/tests/python/pytest.ini000066400000000000000000000000601475635207100210340ustar00rootroot00000000000000[pytest] markers = slow: mark test as slow. astra-toolbox-2.3.0/tests/python/unit/000077500000000000000000000000001475635207100177665ustar00rootroot00000000000000astra-toolbox-2.3.0/tests/python/unit/test_algorithm_module.py000066400000000000000000000032301475635207100247300ustar00rootroot00000000000000import astra import pytest import numpy as np @pytest.fixture def algorithm_config(): proj_geom = astra.create_proj_geom('parallel3d', 1.0, 1.0, 20, 20, np.linspace(0, 1, 30)) vol_geom = astra.create_vol_geom(10, 10, 10) proj_data_id = astra.data3d.create('-sino', proj_geom) vol_data_id = astra.data3d.create('-vol', vol_geom) config = astra.astra_dict('FP3D_CUDA') config['ProjectionDataId'] = proj_data_id config['VolumeDataId'] = vol_data_id yield config astra.data3d.delete(proj_data_id) astra.data3d.delete(vol_data_id) def test_create_run(algorithm_config): algorithm_id = astra.algorithm.create(algorithm_config) astra.algorithm.run(algorithm_id) astra.algorithm.delete(algorithm_id) def test_delete(algorithm_config): algorithm_id = astra.algorithm.create(algorithm_config) astra.algorithm.delete(algorithm_id) with pytest.raises(astra.log.AstraError): astra.algorithm.run(algorithm_id) def test_clear(algorithm_config): algorithm_id1 = astra.algorithm.create(algorithm_config) algorithm_id2 = astra.algorithm.create(algorithm_config) astra.algorithm.clear() with pytest.raises(astra.log.AstraError): astra.algorithm.run(algorithm_id1) with pytest.raises(astra.log.AstraError): astra.algorithm.run(algorithm_id2) def test_info(algorithm_config, capsys): get_n_info_objects = lambda: len(capsys.readouterr().out.split('\n')) - 5 algorithm_id = astra.algorithm.create(algorithm_config) astra.algorithm.info() assert get_n_info_objects() == 1 astra.algorithm.delete(algorithm_id) astra.algorithm.info() assert get_n_info_objects() == 0 astra-toolbox-2.3.0/tests/python/unit/test_algorithms_2d.py000066400000000000000000000551221475635207100241420ustar00rootroot00000000000000import astra import numpy as np import pytest DET_SPACING = 1.0 DET_COUNT = 40 N_ANGLES= 180 ANGLES = np.linspace(0, 2 * np.pi, N_ANGLES, endpoint=False) SOURCE_ORIGIN = 100 ORIGIN_DET = 100 N_ROWS = 50 N_COLS = 60 VOL_SHIFT = 0, 0 VOL_GEOM = astra.create_vol_geom( N_ROWS, N_COLS, -N_COLS/2 + VOL_SHIFT[0], N_COLS/2 + VOL_SHIFT[0], -N_ROWS/2 + VOL_SHIFT[1], N_ROWS/2 + VOL_SHIFT[1] ) DATA_INIT_VALUE = 1.0 @pytest.fixture def proj_geom(request): geometry_type = request.param if geometry_type == 'parallel': yield astra.create_proj_geom('parallel', DET_SPACING, DET_COUNT, ANGLES) elif geometry_type == 'parallel_vec': geom = astra.create_proj_geom('parallel', DET_SPACING, DET_COUNT, ANGLES) yield astra.geom_2vec(geom) elif geometry_type == 'fanflat': yield astra.create_proj_geom('fanflat', DET_SPACING, DET_COUNT, ANGLES, SOURCE_ORIGIN, ORIGIN_DET) elif geometry_type == 'fanflat_vec': geom = astra.create_proj_geom('fanflat', DET_SPACING, DET_COUNT, ANGLES, SOURCE_ORIGIN, ORIGIN_DET) yield astra.geom_2vec(geom) elif geometry_type == 'sparse_matrix': dummy_proj_geom = astra.create_proj_geom('parallel', DET_SPACING, DET_COUNT, ANGLES) dummy_proj_id = astra.create_projector('linear', dummy_proj_geom, VOL_GEOM) matrix_id = astra.projector.matrix(dummy_proj_id) yield astra.create_proj_geom('sparse_matrix', DET_SPACING, DET_COUNT, ANGLES, matrix_id) astra.matrix.delete(matrix_id) elif geometry_type == 'short_scan': cone_angle = np.arctan2(0.5 * DET_COUNT * DET_SPACING, SOURCE_ORIGIN + ORIGIN_DET) angles = np.linspace(0, np.pi + 2 * cone_angle, 180) yield astra.create_proj_geom('fanflat', DET_SPACING, DET_COUNT, angles, SOURCE_ORIGIN, ORIGIN_DET) @pytest.fixture def projector(proj_geom, request): projector_type = request.param projector_id = astra.create_projector(projector_type, proj_geom, VOL_GEOM) yield projector_id astra.projector.delete(projector_id) def _fourier_space_filter(proj_geom): # The full filter size should be the smallest power of two that is at least # twice the number of detector pixels full_filter_size = int(2 ** np.ceil(np.log2(2 * proj_geom['DetectorCount']))) half_filter_size = full_filter_size // 2 + 1 return np.linspace(0, 1, half_filter_size).reshape(1, -1) def _real_space_filter(proj_geom): n = proj_geom['DetectorCount'] kernel = np.zeros([1, n]) for i in range(n//4): f = np.pi * (2*i + 1) val = -2.0 / (f * f) kernel[0, n//2 + (2*i+1)] = val kernel[0, n//2 - (2*i+1)] = val kernel[0, n//2] = 0.5 return kernel @pytest.fixture def custom_filter(proj_geom, request): filter_type = request.param if filter_type == 'projection': kernel = _fourier_space_filter(proj_geom) elif filter_type == 'sinogram': weights = np.random.rand(N_ANGLES) kernel = np.outer(weights, _fourier_space_filter(proj_geom)) elif filter_type == 'rprojection': kernel = _real_space_filter(proj_geom) elif filter_type == 'rsinogram': weights = np.random.rand(N_ANGLES) kernel = np.outer(weights, _real_space_filter(proj_geom)) dummy_geom = astra.create_proj_geom('parallel', 1, kernel.shape[1], np.zeros(kernel.shape[0])) filter_data_id = astra.data2d.create('-sino', dummy_geom, kernel) yield filter_type, filter_data_id astra.data2d.delete(filter_data_id) @pytest.fixture def sinogram_mask(proj_geom): mask = np.random.rand(N_ANGLES, DET_COUNT) > 0.1 mask_data_id = astra.data2d.create('-sino', proj_geom, mask) yield mask_data_id astra.data2d.delete(mask_data_id) @pytest.fixture def reconstruction_mask(): mask = np.random.rand(N_ROWS, N_COLS) > 0.1 mask_data_id = astra.data2d.create('-vol', VOL_GEOM, mask) yield mask_data_id astra.data2d.delete(mask_data_id) def make_algorithm_config(algorithm_type, proj_geom, projector=None, options=None): algorithm_config = astra.astra_dict(algorithm_type) vol_data_id = astra.data2d.create('-vol', VOL_GEOM, DATA_INIT_VALUE) if algorithm_type.startswith('FP'): algorithm_config['VolumeDataId'] = vol_data_id proj_data_id = astra.data2d.create('-sino', proj_geom, DATA_INIT_VALUE) else: algorithm_config['ReconstructionDataId'] = vol_data_id # Make reconstruction contain negative and large numbers for testing # min/max constraint options proj_data = -10 * np.ones([N_ANGLES, DET_COUNT]) proj_data[:, DET_COUNT//4:-DET_COUNT//4] = 10 proj_data_id = astra.data2d.create('-sino', proj_geom, proj_data) algorithm_config['ProjectionDataId'] = proj_data_id if projector is not None: algorithm_config['ProjectorId'] = projector if options is not None: algorithm_config['option'] = options return algorithm_config def get_algorithm_output(algorithm_config, n_iter=None): algorithm_id = astra.algorithm.create(algorithm_config) if n_iter is None: if algorithm_config['type'].startswith(('SIRT', 'CGLS', 'EM')): n_iter = 2 elif algorithm_config['type'].startswith('SART'): n_iter = N_ANGLES elif algorithm_config['type'].startswith('ART'): n_iter = N_ANGLES * DET_COUNT else: n_iter = 1 astra.algorithm.run(algorithm_id, n_iter) if algorithm_config['type'].startswith('FP'): output = astra.data2d.get(algorithm_config['ProjectionDataId']) astra.data2d.delete(algorithm_config['VolumeDataId']) else: output = astra.data2d.get(algorithm_config['ReconstructionDataId']) astra.data2d.delete(algorithm_config['ReconstructionDataId']) astra.data2d.delete(algorithm_config['ProjectionDataId']) astra.algorithm.delete(algorithm_id) return output @pytest.mark.parametrize('proj_geom, projector', [ ('parallel', 'line'), ('parallel', 'strip'), ('parallel', 'linear'), ('parallel_vec', 'line'), ('parallel_vec', 'strip'), ('parallel_vec', 'linear'), ('fanflat', 'line_fanflat'), ('fanflat', 'strip_fanflat'), ('fanflat_vec', 'line_fanflat'), ('sparse_matrix', 'sparse_matrix') ], indirect=True) @pytest.mark.parametrize('algorithm_type', ['FP', 'BP', 'FBP', 'SIRT', 'SART', 'ART', 'CGLS']) def test_cpu_algorithms(projector, proj_geom, algorithm_type): if algorithm_type == 'FBP' and proj_geom['type'] != 'parallel': pytest.xfail('Not implemented') algorithm_config = make_algorithm_config(algorithm_type, proj_geom, projector) output = get_algorithm_output(algorithm_config) assert not np.allclose(output, DATA_INIT_VALUE) @pytest.mark.parametrize( 'proj_geom,', ['parallel', 'parallel_vec', 'fanflat', 'fanflat_vec'], indirect=True ) @pytest.mark.parametrize( 'algorithm_type', ['FP_CUDA', 'BP_CUDA', 'FBP_CUDA', 'SIRT_CUDA', 'SART_CUDA', 'CGLS_CUDA', 'EM_CUDA'] ) def test_gpu_algorithms(proj_geom, algorithm_type): algorithm_config = make_algorithm_config(algorithm_type, proj_geom) output = get_algorithm_output(algorithm_config) assert not np.allclose(output, DATA_INIT_VALUE) @pytest.mark.parametrize('proj_geom', ['parallel'], indirect=True) @pytest.mark.parametrize('projector', ['linear'], indirect=True) class TestOptionsCPU: def test_fp_sinogram_mask(self, proj_geom, projector, sinogram_mask): pytest.xfail('Known bug') algorithm_config = make_algorithm_config( algorithm_type='FP', proj_geom=proj_geom, projector=projector, options={'SinogramMaskId': sinogram_mask} ) projection = get_algorithm_output(algorithm_config) assert not np.allclose(projection, DATA_INIT_VALUE) mask = (astra.data2d.get(sinogram_mask) > 0) assert np.allclose(projection[~mask], DATA_INIT_VALUE) def test_fp_volume_mask(self, proj_geom, projector, reconstruction_mask): pytest.xfail('Known bug') algorithm_no_mask = make_algorithm_config( algorithm_type='FP', proj_geom=proj_geom, projector=projector ) algorithm_with_mask = make_algorithm_config( algorithm_type='FP', proj_geom=proj_geom, projector=projector, options={'VolumeMaskId': reconstruction_mask} ) projection_no_mask = get_algorithm_output(algorithm_no_mask) projection_with_mask = get_algorithm_output(algorithm_with_mask) assert not np.allclose(projection_with_mask, DATA_INIT_VALUE) assert not np.allclose(projection_with_mask, projection_no_mask) @pytest.mark.parametrize('algorithm_type', ['BP', 'SIRT', 'SART', 'ART', 'CGLS']) def test_sinogram_mask(self, proj_geom, projector, algorithm_type, sinogram_mask): algorithm_no_mask = make_algorithm_config(algorithm_type, proj_geom, projector) algorithm_with_mask = make_algorithm_config(algorithm_type, proj_geom, projector, options={'SinogramMaskId': sinogram_mask}) reconstruction_no_mask = get_algorithm_output(algorithm_no_mask) reconstruction_with_mask = get_algorithm_output(algorithm_with_mask) assert not np.allclose(reconstruction_with_mask, DATA_INIT_VALUE) assert not np.allclose(reconstruction_with_mask, reconstruction_no_mask) @pytest.mark.parametrize('algorithm_type', ['BP', 'SIRT', 'SART', 'ART', 'CGLS']) def test_reconstruction_mask(self, proj_geom, projector, algorithm_type, reconstruction_mask): if algorithm_type == 'BP': pytest.xfail('Known bug') algorithm_config = make_algorithm_config( algorithm_type, proj_geom, projector, options={'ReconstructionMaskId': reconstruction_mask} ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) mask = (astra.data2d.get(reconstruction_mask) > 0) assert np.allclose(reconstruction[~mask], DATA_INIT_VALUE) @pytest.mark.parametrize('projection_order', ['random', 'sequential', 'custom']) def test_sart_projection_order(self, proj_geom, projector, projection_order): options = {'ProjectionOrder': projection_order} if projection_order == 'custom': pytest.xfail('Known bug') # Set projection order to 0, 5, 10, ..., 175, 1, 6, 11, ... , 176, 2, 7, ... proj_order_list = np.arange(N_ANGLES).reshape(-1, 5).T.flatten() options['ProjectionOrderList'] = proj_order_list algorithm_config = make_algorithm_config(algorithm_type='SART', proj_geom=proj_geom, projector=projector, options=options) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('ray_order', ['sequential', 'custom']) def test_art_ray_order(self, proj_geom, projector, ray_order): options = {'RayOrder': ray_order} if ray_order == 'custom': pytest.xfail('Known bug') # Every combination of (projection_id, detector_id) all_rays = np.mgrid[:N_ANGLES, :DET_COUNT].T.reshape(-1, 2) ray_order_list = np.random.permutation(all_rays).flatten() options['ProjectionOrderList'] = ray_order_list algorithm_config = make_algorithm_config(algorithm_type='ART', proj_geom=proj_geom, projector=projector, options=options) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('filter_type', ['ram-lak', 'none']) def test_fbp_filters_basic(self, proj_geom, projector, filter_type): algorithm_config = make_algorithm_config(algorithm_type='FBP', proj_geom=proj_geom, projector=projector, options={'FilterType': filter_type}) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('filter_type', ['tukey', 'gaussian', 'blackman', 'kaiser']) def test_fbp_filter_parameter(self, proj_geom, projector, filter_type): algorithm_config = make_algorithm_config( algorithm_type='FBP', proj_geom=proj_geom, projector=projector, options={'FilterType': filter_type, 'FilterParameter': -1.0} ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('filter_type', ['shepp-logan', 'cosine', 'hamming', 'hann']) def test_fbp_filter_d(self, proj_geom, projector, filter_type): algorithm_config = make_algorithm_config( algorithm_type='FBP', proj_geom=proj_geom, projector=projector, options={'FilterType': filter_type, 'FilterD': 1.0} ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize( 'custom_filter', ['projection', 'sinogram', 'rprojection', 'rsinogram'], indirect=True ) def test_fbp_custom_filters(self, proj_geom, projector, custom_filter): filter_type, filter_data_id = custom_filter algorithm_config = make_algorithm_config( algorithm_type='FBP', proj_geom=proj_geom, projector=projector, options={'FilterType': filter_type, 'FilterSinogramId': filter_data_id} ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) class TestOptionsGPU: @pytest.mark.parametrize('proj_geom,', ['parallel', 'fanflat'], indirect=True) def test_detector_supersampling_fp(self, proj_geom): algorithm_no_supersampling = make_algorithm_config( algorithm_type='FP_CUDA', proj_geom=proj_geom ) algorithm_with_supersampling = make_algorithm_config( algorithm_type='FP_CUDA', proj_geom=proj_geom, options={'DetectorSuperSampling': 3} ) proj_no_supersampling = get_algorithm_output(algorithm_no_supersampling) proj_with_supersampling = get_algorithm_output(algorithm_with_supersampling) assert not np.allclose(proj_with_supersampling, DATA_INIT_VALUE) assert not np.allclose(proj_with_supersampling, proj_no_supersampling) @pytest.mark.parametrize('proj_geom,', ['parallel', 'fanflat'], indirect=True) @pytest.mark.parametrize('algorithm_type', ['SIRT_CUDA', 'SART_CUDA', 'CGLS_CUDA', 'EM_CUDA']) def test_detector_supersampling_iterative(self, proj_geom, algorithm_type): algorithm_no_supersampling = make_algorithm_config(algorithm_type, proj_geom) algorithm_with_supersampling = make_algorithm_config( algorithm_type, proj_geom, options={'DetectorSuperSampling': 3} ) rec_no_supersampling = get_algorithm_output(algorithm_no_supersampling) rec_with_supersampling = get_algorithm_output(algorithm_with_supersampling) assert not np.allclose(rec_with_supersampling, DATA_INIT_VALUE) assert not np.allclose(rec_with_supersampling, rec_no_supersampling) @pytest.mark.parametrize('proj_geom,', ['parallel', 'fanflat'], indirect=True) @pytest.mark.parametrize( 'algorithm_type', ['BP_CUDA', 'SIRT_CUDA', 'SART_CUDA', 'CGLS_CUDA', 'EM_CUDA'] ) def test_pixel_supersampling(self, proj_geom, algorithm_type): algorithm_no_supersampling = make_algorithm_config(algorithm_type, proj_geom) algorithm_with_supersampling = make_algorithm_config( algorithm_type, proj_geom=proj_geom, options={'PixelSuperSampling': 3} ) rec_no_supersampling = get_algorithm_output(algorithm_no_supersampling) rec_with_supersampling = get_algorithm_output(algorithm_with_supersampling) assert not np.allclose(rec_with_supersampling, DATA_INIT_VALUE) assert not np.allclose(rec_with_supersampling, rec_no_supersampling) @pytest.mark.parametrize('proj_geom', ['parallel', 'fanflat'], indirect=True) @pytest.mark.parametrize('filter_type', ['ram-lak', 'none']) def test_fbp_filters_basic(self, proj_geom, filter_type): algorithm_config = make_algorithm_config(algorithm_type='FBP_CUDA', proj_geom=proj_geom, options={'FilterType': filter_type}) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('proj_geom', ['parallel', 'fanflat'], indirect=True) @pytest.mark.parametrize('filter_type', ['tukey', 'gaussian', 'blackman', 'kaiser']) def test_fbp_filter_parameter(self, proj_geom, filter_type): algorithm_config = make_algorithm_config( algorithm_type='FBP_CUDA', proj_geom=proj_geom, options={'FilterType': filter_type, 'FilterParameter': -1.0} ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('proj_geom', ['parallel', 'fanflat'], indirect=True) @pytest.mark.parametrize('filter_type', ['shepp-logan', 'cosine', 'hamming', 'hann']) def test_fbp_filter_d(self, proj_geom, filter_type): algorithm_config = make_algorithm_config( algorithm_type='FBP_CUDA', proj_geom=proj_geom, options={'FilterType': filter_type, 'FilterD': 1.0} ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('proj_geom', ['parallel', 'fanflat'], indirect=True) @pytest.mark.parametrize( 'custom_filter', ['projection', 'sinogram', 'rprojection', 'rsinogram'], indirect=True ) def test_fbp_custom_filters(self, proj_geom, custom_filter): filter_type, filter_data_id = custom_filter algorithm_config = make_algorithm_config( algorithm_type='FBP_CUDA', proj_geom=proj_geom, options={'FilterType': filter_type, 'FilterSinogramId': filter_data_id} ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('proj_geom', ['short_scan'], indirect=True) def test_short_scan(self, proj_geom): algorithm_no_short_scan = make_algorithm_config( algorithm_type='FBP_CUDA', proj_geom=proj_geom ) algorithm_with_short_scan = make_algorithm_config( algorithm_type='FBP_CUDA', proj_geom=proj_geom, options={'ShortScan': True} ) reconstruction_no_short_scan = get_algorithm_output(algorithm_no_short_scan) reconstruction_with_short_scan = get_algorithm_output(algorithm_with_short_scan) assert not np.allclose(reconstruction_with_short_scan, DATA_INIT_VALUE) assert not np.allclose(reconstruction_with_short_scan, reconstruction_no_short_scan) @pytest.mark.parametrize('proj_geom,', ['parallel'], indirect=True) @pytest.mark.parametrize('algorithm_type', ['SIRT_CUDA', 'SART_CUDA']) def test_min_max_constraint(self, proj_geom, algorithm_type): algorithm_no_constrains = make_algorithm_config(algorithm_type, proj_geom) algorithm_with_constrains = make_algorithm_config( algorithm_type, proj_geom, options={'MinConstraint': 0.0, 'MaxConstraint': 0.125} ) reconstruction_no_constrains = get_algorithm_output(algorithm_no_constrains) reconstruction_with_constrains = get_algorithm_output(algorithm_with_constrains) assert reconstruction_no_constrains.min() < 0.0 assert reconstruction_no_constrains.max() > 0.125 assert reconstruction_with_constrains.min() == 0.0 assert reconstruction_with_constrains.max() == 0.125 @pytest.mark.parametrize('proj_geom,', ['parallel'], indirect=True) @pytest.mark.parametrize('algorithm_type', ['SIRT_CUDA', 'SART_CUDA', 'CGLS_CUDA']) def test_reconstruction_mask(self, proj_geom, algorithm_type, reconstruction_mask): algorithm_config = make_algorithm_config( algorithm_type, proj_geom, options={'ReconstructionMaskId': reconstruction_mask} ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) mask = (astra.data2d.get(reconstruction_mask) > 0) assert np.allclose(reconstruction[~mask], DATA_INIT_VALUE) @pytest.mark.parametrize('proj_geom,', ['parallel'], indirect=True) def test_sinogram_mask(self, proj_geom, sinogram_mask): algorithm_no_mask = make_algorithm_config(algorithm_type='SIRT_CUDA', proj_geom=proj_geom) algorithm_with_sino_mask = make_algorithm_config( algorithm_type='SIRT_CUDA', proj_geom=proj_geom, options={'SinogramMaskId': sinogram_mask} ) reconstruction_no_mask = get_algorithm_output(algorithm_no_mask) reconstruction_with_sino_mask = get_algorithm_output(algorithm_with_sino_mask) assert not np.allclose(reconstruction_with_sino_mask, DATA_INIT_VALUE) assert not np.allclose(reconstruction_with_sino_mask, reconstruction_no_mask) @pytest.mark.parametrize('proj_geom,', ['parallel'], indirect=True) @pytest.mark.parametrize('projection_order', ['random', 'sequential', 'custom']) def test_sart_projection_order(self, proj_geom, projection_order): options = {'ProjectionOrder': projection_order} if projection_order == 'custom': pytest.xfail('Known bug') # Set projection order to 0, 5, 10, ..., 175, 1, 6, 11, ... , 176, 2, 7, ... proj_order_list = np.arange(N_ANGLES).reshape(-1, 5).T.flatten() options['ProjectionOrderList'] = proj_order_list algorithm_config = make_algorithm_config( algorithm_type='SART_CUDA', proj_geom=proj_geom, options=options ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('proj_geom,', ['parallel'], indirect=True) @pytest.mark.parametrize('algorithm_type', ['SIRT_CUDA', 'SART_CUDA', 'CGLS_CUDA', 'EM_CUDA']) def test_get_res_norm(self, proj_geom, algorithm_type): algorithm_config = make_algorithm_config(algorithm_type, proj_geom) algorithm_id = astra.algorithm.create(algorithm_config) astra.algorithm.run(algorithm_id, 2) res_norm = astra.algorithm.get_res_norm(algorithm_id) astra.algorithm.delete(algorithm_id) assert res_norm > 0.0 astra-toolbox-2.3.0/tests/python/unit/test_algorithms_3d.py000066400000000000000000000332431475635207100241430ustar00rootroot00000000000000import astra import numpy as np import pytest DET_SPACING_X = 1.0 DET_SPACING_Y = 1.0 DET_ROW_COUNT = 20 DET_COL_COUNT = 45 N_ANGLES = 180 ANGLES = np.linspace(0, 2 * np.pi, N_ANGLES, endpoint=False) SOURCE_ORIGIN = 100 ORIGIN_DET = 100 N_ROWS = 40 N_COLS = 30 N_SLICES = 50 VOL_SHIFT = 1, 2, 3 VOL_GEOM = astra.create_vol_geom( N_ROWS, N_COLS, N_SLICES, -N_COLS/2 + VOL_SHIFT[0], N_COLS/2 + VOL_SHIFT[0], -N_ROWS/2 + VOL_SHIFT[1], N_ROWS/2 + VOL_SHIFT[1], -N_SLICES/2 + VOL_SHIFT[2], N_SLICES/2 + VOL_SHIFT[2] ) DATA_INIT_VALUE = 1.0 @pytest.fixture def proj_geom(request): geometry_type = request.param if geometry_type == 'parallel3d': return astra.create_proj_geom('parallel3d', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES) elif geometry_type == 'parallel3d_vec': geom = astra.create_proj_geom('parallel3d', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES) return astra.geom_2vec(geom) elif geometry_type == 'cone': return astra.create_proj_geom('cone', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES, SOURCE_ORIGIN, ORIGIN_DET) elif geometry_type == 'cone_vec': geom = astra.create_proj_geom('cone', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES, SOURCE_ORIGIN, ORIGIN_DET) return astra.geom_2vec(geom) elif geometry_type == 'short_scan': cone_angle = np.arctan2(0.5 * DET_COL_COUNT * DET_SPACING_X, SOURCE_ORIGIN + ORIGIN_DET) angles = np.linspace(0, np.pi + 2 * cone_angle, 180) return astra.create_proj_geom('cone', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, angles, SOURCE_ORIGIN, ORIGIN_DET) def _fourier_space_filter(proj_geom): # The full filter size should be the smallest power of two that is at least # twice the number of detector pixels full_filter_size = int(2 ** np.ceil(np.log2(2 * proj_geom['DetectorColCount']))) half_filter_size = full_filter_size // 2 + 1 return np.linspace(0, 1, half_filter_size).reshape(1, -1) def _real_space_filter(proj_geom): n = proj_geom['DetectorColCount'] kernel = np.zeros([1, n]) for i in range(n//4): f = np.pi * (2*i + 1) val = -2.0 / (f * f) kernel[0, n//2 + (2*i+1)] = val kernel[0, n//2 - (2*i+1)] = val kernel[0, n//2] = 0.5 return kernel @pytest.fixture def custom_filter(proj_geom, request): filter_type = request.param if filter_type == 'projection': kernel = _fourier_space_filter(proj_geom) elif filter_type == 'sinogram': weights = np.random.rand(N_ANGLES) kernel = np.outer(weights, _fourier_space_filter(proj_geom)) elif filter_type == 'rprojection': kernel = _real_space_filter(proj_geom) elif filter_type == 'rsinogram': weights = np.random.rand(N_ANGLES) kernel = np.outer(weights, _real_space_filter(proj_geom)) dummy_geom = astra.create_proj_geom('parallel', 1, kernel.shape[1], np.zeros(kernel.shape[0])) filter_data_id = astra.data2d.create('-sino', dummy_geom, kernel) yield filter_type, filter_data_id astra.data2d.delete(filter_data_id) @pytest.fixture def sinogram_mask(proj_geom): mask = np.random.rand(DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT) > 0.1 mask_data_id = astra.data3d.create('-sino', proj_geom, mask) yield mask_data_id astra.data3d.delete(mask_data_id) @pytest.fixture def reconstruction_mask(): mask = np.random.rand(N_SLICES, N_ROWS, N_COLS) > 0.1 mask_data_id = astra.data3d.create('-vol', VOL_GEOM, mask) yield mask_data_id astra.data3d.delete(mask_data_id) def make_algorithm_config(algorithm_type, proj_geom, options=None): algorithm_config = astra.astra_dict(algorithm_type) vol_data_id = astra.data3d.create('-vol', VOL_GEOM, DATA_INIT_VALUE) if algorithm_type.startswith('FP'): algorithm_config['VolumeDataId'] = vol_data_id proj_data_id = astra.data3d.create('-sino', proj_geom, DATA_INIT_VALUE) else: algorithm_config['ReconstructionDataId'] = vol_data_id # Make reconstruction contain negative and large numbers for testing # min/max constraint options proj_data = -10 * np.ones([DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT]) proj_data[DET_ROW_COUNT//4:-DET_ROW_COUNT//4:, DET_COL_COUNT//4:-DET_COL_COUNT//4] = 10 proj_data_id = astra.data3d.create('-sino', proj_geom, proj_data) algorithm_config['ProjectionDataId'] = proj_data_id if options is not None: algorithm_config['option'] = options return algorithm_config def get_algorithm_output(algorithm_config, n_iter=None): if n_iter is None: if algorithm_config['type'] in ['SIRT3D_CUDA', 'CGLS3D_CUDA']: n_iter = 2 else: n_iter = 1 algorithm_id = astra.algorithm.create(algorithm_config) astra.algorithm.run(algorithm_id, n_iter) if algorithm_config['type'].startswith('FP'): output = astra.data3d.get(algorithm_config['ProjectionDataId']) astra.data3d.delete(algorithm_config['VolumeDataId']) else: output = astra.data3d.get(algorithm_config['ReconstructionDataId']) astra.data3d.delete(algorithm_config['ReconstructionDataId']) astra.data3d.delete(algorithm_config['ProjectionDataId']) astra.algorithm.delete(algorithm_id) return output @pytest.mark.parametrize( 'proj_geom,', ['parallel3d', 'parallel3d_vec', 'cone', 'cone_vec'], indirect=True ) @pytest.mark.parametrize( 'algorithm_type', ['FP3D_CUDA', 'BP3D_CUDA', 'FDK_CUDA', 'SIRT3D_CUDA', 'CGLS3D_CUDA'], ) def test_algorithms(proj_geom, algorithm_type): if algorithm_type == 'FDK_CUDA' and proj_geom['type'] not in ['cone', 'cone_vec']: pytest.xfail('Not implemented') algorithm_config = make_algorithm_config(algorithm_type, proj_geom) output = get_algorithm_output(algorithm_config) assert not np.allclose(output, DATA_INIT_VALUE) class TestOptions: @pytest.mark.parametrize('proj_geom,', ['parallel3d', 'cone'], indirect=True) @pytest.mark.parametrize('algorithm_type', ['FP3D_CUDA', 'SIRT3D_CUDA', 'CGLS3D_CUDA']) def test_detector_supersampling_fp(self, proj_geom, algorithm_type): if algorithm_type == 'FP3D_CUDA': pytest.xfail('Known bug') algorithm_no_supersampling = make_algorithm_config(algorithm_type, proj_geom) algorithm_with_supersampling = make_algorithm_config(algorithm_type, proj_geom, options={'DetectorSuperSampling': 3}) output_no_supersampling = get_algorithm_output(algorithm_no_supersampling) output_with_supersampling = get_algorithm_output(algorithm_with_supersampling) assert not np.allclose(output_with_supersampling, DATA_INIT_VALUE) assert not np.allclose(output_with_supersampling, output_no_supersampling) @pytest.mark.parametrize('proj_geom,', ['parallel3d', 'cone'], indirect=True) @pytest.mark.parametrize( 'algorithm_type', ['BP3D_CUDA', 'FDK_CUDA', 'SIRT3D_CUDA', 'CGLS3D_CUDA'] ) def test_voxel_supersampling(self, proj_geom, algorithm_type): if algorithm_type in ['BP3D_CUDA', 'FDK_CUDA']: pytest.xfail('Known bug') if algorithm_type == 'FDK_CUDA' and proj_geom['type'] == 'parallel3d': pytest.xfail('Not implemented') algorithm_no_supersampling = make_algorithm_config(algorithm_type, proj_geom) algorithm_with_supersampling = make_algorithm_config(algorithm_type, proj_geom, options={'VoxelSuperSampling': 3}) reconstruction_no_supersampling = get_algorithm_output(algorithm_no_supersampling) reconstruction_with_supersampling = get_algorithm_output(algorithm_with_supersampling) assert not np.allclose(reconstruction_with_supersampling, DATA_INIT_VALUE) assert not np.allclose(reconstruction_with_supersampling, reconstruction_no_supersampling) @pytest.mark.parametrize('proj_geom', ['cone'], indirect=True) @pytest.mark.parametrize('filter_type', ['ram-lak', 'none']) def test_fbp_filters_basic(self, proj_geom, filter_type): algorithm_config = make_algorithm_config(algorithm_type='FDK_CUDA', proj_geom=proj_geom, options={'FilterType': filter_type}) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('proj_geom', ['cone'], indirect=True) @pytest.mark.parametrize('filter_type', ['tukey', 'gaussian', 'blackman', 'kaiser']) def test_fbp_filter_parameter(self, proj_geom, filter_type): algorithm_config = make_algorithm_config( algorithm_type='FDK_CUDA', proj_geom=proj_geom, options={'FilterType': filter_type, 'FilterParameter': -1.0} ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('proj_geom', ['cone'], indirect=True) @pytest.mark.parametrize('filter_type', ['shepp-logan', 'cosine', 'hamming', 'hann']) def test_fbp_filter_d(self, proj_geom, filter_type): algorithm_config = make_algorithm_config( algorithm_type='FDK_CUDA', proj_geom=proj_geom, options={'FilterType': filter_type, 'FilterD': 1.0} ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('proj_geom', ['cone'], indirect=True) @pytest.mark.parametrize( 'custom_filter', ['projection', 'sinogram', 'rprojection', 'rsinogram'], indirect=True ) def test_fbp_custom_filters(self, proj_geom, custom_filter): filter_type, filter_data_id = custom_filter algorithm_config = make_algorithm_config( algorithm_type='FDK_CUDA', proj_geom=proj_geom, options={'FilterType': filter_type, 'FilterSinogramId': filter_data_id} ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) @pytest.mark.parametrize('proj_geom', ['short_scan'], indirect=True) def test_short_scan(self, proj_geom): algorithm_no_short_scan = make_algorithm_config('FDK_CUDA', proj_geom) algorithm_with_short_scan = make_algorithm_config('FDK_CUDA', proj_geom, options={'ShortScan': True}) reconstruction_no_short_scan = get_algorithm_output(algorithm_no_short_scan) reconstruction_with_short_scan = get_algorithm_output(algorithm_with_short_scan) assert not np.allclose(reconstruction_with_short_scan, DATA_INIT_VALUE) assert not np.allclose(reconstruction_with_short_scan, reconstruction_no_short_scan) @pytest.mark.parametrize('proj_geom,', ['parallel3d'], indirect=True) def test_min_max_constraint(self, proj_geom): algorithm_no_constrains = make_algorithm_config('SIRT3D_CUDA', proj_geom) algorithm_with_constrains = make_algorithm_config( 'SIRT3D_CUDA', proj_geom, options={'MinConstraint': 0.0, 'MaxConstraint': 0.125} ) reconstruction_no_constrains = get_algorithm_output(algorithm_no_constrains) reconstruction_with_constrains = get_algorithm_output(algorithm_with_constrains) assert reconstruction_no_constrains.min() < 0.0 assert reconstruction_no_constrains.max() > 0.125 assert reconstruction_with_constrains.min() == 0.0 assert reconstruction_with_constrains.max() == 0.125 @pytest.mark.parametrize('proj_geom,', ['parallel3d'], indirect=True) @pytest.mark.parametrize('algorithm_type', ['SIRT3D_CUDA', 'CGLS3D_CUDA']) def test_reconstruction_mask(self, proj_geom, reconstruction_mask, algorithm_type): algorithm_config = make_algorithm_config( algorithm_type, proj_geom, options={'ReconstructionMaskId': reconstruction_mask} ) reconstruction = get_algorithm_output(algorithm_config) assert not np.allclose(reconstruction, DATA_INIT_VALUE) mask = (astra.data3d.get(reconstruction_mask) > 0) assert np.allclose(reconstruction[~mask], DATA_INIT_VALUE) @pytest.mark.parametrize('proj_geom,', ['parallel3d'], indirect=True) def test_sinogram_mask(self, proj_geom, sinogram_mask): algorithm_no_mask = make_algorithm_config('SIRT3D_CUDA', proj_geom) algorithm_with_sino_mask = make_algorithm_config('SIRT3D_CUDA', proj_geom, options={'SinogramMaskId': sinogram_mask}) reconstruction_no_mask = get_algorithm_output(algorithm_no_mask) reconstruction_with_sino_mask = get_algorithm_output(algorithm_with_sino_mask) assert not np.allclose(reconstruction_with_sino_mask, DATA_INIT_VALUE) assert not np.allclose(reconstruction_with_sino_mask, reconstruction_no_mask) @pytest.mark.parametrize('proj_geom,', ['parallel3d'], indirect=True) @pytest.mark.parametrize('algorithm_type', ['SIRT3D_CUDA', 'CGLS3D_CUDA']) def test_get_res_norm(self, proj_geom, algorithm_type): algorithm_config = make_algorithm_config(algorithm_type, proj_geom) algorithm_id = astra.algorithm.create(algorithm_config) astra.algorithm.run(algorithm_id, 2) res_norm = astra.algorithm.get_res_norm(algorithm_id) astra.algorithm.delete(algorithm_id) assert res_norm > 0.0 astra-toolbox-2.3.0/tests/python/unit/test_astra_module.py000066400000000000000000000020031475635207100240510ustar00rootroot00000000000000import astra import pytest @pytest.fixture def vol_data_id(): vol_geom = astra.create_vol_geom(10, 10, 10) vol_data_id = astra.data3d.create('-vol', vol_geom) yield vol_data_id astra.data3d.delete(vol_data_id) def test_credits(capsys): astra.astra.credits() assert len(capsys.readouterr().out) > 0 def test_delete(vol_data_id): astra.astra.delete(vol_data_id) with pytest.raises(astra.log.AstraError): astra.data3d.get(vol_data_id) def test_info(vol_data_id, capsys): get_n_info_objects = lambda: len(capsys.readouterr().out.split('\n')) - 1 astra.astra.info(vol_data_id) assert get_n_info_objects() == 1 astra.astra.delete(vol_data_id) assert get_n_info_objects() == 0 def test_get_gpu_info(): assert isinstance(astra.get_gpu_info(), str) def test_has_feature(): for feature in ['cuda', 'random_string']: assert isinstance(astra.astra.has_feature(feature), bool) def test_use_cuda(): assert isinstance(astra.astra.use_cuda(), bool) astra-toolbox-2.3.0/tests/python/unit/test_data_2d.py000066400000000000000000000151441475635207100227020ustar00rootroot00000000000000import astra import numpy as np import pytest DET_SPACING = 1.0 DET_COUNT = 40 N_ANGLES = 180 ANGLES = np.linspace(0, 2 * np.pi, N_ANGLES, endpoint=False) N_ROWS = 50 N_COLS = 60 @pytest.fixture def geometry(geometry_type): if geometry_type == '-vol': return astra.create_vol_geom(N_ROWS, N_COLS) elif geometry_type == '-sino': return astra.create_proj_geom('parallel', DET_SPACING, DET_COUNT, ANGLES) @pytest.fixture def matrix_initializer(geometry_type): if geometry_type == '-vol': return np.random.rand(N_ROWS, N_COLS) elif geometry_type == '-sino': return np.random.rand(N_ANGLES, DET_COUNT) @pytest.mark.parametrize('geometry_type', ['-vol', '-sino']) class TestAll: def test_default_initializer(self, geometry_type, geometry): data_id = astra.data2d.create(geometry_type, geometry) data = astra.data2d.get(data_id) astra.data2d.delete(data_id) assert np.allclose(data, 0.0) def test_scalar_initializer(self, geometry_type, geometry): data_id = astra.data2d.create(geometry_type, geometry, 1.0) data = astra.data2d.get(data_id) astra.data2d.delete(data_id) assert np.allclose(data, 1.0) def test_matrix_initializer(self, geometry_type, geometry, matrix_initializer): data_id = astra.data2d.create(geometry_type, geometry, matrix_initializer) data = astra.data2d.get(data_id) astra.data2d.delete(data_id) assert np.allclose(data, matrix_initializer) @pytest.mark.parametrize('dtype', [np.float32, np.float64, np.int64, bool]) def test_dtypes(self, geometry_type, geometry, matrix_initializer, dtype): matrix_initializer = matrix_initializer.astype(dtype) data_id = astra.data2d.create(geometry_type, geometry, matrix_initializer) data = astra.data2d.get(data_id) astra.data2d.delete(data_id) assert data.dtype == np.float32 assert np.array_equal(data, matrix_initializer.astype(np.float32)) def test_link(self, geometry_type, geometry, matrix_initializer): linked_array = matrix_initializer.astype(np.float32) data_id = astra.data2d.link(geometry_type, geometry, linked_array) # Assert writing to shared ndarray writes to Astra object linked_array[:] = 0.0 astra_object_contents = astra.data2d.get(data_id) assert np.allclose(astra_object_contents, linked_array) # Assert writing to Astra object writes to shared ndarray astra.data2d.store(data_id, 2.0) assert np.allclose(linked_array, 2.0) # Assert Astra object contents is a copy not a link astra_object_contents[:] = 1.0 assert not np.allclose(astra_object_contents, linked_array) astra.data2d.delete(data_id) def test_get_shared(self, geometry_type, geometry, matrix_initializer): data_id = astra.data2d.create(geometry_type, geometry, matrix_initializer) shared_array = astra.data2d.get_shared(data_id) # Assert writing to shared ndarray writes to Astra object shared_array[:] = 0.0 astra_object_contents = astra.data2d.get(data_id) assert np.allclose(astra_object_contents, shared_array) # Assert writing to Astra object writes to shared ndarray astra.data2d.store(data_id, 2.0) assert np.allclose(shared_array, 2.0) # Assert Astra object contents is a copy not a link astra_object_contents[:] = 1.0 assert not np.allclose(astra_object_contents, shared_array) astra.data2d.delete(data_id) def test_get_geometry(self, geometry_type, geometry): data_id = astra.data2d.create(geometry_type, geometry) geometry_in = geometry.copy() # To safely use `pop` later geometry_out = astra.data2d.get_geometry(data_id) astra.data2d.delete(data_id) if geometry_type == '-sino': assert np.allclose(geometry_in.pop('ProjectionAngles'), geometry_out.pop('ProjectionAngles')) assert geometry_in == geometry_out elif geometry_type == 'vol': # `option`, `options`, `Option` and `Options` are synonymous in astra configs assert geometry_in.pop('option') == geometry_out.pop('options') assert geometry_in == geometry_out def test_change_geometry(self, geometry_type, geometry): data_id = astra.data2d.create(geometry_type, geometry) if geometry_type == '-sino': new_spacing = 2 * DET_SPACING new_angles = np.random.rand(N_ANGLES) new_geometry = astra.create_proj_geom('parallel', new_spacing, DET_COUNT, new_angles) astra.data2d.change_geometry(data_id, new_geometry) changed_geometry = astra.data2d.get_geometry(data_id) astra.data2d.delete(data_id) assert changed_geometry['DetectorWidth'] == new_spacing assert np.allclose(changed_geometry['ProjectionAngles'], new_angles) elif geometry_type == '-vol': new_geometry = astra.create_vol_geom(N_ROWS, N_COLS, 0, 1, 2, 3) astra.data2d.change_geometry(data_id, new_geometry) changed_geometry = astra.data2d.get_geometry(data_id) astra.data2d.delete(data_id) assert changed_geometry['options'] == {'WindowMinX': 0, 'WindowMaxX': 1, 'WindowMinY': 2, 'WindowMaxY': 3} def test_delete(self, geometry_type, geometry): data_id = astra.data2d.create(geometry_type, geometry) astra.data2d.delete(data_id) with pytest.raises(astra.log.AstraError): astra.data2d.get(data_id) def test_clear(self, geometry_type, geometry): data_id1 = astra.data2d.create(geometry_type, geometry) data_id2 = astra.data2d.create(geometry_type, geometry) astra.data2d.clear() with pytest.raises(astra.log.AstraError): astra.data2d.get(data_id1) with pytest.raises(astra.log.AstraError): astra.data2d.get(data_id2) def test_info(self, geometry_type, geometry, capsys): get_n_info_objects = lambda: len(capsys.readouterr().out.split('\n')) - 5 data_id = astra.data2d.create(geometry_type, geometry) astra.data2d.info() assert get_n_info_objects() == 1 astra.data2d.delete(data_id) astra.data2d.info() assert get_n_info_objects() == 0 @pytest.mark.parametrize('modified', [True, False]) def test_shepp_logan(modified): geometry = astra.create_vol_geom(N_ROWS, N_COLS) data_id, data = astra.data2d.shepp_logan(geometry, modified) astra.data2d.delete(data_id) assert not np.allclose(data, 0.0) astra-toolbox-2.3.0/tests/python/unit/test_data_3d.py000066400000000000000000000167431475635207100227110ustar00rootroot00000000000000import astra import numpy as np import pytest DET_SPACING_X = 1.0 DET_SPACING_Y = 1.0 DET_ROW_COUNT = 20 DET_COL_COUNT = 45 N_ANGLES = 180 ANGLES = np.linspace(0, 2 * np.pi, N_ANGLES, endpoint=False) N_ROWS = 40 N_COLS = 30 N_SLICES = 50 @pytest.fixture def geometry(geometry_type): if geometry_type == '-vol': return astra.create_vol_geom(N_ROWS, N_COLS, N_SLICES) elif geometry_type == '-sino': return astra.create_proj_geom('parallel3d', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES) @pytest.fixture def matrix_initializer(geometry_type): if geometry_type == '-vol': return np.random.rand(N_SLICES, N_ROWS, N_COLS) elif geometry_type == '-sino': return np.random.rand(DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT) @pytest.mark.parametrize('geometry_type', ['-vol', '-sino']) class TestAll: def test_default_initializer(self, geometry_type, geometry): data_id = astra.data3d.create(geometry_type, geometry) data = astra.data3d.get(data_id) astra.data3d.delete(data_id) assert np.allclose(data, 0.0) def test_scalar_initializer(self, geometry_type, geometry): data_id = astra.data3d.create(geometry_type, geometry, 1.0) data = astra.data3d.get(data_id) astra.data3d.delete(data_id) assert np.allclose(data, 1.0) def test_matrix_initializer(self, geometry_type, geometry, matrix_initializer): data_id = astra.data3d.create(geometry_type, geometry, matrix_initializer) data = astra.data3d.get(data_id) astra.data3d.delete(data_id) assert np.allclose(data, matrix_initializer) @pytest.mark.parametrize('dtype', [np.float32, np.float64, np.int64, bool]) def test_dtypes(self, geometry_type, geometry, matrix_initializer, dtype): matrix_initializer = matrix_initializer.astype(dtype) data_id = astra.data3d.create(geometry_type, geometry, matrix_initializer) data = astra.data3d.get(data_id) astra.data3d.delete(data_id) assert data.dtype == np.float32 assert np.array_equal(data, matrix_initializer.astype(np.float32)) def test_link(self, geometry_type, geometry, matrix_initializer): linked_array = matrix_initializer.astype(np.float32) data_id = astra.data3d.link(geometry_type, geometry, linked_array) # Assert writing to shared ndarray writes to Astra object linked_array[:] = 0.0 astra_object_contents = astra.data3d.get(data_id) assert np.allclose(astra_object_contents, linked_array) # Assert writing to Astra object writes to shared ndarray astra.data3d.store(data_id, 2.0) assert np.allclose(linked_array, 2.0) # Assert Astra object contents is a copy not a link astra_object_contents[:] = 1.0 assert not np.allclose(astra_object_contents, linked_array) astra.data3d.delete(data_id) def test_get_shared(self, geometry_type, geometry, matrix_initializer): data_id = astra.data3d.create(geometry_type, geometry, matrix_initializer) shared_array = astra.data3d.get_shared(data_id) # Assert writing to shared ndarray writes to Astra object shared_array[:] = 0.0 astra_object_contents = astra.data3d.get(data_id) assert np.allclose(astra_object_contents, shared_array) # Assert writing to Astra object writes to shared ndarray astra.data3d.store(data_id, 2.0) assert np.allclose(shared_array, 2.0) # Assert Astra object contents is a copy not a link astra_object_contents[:] = 1.0 assert not np.allclose(astra_object_contents, shared_array) astra.data3d.delete(data_id) def test_dimensions(self, geometry_type, geometry): data_id = astra.data3d.create(geometry_type, geometry) dimensions = astra.data3d.dimensions(data_id) astra.data3d.delete(data_id) if geometry_type == '-sino': assert dimensions == (DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT) elif geometry_type == 'vol': assert dimensions == (N_ROWS, N_COLS, N_SLICES) def test_get_geometry(self, geometry_type, geometry): data_id = astra.data3d.create(geometry_type, geometry) geometry_in = geometry.copy() # To safely use `pop` later geometry_out = astra.data3d.get_geometry(data_id) if geometry_type == '-sino': assert np.allclose(geometry_in.pop('ProjectionAngles'), geometry_out.pop('ProjectionAngles')) assert geometry_in == geometry_out elif geometry_type == 'vol': # `option`, `options`, `Option` and `Options` are synonymous in astra configs assert geometry_in.pop('option') == geometry_out.pop('options') assert geometry_in == geometry_out astra.data3d.delete(data_id) def test_change_geometry(self, geometry_type, geometry): data_id = astra.data3d.create(geometry_type, geometry) if geometry_type == '-sino': new_spacing = 2 * DET_SPACING_X, 3 * DET_SPACING_Y new_angles = np.random.rand(N_ANGLES) new_geometry = astra.create_proj_geom('parallel3d', new_spacing[0], new_spacing[1], DET_ROW_COUNT, DET_COL_COUNT, new_angles) astra.data3d.change_geometry(data_id, new_geometry) changed_geometry = astra.data3d.get_geometry(data_id) astra.data3d.delete(data_id) assert changed_geometry['DetectorSpacingX'] == new_spacing[0] assert changed_geometry['DetectorSpacingY'] == new_spacing[1] assert np.allclose(changed_geometry['ProjectionAngles'], new_angles) elif geometry_type == '-vol': new_geometry = astra.create_vol_geom(N_ROWS, N_COLS, N_SLICES, 0, 1, 2, 3, 4, 5) astra.data3d.change_geometry(data_id, new_geometry) changed_geometry = astra.data3d.get_geometry(data_id) astra.data3d.delete(data_id) assert changed_geometry['options'] == {'WindowMinX': 0, 'WindowMaxX': 1, 'WindowMinY': 2, 'WindowMaxY': 3, 'WindowMinZ': 4, 'WindowMaxZ': 5} def test_delete(self, geometry_type, geometry): data_id = astra.data3d.create(geometry_type, geometry) astra.data3d.delete(data_id) with pytest.raises(astra.log.AstraError): astra.data3d.get(data_id) def test_clear(self, geometry_type, geometry): data_id1 = astra.data3d.create(geometry_type, geometry) data_id2 = astra.data3d.create(geometry_type, geometry) astra.data3d.clear() with pytest.raises(astra.log.AstraError): astra.data3d.get(data_id1) with pytest.raises(astra.log.AstraError): astra.data3d.get(data_id2) def test_info(self, geometry_type, geometry, capsys): get_n_info_objects = lambda: len(capsys.readouterr().out.split('\n')) - 5 data_id = astra.data3d.create(geometry_type, geometry) astra.data3d.info() assert get_n_info_objects() == 1 astra.data3d.delete(data_id) astra.data3d.info() assert get_n_info_objects() == 0 @pytest.mark.parametrize('modified', [True, False]) def test_shepp_logan(modified): geometry = astra.create_vol_geom(N_ROWS, N_COLS, N_SLICES) data_id, data = astra.data3d.shepp_logan(geometry, modified) astra.data2d.delete(data_id) assert not np.allclose(data, 0.0) astra-toolbox-2.3.0/tests/python/unit/test_experimental.py000066400000000000000000000142701475635207100241000ustar00rootroot00000000000000import astra import astra.experimental import numpy as np import pytest MODE_ADD = 0 MODE_SET = 1 DET_SPACING_X = 1.0 DET_SPACING_Y = 1.0 DET_ROW_COUNT = 20 DET_COL_COUNT = 45 N_ANGLES = 180 ANGLES = np.linspace(0, 2 * np.pi, N_ANGLES, endpoint=False) SOURCE_ORIGIN = 100 ORIGIN_DET = 100 N_ROWS = 40 N_COLS = 30 N_SLICES = 50 VOL_SHIFT = 1, 2, 3 VOL_GEOM = astra.create_vol_geom( N_ROWS, N_COLS, N_SLICES, -N_COLS/2 + VOL_SHIFT[0], N_COLS/2 + VOL_SHIFT[0], -N_ROWS/2 + VOL_SHIFT[1], N_ROWS/2 + VOL_SHIFT[1], -N_SLICES/2 + VOL_SHIFT[2], N_SLICES/2 + VOL_SHIFT[2] ) @pytest.fixture(scope='module') def proj_geom(request): if request.param == 'parallel3d': return astra.create_proj_geom('parallel3d', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES) elif request.param == 'parallel3d_vec': geom = astra.create_proj_geom('parallel3d', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES) return astra.geom_2vec(geom) elif request.param == 'cone': return astra.create_proj_geom('cone', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES, SOURCE_ORIGIN, ORIGIN_DET) elif request.param == 'cone_vec': geom = astra.create_proj_geom('cone', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES, SOURCE_ORIGIN, ORIGIN_DET) return astra.geom_2vec(geom) elif request.param == 'short_scan': cone_angle = np.arctan2(0.5 * DET_COL_COUNT * DET_SPACING_X, SOURCE_ORIGIN + ORIGIN_DET) angles = np.linspace(0, np.pi + 2 * cone_angle, 180) return astra.create_proj_geom('cone', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, angles, SOURCE_ORIGIN, ORIGIN_DET) @pytest.fixture def projector(proj_geom): projector_id = astra.create_projector('cuda3d', proj_geom, VOL_GEOM) yield projector_id astra.projector.delete(projector_id) @pytest.fixture def vol_data(): return np.ones([N_SLICES, N_ROWS, N_COLS], dtype=np.float32) @pytest.fixture def vol_buffer(): return np.zeros([N_SLICES, N_ROWS, N_COLS], dtype=np.float32) @pytest.fixture def proj_data(): return np.ones([DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT], dtype=np.float32) @pytest.fixture def proj_buffer(): return np.zeros([DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT], dtype=np.float32) @pytest.mark.parametrize( 'proj_geom,', ['parallel3d', 'parallel3d_vec', 'cone', 'cone_vec'], indirect=True ) class TestAll: def test_direct_FP3D(self, proj_geom, projector, vol_data, proj_buffer): astra.experimental.direct_FPBP3D(projector, vol_data, proj_buffer, MODE_SET, 'FP') proj_buffer_iter2 = proj_buffer.copy() astra.experimental.direct_FPBP3D(projector, vol_data, proj_buffer_iter2, MODE_ADD, 'FP') assert not np.allclose(proj_buffer, 0.0) assert not np.allclose(proj_buffer_iter2, 0.0) assert not np.allclose(proj_buffer, proj_buffer_iter2) def test_direct_BP3D(self, proj_geom, projector, proj_data, vol_buffer): astra.experimental.direct_FPBP3D(projector, vol_buffer, proj_data, MODE_SET, 'BP') vol_buffer_iter2 = vol_buffer.copy() astra.experimental.direct_FPBP3D(projector, vol_buffer_iter2, proj_data, MODE_ADD, 'BP') assert not np.allclose(vol_buffer, 0.0) assert not np.allclose(vol_buffer_iter2, 0.0) assert not np.allclose(vol_buffer, vol_buffer_iter2) def test_composite_FP3D(self, proj_geom, projector, vol_data, proj_buffer): vol_ids = [astra.data3d.create('-vol', VOL_GEOM, vol_data) for _ in range(2)] proj_ids = [astra.data3d.create('-sino', proj_geom, proj_buffer) for _ in range(2)] astra.experimental.do_composite(projector, vol_ids, proj_ids, MODE_SET, 'FP') proj_data_iter1 = [astra.data3d.get(x) for x in proj_ids] astra.experimental.do_composite(projector, vol_ids, proj_ids, MODE_ADD, 'FP') proj_data_iter2 = [astra.data3d.get(x) for x in proj_ids] assert all([not np.allclose(x, 0.0) for x in proj_data_iter1]) assert all([not np.allclose(x, 0.0) for x in proj_data_iter2]) assert all([not np.allclose(x, y) for x, y in zip(proj_data_iter1, proj_data_iter2)]) def test_composite_BP3D(self, proj_geom, projector, proj_data, vol_buffer): vol_ids = [astra.data3d.create('-vol', VOL_GEOM, vol_buffer) for _ in range(2)] proj_ids = [astra.data3d.create('-sino', proj_geom, proj_data) for _ in range(2)] astra.experimental.do_composite(projector, vol_ids, proj_ids, MODE_SET, 'BP') vol_data_iter1 = [astra.data3d.get(x) for x in vol_ids] astra.experimental.do_composite(projector, vol_ids, proj_ids, MODE_ADD, 'BP') vol_data_iter2 = [astra.data3d.get(x) for x in vol_ids] assert all([not np.allclose(x, 0.0) for x in vol_data_iter1]) assert all([not np.allclose(x, 0.0) for x in vol_data_iter2]) assert all([not np.allclose(x, y) for x, y in zip(vol_data_iter1, vol_data_iter2)]) def test_accumulate_FDK(self, proj_geom, projector, proj_data, vol_buffer): if proj_geom['type'].startswith('parallel'): pytest.xfail('Not implemented') proj_id = astra.data3d.link('-sino', proj_geom, proj_data) vol_id = astra.data3d.link('-vol', VOL_GEOM, vol_buffer) astra.experimental.accumulate_FDK(projector, vol_id, proj_id) vol_buffer_iter1 = vol_buffer.copy() astra.experimental.accumulate_FDK(projector, vol_id, proj_id) assert not np.allclose(vol_buffer, 0.0) assert not np.allclose(vol_buffer_iter1, 0.0) assert not np.allclose(vol_buffer, vol_buffer_iter1) def test_getProjectedBBox(self, proj_geom): minv, maxv = astra.experimental.getProjectedBBox(proj_geom, minx=0, maxx=1, miny=2, maxy=3, minz=4, maxz=5) def test_projectPoint(self, proj_geom): u, v = astra.experimental.projectPoint(proj_geom, x=0, y=1, z=2, angle=3) astra-toolbox-2.3.0/tests/python/unit/test_geometries_2d.py000066400000000000000000000050571475635207100241360ustar00rootroot00000000000000import astra import numpy as np import scipy DET_SPACING = 1.0 DET_COUNT = 40 N_ANGLES = 180 ANGLES = np.linspace(0, 2 * np.pi, N_ANGLES, endpoint=False) SOURCE_ORIGIN = 100 ORIGIN_DET = 100 N_ROWS = 50 N_COLS = 60 def get_data_dimensions(type, geom): data_id = astra.data2d.create(type, geom) data = astra.data2d.get(data_id) astra.data2d.delete(data_id) return data.shape class TestVolumeGeometry: def test_arguments(self): geom = astra.create_vol_geom(N_ROWS) assert get_data_dimensions('-vol', geom) == (N_ROWS, N_ROWS) geom = astra.create_vol_geom([N_ROWS, N_COLS]) assert get_data_dimensions('-vol', geom) == (N_ROWS, N_COLS) geom = astra.create_vol_geom(N_ROWS, N_COLS) assert get_data_dimensions('-vol', geom) == (N_ROWS, N_COLS) def test_window(self): geom = astra.create_vol_geom(N_ROWS, N_COLS, 0, 1, 2, 3) assert geom['option'] == {'WindowMinX': 0, 'WindowMaxX': 1, 'WindowMinY': 2, 'WindowMaxY': 3} def test_default_window(self): geom = astra.create_vol_geom(N_ROWS, N_COLS) assert geom['option'] == {'WindowMinX': -N_COLS/2, 'WindowMaxX': N_COLS/2, 'WindowMinY': -N_ROWS/2, 'WindowMaxY': N_ROWS/2} class TestProjectionGeometries: def test_parallel(self): geom = astra.create_proj_geom('parallel', DET_SPACING, DET_COUNT, ANGLES) assert get_data_dimensions('-sino', geom) == (N_ANGLES, DET_COUNT) def test_fanflat(self): geom = astra.create_proj_geom('fanflat', DET_SPACING, DET_COUNT, ANGLES, SOURCE_ORIGIN, ORIGIN_DET) assert get_data_dimensions('-sino', geom) == (N_ANGLES, DET_COUNT) def test_parallel_vec(self): vectors = np.random.rand(N_ANGLES, 6) geom = astra.create_proj_geom('parallel_vec', DET_COUNT, vectors) assert get_data_dimensions('-sino', geom) == (N_ANGLES, DET_COUNT) def test_fanflat_vec(self): vectors = np.random.rand(N_ANGLES, 6) geom = astra.create_proj_geom('fanflat_vec', DET_COUNT, vectors) assert get_data_dimensions('-sino', geom) == (N_ANGLES, DET_COUNT) def test_sparse_matrix(self): matrix = scipy.sparse.csc_array(np.zeros([DET_COUNT * N_ANGLES, N_ROWS * N_COLS])) matrix_id = astra.matrix.create(matrix) geom = astra.create_proj_geom('sparse_matrix', DET_SPACING, DET_COUNT, ANGLES, matrix_id) assert get_data_dimensions('-sino', geom) == (N_ANGLES, DET_COUNT) astra.matrix.delete(matrix_id) astra-toolbox-2.3.0/tests/python/unit/test_geometries_3d.py000066400000000000000000000050351475635207100241330ustar00rootroot00000000000000import astra import numpy as np DET_SPACING_X = 1.0 DET_SPACING_Y = 1.0 DET_ROW_COUNT = 20 DET_COL_COUNT = 45 N_ANGLES = 180 ANGLES = np.linspace(0, 2 * np.pi, 180, endpoint=False) SOURCE_ORIGIN = 100 ORIGIN_DET = 100 N_ROWS = 40 N_COLS = 60 N_SLICES = 50 def get_data_dimensions(type, geom): data_id = astra.data3d.create(type, geom) data = astra.data3d.get(data_id) astra.data3d.delete(data_id) return data.shape class TestVolumeGeometry: def test_arguments(self): geom = astra.create_vol_geom(N_ROWS, N_COLS, N_SLICES) assert get_data_dimensions('-vol', geom) == (N_SLICES, N_ROWS, N_COLS) geom = astra.create_vol_geom(N_ROWS, N_COLS, N_SLICES) assert get_data_dimensions('-vol', geom) == (N_SLICES, N_ROWS, N_COLS) def test_window(self): geom = astra.create_vol_geom(N_ROWS, N_COLS, N_SLICES, 0, 1, 2, 3, 4, 5) assert geom['option'] == {'WindowMinX': 0, 'WindowMaxX': 1, 'WindowMinY': 2, 'WindowMaxY': 3, 'WindowMinZ': 4, 'WindowMaxZ': 5} def test_default_window(self): geom = astra.create_vol_geom(N_ROWS, N_COLS, N_SLICES) assert geom['option'] == {'WindowMinX': -N_COLS/2, 'WindowMaxX': N_COLS/2, 'WindowMinY': -N_ROWS/2, 'WindowMaxY': N_ROWS/2, 'WindowMinZ': -N_SLICES/2, 'WindowMaxZ': N_SLICES/2} class TestProjectionGeometries: def test_parallel3d(self): geom = astra.create_proj_geom('parallel3d', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES) assert get_data_dimensions('-sino', geom) == (DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT) def test_cone(self): geom = astra.create_proj_geom('cone', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES, SOURCE_ORIGIN, ORIGIN_DET) assert get_data_dimensions('-sino', geom) == (DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT) def test_parallel3d_vec(self): vectors = np.random.rand(N_ANGLES, 12) geom = astra.create_proj_geom('parallel3d_vec', DET_ROW_COUNT, DET_COL_COUNT, vectors) assert get_data_dimensions('-sino', geom) == (DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT) def test_cone_vec(self): vectors = np.random.rand(N_ANGLES, 12) geom = astra.create_proj_geom('cone_vec', DET_ROW_COUNT, DET_COL_COUNT, vectors) assert get_data_dimensions('-sino', geom) == (DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT) astra-toolbox-2.3.0/tests/python/unit/test_matrix.py000066400000000000000000000034331475635207100227060ustar00rootroot00000000000000import astra import pytest import scipy import numpy as np import scipy.sparse @pytest.fixture def scipy_matrix(): data = np.random.rand(10, 10).astype(np.float32) return scipy.sparse.csr_array(data) def test_create_get(scipy_matrix): matrix_id = astra.matrix.create(scipy_matrix) astra_matrix = astra.matrix.get(matrix_id) astra.matrix.delete(matrix_id) assert np.array_equal(astra_matrix.todense(), scipy_matrix.todense()) def test_get_size(scipy_matrix): matrix_id = astra.matrix.create(scipy_matrix) astra_matrix_size = astra.matrix.get_size(matrix_id) astra.matrix.delete(matrix_id) assert astra_matrix_size == scipy_matrix.shape def test_store(scipy_matrix): matrix_id = astra.matrix.create(scipy_matrix) astra.matrix.store(matrix_id, -scipy_matrix) astra_matrix = astra.matrix.get(matrix_id) astra.matrix.delete(matrix_id) assert np.array_equal(astra_matrix.todense(), -scipy_matrix.todense()) def test_delete(scipy_matrix): matrix_id = astra.matrix.create(scipy_matrix) astra.matrix.delete(matrix_id) with pytest.raises(astra.log.AstraError): astra.matrix.get(matrix_id) def test_delete(scipy_matrix): matrix_id1 = astra.matrix.create(scipy_matrix) matrix_id2 = astra.matrix.create(scipy_matrix) astra.matrix.clear() with pytest.raises(astra.log.AstraError): astra.matrix.get(matrix_id1) with pytest.raises(astra.log.AstraError): astra.matrix.get(matrix_id2) def test_info(scipy_matrix, capsys): get_n_info_objects = lambda: len(capsys.readouterr().out.split('\n')) - 5 matrix_id = astra.matrix.create(scipy_matrix) astra.matrix.info() assert get_n_info_objects() == 1 astra.matrix.delete(matrix_id) astra.matrix.info() assert get_n_info_objects() == 0 astra-toolbox-2.3.0/tests/python/unit/test_optomo.py000066400000000000000000000077201475635207100227220ustar00rootroot00000000000000import astra import pytest import numpy as np import scipy DET_SPACING_X = 1.0 DET_SPACING_Y = 1.0 DET_ROW_COUNT = 20 DET_COL_COUNT = 45 N_ANGLES = 180 ANGLES = np.linspace(0, 2 * np.pi, N_ANGLES, endpoint=False) N_ROWS = 40 N_COLS = 30 N_SLICES = 50 @pytest.fixture def op_tomo(dimensionality): if dimensionality == '2d': vol_geom = astra.create_vol_geom(N_ROWS, N_COLS) proj_geom = astra.create_proj_geom('parallel', DET_SPACING_Y, DET_COL_COUNT, ANGLES) projector_id = astra.create_projector('cuda', proj_geom, vol_geom) elif dimensionality == '3d': vol_geom = astra.create_vol_geom(N_ROWS, N_COLS, N_SLICES) proj_geom = astra.create_proj_geom('parallel3d', DET_SPACING_X, DET_SPACING_Y, DET_ROW_COUNT, DET_COL_COUNT, ANGLES) projector_id = astra.create_projector('cuda3d', proj_geom, vol_geom) yield astra.OpTomo(projector_id) astra.projector.delete(projector_id) @pytest.fixture def vol_data(dimensionality): if dimensionality == '2d': return np.ones([N_ROWS, N_COLS], dtype=np.float32) elif dimensionality == '3d': return np.ones([N_SLICES, N_ROWS, N_COLS], dtype=np.float32) @pytest.fixture def vol_buffer(dimensionality): if dimensionality == '2d': return np.zeros([N_ROWS, N_COLS], dtype=np.float32) elif dimensionality == '3d': return np.zeros([N_SLICES, N_ROWS, N_COLS], dtype=np.float32) @pytest.fixture def proj_data(dimensionality): if dimensionality == '2d': return np.ones([N_ANGLES, DET_COL_COUNT], dtype=np.float32) elif dimensionality == '3d': return np.ones([DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT], dtype=np.float32) @pytest.fixture def proj_buffer(dimensionality): if dimensionality == '2d': return np.zeros([N_ANGLES, DET_COL_COUNT], dtype=np.float32) elif dimensionality == '3d': return np.zeros([DET_ROW_COUNT, N_ANGLES, DET_COL_COUNT], dtype=np.float32) @pytest.fixture def algorithm(dimensionality): if dimensionality == '2d': return 'SIRT_CUDA' elif dimensionality == '3d': return 'SIRT3D_CUDA' @pytest.mark.parametrize('dimensionality', ['2d', '3d']) class TestAll: def test_fp(self, dimensionality, op_tomo, vol_data): fp = op_tomo.FP(vol_data) assert not np.allclose(fp, 0.0) def test_fp_flattened(self, dimensionality, op_tomo, vol_data): fp = op_tomo.FP(vol_data.flatten()) assert not np.allclose(fp, 0.0) def test_fp_out_arg(self, dimensionality, op_tomo, vol_data, proj_buffer): op_tomo.FP(vol_data, out=proj_buffer) assert not np.allclose(proj_buffer, 0.0) def test_bp(self, dimensionality, op_tomo, proj_data): bp = op_tomo.BP(proj_data) assert not np.allclose(bp, 0.0) def test_bp_flattened(self, dimensionality, op_tomo, proj_data): bp = op_tomo.BP(proj_data.flatten()) assert not np.allclose(bp, 0.0) def test_bp_out_arg(self, dimensionality, op_tomo, proj_data, vol_buffer): op_tomo.BP(proj_data, out=vol_buffer) assert not np.allclose(vol_buffer, 0.0) def test_matvec(self, dimensionality, op_tomo, vol_data): fp = op_tomo(vol_data) assert not np.allclose(fp, 0.0) def test_rmatvec(self, dimensionality, op_tomo, proj_data): bp = op_tomo.T(proj_data) assert not np.allclose(bp, 0.0) def test_mul(self, dimensionality, op_tomo, vol_data): fp = op_tomo * vol_data assert not np.allclose(fp, 0.0) def test_reconstruct(self, dimensionality, op_tomo, proj_data, algorithm): rec = op_tomo.reconstruct(algorithm, proj_data, iterations=2, extraOptions={'MinConstraint': 0.0}) assert not np.allclose(rec, 0.0) def test_scipy_solver(self, dimensionality, op_tomo, proj_data): result = scipy.sparse.linalg.lsqr(op_tomo, proj_data.flatten(), iter_lim=2) rec = result[0] assert not np.allclose(rec, 0.0) astra-toolbox-2.3.0/tests/python/unit/test_projector_module.py000066400000000000000000000047031475635207100247570ustar00rootroot00000000000000import astra import numpy as np import pytest DET_SPACING = 1.0 DET_COUNT = 40 N_ANGLES= 180 ANGLES = np.linspace(0, 2 * np.pi, N_ANGLES, endpoint=False) PROJ_GEOM = astra.create_proj_geom('parallel', DET_SPACING, DET_COUNT, ANGLES) N_ROWS = 50 N_COLS = 60 VOL_GEOM = astra.create_vol_geom(N_ROWS, N_COLS) def test_get_volume_geometry(): projector_id = astra.create_projector('linear', PROJ_GEOM, VOL_GEOM) geometry_in = VOL_GEOM.copy() # To safely use `pop` later geometry_out = astra.projector.volume_geometry(projector_id) astra.projector.delete(projector_id) # `option`, `options`, `Option` and `Options` are synonymous in astra configs assert geometry_in.pop('option') == geometry_out.pop('options') assert geometry_in == geometry_out def test_get_projection_geometry(): projector_id = astra.create_projector('linear', PROJ_GEOM, VOL_GEOM) geometry_in = PROJ_GEOM.copy() # To safely use `pop` later geometry_out = astra.projector.projection_geometry(projector_id) astra.projector.delete(projector_id) assert np.allclose(geometry_in.pop('ProjectionAngles'), geometry_out.pop('ProjectionAngles')) assert geometry_in == geometry_out def test_is_cuda(): projector_id = astra.create_projector('cuda', PROJ_GEOM, VOL_GEOM) assert astra.projector.is_cuda(projector_id) astra.projector.delete(projector_id) projector_id = astra.create_projector('linear', PROJ_GEOM, VOL_GEOM) assert not astra.projector.is_cuda(projector_id) astra.projector.delete(projector_id) def test_delete(): projector_id = astra.create_projector('linear', PROJ_GEOM, VOL_GEOM) astra.projector.delete(projector_id) with pytest.raises(astra.log.AstraError): astra.projector.is_cuda(projector_id) def test_clear(): projector_id1 = astra.create_projector('linear', PROJ_GEOM, VOL_GEOM) projector_id2 = astra.create_projector('linear', PROJ_GEOM, VOL_GEOM) astra.projector.clear() with pytest.raises(astra.log.AstraError): astra.projector.is_cuda(projector_id1) with pytest.raises(astra.log.AstraError): astra.projector.is_cuda(projector_id2) def test_info(capsys): get_n_info_objects = lambda: len(capsys.readouterr().out.split('\n')) - 5 projector_id = astra.create_projector('linear', PROJ_GEOM, VOL_GEOM) astra.projector.info() assert get_n_info_objects() == 1 astra.projector.delete(projector_id) astra.projector.info() assert get_n_info_objects() == 0 astra-toolbox-2.3.0/tests/python/unit/test_tests_module.py000066400000000000000000000004621475635207100241100ustar00rootroot00000000000000import astra def test_cuda(capsys): astra.test_CUDA() n_stdout_lines = len(capsys.readouterr().out.split('\n')) - 1 assert n_stdout_lines == 5 def test_no_cuda(capsys): astra.test_noCUDA() n_stdout_lines = len(capsys.readouterr().out.split('\n')) - 1 assert n_stdout_lines == 2 astra-toolbox-2.3.0/tests/test_AstraObjectManager.cpp000066400000000000000000000047651475635207100227410ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define BOOST_TEST_DYN_LINK #include #include #include "astra/AstraObjectManager.h" struct TestT { TestT(int _x) : x(_x) { } bool operator==(int _x) const { return x == _x; } int x; bool isInitialized() const { return true; } std::string description() const { return ""; } }; namespace astra { class CTestManager : public Singleton, public CAstraObjectManager { virtual std::string getType() const { return "test"; } }; DEFINE_SINGLETON(CTestManager); } BOOST_AUTO_TEST_CASE( testAstraObjectManager ) { astra::CTestManager &man = astra::CTestManager::getSingleton(); int i1 = man.store(new TestT(1)); BOOST_REQUIRE(man.hasIndex(i1)); BOOST_CHECK(*(man.get(i1)) == 1); int i2 = man.store(new TestT(2)); BOOST_REQUIRE(man.hasIndex(i2)); BOOST_CHECK(*(man.get(i1)) == 1); BOOST_CHECK(*(man.get(i2)) == 2); man.remove(i1); BOOST_CHECK(!man.hasIndex(i1)); BOOST_REQUIRE(man.hasIndex(i2)); int i3 = man.store(new TestT(3)); BOOST_REQUIRE(man.hasIndex(i3)); BOOST_CHECK(*(man.get(i2)) == 2); BOOST_CHECK(*(man.get(i3)) == 3); TestT* pi4 = new TestT(4); int i4 = man.store(pi4); BOOST_REQUIRE(man.hasIndex(i4)); BOOST_CHECK(*(man.get(i2)) == 2); BOOST_CHECK(*(man.get(i3)) == 3); BOOST_CHECK(*(man.get(i4)) == 4); BOOST_CHECK(man.getIndex(pi4) == i4); man.clear(); BOOST_CHECK(!man.hasIndex(i1)); BOOST_CHECK(!man.hasIndex(i2)); BOOST_CHECK(!man.hasIndex(i3)); BOOST_CHECK(!man.hasIndex(i4)); BOOST_CHECK(!man.getIndex(pi4)); } astra-toolbox-2.3.0/tests/test_FanFlatProjectionGeometry2D.cpp000066400000000000000000000077151475635207100245150ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define BOOST_TEST_DYN_LINK #include #include #include #include #include #include "astra/FanFlatProjectionGeometry2D.h" BOOST_AUTO_TEST_CASE( testFanFlatProjectionGeometry2D_Constructor ) { astra::float32 angles[] = { 0.0f, 1.0f, 2.0f, 3.0f }; astra::CFanFlatProjectionGeometry2D geom(4, 8, 0.5f, angles, 1.0f, 2.0f); BOOST_REQUIRE( geom.isInitialized() ); BOOST_CHECK( geom.getProjectionAngleCount() == 4 ); BOOST_CHECK( geom.getDetectorCount() == 8 ); BOOST_CHECK( geom.getDetectorWidth() == 0.5f ); BOOST_CHECK( geom.getProjectionAngle(0) == 0.0f ); BOOST_CHECK( geom.getProjectionAngle(1) == 1.0f ); BOOST_CHECK( geom.getProjectionAngle(2) == 2.0f ); BOOST_CHECK( geom.getProjectionAngle(3) == 3.0f ); BOOST_CHECK( geom.getProjectionAngles()[0] == 0.0f ); BOOST_CHECK( geom.getProjectionAngles()[3] == 3.0f ); BOOST_CHECK( geom.getOriginSourceDistance() == 1.0f ); BOOST_CHECK( geom.getOriginDetectorDistance() == 2.0f ); } BOOST_AUTO_TEST_CASE( testFanFlatProjectionGeometry2D_Offsets ) { astra::float32 angles[] = { 0.0f, 1.0f, 2.0f, 3.0f }; astra::CFanFlatProjectionGeometry2D geom(4, 8, 0.5f, angles, 1.0f, 2.0f); BOOST_REQUIRE( geom.isInitialized() ); BOOST_CHECK( geom.getSourceDetectorDistance() == 3.0f ); BOOST_CHECK_SMALL( geom.getProjectionAngleDegrees(2) - 114.591559026165f, 1e-5f ); // CHECKME: where is the center of the detector array? BOOST_CHECK( geom.detectorOffsetToIndexFloat(0.0f) == 3.5f ); BOOST_CHECK( geom.detectorOffsetToIndexFloat(0.625f) == 4.75f ); BOOST_CHECK( geom.detectorOffsetToIndex(-0.1f) == 3 ); BOOST_CHECK( geom.indexToDetectorOffset(0) == -1.75f ); BOOST_CHECK( geom.indexToDetectorOffset(1) == -1.25f ); int angle, detector; geom.indexToAngleDetectorIndex(10, angle, detector); BOOST_CHECK( angle == 1 ); BOOST_CHECK( detector == 2 ); } BOOST_AUTO_TEST_CASE( testFanFlatProjectionGeometry2D_Clone ) { astra::float32 angles[] = { 0.0f, 1.0f, 2.0f, 3.0f }; astra::CFanFlatProjectionGeometry2D geom(4, 8, 0.5f, angles, 1.0f, 2.0f); BOOST_REQUIRE( geom.isInitialized() ); astra::CFanFlatProjectionGeometry2D* geom2; geom2 = dynamic_cast(geom.clone()); BOOST_REQUIRE( geom2 ); BOOST_REQUIRE( geom2->isInitialized() ); BOOST_CHECK( geom.isEqual(geom2) ); BOOST_CHECK( geom2->getProjectionAngleCount() == 4 ); BOOST_CHECK( geom2->getDetectorCount() == 8 ); BOOST_CHECK( geom2->getDetectorWidth() == 0.5f ); BOOST_CHECK( geom2->getProjectionAngle(0) == 0.0f ); BOOST_CHECK( geom2->getProjectionAngle(1) == 1.0f ); BOOST_CHECK( geom2->getProjectionAngle(2) == 2.0f ); BOOST_CHECK( geom2->getProjectionAngle(3) == 3.0f ); BOOST_CHECK( geom2->getProjectionAngles()[0] == 0.0f ); BOOST_CHECK( geom2->getProjectionAngles()[3] == 3.0f ); BOOST_CHECK( geom2->getOriginSourceDistance() == 1.0f ); BOOST_CHECK( geom2->getOriginDetectorDistance() == 2.0f ); delete geom2; } astra-toolbox-2.3.0/tests/test_Float32Data2D.cpp000066400000000000000000000145021475635207100214250ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define BOOST_TEST_DYN_LINK #include #include #include #include #include "astra/Float32Data2D.h" // Utility class to test Float32Data2D class CTestFloat32Data2D : public astra::CFloat32Data2D { public: CTestFloat32Data2D(int _iWidth, int _iHeight) { m_bInitialized = _initialize(_iWidth, _iHeight); } CTestFloat32Data2D(int _iWidth, int _iHeight, const astra::float32* _pfData) { m_bInitialized = _initialize(_iWidth, _iHeight, _pfData); } CTestFloat32Data2D(int _iWidth, int _iHeight, astra::float32 _fScalar) { m_bInitialized = _initialize(_iWidth, _iHeight, _fScalar); } CTestFloat32Data2D() { } }; struct TestFloat32Data2D { static astra::float32 d[]; TestFloat32Data2D() : data(2,2,d) { } ~TestFloat32Data2D() { } CTestFloat32Data2D data; }; astra::float32 TestFloat32Data2D::d[] = { 1.0f, 2.0f, 3.0f, 4.0f }; BOOST_AUTO_TEST_CASE( testFloat32Data2D_Constructor1 ) { CTestFloat32Data2D data(2,2); BOOST_REQUIRE( data.isInitialized() ); BOOST_CHECK( data.getWidth() == 2 ); BOOST_CHECK( data.getHeight() == 2 ); BOOST_CHECK( data.getSize() == 4 ); BOOST_CHECK( data.getDimensionCount() == 2 ); BOOST_REQUIRE( data.getDataConst() != 0 ); BOOST_REQUIRE( data.getData() != 0 ); BOOST_REQUIRE( data.getData2D() != 0 ); BOOST_REQUIRE( data.getData2DConst() != 0 ); BOOST_REQUIRE( data.getData2D()[0] != 0 ); BOOST_REQUIRE( data.getData2DConst()[0] != 0 ); data.setData(1.0f); // CHECKME: should this be necessary? data.updateStatistics(); BOOST_CHECK( data.getGlobalMin() == 1.0f ); BOOST_CHECK( data.getGlobalMax() == 1.0f ); BOOST_CHECK( data.getGlobalMean() == 1.0f ); BOOST_CHECK( data.getData()[0] == 1.0f ); BOOST_CHECK( data.getDataConst()[0] == 1.0f ); BOOST_CHECK( data.getData2D()[0][0] == 1.0f ); BOOST_CHECK( data.getData2DConst()[0][0] == 1.0f ); } BOOST_AUTO_TEST_CASE( testFloat32Data2D_Constructor2 ) { CTestFloat32Data2D data(2,2,1.5f); // CHECKME: should this be necessary? data.updateStatistics(); BOOST_REQUIRE( data.isInitialized() ); BOOST_CHECK( data.getWidth() == 2 ); BOOST_CHECK( data.getHeight() == 2 ); BOOST_CHECK( data.getSize() == 4 ); BOOST_CHECK( data.getDimensionCount() == 2 ); BOOST_REQUIRE( data.getDataConst() != 0 ); BOOST_REQUIRE( data.getData() != 0 ); BOOST_REQUIRE( data.getData2D() != 0 ); BOOST_REQUIRE( data.getData2DConst() != 0 ); BOOST_REQUIRE( data.getData2D()[0] != 0 ); BOOST_REQUIRE( data.getData2DConst()[0] != 0 ); BOOST_CHECK( data.getGlobalMin() == 1.5f ); BOOST_CHECK( data.getGlobalMax() == 1.5f ); BOOST_CHECK( data.getGlobalMean() == 1.5f ); BOOST_CHECK( data.getData()[0] == 1.5f ); BOOST_CHECK( data.getDataConst()[0] == 1.5f ); BOOST_CHECK( data.getData2D()[0][0] == 1.5f ); BOOST_CHECK( data.getData2DConst()[0][0] == 1.5f ); } BOOST_AUTO_TEST_CASE( testFloat32Data2D_Constructor3 ) { astra::float32 d[] = { 1.0f, 2.0f, 3.0f, 4.0f }; CTestFloat32Data2D data(2,2,d); // CHECKME: should this be necessary? data.updateStatistics(); BOOST_REQUIRE( data.isInitialized() ); BOOST_CHECK( data.getWidth() == 2 ); BOOST_CHECK( data.getHeight() == 2 ); BOOST_CHECK( data.getSize() == 4 ); BOOST_CHECK( data.getDimensionCount() == 2 ); BOOST_REQUIRE( data.getDataConst() != 0 ); BOOST_REQUIRE( data.getData() != 0 ); BOOST_REQUIRE( data.getData2D() != 0 ); BOOST_REQUIRE( data.getData2DConst() != 0 ); BOOST_REQUIRE( data.getData2D()[0] != 0 ); BOOST_REQUIRE( data.getData2DConst()[0] != 0 ); BOOST_CHECK( data.getGlobalMin() == 1.0f ); BOOST_CHECK( data.getGlobalMax() == 4.0f ); BOOST_CHECK( data.getData()[0] == 1.0f ); BOOST_CHECK( data.getDataConst()[0] == 1.0f ); BOOST_CHECK( data.getData2D()[0][0] == 1.0f ); BOOST_CHECK( data.getData2DConst()[0][0] == 1.0f ); } BOOST_FIXTURE_TEST_CASE( testFloat32Data2D_Operators, TestFloat32Data2D ) { // Note: all operations below involve exactly representable floats, // so there is no need to use epsilons in the checks data.updateStatistics(); // FIXME: should those be correct here? BOOST_CHECK( data.getGlobalMin() == 1.0f ); BOOST_CHECK( data.getGlobalMax() == 4.0f ); BOOST_CHECK( data.getGlobalMean() == 2.5f ); data *= 2.0f; BOOST_CHECK( data.getDataConst()[0] == 2.0f ); BOOST_CHECK( data.getDataConst()[3] == 8.0f ); data /= 0.5f; BOOST_CHECK( data.getDataConst()[0] == 4.0f ); BOOST_CHECK( data.getDataConst()[3] == 16.0f ); astra::float32 d[] = { 1.0f, 2.0f, 3.0f, 4.0f }; CTestFloat32Data2D data2(2,2,d); data += data2; BOOST_CHECK( data.getDataConst()[0] == 5.0f ); BOOST_CHECK( data.getDataConst()[3] == 20.0f ); data *= data2; BOOST_CHECK( data.getDataConst()[0] == 5.0f ); BOOST_CHECK( data.getDataConst()[3] == 80.0f ); data -= data2; BOOST_CHECK( data.getDataConst()[0] == 4.0f ); BOOST_CHECK( data.getDataConst()[3] == 76.0f ); data += 0.5f; BOOST_CHECK( data.getDataConst()[0] == 4.5f ); BOOST_CHECK( data.getDataConst()[3] == 76.5f ); data -= 0.5f; BOOST_CHECK( data.getDataConst()[0] == 4.0f ); BOOST_CHECK( data.getDataConst()[3] == 76.0f ); } BOOST_FIXTURE_TEST_CASE( testFloat32Data2D_Update, TestFloat32Data2D ) { data.getData()[2] = 42.0f; data.getData()[1] = -37.0f; data.updateStatistics(); BOOST_CHECK( data.getGlobalMin() == -37.0f ); BOOST_CHECK( data.getGlobalMax() == 42.0f ); BOOST_CHECK( data.getGlobalMean() == 2.5f ); } astra-toolbox-2.3.0/tests/test_Float32ProjectionData2D.cpp000066400000000000000000000072661475635207100234730ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define BOOST_TEST_DYN_LINK #include #include #include #include #include "astra/Float32ProjectionData2D.h" #include "astra/ParallelProjectionGeometry2D.h" // Note: most of the features of CFloat32ProjectionData2D are tested by // the CFloat32Data2D tests. BOOST_AUTO_TEST_CASE( testFloat32ProjectionData2D_Constructor1 ) { astra::float32 angles[] = { 0.0f, 1.0f }; astra::CParallelProjectionGeometry2D geom(2, 4, 0.5f, angles); BOOST_REQUIRE( geom.isInitialized() ); astra::CFloat32ProjectionData2D data(&geom); BOOST_REQUIRE( data.isInitialized() ); BOOST_CHECK( data.getType() == astra::CFloat32Data2D::PROJECTION ); BOOST_CHECK( data.getGeometry()->isEqual(&geom) ); } BOOST_AUTO_TEST_CASE( testFloat32ProjectionData2D_Constructor2 ) { astra::float32 angles[] = { 0.0f, 1.0f }; astra::float32 d[] = { 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.f }; astra::CParallelProjectionGeometry2D geom(2, 4, 0.5f, angles); BOOST_REQUIRE( geom.isInitialized() ); astra::CFloat32ProjectionData2D data(&geom, d); BOOST_REQUIRE( data.isInitialized() ); BOOST_CHECK( data.getType() == astra::CFloat32Data2D::PROJECTION ); BOOST_CHECK( data.getGeometry()->isEqual(&geom) ); // CHECKME: should this be necessary? data.updateStatistics(); BOOST_CHECK( data.getGlobalMax() == 10.0f ); } BOOST_AUTO_TEST_CASE( testFloat32ProjectionData2D_Constructor3 ) { astra::float32 angles[] = { 0.0f, 1.0f }; astra::CParallelProjectionGeometry2D geom(2, 4, 0.5f, angles); BOOST_REQUIRE( geom.isInitialized() ); astra::CFloat32ProjectionData2D data(&geom, 3.5f); BOOST_REQUIRE( data.isInitialized() ); BOOST_CHECK( data.getType() == astra::CFloat32Data2D::PROJECTION ); BOOST_CHECK( data.getGeometry()->isEqual(&geom) ); // CHECKME: should this be necessary? data.updateStatistics(); BOOST_CHECK( data.getGlobalMax() == 3.5f ); } BOOST_AUTO_TEST_CASE( testFloat32ProjectionData2D_Clone ) { astra::float32 angles[] = { 0.0f, 1.0f }; astra::CParallelProjectionGeometry2D geom(2, 4, 0.5f, angles); BOOST_REQUIRE( geom.isInitialized() ); astra::CFloat32ProjectionData2D data(&geom, 3.5f); BOOST_REQUIRE( data.isInitialized() ); astra::CFloat32ProjectionData2D data2(data); BOOST_REQUIRE( data2.isInitialized() ); BOOST_CHECK( data2.getGeometry()->isEqual(&geom) ); BOOST_CHECK( data2.getDataConst()[0] == 3.5f ); BOOST_CHECK( data2.getDataConst()[3] == 3.5f ); astra::CFloat32ProjectionData2D data3; data3 = data; BOOST_REQUIRE( data3.isInitialized() ); BOOST_CHECK( data3.getGeometry()->isEqual(&geom) ); BOOST_CHECK( data3.getDataConst()[0] == 3.5f ); BOOST_CHECK( data3.getDataConst()[3] == 3.5f ); } astra-toolbox-2.3.0/tests/test_Float32VolumeData2D.cpp000066400000000000000000000065221475635207100226200ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define BOOST_TEST_DYN_LINK #include #include #include #include #include "astra/Float32VolumeData2D.h" // Note: most of the features of CFloat32VolumeData2D are tested by // the CFloat32Data2D tests. BOOST_AUTO_TEST_CASE( testFloat32VolumeData2D_Constructor1 ) { astra::CVolumeGeometry2D geom(16, 32); BOOST_REQUIRE( geom.isInitialized() ); astra::CFloat32VolumeData2D data(&geom); BOOST_REQUIRE( data.isInitialized() ); BOOST_CHECK( data.getType() == astra::CFloat32Data2D::VOLUME ); BOOST_CHECK( data.getGeometry()->isEqual(&geom) ); } BOOST_AUTO_TEST_CASE( testFloat32VolumeData2D_Constructor1odd ) { astra::CVolumeGeometry2D geom(16, 32); BOOST_REQUIRE( geom.isInitialized() ); astra::CFloat32VolumeData2D data(&geom, 1.0f); BOOST_REQUIRE( data.isInitialized() ); BOOST_CHECK( data.getType() == astra::CFloat32Data2D::VOLUME ); BOOST_CHECK( data.getGeometry()->isEqual(&geom) ); // CHECKME: should this be necessary? data.updateStatistics(); BOOST_CHECK( data.getGlobalMax() == 1.0f ); } BOOST_AUTO_TEST_CASE( testFloat32VolumeData2D_Constructor2 ) { astra::float32 d[] = { 1.0f, 2.0f, 3.0f, 4.0f }; astra::CVolumeGeometry2D geom(2, 2); BOOST_REQUIRE( geom.isInitialized() ); astra::CFloat32VolumeData2D data(&geom, d); BOOST_REQUIRE( data.isInitialized() ); BOOST_CHECK( data.getType() == astra::CFloat32Data2D::VOLUME ); BOOST_CHECK( data.getGeometry()->isEqual(&geom) ); // CHECKME: should this be necessary? data.updateStatistics(); BOOST_CHECK( data.getGlobalMax() == 4.0f ); } BOOST_AUTO_TEST_CASE( testFloat32VolumeData2D_Clone ) { astra::float32 d[] = { 1.0f, 2.0f, 3.0f, 4.0f }; astra::CVolumeGeometry2D geom(2, 2); BOOST_REQUIRE( geom.isInitialized() ); astra::CFloat32VolumeData2D data(&geom, d); BOOST_REQUIRE( data.isInitialized() ); astra::CFloat32VolumeData2D data2(data); BOOST_REQUIRE( data2.isInitialized() ); BOOST_CHECK( data2.getGeometry()->isEqual(&geom) ); BOOST_CHECK( data2.getDataConst()[0] == 1.0f ); BOOST_CHECK( data2.getDataConst()[3] == 4.0f ); astra::CFloat32VolumeData2D data3; data3 = data; BOOST_REQUIRE( data3.isInitialized() ); BOOST_CHECK( data3.getGeometry()->isEqual(&geom) ); BOOST_CHECK( data3.getDataConst()[0] == 1.0f ); BOOST_CHECK( data3.getDataConst()[3] == 4.0f ); } astra-toolbox-2.3.0/tests/test_Fourier.cpp000066400000000000000000000036751475635207100206570ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define BOOST_TEST_DYN_LINK #include #include #include #include #include "astra/Fourier.h" BOOST_AUTO_TEST_CASE( testFourier_FFT_1D_1 ) { astra::float32 data[16] = { 1.0f,0.0f, 1.0f,0.0f, 1.0f,0.0f, 0.0f,0.0f, 0.0f,0.0f, 0.0f,0.0f, 1.0f,0.0f, 1.0f,0.0f }; int ip[6]; astra::float32 w[8]; ip[0] = 0; astra::cdft(16, -1, data, ip, w); astra::float32 expected1[16] = { 5.0f,0.0f, 2.414214f,0.0f, -1.0f,0.0f, -0.414214f,0.0f, 1.0f,0.0f, -0.414214f,0.0f, -1.0f,0.0f, 2.414214f,0.0f }; for (unsigned int i = 0; i < 16; ++i) { BOOST_CHECK_SMALL(data[i] - expected1[i], 0.00001f); } astra::cdft(16, 1, data, ip, w); astra::float32 expected2[16] = { 8.0f,0.0f, 8.0f,0.0f, 8.0f,0.0f, 0.0f,0.0f, 0.0f,0.0f, 0.0f,0.0f, 8.0f,0.0f, 8.0f,0.0f }; for (unsigned int i = 0; i < 16; ++i) { BOOST_CHECK_SMALL(data[i] - expected2[i], 0.00001f); } } astra-toolbox-2.3.0/tests/test_ParallelProjectionGeometry2D.cpp000066400000000000000000000075201475635207100247300ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define BOOST_TEST_DYN_LINK #include #include #include #include #include "astra/ParallelProjectionGeometry2D.h" BOOST_AUTO_TEST_CASE( testParallelProjectionGeometry2D_Constructor ) { astra::float32 angles[] = { 0.0f, 1.0f, 2.0f, 3.0f }; astra::CParallelProjectionGeometry2D geom(4, 8, 0.5f, angles); BOOST_REQUIRE( geom.isInitialized() ); BOOST_CHECK( geom.getProjectionAngleCount() == 4 ); BOOST_CHECK( geom.getDetectorCount() == 8 ); BOOST_CHECK( geom.getDetectorWidth() == 0.5f ); BOOST_CHECK( geom.getProjectionAngle(0) == 0.0f ); BOOST_CHECK( geom.getProjectionAngle(1) == 1.0f ); BOOST_CHECK( geom.getProjectionAngle(2) == 2.0f ); BOOST_CHECK( geom.getProjectionAngle(3) == 3.0f ); BOOST_CHECK( geom.getProjectionAngles()[0] == 0.0f ); BOOST_CHECK( geom.getProjectionAngles()[3] == 3.0f ); } BOOST_AUTO_TEST_CASE( testParallelProjectionGeometry2D_Offsets ) { astra::float32 angles[] = { 0.0f, 1.0f, 2.0f, 3.0f }; astra::CParallelProjectionGeometry2D geom(4, 8, 0.5f, angles); BOOST_REQUIRE( geom.isInitialized() ); BOOST_CHECK_SMALL( geom.getProjectionAngleDegrees(2) - 114.59155902f, 1e-5f ); BOOST_CHECK( geom.detectorOffsetToIndexFloat(0.0f) == 3.5f ); BOOST_CHECK( geom.detectorOffsetToIndexFloat(0.625f) == 4.75f ); BOOST_CHECK( geom.detectorOffsetToIndex(-0.1f) == 3 ); BOOST_CHECK( geom.indexToDetectorOffset(0) == -1.75f ); BOOST_CHECK( geom.indexToDetectorOffset(1) == -1.25f ); int angle, detector; geom.indexToAngleDetectorIndex(10, angle, detector); BOOST_CHECK( angle == 1 ); BOOST_CHECK( detector == 2 ); } BOOST_AUTO_TEST_CASE( testParallelProjectionGeometry2D_Offsets_odd ) { astra::float32 angles[] = { 0.0f, 1.0f, 2.0f, 3.0f }; astra::CParallelProjectionGeometry2D geom(4, 7, 0.5f, angles); BOOST_REQUIRE( geom.isInitialized() ); BOOST_CHECK( geom.detectorOffsetToIndexFloat(0.0f) == 3.0f ); BOOST_CHECK( geom.detectorOffsetToIndexFloat(0.625f) == 4.25f ); } BOOST_AUTO_TEST_CASE( testParallelProjectionGeometry2D_Clone ) { astra::float32 angles[] = { 0.0f, 1.0f, 2.0f, 3.0f }; astra::CParallelProjectionGeometry2D geom(4, 8, 0.5f, angles); BOOST_REQUIRE( geom.isInitialized() ); astra::CProjectionGeometry2D* geom2 = geom.clone(); BOOST_REQUIRE( geom2->isInitialized() ); BOOST_CHECK( geom.isEqual(geom2) ); BOOST_CHECK( geom2->getProjectionAngleCount() == 4 ); BOOST_CHECK( geom2->getDetectorCount() == 8 ); BOOST_CHECK( geom2->getDetectorWidth() == 0.5f ); BOOST_CHECK( geom2->getProjectionAngle(0) == 0.0f ); BOOST_CHECK( geom2->getProjectionAngle(1) == 1.0f ); BOOST_CHECK( geom2->getProjectionAngle(2) == 2.0f ); BOOST_CHECK( geom2->getProjectionAngle(3) == 3.0f ); BOOST_CHECK( geom2->getProjectionAngles()[0] == 0.0f ); BOOST_CHECK( geom2->getProjectionAngles()[3] == 3.0f ); delete geom2; } astra-toolbox-2.3.0/tests/test_VolumeGeometry2D.cpp000066400000000000000000000120661475635207100224070ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define BOOST_TEST_DYN_LINK #include #include #include #include #include "astra/VolumeGeometry2D.h" BOOST_AUTO_TEST_CASE( testVolumeGeometry2D_Constructor1 ) { astra::CVolumeGeometry2D geom(16, 32); BOOST_REQUIRE( geom.isInitialized() ); BOOST_CHECK( geom.getGridColCount() == 16 ); BOOST_CHECK( geom.getGridRowCount() == 32 ); BOOST_CHECK( geom.getGridTotCount() == 512 ); BOOST_CHECK( geom.getWindowLengthX() == 16.0f ); BOOST_CHECK( geom.getWindowLengthY() == 32.0f ); BOOST_CHECK( geom.getWindowArea() == 512.0f ); BOOST_CHECK( geom.getPixelLengthX() == 1.0f ); BOOST_CHECK( geom.getPixelLengthY() == 1.0f ); BOOST_CHECK( geom.getPixelArea() == 1.0f ); BOOST_CHECK( geom.getWindowMinX() == -8.0f ); BOOST_CHECK( geom.getWindowMaxX() == 8.0f ); BOOST_CHECK( geom.getWindowMinY() == -16.0f ); BOOST_CHECK( geom.getWindowMaxY() == 16.0f ); } BOOST_AUTO_TEST_CASE( testVolumeGeometry2D_Constructor1odd ) { astra::CVolumeGeometry2D geom(5, 7); BOOST_REQUIRE( geom.isInitialized() ); BOOST_CHECK( geom.getGridColCount() == 5 ); BOOST_CHECK( geom.getGridRowCount() == 7 ); BOOST_CHECK( geom.getGridTotCount() == 35 ); BOOST_CHECK( geom.getWindowLengthX() == 5.0f ); BOOST_CHECK( geom.getWindowLengthY() == 7.0f ); BOOST_CHECK( geom.getWindowArea() == 35.0f ); BOOST_CHECK( geom.getPixelLengthX() == 1.0f ); BOOST_CHECK( geom.getPixelLengthY() == 1.0f ); BOOST_CHECK( geom.getPixelArea() == 1.0f ); BOOST_CHECK( geom.getWindowMinX() == -2.5f ); BOOST_CHECK( geom.getWindowMaxX() == 2.5f ); BOOST_CHECK( geom.getWindowMinY() == -3.5f ); BOOST_CHECK( geom.getWindowMaxY() == 3.5f ); } BOOST_AUTO_TEST_CASE( testVolumeGeometry2D_Constructor2 ) { astra::CVolumeGeometry2D geom(16, 32, -1.0f, -2.0f, 3.0f, 4.0f); BOOST_REQUIRE( geom.isInitialized() ); BOOST_CHECK( geom.getGridColCount() == 16 ); BOOST_CHECK( geom.getGridRowCount() == 32 ); BOOST_CHECK( geom.getGridTotCount() == 512 ); BOOST_CHECK( geom.getWindowLengthX() == 4.0f ); BOOST_CHECK( geom.getWindowLengthY() == 6.0f ); BOOST_CHECK( geom.getWindowArea() == 24.0f ); BOOST_CHECK( geom.getPixelLengthX() == 0.25f ); BOOST_CHECK( geom.getPixelLengthY() == 0.1875f ); BOOST_CHECK( geom.getPixelArea() == 0.046875f ); BOOST_CHECK( geom.getWindowMinX() == -1.0f ); BOOST_CHECK( geom.getWindowMaxX() == 3.0f ); BOOST_CHECK( geom.getWindowMinY() == -2.0f ); BOOST_CHECK( geom.getWindowMaxY() == 4.0f ); } BOOST_AUTO_TEST_CASE( testVolumeGeometry2D_Clone ) { astra::CVolumeGeometry2D geom(16, 32, -1.0f, -2.0f, 3.0f, 4.0f); astra::CVolumeGeometry2D* geom2 = geom.clone(); BOOST_REQUIRE( geom2->isInitialized() ); BOOST_CHECK( geom.isEqual(geom2) ); BOOST_CHECK( geom2->getGridColCount() == 16 ); BOOST_CHECK( geom2->getGridRowCount() == 32 ); BOOST_CHECK( geom2->getGridTotCount() == 512 ); BOOST_CHECK( geom2->getWindowLengthX() == 4.0f ); BOOST_CHECK( geom2->getWindowLengthY() == 6.0f ); BOOST_CHECK( geom2->getWindowArea() == 24.0f ); BOOST_CHECK( geom2->getPixelLengthX() == 0.25f ); BOOST_CHECK( geom2->getPixelLengthY() == 0.1875f ); BOOST_CHECK( geom2->getPixelArea() == 0.046875f ); BOOST_CHECK( geom2->getWindowMinX() == -1.0f ); BOOST_CHECK( geom2->getWindowMaxX() == 3.0f ); BOOST_CHECK( geom2->getWindowMinY() == -2.0f ); BOOST_CHECK( geom2->getWindowMaxY() == 4.0f ); delete geom2; } BOOST_AUTO_TEST_CASE( testVolumeGeometry2D_Offsets ) { astra::CVolumeGeometry2D geom(16, 32, -1.0f, -2.0f, 3.0f, 4.0f); BOOST_REQUIRE( geom.isInitialized() ); BOOST_CHECK( geom.pixelRowColToIndex(1,2) == 18 ); int r,c; geom.pixelIndexToRowCol(66, r, c); BOOST_CHECK( r == 4 ); BOOST_CHECK( c == 2 ); BOOST_CHECK( geom.pixelColToCenterX(2) == -0.375f ); BOOST_CHECK( geom.pixelColToMinX(2) == -0.5f ); BOOST_CHECK( geom.pixelColToMaxX(2) == -0.25f ); BOOST_CHECK( geom.pixelRowToCenterY(29) == -1.53125f ); BOOST_CHECK( geom.pixelRowToMinY(29) == -1.625f ); BOOST_CHECK( geom.pixelRowToMaxY(29) == -1.4375f ); BOOST_CHECK( geom.coordXToCol(0.1f) == 4 ); BOOST_CHECK( geom.coordYToRow(0.1f) == 20 ); } astra-toolbox-2.3.0/tests/test_XMLDocument.cpp000066400000000000000000000100101475635207100213600ustar00rootroot00000000000000/* ----------------------------------------------------------------------- Copyright: 2010-2022, imec Vision Lab, University of Antwerp 2014-2022, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #define BOOST_TEST_DYN_LINK #include #include #include "astra/XMLDocument.h" #include "astra/XMLConfig.h" BOOST_AUTO_TEST_CASE( testXMLDocument_Constructor1 ) { astra::XMLDocument *doc = astra::XMLDocument::createDocument("test"); BOOST_REQUIRE(doc); astra::XMLNode root = doc->getRootNode(); BOOST_REQUIRE(root); BOOST_CHECK(root.getName() == "test"); BOOST_CHECK(root.getContent().empty()); delete doc; } BOOST_AUTO_TEST_CASE( testXMLDocument_FileIO ) { astra::XMLDocument *doc = astra::XMLDocument::createDocument("test"); doc->saveToFile("test.xml"); astra::XMLDocument *doc2 = astra::XMLDocument::readFromFile("test.xml"); astra::XMLNode root = doc2->getRootNode(); BOOST_REQUIRE(root); BOOST_CHECK(root.getName() == "test"); BOOST_CHECK(root.getContent().empty()); delete doc2; delete doc; } BOOST_AUTO_TEST_CASE( testXMLDocument_CreateNodes ) { astra::XMLDocument *doc = astra::XMLDocument::createDocument("test"); BOOST_REQUIRE(doc); astra::XMLNode root = doc->getRootNode(); BOOST_REQUIRE(root); astra::XMLNode node = root.addChildNode("child"); BOOST_REQUIRE(node); node.addAttribute("attr", "val"); doc->saveToFile("test2.xml"); delete doc; doc = astra::XMLDocument::readFromFile("test2.xml"); BOOST_REQUIRE(doc); root = doc->getRootNode(); BOOST_REQUIRE(node); node = root.getSingleNode("child"); BOOST_REQUIRE(node); BOOST_CHECK(node.hasAttribute("attr")); BOOST_CHECK(node.getAttribute("attr") == "val"); delete doc; } BOOST_AUTO_TEST_CASE( testXMLDocument_Options ) { astra::XMLDocument *doc = astra::XMLDocument::createDocument("test"); BOOST_REQUIRE(doc); astra::XMLNode root = doc->getRootNode(); BOOST_REQUIRE(root); BOOST_CHECK(!root.hasOption("opt")); root.addOption("opt", "val"); BOOST_CHECK(root.hasOption("opt")); BOOST_CHECK(root.getOption("opt") == "val"); delete doc; } BOOST_AUTO_TEST_CASE( testXMLDocument_List ) { astra::XMLDocument *doc = astra::XMLDocument::createDocument("test"); BOOST_REQUIRE(doc); astra::XMLNode root = doc->getRootNode(); BOOST_REQUIRE(root); astra::XMLNode node = root.addChildNode("child"); BOOST_REQUIRE(node); float fl[] = { 1.0, 3.5, 2.0, 4.75 }; node.setContent(fl, sizeof(fl)/sizeof(fl[0])); doc->saveToFile("test3.xml"); delete doc; doc = astra::XMLDocument::readFromFile("test3.xml"); BOOST_REQUIRE(doc); root = doc->getRootNode(); BOOST_REQUIRE(root); node = root.getSingleNode("child"); BOOST_REQUIRE(node); std::vector f = node.getContentNumericalArray(); BOOST_CHECK(f[0] == fl[0]); BOOST_CHECK(f[1] == fl[1]); BOOST_CHECK(f[2] == fl[2]); BOOST_CHECK(f[3] == fl[3]); delete doc; } BOOST_AUTO_TEST_CASE( testXMLDocument_Config ) { astra::XMLConfig* cfg = new astra::XMLConfig("VolumeGeometry2D"); cfg->self.addChildNode("GridColCount", 1); cfg->self.addChildNode("GridRowCount", 2); cfg->self.addOption("WindowMinX", 3); cfg->self.addOption("WindowMaxX", 4); cfg->self.addOption("WindowMinY", 5); cfg->self.addOption("WindowMaxY", 6); delete cfg; }