├── .gitignore ├── .travis.yml ├── CHANGELOG ├── LICENSE ├── README.md ├── doc ├── data.json ├── development │ ├── darwin.md │ └── ubuntu.md ├── images │ ├── debian.png │ ├── fedora.png │ ├── macosx.png │ ├── main.png │ ├── raspbian.png │ ├── ubuntu.png │ └── windows.png ├── installation │ ├── debian.md │ └── fedora.md └── readthedocs │ ├── Makefile │ ├── bin │ ├── build.sh │ └── update.sh │ ├── conf.py │ ├── index.rst │ ├── locale │ ├── en │ │ └── LC_MESSAGES │ │ │ ├── index.mo │ │ │ ├── index.po │ │ │ ├── source.mo │ │ │ └── source.po │ └── es │ │ └── LC_MESSAGES │ │ ├── index.mo │ │ ├── index.po │ │ ├── source.mo │ │ └── source.po │ └── source │ ├── _static │ ├── getting-started │ │ ├── main-window.png │ │ ├── menu-combo.png │ │ ├── menu-edit.png │ │ ├── menu-file.png │ │ ├── menu-help.png │ │ ├── menu-view.png │ │ ├── pattern-origin-distance.png │ │ ├── preferences-mini.png │ │ ├── preferences.png │ │ ├── scan-finished.png │ │ ├── scanning.png │ │ ├── scanning2.png │ │ ├── welcome.png │ │ ├── wizard-autocheck.png │ │ ├── wizard-calibration.png │ │ ├── wizard-connection.png │ │ └── wizard-scanning.png │ ├── horus-logo.svg │ ├── installation │ │ ├── apple-logo.svg │ │ ├── ubuntu-logo.svg │ │ └── windows-logo.svg │ ├── scanner-components │ │ ├── chessboard-pattern.svg │ │ ├── line-laser.png │ │ ├── logitech-c270.png │ │ ├── pattern-detected.png │ │ ├── pattern-distance.jpg │ │ ├── stepper-motor.png │ │ ├── zum-scan.jpg │ │ └── zum.jpg │ └── workbenches │ │ ├── adjustment-calibration-capture-laser.png │ │ ├── adjustment-calibration-capture-pattern.png │ │ ├── adjustment-calibration-segmentation.png │ │ ├── adjustment-scan-capture-laser.png │ │ ├── adjustment-scan-capture-texture.png │ │ ├── adjustment-scan-segmentation.png │ │ ├── calibration-advanced-mode.png │ │ ├── calibration-autocheck.png │ │ ├── calibration-intrinsics.png │ │ ├── calibration-laser-result.png │ │ ├── calibration-laser.png │ │ ├── calibration-pattern.png │ │ ├── calibration-platform-result.png │ │ ├── calibration-platform.png │ │ ├── calibration-video.png │ │ ├── control-camera.png │ │ ├── control-disconnected.png │ │ ├── control-gcode.png │ │ ├── control-lasers.png │ │ ├── control-motor.png │ │ ├── main-window.png │ │ ├── scanning-3d-scene.png │ │ ├── scanning-motor.png │ │ ├── scanning-parameters.png │ │ ├── scanning-pointcloud-color.png │ │ ├── scanning-pointcloud-roi.png │ │ ├── scanning-video-gray.png │ │ ├── scanning-video-laser.png │ │ └── scanning-video-texture.png │ ├── getting-started │ ├── index.rst │ ├── menu.rst │ ├── scanning.rst │ └── wizard.rst │ ├── installation │ ├── index.rst │ ├── macosx.rst │ ├── ubuntu.rst │ └── windows.rst │ ├── scanner-components │ ├── board.rst │ ├── camera.rst │ ├── index.rst │ ├── lasers.rst │ ├── motor.rst │ └── pattern.rst │ └── workbenches │ ├── adjustment.rst │ ├── calibration.rst │ ├── control.rst │ ├── index.rst │ └── scanning.rst ├── horus ├── package.sh ├── pkg ├── darwin │ └── create-dmg │ │ ├── LICENSE │ │ ├── README.md │ │ ├── builder │ │ └── create-dmg.builder │ │ ├── create-dmg │ │ ├── sample │ │ └── support │ │ ├── dmg-license.py │ │ └── template.applescript ├── linux │ ├── debian │ │ ├── changelog │ │ ├── control │ │ ├── postinst │ │ └── postrm │ └── horus.desktop └── win32 │ ├── drivers │ ├── CDM v2.10.00 WHQL Certified.exe │ ├── arduino.cat │ ├── arduino.inf │ ├── dpinst32.exe │ └── dpinst64.exe │ ├── horus.bat │ ├── installer.nsi │ └── nsisPlugins │ └── AccessControl.dll ├── res ├── firmware │ ├── eeprom_clear.hex │ └── horus-fw.hex ├── horus.icns ├── horus.ico ├── images │ ├── checkerboard.png │ ├── connect.png │ ├── disconnect.png │ ├── horus.ico │ ├── installer_background.png │ ├── logo.png │ ├── nusb.png │ ├── pattern-distance.jpg │ ├── pattern-position.png │ ├── pause.png │ ├── play.png │ ├── restore.png │ ├── splash.png │ ├── stop.png │ ├── undo.png │ ├── views.png │ └── void.png ├── locale │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── horus.mo │ │ │ └── horus.po │ ├── en │ │ └── LC_MESSAGES │ │ │ ├── horus.mo │ │ │ └── horus.po │ ├── es │ │ └── LC_MESSAGES │ │ │ ├── horus.mo │ │ │ └── horus.po │ ├── fr │ │ └── LC_MESSAGES │ │ │ ├── horus.mo │ │ │ └── horus.po │ ├── horus.pot │ ├── it │ │ └── LC_MESSAGES │ │ │ ├── horus.mo │ │ │ └── horus.po │ ├── pt │ │ └── LC_MESSAGES │ │ │ ├── horus.mo │ │ │ └── horus.po │ └── update-translations.sh ├── logger │ └── logger.conf ├── meshes │ └── ciclop_platform.stl └── tools │ ├── darwin │ ├── avrdude │ ├── avrdude.conf │ ├── avrdude_bin │ ├── lib │ │ ├── libusb-0.1.4.dylib │ │ └── libusb-1.0.0.dylib │ └── uvcc.so │ ├── linux │ └── avrdude.conf │ └── windows │ ├── avrdude.conf │ ├── avrdude.exe │ └── libusb0.dll ├── setup.py ├── setup_mac.py ├── src └── horus │ ├── __init__.py │ ├── engine │ ├── __init__.py │ ├── algorithms │ │ ├── __init__.py │ │ ├── image_capture.py │ │ ├── image_detection.py │ │ ├── laser_segmentation.py │ │ ├── point_cloud_generation.py │ │ └── point_cloud_roi.py │ ├── calibration │ │ ├── __init__.py │ │ ├── autocheck.py │ │ ├── calibration.py │ │ ├── calibration_data.py │ │ ├── camera_intrinsics.py │ │ ├── combo_calibration.py │ │ ├── laser_triangulation.py │ │ ├── moving_calibration.py │ │ ├── pattern.py │ │ └── platform_extrinsics.py │ ├── driver │ │ ├── __init__.py │ │ ├── board.py │ │ ├── camera.py │ │ ├── driver.py │ │ └── uvc │ │ │ ├── __init__.py │ │ │ └── mac │ │ │ ├── __init__.py │ │ │ ├── cf_string.py │ │ │ ├── libuvcc-defs.h │ │ │ ├── libuvcc-wrappers.c │ │ │ ├── libuvcc.c │ │ │ ├── libuvcc.h │ │ │ ├── makefile │ │ │ └── raw.py │ └── scan │ │ ├── __init__.py │ │ ├── ciclop_scan.py │ │ ├── current_video.py │ │ ├── scan.py │ │ └── scan_capture.py │ ├── gui │ ├── __init__.py │ ├── app.py │ ├── engine.py │ ├── main.py │ ├── splash.py │ ├── util │ │ ├── __init__.py │ │ ├── custom_panels.py │ │ ├── image_view.py │ │ ├── machine_settings.py │ │ ├── opengl_gui.py │ │ ├── opengl_helpers.py │ │ ├── pattern_distance_window.py │ │ ├── preferences.py │ │ ├── scene_view.py │ │ ├── version_window.py │ │ └── video_view.py │ ├── welcome.py │ ├── wizard │ │ ├── __init__.py │ │ ├── calibration_page.py │ │ ├── connection_page.py │ │ ├── main.py │ │ ├── scanning_page.py │ │ └── wizard_page.py │ └── workbench │ │ ├── __init__.py │ │ ├── adjustment │ │ ├── __init__.py │ │ ├── current_video.py │ │ ├── main.py │ │ └── panels.py │ │ ├── calibration │ │ ├── __init__.py │ │ ├── main.py │ │ ├── pages │ │ │ ├── __init__.py │ │ │ ├── camera_intrinsics.py │ │ │ ├── capture_page.py │ │ │ ├── laser_triangulation.py │ │ │ ├── page.py │ │ │ ├── platform_extrinsics.py │ │ │ ├── scanner_autocheck.py │ │ │ └── video_page.py │ │ └── panels.py │ │ ├── control │ │ ├── __init__.py │ │ ├── main.py │ │ └── panels.py │ │ ├── scanning │ │ ├── __init__.py │ │ ├── main.py │ │ ├── panels.py │ │ └── view_page.py │ │ ├── toolbar.py │ │ └── workbench.py │ └── util │ ├── __init__.py │ ├── avr_helpers.py │ ├── mesh_loader.py │ ├── mesh_loaders │ ├── __init__.py │ ├── ply.py │ └── stl.py │ ├── model.py │ ├── profile.py │ ├── resources.py │ ├── system.py │ └── version.py └── test └── engine ├── __init__.py └── test_board.py /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | *.tar.bz2 4 | *.tar.gz 5 | *.7z 6 | *.pyc 7 | *.po~ 8 | *.zip 9 | *.exe 10 | *.dmg 11 | !CDM v2.10.00 WHQL Certified.exe 12 | !dpinst32.exe 13 | !dpinst64.exe 14 | !avrdude.exe 15 | *.deb 16 | *.ini 17 | *.idea 18 | *.ply 19 | *.db 20 | pkg/win32/dist* 21 | deb_dist/* 22 | win_dist/* 23 | dar_dist/* 24 | _site 25 | build/ 26 | dist/ 27 | src/Horus.egg-info/ 28 | horus.egg-info/ 29 | horus.log 30 | version 31 | doc/readthedocs/_build 32 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "2.7" 5 | 6 | notifications: 7 | email: false 8 | 9 | virtualenv: 10 | system_site_packages: true 11 | 12 | before_script: 13 | - sudo apt-get update -qq 14 | - sudo apt-get install -y python-serial python-wxgtk2.8 python-opengl python-pyglet python-numpy python-scipy python-matplotlib python-opencv avrdude v4l-utils 15 | 16 | script: 17 | - nosetests test -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Version 0.2 2 | 3 | * New profile format: 4 | * Using separated calibration and scanner profiles 5 | * Wizard: combined laser and platform calibration in one 6 | * New auto check algorithm: check motor direction and pattern and laser detection 7 | * New adjustment workbench: 8 | * Scan adjustment: separated camera settings for texture and laser detection 9 | * Calibration adjustment: separated settings for pattern and laser detection 10 | * Added auto check to calibration workbench 11 | * Improved laser detection: 12 | * Easy to adjust to light conditions 13 | * Easy to adjust for different object materials, colors, surfaces 14 | * Improved calibration algorithms 15 | * Improved scan algorithm: 16 | * Using separated camera settings for texture and laser detection 17 | * Improved performance 18 | * Apply point cloud ROI before scanning 19 | * Fixed Clear EEPROM 20 | * New updates system 21 | * Added verbose log file 22 | * Log file management 23 | * Internal refactor 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Horus 2 | 3 | [![R&D](https://img.shields.io/badge/-R%26D-brightgreen.svg)](https://github.com/bqlabs/horus) 4 | [![License](http://img.shields.io/:license-gpl-blue.svg)](http://opensource.org/licenses/GPL-2.0) 5 | [![Documentation Status](https://readthedocs.org/projects/horus/badge/?version=release-0.2)](http://horus.readthedocs.io/en/release-0.2/?badge=release-0.2) 6 | 7 | Horus is a general solution for 3D laser scanning. It provides graphic user interfaces for connection, configuration, control, calibration and scanning with Open Source [Ciclop 3D Scanner](https://github.com/bqlabs/ciclop). 8 | 9 | This is a research project to explore the 3D laser scan with free tools. Feel free to use it for experiments, modify and adapt it to new devices and contribute new features or ideas. 10 | 11 | This project has been developed in [Python](https://www.python.org/) language and it is distributed under [GPL v2](https://www.gnu.org/licenses/gpl-2.0.html) license. 12 | 13 | ## Installation 14 | 15 | #### Supported 16 | 17 | ###### Current version: 0.2rc1 18 | 19 | | Logo | Name | Instructions | 20 | |:-----------------:|:--------:|:-----------------------------------:| 21 | | ![][ubuntu-logo] | Ubuntu | [[en]](http://horus.readthedocs.io/en/release-0.2/source/installation/ubuntu.html) [[es]](http://horus.readthedocs.io/es/release-0.2/source/installation/ubuntu.html) | 22 | | ![][windows-logo] | Windows | [[en]](http://horus.readthedocs.io/en/release-0.2/source/installation/windows.html) [[es]](http://horus.readthedocs.io/es/release-0.2/source/installation/windows.html) | 23 | | ![][macosx-logo] | Mac OS X | [[en]](http://horus.readthedocs.io/en/release-0.2/source/installation/macosx.html) [[es]](http://horus.readthedocs.io/es/release-0.2/source/installation/macosx.html) | 24 | 25 | #### Experimental 26 | 27 | **Horus 0.2 is not supported for the following distributions**. 28 | 29 | However, anyone can test it and contribute to its support. 30 | 31 | | Logo | Name | Instructions | 32 | |:------------------:|:---------:|:-------------------------------------:| 33 | | ![][debian-logo] | Debian | [[en]](doc/installation/debian.md) | 34 | | ![][fedora-logo] | Fedora | [[en]](doc/installation/fedora.md) | 35 | 36 | ## Documentation 37 | 38 | Here you will find the official documentation of the application: 39 | 40 | * [User's manual](http://horus.readthedocs.io/en/release-0.2/) [[es](http://horus.readthedocs.io/es/release-0.2/)] 41 | 42 | And also all the scientific background of the project in nice Jupyter notebooks: 43 | 44 | * [Notebooks](http://nbviewer.jupyter.org/github/Jesus89/3DScanScience/tree/master/notebooks/) 45 | * [Repository](https://github.com/Jesus89/3DScanScience) 46 | 47 | ## Development 48 | 49 | Horus is an Open Source Project. Anyone has the freedom to use, modify, share and distribute this software. If you want to: 50 | * run the source code 51 | * make your own modifications 52 | * contribute to the project 53 | * build packages 54 | 55 | follow the next instructions 56 | 57 | #### GNU/Linux 58 | 59 | Horus has been developed using [Ubuntu Gnome](http://ubuntugnome.org/), that is based on [Debian](https://www.debian.org/), like [Raspbian](https://www.raspbian.org/), [Mint](http://linuxmint.com/), etc. All instructions provided in this section probably work for most of these systems. 60 | 61 | * [Ubuntu development](doc/development/ubuntu.md) 62 | 63 | NOTE: *deb* and *exe* packages can be generated in *debian like* systems 64 | 65 | #### Mac OS X 66 | 67 | * [Darwin development](doc/development/darwin.md) 68 | 69 | NOTE: *dmg* packages only can be generated in Mac OS X 70 | 71 | 72 | More interest links are shown below: 73 | 74 | * [Presentation](http://diwo.bq.com/en/presentacion-ciclop-horus/) [[es](http://diwo.bq.com/presentacion-ciclop-horus/)] 75 | * [3D Design](http://diwo.bq.com/en/ciclop-released/) [[es](http://diwo.bq.com/ciclop-released/)] 76 | * [Electronics](http://diwo.bq.com/en/zum-scan-released/) [[es](http://diwo.bq.com/zum-scan-released/)] 77 | * [Firmware](http://diwo.bq.com/en/horus-fw-released/) [[es](http://diwo.bq.com/horus-fw-released/)] 78 | * [Software](http://diwo.bq.com/en/horus-released/) [[es](http://diwo.bq.com/horus-released/)] 79 | * [Product documentation](http://diwo.bq.com/en/documentation-ciclop-and-horus/) [[es](http://diwo.bq.com/documentation-ciclop-and-horus/)] 80 | * [Google group](https://groups.google.com/forum/?hl=en#!forum/ciclop-3d-scanner) 81 | 82 | [ubuntu-logo]: doc/images/ubuntu.png 83 | [windows-logo]: doc/images/windows.png 84 | [macosx-logo]: doc/images/macosx.png 85 | [debian-logo]: doc/images/debian.png 86 | [raspbian-logo]: doc/images/raspbian.png 87 | [fedora-logo]: doc/images/fedora.png 88 | -------------------------------------------------------------------------------- /doc/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "doc/images/main.png", 3 | "tags": ["software", "scanner"] 4 | } 5 | -------------------------------------------------------------------------------- /doc/development/darwin.md: -------------------------------------------------------------------------------- 1 | #Horus development in Mac OS X 2 | 3 | [return to Home](../../README.md) 4 | 5 | 6 | If you are a developer and you want to modify the code, contribute, build packages, etc. you may follow this steps 7 | 8 | ## 1. Set up the environment 9 | 10 | ### Tools 11 | 12 | #### Install FTDI driver 13 | * [FTDI USB Driver](http://www.ftdichip.com/Drivers/VCP/MacOSX/FTDIUSBSerialDriver_v2_3.dmg) 14 | 15 | #### Atom IDE 16 | Download open source [Atom code editor](https://atom.io/). 17 | 18 | #### Install Homebrew 19 | ```bash 20 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 21 | ``` 22 | 23 | #### Git version control 24 | ```bash 25 | brew install git 26 | ``` 27 | 28 | #### Python 29 | Install non-system, framework-based, universal [Python](https://www.python.org/ftp/python/2.7.10/python-2.7.10-macosx10.6.pkg). Not with brew. 30 | 31 | #### Python tools 32 | ```bash 33 | pip install -U pip setuptools 34 | pip install -U virtualenv 35 | ``` 36 | 37 | ### Dependencies 38 | 39 | First you need to configure a virtualenv 40 | 41 | ```bash 42 | virtualenv $HOME/venv 43 | ``` 44 | 45 | In order to use wxPython from the virtualenv this hack is needed 46 | 47 | ```bash 48 | cp `which python` $HOME/venv/bin/python; 49 | echo 'export PYTHONHOME=$HOME/venv' >> $HOME/venv/bin/activate; 50 | source $HOME/venv/bin/activate 51 | ``` 52 | 53 | Then, you can install the python dependencies into the virtualenv 54 | 55 | #### OpenCV 56 | ```bash 57 | brew tap homebrew/science 58 | brew info opencv 59 | brew install opencv 60 | ``` 61 | 62 | ```bash 63 | ln -s /usr/local/Cellar/opencv/2.4.12_2/lib/python2.7/site-packages/cv.py $HOME/venv/lib/python2.7/site-packages; 64 | ln -s /usr/local/Cellar/opencv/2.4.12_2/lib/python2.7/site-packages/cv2.so $HOME/venv/lib/python2.7/site-packages 65 | ``` 66 | 67 | #### wxPython 68 | ```bash 69 | brew install wxpython 70 | ``` 71 | 72 | ```bash 73 | ln -s /usr/local/Cellar/wxpython/3.0.2.0/lib/python2.7/site-packages/wx* $HOME/venv/lib/python2.7/site-packages 74 | ``` 75 | 76 | #### Python modules 77 | ```bash 78 | pip install -U pyserial pyopengl pyopengl-accelerate numpy scipy matplotlib==1.4.0 79 | ``` 80 | 81 | ##### Pyobjc QTKit 82 | 83 | * Install Xcode.app. Then, switch xcode commands and accept the xcodebuild license 84 | 85 | ```bash 86 | sudo xcode-select -s /Applications/Xcode.app/Contents/Developer 87 | sudo xcodebuild 88 | ``` 89 | 90 | * Downgrade setuptools 91 | ```bash 92 | pip install -U setuptools==3.4 93 | ``` 94 | 95 | * Install qtkit 2.5.1 96 | ```bash 97 | pip install -U pyobjc-core==2.5.1 98 | pip install -U pyobjc-framework-cocoa==2.5.1 99 | pip install -U pyobjc-framework-quartz==2.5.1 100 | pip install -U pyobjc-framework-qtkit==2.5.1 101 | ``` 102 | 103 | * Restore setuptools 104 | ```bash 105 | pip install -U setuptools 106 | ``` 107 | 108 | In order to generate dmg package, some extra dependencies are needed 109 | 110 | #### Packaging dependencies 111 | ```bash 112 | pip install -U py2app==0.7.2 113 | ``` 114 | 115 | Also some patches are needed to make py2app work 116 | 117 | ```bash 118 | cd $HOME/venv/lib/python2.7/site-packages/py2app/recipes; 119 | 120 | sed -i '' 's/scan_code/_scan_code/g' virtualenv.py; 121 | sed -i '' 's/load_module/_load_module/g' virtualenv.py; 122 | 123 | cd $HOME/venv/lib/python2.7/site-packages/macholib; 124 | 125 | sed -i '' 's/loader=loader.filename/loader_path=loader.filename/g' MachOGraph.py 126 | ``` 127 | 128 | To reduce the package size, "tests" directories must be removed 129 | 130 | ```bash 131 | cd $HOME/venv/lib/python2.7/site-packages; 132 | find . -name tests -type d -exec rm -r {} + 133 | ``` 134 | 135 | ## 2. Download source code 136 | 137 | All source code is available on GitHub. You can download main Horus project by doing: 138 | 139 | ### Horus 140 | ```bash 141 | git clone https://github.com/bq/horus.git 142 | cd horus 143 | ``` 144 | 145 | ## 3. Execute source code 146 | 147 | In the project directory, execute the command: 148 | 149 | ```bash 150 | ./horus 151 | ``` 152 | 153 | ## 4. Build packages 154 | 155 | Horus development comes with a script *package.sh*. This script generates a final release package. You should not need it during development, unless you are changing the release process. If you want to distribute your own version of Horus, then the *package.sh* script will allow you to do that. 156 | 157 | ```bash 158 | bash package.sh darwin # Generate dmg package 159 | ``` 160 | -------------------------------------------------------------------------------- /doc/development/ubuntu.md: -------------------------------------------------------------------------------- 1 | #Horus development in Ubuntu 2 | 3 | [return to Home](../../README.md) 4 | 5 | If you are a developer and you want to modify the code, contribute, build packages, etc. you may follow this steps 6 | 7 | ## 1. Set up the environment 8 | 9 | ### Tools 10 | 11 | #### Atom IDE 12 | Download open source [Atom code editor](https://atom.io/). 13 | 14 | #### Git version control 15 | ```bash 16 | sudo apt-get install git gitk 17 | ``` 18 | 19 | ### Dependencies 20 | 21 | Following dependencies are included in deb package, but if you want to install it manually, they are: 22 | 23 | #### Python modules 24 | ```bash 25 | sudo apt-get install python-serial python-opengl python-pyglet python-numpy python-scipy python-matplotlib 26 | ``` 27 | 28 | ##### wxPython 29 | 30 | For older ubuntu versions 31 | 32 | ```bash 33 | sudo apt-get install python-wxgtk2.8 34 | ``` 35 | 36 | For newer ubuntu versions 37 | 38 | ```bash 39 | sudo apt-get install python-wxgtk3.0 40 | ``` 41 | 42 | #### Custom OpenCV 43 | 44 | *NOTE*: first try to remove previous versions of opencv: 45 | 46 | ```bash 47 | sudo apt-get remove python-opencv 48 | sudo apt-get autoremove 49 | ``` 50 | 51 | ```bash 52 | sudo add-apt-repository ppa:bqlabs/horus 53 | sudo apt-get update 54 | sudo apt-get install python-opencv 55 | ``` 56 | 57 | #### AVRDUDE 58 | ```bash 59 | sudo apt-get install avrdude # include libftdi1 60 | ``` 61 | 62 | #### Video 4 Linux 63 | ```bash 64 | sudo apt-get install v4l-utils 65 | ``` 66 | 67 | In order to generate Debian and Windows packages, some extra dependencies are needed 68 | 69 | #### Packaging dependencies 70 | ```bash 71 | sudo apt-get install build-essential pkg-config python-dev python-stdeb p7zip-full curl nsis 72 | ``` 73 | 74 | ## 2. Download source code 75 | 76 | All source code is available on GitHub. You can download main Horus project by doing: 77 | 78 | ### Horus 79 | ```bash 80 | git clone https://github.com/bq/horus.git 81 | ``` 82 | or 83 | ```bash 84 | git clone git@github.com:bq/horus.git 85 | ``` 86 | 87 | ### Custom OpenCV 88 | 89 | Several improvements and optimizations have been made in GNU/Linux version of OpenCV libraries. If you want to contribute to this custom version, you can download it from: 90 | 91 | ```bash 92 | git clone https://github.com/bq/opencv.git 93 | ``` 94 | or 95 | ```bash 96 | git clone git@github.com:bq/opencv.git 97 | ``` 98 | 99 | And build it your own: [instructions](https://github.com/bqlabs/opencv/wiki/Build) 100 | 101 | ## 3. Execute source code 102 | 103 | In the project directory, execute the command: 104 | 105 | ```bash 106 | ./horus 107 | ``` 108 | 109 | ### Unit testing 110 | 111 | To run the tests install nose: 112 | 113 | ```bash 114 | sudo -H pip install -U nose 115 | ``` 116 | 117 | And execute: 118 | 119 | ```bash 120 | nosetests test 121 | ``` 122 | 123 | ## 4. Build packages 124 | 125 | Horus development comes with a script *package.sh*. This script generates a final release package. You should not need it during development, unless you are changing the release process. If you want to distribute your own version of Horus, then the *package.sh* script will allow you to do that. 126 | 127 | ### Version 128 | ```bash 129 | bash package.sh version # Generate version file 130 | ``` 131 | 132 | ### GNU/Linux Ubuntu 133 | ```bash 134 | bash package.sh debian # Generate deb package 135 | bash package.sh debian -s # Generate sources 136 | bash package.sh debian -i # Install deb package 137 | bash package.sh debian -u # Upload to launchpad 138 | ``` 139 | 140 | ### Windows 141 | ```bash 142 | bash package.sh win32 # Generate exe package 143 | bash package.sh win32 /path # Generate exe using /path for deps 144 | ``` 145 | -------------------------------------------------------------------------------- /doc/images/debian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/images/debian.png -------------------------------------------------------------------------------- /doc/images/fedora.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/images/fedora.png -------------------------------------------------------------------------------- /doc/images/macosx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/images/macosx.png -------------------------------------------------------------------------------- /doc/images/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/images/main.png -------------------------------------------------------------------------------- /doc/images/raspbian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/images/raspbian.png -------------------------------------------------------------------------------- /doc/images/ubuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/images/ubuntu.png -------------------------------------------------------------------------------- /doc/images/windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/images/windows.png -------------------------------------------------------------------------------- /doc/installation/debian.md: -------------------------------------------------------------------------------- 1 | # Horus installation in Debian ![][debian-logo] 2 | 3 | [return to Home](../../README.md) 4 | 5 | ### Not supported for version 0.2 6 | 7 | Versions: 8 8 | 9 | However it can be installed following the next instructions. 10 | 11 | First you need to build and install our [custom OpenCV](https://github.com/bqlabs/opencv/wiki/Build) 12 | 13 | Then, generate the executable file following the next [instructions](../development/ubuntu.md). 14 | 15 | ```bash 16 | bash package.sh debian -i 17 | ``` 18 | 19 | If user has no access to serial port, execute: 20 | 21 | ```bash 22 | sudo usermod -a -G dialout $USER 23 | sudo reboot 24 | ``` 25 | 26 | [debian-logo]: ../images/debian.png 27 | -------------------------------------------------------------------------------- /doc/installation/fedora.md: -------------------------------------------------------------------------------- 1 | # Horus installation in Fedora ![][fedora-logo] 2 | 3 | [return to Home](../../README.md) 4 | 5 | ### Not supported for version 0.2 6 | 7 | Versions: 21, 22 8 | 9 | However it can be installed following the next instructions. 10 | 11 | Add a [Copr repository with horus](https://copr.fedoraproject.org/coprs/churchyard/horus/): 12 | 13 | ```bash 14 | sudo dnf copr enable churchyard/horus 15 | ``` 16 | 17 | (Use `yum` instead of `dnf` on older Fedoras.) 18 | 19 | Install Horus: 20 | 21 | ```bash 22 | sudo dnf install horus 23 | ``` 24 | 25 | If user has no access to serial port, execute: 26 | 27 | ```bash 28 | sudo usermod -a -G dialout $USER 29 | ``` 30 | 31 | Log out and in again after adding yourself to the group. 32 | 33 | ### Update Horus 34 | 35 | If there is a new release just execute: 36 | 37 | ```bash 38 | sudo dnf upgrade horus 39 | ``` 40 | 41 | [fedora-logo]: ../images/fedora.png 42 | -------------------------------------------------------------------------------- /doc/readthedocs/bin/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | make clean -e SPHINXOPTS="-D language='$1'" html 4 | -------------------------------------------------------------------------------- /doc/readthedocs/bin/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | make clean gettext 4 | 5 | LANGUAGES="" 6 | for var in "$@" 7 | do 8 | LANGUAGES="$LANGUAGES -l $var " 9 | done 10 | 11 | sphinx-intl update -p _build/locale $LANGUAGES 12 | -------------------------------------------------------------------------------- /doc/readthedocs/index.rst: -------------------------------------------------------------------------------- 1 | .. image:: source/_static/horus-logo.svg 2 | :alt: The Horus Logo 3 | :align: right 4 | :width: 150 px 5 | 6 | ================================= 7 | Welcome to Horus's documentation! 8 | ================================= 9 | 10 | Contents 11 | ```````` 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | 16 | source/installation/index.rst 17 | source/getting-started/index.rst 18 | source/workbenches/index.rst 19 | source/scanner-components/index.rst 20 | -------------------------------------------------------------------------------- /doc/readthedocs/locale/en/LC_MESSAGES/index.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/locale/en/LC_MESSAGES/index.mo -------------------------------------------------------------------------------- /doc/readthedocs/locale/en/LC_MESSAGES/index.po: -------------------------------------------------------------------------------- 1 | # Horus en translation. 2 | # Copyright (C) 2016, Mundo Reader S.L. 3 | # This file is distributed under the same license as the Horus package. 4 | # Jesús Arroyo Torrens , 2016. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: Horus 0.2rc1\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2016-05-31 18:47+0200\n" 11 | "PO-Revision-Date: 2016-05-26 11:10+0100\n" 12 | "Last-Translator: \n" 13 | "Language-Team: \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=utf-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Generated-By: Babel 2.1.1\n" 18 | 19 | #: ../../index.rst:3 20 | msgid "Welcome to Horus's documentation!" 21 | msgstr "" 22 | 23 | #: ../../index.rst:11 24 | msgid "Contents" 25 | msgstr "" 26 | 27 | #~ msgid "Content:" 28 | #~ msgstr "" 29 | 30 | -------------------------------------------------------------------------------- /doc/readthedocs/locale/en/LC_MESSAGES/source.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/locale/en/LC_MESSAGES/source.mo -------------------------------------------------------------------------------- /doc/readthedocs/locale/es/LC_MESSAGES/index.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/locale/es/LC_MESSAGES/index.mo -------------------------------------------------------------------------------- /doc/readthedocs/locale/es/LC_MESSAGES/index.po: -------------------------------------------------------------------------------- 1 | # Horus en translation. 2 | # Copyright (C) 2016, Mundo Reader S.L. 3 | # This file is distributed under the same license as the Horus package. 4 | # Jesús Arroyo Torrens , 2016. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: Horus 0.2rc1\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2016-05-31 18:47+0200\n" 11 | "PO-Revision-Date: 2016-06-01 10:44+0100\n" 12 | "Last-Translator: \n" 13 | "Language-Team: \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Generated-By: Babel 2.1.1\n" 18 | "Language: rst\n" 19 | "X-Generator: Poedit 1.5.4\n" 20 | 21 | #: ../../index.rst:3 22 | msgid "Welcome to Horus's documentation!" 23 | msgstr "¡Bienvenido a la documentación de Horus!" 24 | 25 | #: ../../index.rst:11 26 | msgid "Contents" 27 | msgstr "Contenidos" 28 | -------------------------------------------------------------------------------- /doc/readthedocs/locale/es/LC_MESSAGES/source.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/locale/es/LC_MESSAGES/source.mo -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/main-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/main-window.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/menu-combo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/menu-combo.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/menu-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/menu-edit.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/menu-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/menu-file.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/menu-help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/menu-help.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/menu-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/menu-view.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/pattern-origin-distance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/pattern-origin-distance.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/preferences-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/preferences-mini.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/preferences.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/scan-finished.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/scan-finished.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/scanning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/scanning.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/scanning2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/scanning2.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/welcome.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/wizard-autocheck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/wizard-autocheck.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/wizard-calibration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/wizard-calibration.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/wizard-connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/wizard-connection.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/getting-started/wizard-scanning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/getting-started/wizard-scanning.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/horus-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 15 | 16 | -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/installation/apple-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/installation/ubuntu-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | ]> 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/installation/windows-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/scanner-components/line-laser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/scanner-components/line-laser.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/scanner-components/logitech-c270.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/scanner-components/logitech-c270.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/scanner-components/pattern-detected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/scanner-components/pattern-detected.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/scanner-components/pattern-distance.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/scanner-components/pattern-distance.jpg -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/scanner-components/stepper-motor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/scanner-components/stepper-motor.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/scanner-components/zum-scan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/scanner-components/zum-scan.jpg -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/scanner-components/zum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/scanner-components/zum.jpg -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/adjustment-calibration-capture-laser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/adjustment-calibration-capture-laser.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/adjustment-calibration-capture-pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/adjustment-calibration-capture-pattern.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/adjustment-calibration-segmentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/adjustment-calibration-segmentation.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/adjustment-scan-capture-laser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/adjustment-scan-capture-laser.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/adjustment-scan-capture-texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/adjustment-scan-capture-texture.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/adjustment-scan-segmentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/adjustment-scan-segmentation.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/calibration-advanced-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/calibration-advanced-mode.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/calibration-autocheck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/calibration-autocheck.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/calibration-intrinsics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/calibration-intrinsics.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/calibration-laser-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/calibration-laser-result.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/calibration-laser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/calibration-laser.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/calibration-pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/calibration-pattern.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/calibration-platform-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/calibration-platform-result.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/calibration-platform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/calibration-platform.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/calibration-video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/calibration-video.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/control-camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/control-camera.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/control-disconnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/control-disconnected.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/control-gcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/control-gcode.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/control-lasers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/control-lasers.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/control-motor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/control-motor.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/main-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/main-window.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/scanning-3d-scene.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/scanning-3d-scene.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/scanning-motor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/scanning-motor.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/scanning-parameters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/scanning-parameters.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/scanning-pointcloud-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/scanning-pointcloud-color.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/scanning-pointcloud-roi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/scanning-pointcloud-roi.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/scanning-video-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/scanning-video-gray.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/scanning-video-laser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/scanning-video-laser.png -------------------------------------------------------------------------------- /doc/readthedocs/source/_static/workbenches/scanning-video-texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/doc/readthedocs/source/_static/workbenches/scanning-video-texture.png -------------------------------------------------------------------------------- /doc/readthedocs/source/getting-started/index.rst: -------------------------------------------------------------------------------- 1 | .. _sec-getting-started: 2 | 3 | Getting started 4 | =============== 5 | 6 | Horus is a multiplatform application to experiment with the `open 3D scanner Ciclop`_. 7 | 8 | It provides a graphical interface that allows you to connect the scanner, control its devices, set scanning parameters, calibrate the scanner and scan 3D objects with Ciclop. It also includes a 3D scene of the point cloud obtained in real time. 9 | 10 | .. image:: ../_static/getting-started/main-window.png 11 | 12 | It was created by `bqlabs`_, the Department of Innovation and Robotics at `BQ`_, developed in `Python`_ and released under the `GPLv2`_ license. 13 | 14 | .. _open 3D scanner Ciclop: https://github.com/bqlabs/ciclop 15 | .. _bqlabs: https://github.com/bqlabs 16 | .. _BQ: http://www.bq.com 17 | .. _Python: https://www.python.org/ 18 | .. _GPLv2: https://www.gnu.org/licenses/gpl-2.0.html 19 | 20 | .. toctree:: 21 | :maxdepth: 1 22 | 23 | wizard.rst 24 | menu.rst 25 | scanning.rst 26 | -------------------------------------------------------------------------------- /doc/readthedocs/source/getting-started/menu.rst: -------------------------------------------------------------------------------- 1 | .. _sec-getting-started-menu: 2 | 3 | Menu 4 | ==== 5 | 6 | File 7 | ---- 8 | 9 | * **Launch wizard**: opens the *Wizard*. 10 | * **Open model**: opens a 3D model (*ply* point cloud or *stl* mesh). 11 | * **Save model**: saves the point cloud of the 3D scene in *ply* format. 12 | * **Clear model**: removes the current 3D model from the 3D scene. 13 | * **Open profile**: loads all the control, adjustment and scanning parameters. 14 | * **Save profile**: saves all the control, adjustment and scanning parameters in JSON format. 15 | * **Reset profile**: resets all the control, adjustment and scanning parameters to the default values. 16 | * **Open calibration**: loads all the calibration parameters. 17 | * **Save calibration**: saves all the calibration parameters in JSON format. 18 | * **Reset calibration**: resets all the calibration parameters to the default values. 19 | * **Export log**: saves a log file with the registry of the previous commands. 20 | * **Clear log**: removes the log file. It is automatically removed each 7 days. 21 | * **Exit**: closes the application. 22 | 23 | .. image:: ../_static/getting-started/menu-file.png 24 | 25 | Edit 26 | ---- 27 | 28 | * **Preferences**: opens the complete preferences window: 29 | 30 | * **Connection section**: *Camera ID*, *Serial name* and *Baud rate*. 31 | * **Adjustment section**: *Luminosity* and *Invert the motor direction*. 32 | * **Firmware section**: allows to upload the default or a selected firmware to *BT ATmega328* and *Arduino UNO* boards. Also, it can clear EEPROM before uploading the firmware. 33 | * **Language section**: allows to select the application's current language. 34 | 35 | .. image:: ../_static/getting-started/preferences.png 36 | :align: center 37 | :width: 284 px 38 | 39 | .. image:: ../_static/getting-started/menu-edit.png 40 | 41 | .. note:: 42 | 43 | The *Firmware section* is only enabled if the scanner is disconnected. 44 | 45 | View 46 | ---- 47 | 48 | This menu shows and hides the *Scanning workbench* panels: the configuration panel, the video and the 3D scene. 49 | 50 | It also contains the *Advanced mode* option, that enables advanced options in *Calibration workbench*. 51 | 52 | .. image:: ../_static/getting-started/menu-view.png 53 | 54 | Help 55 | ---- 56 | 57 | In this menu you can open the *Welcome* window, check for updates and to access to project's web resources. 58 | 59 | .. image:: ../_static/getting-started/menu-help.png 60 | 61 | Workbench combo box 62 | ------------------- 63 | 64 | With this combo box, the current workbench is selected. 65 | 66 | .. image:: ../_static/getting-started/menu-combo.png 67 | -------------------------------------------------------------------------------- /doc/readthedocs/source/getting-started/scanning.rst: -------------------------------------------------------------------------------- 1 | .. _sec-getting-started-scanning: 2 | 3 | Scanning 4 | ======== 5 | 6 | .. image:: ../_static/getting-started/main-window.png 7 | 8 | To start scanning press the *Play* button. Also the process can be stopped, paused and resumed. 9 | 10 | During the scanning, the progress is shown in the bottom of the scene. 11 | 12 | .. image:: ../_static/getting-started/scanning.png 13 | 14 | You can navigate in the 3D scene using the following shortcuts: 15 | 16 | .. list-table:: 17 | :widths: 1 1 1 18 | 19 | * - **Action** 20 | - **Shortcut 1** 21 | - **Shortcut 2** 22 | * - Default views 23 | - Home / PgUp / PgDn / End 24 | - 25 | * - Rotate 26 | - Left click 27 | - Shift + Up/Down 28 | * - Rotate horizontally 29 | - Up / Down 30 | - 31 | * - Rotate vertically 32 | - Left / Right 33 | - 34 | * - Vertical shift 35 | - Ctrl + Mouse wheel 36 | - Ctrl + Up / Down 37 | * - Reset vertical shift 38 | - Dobule left click 39 | - 40 | * - Traslation 41 | - Shift + Left click 42 | - 43 | * - Zoom 44 | - Mouse wheel 45 | - Shift + Up /Down 46 | * - Delete object 47 | - Right click + Delete object 48 | - Del 49 | * - Quit program 50 | - Ctrl + Q 51 | - 52 | 53 | Upon completion of the scanning process, the object can be saved in *File > Save model*. The point cloud is saved in *ply* format. 54 | 55 | .. image:: ../_static/getting-started/scan-finished.png 56 | -------------------------------------------------------------------------------- /doc/readthedocs/source/getting-started/wizard.rst: -------------------------------------------------------------------------------- 1 | .. _sec-getting-started-wizard: 2 | 3 | Wizard 4 | ====== 5 | 6 | When you first open *Horus*, a **Welcome** window appears. This windows has two parts: 7 | 8 | * **Create new**: it allows to launch the Wizard or any workbench. 9 | * **Open recent file**: it provides direct access to the latest models (*ply* or *stl*). 10 | 11 | .. image:: ../_static/getting-started/welcome.png 12 | 13 | Pressing over **Wizard mode**, an interactive menu appears for configuring the scanner step by step. 14 | 15 | Connection window 16 | ----------------- 17 | 18 | This window contains the scanner connection, a preferences panel and the auto check process. 19 | 20 | .. image:: ../_static/getting-started/wizard-connection.png 21 | 22 | * **Connect/Disconnect**: connects the camera and the electronics of Ciclop. If a device can not be found, a notification message is thrown. 23 | * **Preferences**: allows to modify the *Camera ID* and the *Serial name* of the scanner. Also the *Luminosity*. This setting can take *High, Medium or Low* for high, medium of low ambient light. The setting *Invert the motor direction* inverts all the motor angle commands. 24 | 25 | .. image:: ../_static/getting-started/preferences-mini.png 26 | :align: center 27 | :width: 284 px 28 | 29 | * **Auto check**: in order to perform this process, putting the pattern over the platform is required, as shown in the picture. This process makes a full turn of the platform, and checks: 30 | 31 | * **Pattern detection**: indicates whether the pattern is not detected correctly by luminosity or brightness problems. 32 | * **Motor direction**: detects if the motor direction is reversed and how to correct it. 33 | * **Lasers detection**: detects if the lasers have been connected properly and are working. 34 | 35 | .. image:: ../_static/getting-started/wizard-autocheck.png 36 | 37 | .. note:: 38 | 39 | The first time *Auto check* is pressed, a menu appears indicating if the lasers want to be aligned. This is for manually moving the orientation of the lasers until the emitted line is perpendicular to the platform. It requires an Allen wrench. 40 | 41 | Calibration window 42 | ------------------ 43 | 44 | In this windows both the lasers calibration and the platform calibration are performed. 45 | 46 | .. figure:: ../_static/getting-started/wizard-calibration.png 47 | 48 | This calibration process computes the planes in the space respect to the camera for each laser, as well as the spatial relation between the turntable and the optical center of the camera. 49 | 50 | .. note:: 51 | 52 | The first time the calibration is performed, *Origin pattern distance* must be set. This distance is fundamental for the platform's calibration, because it indicates the real relation between the position of the pattern's sticker and the base. 53 | 54 | .. image:: ../_static/getting-started/pattern-origin-distance.png 55 | :align: center 56 | :width: 456 px 57 | 58 | Scanning window 59 | --------------- 60 | 61 | In this window, scanning parameters are set: 62 | 63 | * **Resolution**: it is related to the number of steps per revolution of the motor. 64 | 65 | * *High*: 800 steps (0.45º) 66 | * *Medium*: 400 steps (0.9º) 67 | * *Low*: 200 steps (1.8º) 68 | 69 | * **Laser**: selects left laser, right laser or both. 70 | * **Capture texture**: enabling this option captures the real color of the object. Otherwise the point cloud has an uniform fake color. 71 | 72 | .. image:: ../_static/getting-started/wizard-scanning.png 73 | 74 | Once the **Wizard** is completed, access the main window. 75 | -------------------------------------------------------------------------------- /doc/readthedocs/source/installation/index.rst: -------------------------------------------------------------------------------- 1 | .. _sec-installation: 2 | 3 | Installation 4 | ============ 5 | 6 | .. toctree:: 7 | :maxdepth: 1 8 | 9 | ubuntu.rst 10 | windows.rst 11 | macosx.rst 12 | -------------------------------------------------------------------------------- /doc/readthedocs/source/installation/macosx.rst: -------------------------------------------------------------------------------- 1 | .. _sec-installation-macosx: 2 | 3 | .. image:: ../_static/installation/apple-logo.svg 4 | :align: right 5 | :width: 100 px 6 | 7 | Install on Mac OS X 8 | =================== 9 | 10 | **Supported versions**: 10.9, 10.10, 10.11 11 | 12 | System setup 13 | ------------ 14 | 15 | Install the FTDI drivers 16 | 17 | * `FTDI USB Drivers`_ 18 | 19 | 20 | Install Horus 21 | ------------- 22 | 23 | * `Horus installer`_ 24 | 25 | Execute the installer and drag Horus icon into *Applications*. 26 | 27 | Reboot the computer to apply the changes. 28 | 29 | 30 | .. _FTDI USB Drivers: http://www.ftdichip.com/Drivers/VCP/MacOSX/FTDIUSBSerialDriver_v2_3.dmg 31 | .. _Horus installer: https://github.com/bqlabs/horus/releases/download/0.2rc1/Horus_0.2rc1.dmg 32 | -------------------------------------------------------------------------------- /doc/readthedocs/source/installation/ubuntu.rst: -------------------------------------------------------------------------------- 1 | .. _sec-installation-ubuntu: 2 | 3 | .. image:: ../_static/installation/ubuntu-logo.svg 4 | :align: right 5 | :width: 100 px 6 | 7 | Install on Ubuntu 8 | ================= 9 | 10 | **Supported versions**: 14.04, 15.04, 15.10, 16.04 11 | 12 | System setup 13 | ------------ 14 | 15 | .. code-block:: bash 16 | 17 | sudo add-apt-repository ppa:bqlabs/horus-dev 18 | sudo apt-get update 19 | 20 | Official versions are hosted in **ppa:bqlabs/horus**: `PPA Horus`_. 21 | Alpha, beta and rc versions are hosted in **ppa:bqlabs/horus-dev**: `PPA Horus dev`_. 22 | 23 | .. note:: 24 | 25 | A `custom OpenCV`_ version is used, because of the next `reasons`_. 26 | 27 | 28 | 29 | Install Horus 30 | ------------- 31 | 32 | This command installs all the dependencies, including custom OpenCV libraries. 33 | 34 | .. code-block:: bash 35 | 36 | sudo apt-get install horus 37 | 38 | .. note:: 39 | 40 | If user has no access to serial port, execute ``sudo usermod -a -G dialout $USER`` and reboot. 41 | 42 | 43 | Update Horus 44 | ------------ 45 | 46 | If there is a new release just execute 47 | 48 | .. code-block:: bash 49 | 50 | sudo apt-get update 51 | sudo apt-get install horus 52 | 53 | .. _PPA Horus: https://launchpad.net/~bqlabs/+archive/ubuntu/horus/ 54 | .. _PPA Horus dev: https://launchpad.net/~bqlabs/+archive/ubuntu/horus-dev/ 55 | .. _custom OpenCV: https://github.com/bqlabs/opencv 56 | .. _reasons: https://github.com/bqlabs/opencv/wiki 57 | -------------------------------------------------------------------------------- /doc/readthedocs/source/installation/windows.rst: -------------------------------------------------------------------------------- 1 | .. _sec-installation-windows: 2 | 3 | .. image:: ../_static/installation/windows-logo.svg 4 | :align: right 5 | :width: 100 px 6 | 7 | Install on Windows 8 | ================== 9 | 10 | **Supported versions**: 7, 10 11 | 12 | System setup 13 | ------------ 14 | 15 | Install the USB Camera drivers 16 | 17 | * `Logitech Camera C270 Drivers`_ 18 | 19 | 20 | Install Horus 21 | ------------- 22 | 23 | * `Horus installer`_ 24 | 25 | Execute the installer and follow the wizard. This package contains all dependencies and also Arduino and FTDI drivers. 26 | 27 | Reboot the computer to apply the changes. 28 | 29 | .. note:: 30 | 31 | In **Windows 10**, if the application is blurred, follow these steps: 32 | 33 | 1. Right click on the application and select *Properties* 34 | 2. Go to *Compatibility* tab. 35 | 3. Under *Settings* section, check *Disable display scaling on high DPI settings* 36 | 4. *Apply* and close the window. 37 | 38 | .. _Logitech Camera C270 Drivers: http://support.logitech.com/en_us/product/hd-webcam-c270 39 | .. _Horus installer: https://github.com/bqlabs/horus/releases/download/0.2rc1/Horus_0.2rc1.exe 40 | -------------------------------------------------------------------------------- /doc/readthedocs/source/scanner-components/board.rst: -------------------------------------------------------------------------------- 1 | .. _sec-scanner-components-board: 2 | 3 | Board 4 | ===== 5 | 6 | Supported boards 7 | ---------------- 8 | 9 | ZUM BT-328 10 | ``````````` 11 | 12 | .. image:: ../_static/scanner-components/zum.jpg 13 | :width: 250 px 14 | 15 | ZUM SCAN Shield 16 | ``````````````` 17 | 18 | .. image:: ../_static/scanner-components/zum-scan.jpg 19 | :width: 250 px 20 | 21 | 22 | Horus Firmware 23 | -------------- 24 | 25 | The firmware is an adaptation of Grbl. Source code is here: https://github.com/bqlabs/horus-fw. 26 | 27 | It can be uploaded in **Preferences > Upload firmware**. 28 | 29 | .. Troubleshooting 30 | .. --------------- 31 | -------------------------------------------------------------------------------- /doc/readthedocs/source/scanner-components/camera.rst: -------------------------------------------------------------------------------- 1 | .. _sec-scanner-components-camera: 2 | 3 | Camera 4 | ====== 5 | 6 | Supported cameras 7 | ----------------- 8 | 9 | Logitech C270 10 | ````````````` 11 | 12 | .. image:: ../_static/scanner-components/logitech-c270.png 13 | :width: 250 px 14 | 15 | HD USB webcam with fixed focus. 16 | 17 | .. list-table:: 18 | :widths: 5 3 4 19 | 20 | * - **Name** 21 | - **Value** 22 | - **Setting** 23 | * - Width 24 | - 1280 px 25 | - ``camera_width`` 26 | * - Height 27 | - 960 px 28 | - ``camera_height`` 29 | * - Frame rate 30 | - 30 fps 31 | - ``frame_rate`` 32 | * - Rotation 33 | - Yes 34 | - ``camera_rotate`` 35 | * - Horizontal flip 36 | - Yes 37 | - ``camera_hflip`` 38 | * - Vertical flip 39 | - No 40 | - ``camera_vflip`` 41 | * - Focal length x 42 | - 1430 px 43 | - ``camera_matrix`` 44 | * - Focal length y 45 | - 1430 px 46 | - ``camera_matrix`` 47 | * - Optical center x 48 | - 480 px 49 | - ``camera_matrix`` 50 | * - Optical center y 51 | - 620 px 52 | - ``camera_matrix`` 53 | * - Distortion 54 | - No 55 | - ``use_distortion`` 56 | 57 | Image controls 58 | -------------- 59 | 60 | .. list-table:: 61 | :widths: 5 3 4 62 | 63 | * - **Name** 64 | - **Range** 65 | - **Setting** 66 | * - Brightness 67 | - 0-255 68 | - ``brightness_`` 69 | * - Contrast 70 | - 0-255 71 | - ``contrast_`` 72 | * - Saturation 73 | - 0-255 74 | - ``saturation_`` 75 | * - Exposure 76 | - 1-64 77 | - ``exposure_`` 78 | 79 | These parameters have different values for each situation: 80 | 81 | * Capture texture 82 | * Pattern detection 83 | * Laser detection over the object 84 | * Laser detection over the pattern 85 | 86 | Therefore, for each case the optimum values can be set. 87 | 88 | 89 | Flush buffer 90 | ------------ 91 | 92 | OpenCV is used to manage the camera. It wraps all the functionality to allow easy access. 93 | 94 | At the low level driver, each operating system has different behavior regarding to the buffer of stored images. Moreover, if the exposure time is set over the frame rate (33 ms), buffer filling may vary if image controls are being updated. 95 | This may cause synchronization problems between the laser and the camera. Instead of using a long delay to reach the synchronization, a better approach is implemented using custom flush values. 96 | 97 | .. list-table:: 98 | :widths: 4 3 2 4 99 | 100 | * - **Name** 101 | - **OS** 102 | - **Value** 103 | - **Setting** 104 | * - Texture flush 105 | - Linux 106 | - 3 107 | - ``flush_linux_texture`` 108 | * - Laser flush 109 | - Linux 110 | - 2 111 | - ``flush_linux_laser`` 112 | * - Pattern flush 113 | - Linux 114 | - 3 115 | - ``flush_linux_pattern`` 116 | * - Texture flush 117 | - Windows 118 | - 4 119 | - ``flush_windows_texture`` 120 | * - Laser flush 121 | - Windows 122 | - 3 123 | - ``flush_windows_laser`` 124 | * - Pattern flush 125 | - Windows 126 | - 4 127 | - ``flush_windows_pattern`` 128 | * - Texture flush 129 | - MacOSX 130 | - 4 131 | - ``flush_darwin_texture`` 132 | * - Laser flush 133 | - MacOSX 134 | - 3 135 | - ``flush_darwin_laser`` 136 | * - Pattern flush 137 | - MacOSX 138 | - 4 139 | - ``flush_darwin_pattern`` 140 | 141 | .. note:: 142 | 143 | In Linux, a `custom OpenCV`_ version is used, because of the next `reasons`_. In Windows and Mac, standard 2.4.9 version is used. 144 | 145 | Troubleshooting 146 | --------------- 147 | 148 | Focus image 149 | ``````````` 150 | 151 | Logitech C270 camera is not focused at the scanner working distance (about 300 mm), but it is focused at a longer distance. This may cause inaccurate pattern detection and worse calibration values. 152 | 153 | To improve that, the camera can be re-focused manually: 154 | 155 | 1. Remove the electronics: the camera can not be removed if the board is fixed. 156 | 2. Disassemble the camera: remove the 3 screws and the front cover. 157 | 3. Move the lens to break up the glue. 158 | 4. Put the camera into the scanner. 159 | 5. Put the pattern on the middle of the platform. 160 | 6. Open the video and move the focus until the pattern is focused. 161 | 7. Assemble again the camera and the rest of the scanner. 162 | 163 | In this `video`_ the camera manual focus is explained. 164 | 165 | 166 | .. _custom OpenCV: https://github.com/bqlabs/opencv 167 | .. _reasons: https://github.com/bqlabs/opencv/wiki 168 | .. _video: https://www.youtube.com/watch?v=v-gYgBeiOVI 169 | -------------------------------------------------------------------------------- /doc/readthedocs/source/scanner-components/index.rst: -------------------------------------------------------------------------------- 1 | .. _sec-scanner-components: 2 | 3 | Scanner components 4 | ================== 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | camera.rst 10 | board.rst 11 | lasers.rst 12 | motor.rst 13 | pattern.rst 14 | -------------------------------------------------------------------------------- /doc/readthedocs/source/scanner-components/lasers.rst: -------------------------------------------------------------------------------- 1 | .. _sec-scanner-components-lasers: 2 | 3 | Lasers 4 | ====== 5 | 6 | Supported lasers 7 | ---------------- 8 | 9 | Line laser 10 | `````````` 11 | 12 | .. image:: ../_static/scanner-components/line-laser.png 13 | :width: 250 px 14 | 15 | Red Class 1 line laser. 16 | 17 | .. list-table:: 18 | :widths: 50 25 19 | 20 | * - **Name** 21 | - **Value** 22 | * - Wavelength 23 | - 650 nm 24 | * - Voltage 25 | - 5 V 26 | * - Working distance 27 | - 300 mm 28 | * - Output aperture 29 | - 3 mm 30 | * - Output power 31 | - 2.5 mW 32 | * - Fan angle 33 | - > 60 º 34 | * - Body diameter 35 | - 8 mm 36 | * - Body length 37 | - 26 mm 38 | * - Wire length 39 | - 250 mm 40 | * - Security class 41 | - 1 42 | * - TÜV certificate 43 | - Yes 44 | 45 | .. warning:: 46 | 47 | Only **Class 1** lasers according to *IEC 60825-1:2014* are recommended to ensure eye safety. The emission limits for this wavelength must be less than 390 uW. 48 | 49 | Gcodes 50 | ------ 51 | 52 | M70 53 | ```` 54 | Switch off the laser specified in the T command. 55 | 56 | :Example: M70 T1 57 | 58 | M71 59 | ```` 60 | Switch on the laser specified in the T command. 61 | 62 | :Example: M71 T2 63 | 64 | .. note:: 65 | 66 | Lasers are switched off automatically after 255 seconds as a safety measure. 67 | 68 | Troubleshooting 69 | --------------- 70 | 71 | Laser not detected correctly 72 | ```````````````````````````` 73 | 74 | If the laser is not detected correctly: 75 | 76 | 1. Improve the environment light conditions. 77 | 2. Adjust the camera settings in *Adjustment workbench*: 78 | 79 | * *Scan capture > Laser* for the laser over the scanning object. 80 | * *Calibration capture > Laser* for the laser over the pattern. 81 | -------------------------------------------------------------------------------- /doc/readthedocs/source/scanner-components/motor.rst: -------------------------------------------------------------------------------- 1 | .. _sec-scanner-components-motor: 2 | 3 | Motor 4 | ===== 5 | 6 | Supported motors 7 | ---------------- 8 | 9 | Nema 17 10 | ``````` 11 | 12 | .. image:: ../_static/scanner-components/stepper-motor.png 13 | :width: 250 px 14 | 15 | Bipolar stepper motor. 16 | 17 | .. list-table:: 18 | :widths: 50 25 19 | 20 | * - **Name** 21 | - **Value** 22 | * - Phase 23 | - 2 24 | * - Voltage 25 | - 2.55 V 26 | * - Step angle 27 | - 1.8º 28 | * - Fan angle 29 | - > 60 º 30 | * - Shaft height 31 | - 22 mm 32 | * - Body height 33 | - 40 mm 34 | * - Wire length 35 | - 600 mm 36 | * - Chamfer 37 | - Yes 38 | 39 | Gcodes 40 | ------ 41 | 42 | G1 43 | ```` 44 | Perform an angular movement. Using the F command, the angular speed is set to the specified value in degrees per second. Also with the X command, the motor moves to the specified absolute position in degrees. 45 | 46 | :Examples: G1 F200, G1 X-90 47 | 48 | G50 49 | ```` 50 | Reset all positions to zero. It is recommended to be executed after M18 to avoid the oscillation anchor. 51 | 52 | M17 53 | ```` 54 | Enable the motor. It remains enabled even after sending G1 commands. 55 | 56 | .. warning:: 57 | 58 | If the motor is enabled for a long time, the driver and the motor can overheat and even break. 59 | 60 | 61 | M18 62 | ```` 63 | Disable the motor. It remains disabled even after sending G1 commands. 64 | 65 | Troubleshooting 66 | --------------- 67 | 68 | Reversed direction of rotation 69 | `````````````````````````````` 70 | 71 | Motor rotation must turn in a counterclockwise direction with positive angles. If it turns in reverse, probably the connector is connected in the wrong way. This can be solved by editing the parameter ``invert_motor`` in Preferences > Invert the motor direction. 72 | 73 | Strange movements 74 | ````````````````` 75 | 76 | To avoid possible strange movements in the motor: 77 | 78 | 1. Make sure the motor step is 1.8º. 79 | 2. Put all the jumpers in the Zum Scan board to enable micro-stepping. 80 | 3. Adjust the electrical current with the potentiometer in the pololu driver. Recommended values are about 200 mA. 81 | -------------------------------------------------------------------------------- /doc/readthedocs/source/scanner-components/pattern.rst: -------------------------------------------------------------------------------- 1 | .. _sec-scanner-components-pattern: 2 | 3 | Pattern 4 | ======= 5 | 6 | Supported pattern 7 | ----------------- 8 | 9 | Chessboard patterns 10 | ``````````````````` 11 | 12 | .. image:: ../_static/scanner-components/chessboard-pattern.svg 13 | :width: 250 px 14 | 15 | .. list-table:: 16 | :widths: 5 3 5 17 | 18 | * - **Name** 19 | - **Value** 20 | - **Setting** 21 | * - Rows 22 | - 6 23 | - ``pattern_rows`` 24 | * - Columns 25 | - 11 26 | - ``pattern_columns`` 27 | * - Square width 28 | - 13 mm 29 | - ``pattern_square_width`` 30 | * - Origin distance 31 | - > 0.0 mm 32 | - ``pattern_origin_distance`` 33 | 34 | .. note:: 35 | 36 | These values can be set in *Calibration workbench > Pattern settings*. 37 | 38 | .. warning:: 39 | 40 | It must be an odd number of columns in order to define an unique origin of the pattern. 41 | 42 | 43 | Origin distance 44 | --------------- 45 | 46 | The origin distance is the minimum distance between the origin of the pattern (bottom-left corner) and the pattern's base surface. This value is used to define where is the pattern with respect to the turntable. 47 | 48 | .. image:: ../_static/scanner-components/pattern-distance.jpg 49 | :width: 250 px 50 | 51 | Pattern detection 52 | ----------------- 53 | 54 | When the pattern is correctly detected, the following colored dots and lines are drawn. 55 | 56 | .. image:: ../_static/scanner-components/pattern-detected.png 57 | :width: 250 px 58 | 59 | Troubleshooting 60 | --------------- 61 | 62 | Pattern not detected 63 | ```````````````````` 64 | If the pattern is not detected: 65 | 66 | 1. Check the *Calibration workbench > Pattern settings*. 67 | 2. Improve the environment light conditions. 68 | 3. Adjust the camera settings in *Adjustment workbench > Calibration capture > Pattern*. 69 | -------------------------------------------------------------------------------- /doc/readthedocs/source/workbenches/adjustment.rst: -------------------------------------------------------------------------------- 1 | .. _sec-workbenches-adjustment: 2 | 3 | Adjustment 4 | ========== 5 | 6 | In this workbench, camera capture and image processing parameters are adjusted for the different states of the system. The current state and its changes are displayed in the video wall in real time. 7 | 8 | Scanning adjustments 9 | -------------------- 10 | 11 | These settings are applied during the scanning process. 12 | 13 | Capture 14 | ~~~~~~~ 15 | 16 | In this section you can adjust the parameters of the capture during the scanning process. These parameters must be adjusted with the object to be scanned. 17 | 18 | The *Texture mode* contains the settings used to capture the texture/color of the scanned object. These are: brightness, constrast, saturation and exposure. 19 | 20 | .. image:: ../_static/workbenches/adjustment-scan-capture-texture.png 21 | 22 | The *Laser mode* contains the settings used to capture and detect the laser over the scanned object. These are: brightness, constrast, saturation, exposure and background removal. The *Remove background* option improves the laser detection by consuming twice as long. 23 | 24 | .. image:: ../_static/workbenches/adjustment-scan-capture-laser.png 25 | 26 | Segmentation 27 | ~~~~~~~~~~~~ 28 | 29 | In this section you can adjust the parameters for the laser stripe segmentation during the scanning process. 30 | 31 | * **Threshold**: remove all pixels which intensity is less that the threshold value. 32 | * **Blur**: blur with Normalized box filter. Kernel size: 2 * value + 1. 33 | * **Window**: filter pixels out of 2 * window value around the intensity peak. 34 | * **Refinement**: apply None or SGF algorithms for line smooth. SGF produces continous surfaces. 35 | 36 | .. image:: ../_static/workbenches/adjustment-scan-segmentation.png 37 | 38 | Calibration adjustments 39 | ----------------------- 40 | 41 | These settings are applied during the calibration processes. 42 | 43 | Capture 44 | ~~~~~~~ 45 | 46 | In this section you can adjust the parameters of the capture during the calibration process. These parameters must be adjusted with the calibration pattern. 47 | 48 | The *Pattern mode* contains the settings used to capture the pattern. These are: brightness, constrast, saturation and exposure. 49 | 50 | .. image:: ../_static/workbenches/adjustment-calibration-capture-pattern.png 51 | 52 | The *Laser mode* contains the settings used to capture and detect the laser over the pattern. These are: brightness, constrast, saturation, exposure and background removal. The *Remove background* option improves the laser detection by consuming twice as long. 53 | 54 | .. image:: ../_static/workbenches/adjustment-calibration-capture-laser.png 55 | 56 | Segmentation 57 | ~~~~~~~~~~~~ 58 | 59 | In this section you can adjust the parameters for the laser stripe segmentation during the calibration processes. 60 | 61 | * **Threshold**: remove all pixels which intensity is less that the threshold value. 62 | * **Blur**: blur with Normalized box filter. Kernel size: 2 * value + 1. 63 | * **Window**: filter pixels out of 2 * window value around the intensity peak. 64 | * **Refinement**: apply None, SGF, RANSAC algorithms for line smooth. SGF produces continous surfaces. RANSAC produces flat surfaces. 65 | 66 | .. image:: ../_static/workbenches/adjustment-calibration-segmentation.png 67 | -------------------------------------------------------------------------------- /doc/readthedocs/source/workbenches/calibration.rst: -------------------------------------------------------------------------------- 1 | .. _sec-workbenches-calibration: 2 | 3 | Calibration 4 | =========== 5 | 6 | This workbench contains all the scanner calibration processes. 7 | 8 | Pattern settings 9 | ---------------- 10 | 11 | This section contains the calibration pattern features: 12 | 13 | * **Pattern rows**: number of corner rows in the pattern. Default value 6. 14 | * **Pattern columns**: number of corner columns in the pattern. Default value 11. 15 | * **Square width**: default value 13 mm. 16 | * **Origin distance**: minimum distance between the origin of the pattern (bottom-left corner) and the pattern's base surface in mm. There is no default value because it depends on the physical pattern. 17 | 18 | .. image:: ../_static/workbenches/calibration-pattern.png 19 | 20 | Autocheck 21 | --------- 22 | 23 | This section contains the auto check process in which is detected whether the pattern, motor and lasers are configured properly. 24 | 25 | The pattern should be placed as shown in the picture. If the process ends successfully, the pattern will be placed perpendicular to the camera. Otherwise, a notification will be displayed. 26 | 27 | .. image:: ../_static/workbenches/calibration-autocheck.png 28 | 29 | Laser calibration 30 | ----------------- 31 | 32 | In this section, the laser's planes are calculated. Each plane is defined by a normal vector and the minimum distance from the plane to the optical center of the camera. 33 | 34 | To begin the calibration, the pattern must be placed perpendicular to the camera, as shown in the picture. At any time you can cancel the calibration and the pattern will return to its initial position. 35 | 36 | .. image:: ../_static/workbenches/calibration-laser.png 37 | 38 | Finally the result is shown numerically and depicted in 3D. Also, the dispersion of the captured points during calibration appears. This value must be less than 0.1 mm. You can accept or reject the calibration result. 39 | 40 | .. image:: ../_static/workbenches/calibration-laser-result.png 41 | 42 | Platform calibration 43 | -------------------- 44 | 45 | In this section, the homogeneous transformation matrix from the rotation center of the turntable with respect to the camera system is calculated. This matrix is composed by a rotation matrix and a traslation vector in mm. 46 | 47 | To begin the calibration, the pattern must be placed perpendicular to the camera, as shown in the picture. At any time you can cancel the calibration and the pattern will return to its initial position. 48 | 49 | .. image:: ../_static/workbenches/calibration-platform.png 50 | 51 | Finally the result is shown numerically and depicted in 3D. You can accept or reject the calibration result. 52 | 53 | .. image:: ../_static/workbenches/calibration-platform-result.png 54 | 55 | Once this process is completed, the scanner is calibrated. 56 | 57 | Video settings (advanced) 58 | ------------------------- 59 | 60 | This is an advanced section. It contains the rotation flags for the video. Also you can set the camera resolution in px. 61 | 62 | .. image:: ../_static/workbenches/calibration-video.png 63 | 64 | .. hint:: 65 | 66 | If a wrong resolution is set, the nearest resolution is recommended. Also, you can go back to the latest resolution. 67 | 68 | .. note:: 69 | 70 | In Mac OS the resolution can not be set in runtime because of its OpenCV version. 71 | 72 | Camera calibration (advanced) 73 | ----------------------------- 74 | 75 | This is an advanced section. Default values are recommended. 76 | 77 | To begin the calibration, press the "space" key to capture the pattern in different positions. Once taken all the captures, the calibration starts automatically. You can reset the taken captures at any time. 78 | 79 | .. image:: ../_static/workbenches/calibration-intrinsics.png 80 | 81 | .. warning:: 82 | 83 | If camera intrinsics values are modified, laser and platform calibrations must be performed again. 84 | 85 | | 86 | 87 | .. note:: 88 | 89 | To enable the advanced mode go to the menu *View > Advanced mode*. 90 | 91 | .. image:: ../_static/workbenches/calibration-advanced-mode.png 92 | -------------------------------------------------------------------------------- /doc/readthedocs/source/workbenches/control.rst: -------------------------------------------------------------------------------- 1 | .. _sec-workbenches-control: 2 | 3 | Control 4 | ======= 5 | 6 | This workbench is used to test the scanner components: camera, lasers and motor. 7 | 8 | .. image:: ../_static/workbenches/control-disconnected.png 9 | 10 | Camera 11 | ------ 12 | 13 | In this section you can adjust the brightness, contrast, saturation and exposure of the camera. 14 | 15 | .. image:: ../_static/workbenches/control-camera.png 16 | 17 | Also it allows to capture and save images from the camera in *png* format. 18 | 19 | Laser 20 | ----- 21 | 22 | In this section you can turn on and off the lasers. When leaving the workbench the lasers are automatically turned off. 23 | 24 | .. image:: ../_static/workbenches/control-lasers.png 25 | 26 | Motor 27 | ----- 28 | 29 | In this section you can move the motor to an absolute angle, with a specific speed and acceleration. These values affect only to this workbench. 30 | 31 | .. image:: ../_static/workbenches/control-motor.png 32 | 33 | Also the motor can be enabled or disabled and the position stored inside the firmware reseted. 34 | 35 | Gcode 36 | ----- 37 | 38 | This section contains a terminal that allows to communicate with the firmware through Gcode commands. 39 | 40 | .. image:: ../_static/workbenches/control-gcode.png 41 | -------------------------------------------------------------------------------- /doc/readthedocs/source/workbenches/index.rst: -------------------------------------------------------------------------------- 1 | .. _sec-workbenches: 2 | 3 | Workbenches 4 | =========== 5 | 6 | .. toctree:: 7 | :maxdepth: 1 8 | 9 | control.rst 10 | adjustment.rst 11 | calibration.rst 12 | scanning.rst 13 | -------------------------------------------------------------------------------- /doc/readthedocs/source/workbenches/scanning.rst: -------------------------------------------------------------------------------- 1 | .. _sec-workbenches-scanning: 2 | 3 | Scanning 4 | ======== 5 | 6 | This workbench is where the 3D scanning process is performed. This process generates a three dimensional point cloud from a physical object. It has three components: 7 | 8 | * Settings panel 9 | * Video wall 10 | * 3D scene 11 | 12 | Through the menu *View* these panels can be shown or hidden. 13 | 14 | Settings panel 15 | -------------- 16 | 17 | Scan parameters 18 | ~~~~~~~~~~~~~~~ 19 | 20 | * **Capture texture**: enabling this option captures the real color of the object. Otherwise the point cloud has an uniform fake color. If this option is disabled, the process is faster and the color used is the one defined in the *Point cloud color* section. 21 | * **Use laser**: selects left laser, right laser or both. 22 | 23 | .. image:: ../_static/workbenches/scanning-parameters.png 24 | 25 | Rotating platform 26 | ~~~~~~~~~~~~~~~~~ 27 | 28 | * **Show center**: shows the center of the platform using the current calibration values. 29 | * **Step**: is the angle increased in each scan iteration. The smaller the step, the greater radial resolution, and also more scanning time. The default value is 0.45º, that is 800 steps per revolution. 30 | * **Speed**: is the motor speed in degrees per second. Te default value is 200 º/s. 31 | * **Acceleration**: is the motor acceleration in degress per secon squared. The default value is 200 º/s². 32 | 33 | .. image:: ../_static/workbenches/scanning-motor.png 34 | 35 | Point cloud ROI 36 | ~~~~~~~~~~~~~~~ 37 | 38 | In this section the ROI (Region of interest) is defined. It is a cylindrical volume in the point cloud and a rectangle in the video. 39 | 40 | * **Use ROI**: enabling this option applies the ROI. This region is the one being scanned both in the video and in the point cloud. All information outside won't be taken into account during the scanning process. 41 | * **Diameter**: ROI diameter in mm. The default value is 200 mm. 42 | * **Height**: ROI height in mm. The default value is 200 mm. 43 | 44 | .. image:: ../_static/workbenches/scanning-pointcloud-roi.png 45 | 46 | Point cloud color 47 | ~~~~~~~~~~~~~~~~~ 48 | 49 | In this section is selected the color for the point cloud when no texture is captured. 50 | 51 | .. image:: ../_static/workbenches/scanning-pointcloud-color.png 52 | 53 | 54 | | 55 | 56 | .. note:: 57 | 58 | The *Settings panel* is hidden during the scanning process. 59 | 60 | Video wall 61 | ---------- 62 | 63 | In this window, two states are distinguished: while not scanning, it shows the video in texture mode. When the scan starts, you can select several views corresponding to the different stages of the image processing. 64 | 65 | * **Texture** 66 | 67 | .. image:: ../_static/workbenches/scanning-video-texture.png 68 | 69 | * **Laser** 70 | 71 | .. image:: ../_static/workbenches/scanning-video-laser.png 72 | 73 | * **Gray** 74 | 75 | .. image:: ../_static/workbenches/scanning-video-gray.png 76 | 77 | 3D scene 78 | --------- 79 | 80 | This section is a three-dimensional scene where the scanned point cloud (model) is shown. Also it allows to view meshes in *stl* format. 81 | 82 | To start scanning press the *Play* button. Also the process can be stopped, paused and resumed. During the scanning, the progress is shown in the bottom of the scene. 83 | 84 | You can navigate in the 3D scene using the following shortcuts: 85 | 86 | .. list-table:: 87 | :widths: 1 1 1 88 | 89 | * - **Action** 90 | - **Shortcut 1** 91 | - **Shortcut 2** 92 | * - Default views 93 | - Home / PgUp / PgDn / End 94 | - 95 | * - Rotate 96 | - Left click 97 | - Shift + Up/Down 98 | * - Rotate horizontally 99 | - Up / Down 100 | - 101 | * - Rotate vertically 102 | - Left / Right 103 | - 104 | * - Vertical shift 105 | - Ctrl + Mouse wheel 106 | - Ctrl + Up / Down 107 | * - Reset vertical shift 108 | - Dobule left click 109 | - 110 | * - Traslation 111 | - Shift + Left click 112 | - 113 | * - Zoom 114 | - Mouse wheel 115 | - Shift + Up /Down 116 | * - Delete object 117 | - Right click + Delete object 118 | - Del 119 | * - Quit program 120 | - Ctrl + Q 121 | - 122 | 123 | .. image:: ../_static/workbenches/scanning-3d-scene.png 124 | 125 | To load, save or reset the 3D model, access from the menu *File*. 126 | -------------------------------------------------------------------------------- /horus: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # This file is part of the Horus Project 4 | 5 | __author__ = 'Jesús Arroyo Torrens ' 6 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 7 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 8 | 9 | 10 | # Check dependencies 11 | try: 12 | import os 13 | import wx 14 | import sys 15 | import cv2 16 | import OpenGL 17 | import serial 18 | import numpy 19 | import scipy 20 | import matplotlib 21 | except ImportError as e: 22 | print(e.message) 23 | exit(1) 24 | 25 | # Try first the sources 26 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) 27 | 28 | from horus.util import resources 29 | 30 | resdir = os.path.join(os.path.dirname(__file__), "res") 31 | if not os.path.exists(resdir): 32 | resdir = "/usr/share/horus" 33 | 34 | resources.set_base_path(resdir) 35 | 36 | 37 | def main(): 38 | from horus.gui import app 39 | app.HorusApp().MainLoop() 40 | 41 | if __name__ == '__main__': 42 | main() 43 | -------------------------------------------------------------------------------- /pkg/darwin/create-dmg/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2008-2014 Andrey Tarantsov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pkg/darwin/create-dmg/README.md: -------------------------------------------------------------------------------- 1 | create-dmg 2 | ========== 3 | 4 | A shell script to build fancy DMGs. 5 | 6 | 7 | Status and contribution policy 8 | ------------------------------ 9 | 10 | This project is maintained thanks to the contributors who send pull requests. The original author has no use for the project, so his only role is reviewing and merging pull requests. 11 | 12 | I will merge any pull request that adds something useful and does not break existing things. 13 | 14 | Starting in January 2015, everyone who gets a pull request merged gets commit access to the repository. 15 | 16 | 17 | Installation 18 | ------------ 19 | 20 | By being a shell script, yoursway-create-dmg installation is very simple. Simply download and run. 21 | 22 | > git clone https://github.com/andreyvit/yoursway-create-dmg.git 23 | > cd yoursway-create-dmg 24 | > ./create-dmg [options] 25 | 26 | 27 | Usage 28 | ----- 29 | 30 | > create-dmg [options...] [output\_name.dmg] [source\_folder] 31 | 32 | All contents of source\_folder will be copied into the disk image. 33 | 34 | **Options:** 35 | 36 | * **--volname [name]:** set volume name (displayed in the Finder sidebar and window title) 37 | * **--volicon [icon.icns]:** set volume icon 38 | * **--background [pic.png]:** set folder background image (provide png, gif, jpg) 39 | * **--window-pos [x y]:** set position the folder window 40 | * **--window-size [width height]:** set size of the folder window 41 | * **--text-size [text size]:** set window text size (10-16) 42 | * **--icon-size [icon size]:** set window icons size (up to 128) 43 | * **--icon [file name] [x y]:** set position of the file's icon 44 | * **--hide-extension [file name]:** hide the extension of file 45 | * **--custom-icon [file name]/[custom icon]/[sample file] [x y]:** set position and custom icon 46 | * **--app-drop-link [x y]:** make a drop link to Applications, at location x, y 47 | * **--eula [eula file]:** attach a license file to the dmg 48 | * **--no-internet-enable:** disable automatic mount© 49 | * **--version:** show tool version number 50 | * **-h, --help:** display the help 51 | 52 | 53 | Example 54 | ------- 55 | 56 | > \#!/bin/sh 57 | > test -f Application-Installer.dmg && rm Application-Installer.dmg 58 | > create-dmg \ 59 | > --volname "Application Installer" \ 60 | > --volicon "application\_icon.icns" \ 61 | > --background "installer\_background.png" \ 62 | > --window-pos 200 120 \ 63 | > --window-size 800 400 \ 64 | > --icon-size 100 \ 65 | > --icon Application.app 200 190 \ 66 | > --hide-extension Application.app \ 67 | > --app-drop-link 600 185 \ 68 | > Application-Installer.dmg \ 69 | > source\_folder/ 70 | 71 | 72 | Alternatives 73 | ------------ 74 | 75 | * [node-appdmg](https://github.com/LinusU/node-appdmg) 76 | * [dmgbuild](https://pypi.python.org/pypi/dmgbuild) 77 | * see the [StackOverflow question](http://stackoverflow.com/questions/96882/how-do-i-create-a-nice-looking-dmg-for-mac-os-x-using-command-line-tools) 78 | -------------------------------------------------------------------------------- /pkg/darwin/create-dmg/builder/create-dmg.builder: -------------------------------------------------------------------------------- 1 | SET app_name create-dmg 2 | 3 | VERSION create-dmg.cur create-dmg heads/master 4 | 5 | NEWDIR build.dir temp %-build - 6 | 7 | NEWFILE create-dmg.zip featured %.zip % 8 | 9 | 10 | COPYTO [build.dir] 11 | INTO create-dmg [create-dmg.cur]/create-dmg 12 | INTO sample [create-dmg.cur]/sample 13 | INTO support [create-dmg.cur]/support 14 | 15 | SUBSTVARS [build.dir]/create-dmg [[]] 16 | 17 | 18 | ZIP [create-dmg.zip] 19 | INTO [build-files-prefix] [build.dir] 20 | 21 | 22 | PUT megabox-builds create-dmg.zip 23 | PUT megabox-builds build.log 24 | 25 | PUT s3-builds create-dmg.zip 26 | PUT s3-builds build.log 27 | -------------------------------------------------------------------------------- /pkg/darwin/create-dmg/sample: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | test -f test2.dmg && rm test2.dmg 3 | ./create-dmg --window-size 500 300 --background ~/Projects/eclipse-osx-repackager/build/background.gif --icon-size 96 --volname "Hyper Foo" --app-drop-link 380 205 --icon "Eclipse OS X Repackager" 110 205 test2.dmg /Users/andreyvit/Projects/eclipse-osx-repackager/temp/Eclipse\ OS\ X\ Repackager\ r10/ 4 | -------------------------------------------------------------------------------- /pkg/darwin/create-dmg/support/template.applescript: -------------------------------------------------------------------------------- 1 | on run (volumeName) 2 | tell application "Finder" 3 | tell disk (volumeName as string) 4 | open 5 | 6 | set theXOrigin to WINX 7 | set theYOrigin to WINY 8 | set theWidth to WINW 9 | set theHeight to WINH 10 | 11 | set theBottomRightX to (theXOrigin + theWidth) 12 | set theBottomRightY to (theYOrigin + theHeight) 13 | set dsStore to "\"" & "/Volumes/" & volumeName & "/" & ".DS_STORE\"" 14 | 15 | tell container window 16 | set current view to icon view 17 | set toolbar visible to false 18 | set statusbar visible to false 19 | set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY} 20 | set statusbar visible to false 21 | REPOSITION_HIDDEN_FILES_CLAUSE 22 | end tell 23 | 24 | set opts to the icon view options of container window 25 | tell opts 26 | set icon size to ICON_SIZE 27 | set text size to TEXT_SIZE 28 | set arrangement to not arranged 29 | end tell 30 | BACKGROUND_CLAUSE 31 | 32 | -- Positioning 33 | POSITION_CLAUSE 34 | 35 | -- Hiding 36 | HIDING_CLAUSE 37 | 38 | -- Application Link Clause 39 | APPLICATION_CLAUSE 40 | close 41 | open 42 | 43 | update without registering applications 44 | -- Force saving of the size 45 | delay 1 46 | 47 | tell container window 48 | set statusbar visible to false 49 | set the bounds to {theXOrigin, theYOrigin, theBottomRightX - 10, theBottomRightY - 10} 50 | end tell 51 | 52 | update without registering applications 53 | end tell 54 | 55 | delay 1 56 | 57 | tell disk (volumeName as string) 58 | tell container window 59 | set statusbar visible to false 60 | set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY} 61 | end tell 62 | 63 | update without registering applications 64 | end tell 65 | 66 | --give the finder some time to write the .DS_Store file 67 | delay 3 68 | 69 | set waitTime to 0 70 | set ejectMe to false 71 | repeat while ejectMe is false 72 | delay 1 73 | set waitTime to waitTime + 1 74 | 75 | if (do shell script "[ -f " & dsStore & " ]; echo $?") = "0" then set ejectMe to true 76 | end repeat 77 | log "waited " & waitTime & " seconds for .DS_STORE to be created." 78 | end tell 79 | end run 80 | -------------------------------------------------------------------------------- /pkg/linux/debian/changelog: -------------------------------------------------------------------------------- 1 | horus (0.2rc1) unstable; urgency=medium 2 | 3 | * Release candidate 1 4 | 5 | -- Jesús Arroyo Torrens Fri, 20 May 2016 00:00:00 +0100 6 | 7 | horus (0.2b4) unstable; urgency=medium 8 | 9 | * Beta 4 10 | 11 | -- Jesús Arroyo Torrens Mon, 25 Apr 2016 00:00:00 +0100 12 | 13 | horus (0.2b3) unstable; urgency=medium 14 | 15 | * Beta 3 16 | 17 | -- Jesús Arroyo Torrens Fri, 18 Mar 2016 00:00:00 +0100f 18 | 19 | horus (0.2b2.1) unstable; urgency=medium 20 | 21 | * Beta 2 22 | 23 | -- Jesús Arroyo Torrens Wed, 17 Feb 2016 00:00:00 +0100 24 | 25 | horus (0.2b1) unstable; urgency=medium 26 | 27 | * Beta 1 28 | 29 | -- Jesús Arroyo Torrens Mon, 18 Jan 2016 00:00:00 +0100 30 | 31 | horus (0.1.2.4-bq1) trusty; urgency=medium 32 | 33 | * Working on mac. 34 | * Version updates. 35 | * Load hex files. 36 | 37 | -- Jesús Arroyo Torrens Mon, 20 Jul 2015 00:00:00 +0100 38 | 39 | horus (0.1.1.1-bq1) trusty; urgency=medium 40 | 41 | * Stable version release. 42 | 43 | -- Jesús Arroyo Torrens Mon, 27 Apr 2015 00:00:00 +0100 44 | 45 | horus (0.1-bq1) trusty; urgency=medium 46 | 47 | * Initial release. 48 | 49 | -- Jesús Arroyo Torrens Thu, 26 Feb 2015 00:00:00 +0100 50 | -------------------------------------------------------------------------------- /pkg/linux/debian/control: -------------------------------------------------------------------------------- 1 | Source: horus 2 | Maintainer: Jesús Arroyo Torrens 3 | Section: misc 4 | Priority: optional 5 | Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 7.4.3) 6 | Standards-Version: 3.9.5 7 | 8 | Package: horus 9 | Architecture: any 10 | Depends: ${misc:Depends}, ${python:Depends}, python, python-serial, python-wxgtk2.8 | python-wxgtk3.0, python-opengl, python-pyglet, python-numpy, python-scipy, python-matplotlib, python-opencv, avrdude, libftdi1, v4l-utils 11 | Description: Horus is a full software solution for 3D scanning 12 | -------------------------------------------------------------------------------- /pkg/linux/debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | case "$1" in 6 | configure) 7 | rm -rf ~/.horus/ 8 | ;; 9 | 10 | abort-upgrade|abort-remove|abort-deconfigure) 11 | exit 0 12 | ;; 13 | 14 | *) 15 | echo "postinst called with unknown argument \`\$1'" >&2 16 | exit 1 17 | ;; 18 | esac 19 | 20 | exit 0 -------------------------------------------------------------------------------- /pkg/linux/debian/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | case "$1" in 6 | remove|purge|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) 7 | rm -rf ~/.horus/ 8 | ;; 9 | *) 10 | echo "postrm called with unknown argument \`\$1'" >&2 11 | exit 1 12 | ;; 13 | esac 14 | 15 | exit 0 -------------------------------------------------------------------------------- /pkg/linux/horus.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=Horus 4 | Comment=3D Scanning Application 5 | Icon=/usr/share/horus/horus.ico 6 | Exec=horus 7 | StartupNotify=true 8 | Terminal=false 9 | Categories=GNOME;GTK;Utility;Graphics; 10 | -------------------------------------------------------------------------------- /pkg/win32/drivers/CDM v2.10.00 WHQL Certified.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/pkg/win32/drivers/CDM v2.10.00 WHQL Certified.exe -------------------------------------------------------------------------------- /pkg/win32/drivers/arduino.cat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/pkg/win32/drivers/arduino.cat -------------------------------------------------------------------------------- /pkg/win32/drivers/dpinst32.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/pkg/win32/drivers/dpinst32.exe -------------------------------------------------------------------------------- /pkg/win32/drivers/dpinst64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/pkg/win32/drivers/dpinst64.exe -------------------------------------------------------------------------------- /pkg/win32/horus.bat: -------------------------------------------------------------------------------- 1 | @python\python.exe horus.py %* 2 | -------------------------------------------------------------------------------- /pkg/win32/nsisPlugins/AccessControl.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/pkg/win32/nsisPlugins/AccessControl.dll -------------------------------------------------------------------------------- /res/firmware/eeprom_clear.hex: -------------------------------------------------------------------------------- 1 | :100000000C9457000C9469000C9469000C946900DE 2 | :100010000C9469000C9469000C9469000C946900BC 3 | :100020000C9469000C9469000C9469000C946900AC 4 | :100030000C9469000C9469000C9469000C9469009C 5 | :100040000C94F1000C9469000C9469000C94690004 6 | :100050000C9469000C9469000C9469000C9469007C 7 | :100060000C9469000C946900000000070002010074 8 | :100070000003040600000000000000000102040864 9 | :100080001020408001020408102001020408102002 10 | :10009000040404040404040402020202020203032E 11 | :1000A0000303030300000000250028002B00112497 12 | :1000B0001FBECFEFD8E0DEBFCDBF11E0A0E0B1E0C2 13 | :1000C00001C01D92AA30B107E1F70E94E4000C9430 14 | :1000D00084010C940000CF93DF93C0E0D0E040E0B7 15 | :1000E000BE0180E091E00E9481002196C11582E06E 16 | :1000F000D807A9F761E08DE0DF91CF910C94AE00B5 17 | :100100000895CB01642F0C947601833081F028F49C 18 | :10011000813099F08230A1F008958630A9F08730BF 19 | :10012000B9F08430D1F4809180008F7D03C080913C 20 | :1001300080008F7780938000089584B58F7702C008 21 | :1001400084B58F7D84BD08958091B0008F7703C002 22 | :100150008091B0008F7D8093B00008950F931F931E 23 | :10016000CF93DF931F92CDB7DEB7282F30E0F90190 24 | :10017000E859FF4F8491F901E458FF4F1491F901B8 25 | :10018000E057FF4F04910023C9F0882321F06983D1 26 | :100190000E9485006981E02FF0E0EE0FFF1FEC5513 27 | :1001A000FF4FA591B4919FB7F8948C91611103C052 28 | :1001B0001095812301C0812B8C939FBF0F90DF91FD 29 | :1001C000CF911F910F9108950E943B010E946B00F7 30 | :1001D000C0E0D0E00E9480002097E1F30E94000080 31 | :1001E000F9CF1F920F920FB60F9211242F933F93C6 32 | :1001F0008F939F93AF93BF938091020190910301DE 33 | :10020000A0910401B09105013091010123E0230F79 34 | :100210002D3720F40196A11DB11D05C026E8230F3E 35 | :100220000296A11DB11D20930101809302019093BC 36 | :100230000301A0930401B093050180910601909100 37 | :100240000701A0910801B09109010196A11DB11DFE 38 | :100250008093060190930701A0930801B0930901D0 39 | :10026000BF91AF919F918F913F912F910F900FBEB2 40 | :100270000F901F901895789484B5826084BD84B5E2 41 | :10028000816084BD85B5826085BD85B5816085BD91 42 | :10029000EEE6F0E0808181608083E1E8F0E01082AA 43 | :1002A000808182608083808181608083E0E8F0E0EB 44 | :1002B000808181608083E1EBF0E0808184608083D5 45 | :1002C000E0EBF0E0808181608083EAE7F0E080810C 46 | :1002D000846080838081826080838081816080836C 47 | :1002E0008081806880831092C1000895262FF9993B 48 | :1002F000FECF1FBA92BD81BD20BD0FB6F894FA9A09 49 | :0C030000F99A0FBE01960895F894FFCF03 50 | :00000001FF 51 | -------------------------------------------------------------------------------- /res/horus.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/horus.icns -------------------------------------------------------------------------------- /res/horus.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/horus.ico -------------------------------------------------------------------------------- /res/images/checkerboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/checkerboard.png -------------------------------------------------------------------------------- /res/images/connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/connect.png -------------------------------------------------------------------------------- /res/images/disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/disconnect.png -------------------------------------------------------------------------------- /res/images/horus.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/horus.ico -------------------------------------------------------------------------------- /res/images/installer_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/installer_background.png -------------------------------------------------------------------------------- /res/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/logo.png -------------------------------------------------------------------------------- /res/images/nusb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/nusb.png -------------------------------------------------------------------------------- /res/images/pattern-distance.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/pattern-distance.jpg -------------------------------------------------------------------------------- /res/images/pattern-position.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/pattern-position.png -------------------------------------------------------------------------------- /res/images/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/pause.png -------------------------------------------------------------------------------- /res/images/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/play.png -------------------------------------------------------------------------------- /res/images/restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/restore.png -------------------------------------------------------------------------------- /res/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/splash.png -------------------------------------------------------------------------------- /res/images/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/stop.png -------------------------------------------------------------------------------- /res/images/undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/undo.png -------------------------------------------------------------------------------- /res/images/views.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/views.png -------------------------------------------------------------------------------- /res/images/void.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/images/void.png -------------------------------------------------------------------------------- /res/locale/de/LC_MESSAGES/horus.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/locale/de/LC_MESSAGES/horus.mo -------------------------------------------------------------------------------- /res/locale/en/LC_MESSAGES/horus.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/locale/en/LC_MESSAGES/horus.mo -------------------------------------------------------------------------------- /res/locale/es/LC_MESSAGES/horus.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/locale/es/LC_MESSAGES/horus.mo -------------------------------------------------------------------------------- /res/locale/fr/LC_MESSAGES/horus.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/locale/fr/LC_MESSAGES/horus.mo -------------------------------------------------------------------------------- /res/locale/it/LC_MESSAGES/horus.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/locale/it/LC_MESSAGES/horus.mo -------------------------------------------------------------------------------- /res/locale/pt/LC_MESSAGES/horus.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/locale/pt/LC_MESSAGES/horus.mo -------------------------------------------------------------------------------- /res/locale/update-translations.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | xgettext --language=Python \ 3 | --copyright-holder='bq' \ 4 | --package-name=Horus \ 5 | --package-version=0.2 \ 6 | --msgid-bugs-address=jesus.arroyo@bq.com \ 7 | --keyword=_ \ 8 | --output=horus.pot \ 9 | --from-code=UTF-8 \ 10 | `find ../../src/horus -name "*.py"` 11 | 12 | for LANG in `ls .`; do 13 | if [ -e $LANG/LC_MESSAGES/horus.po ]; then 14 | msgmerge -U $LANG/LC_MESSAGES/horus.po horus.pot 15 | msgfmt $LANG/LC_MESSAGES/horus.po --output-file $LANG/LC_MESSAGES/horus.mo 16 | fi 17 | done 18 | -------------------------------------------------------------------------------- /res/logger/logger.conf: -------------------------------------------------------------------------------- 1 | [loggers] 2 | keys=root,horus 3 | 4 | [handlers] 5 | keys=fileHandler,consoleHandler 6 | 7 | [formatters] 8 | keys=horusFormatter 9 | 10 | [logger_root] 11 | level=CRITICAL 12 | handlers=consoleHandler 13 | 14 | [logger_horus] 15 | level=DEBUG 16 | handlers=fileHandler 17 | qualname=horus 18 | 19 | [handler_fileHandler] 20 | class=FileHandler 21 | formatter=horusFormatter 22 | args=("horus.log",) 23 | 24 | [handler_consoleHandler] 25 | class=StreamHandler 26 | level=DEBUG 27 | formatter=horusFormatter 28 | args=(sys.stdout,) 29 | 30 | [formatter_horusFormatter] 31 | #format=%(asctime)s - %(levelname)s - %(message)s 32 | format=%(asctime)s.%(msecs)03d - %(message)s 33 | datefmt=%Y-%m-%d %H:%M:%S 34 | -------------------------------------------------------------------------------- /res/meshes/ciclop_platform.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/meshes/ciclop_platform.stl -------------------------------------------------------------------------------- /res/tools/darwin/avrdude: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DYLD_LIBRARY_PATH="`dirname "$0"`/lib/" 4 | exec -a "$0" "`dirname "$0"`/avrdude_bin" "$@" 5 | -------------------------------------------------------------------------------- /res/tools/darwin/avrdude_bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/tools/darwin/avrdude_bin -------------------------------------------------------------------------------- /res/tools/darwin/lib/libusb-0.1.4.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/tools/darwin/lib/libusb-0.1.4.dylib -------------------------------------------------------------------------------- /res/tools/darwin/lib/libusb-1.0.0.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/tools/darwin/lib/libusb-1.0.0.dylib -------------------------------------------------------------------------------- /res/tools/darwin/uvcc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/tools/darwin/uvcc.so -------------------------------------------------------------------------------- /res/tools/windows/avrdude.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/tools/windows/avrdude.exe -------------------------------------------------------------------------------- /res/tools/windows/libusb0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/res/tools/windows/libusb0.dll -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | import os 4 | import sys 5 | from setuptools import setup, find_packages 6 | 7 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) 8 | from horus import __version__ 9 | 10 | 11 | def package_data_dirs(source, basedir='/usr/share/horus'): 12 | dirs = [] 13 | 14 | for dirname, _, files in os.walk(source): 15 | dirname = os.path.relpath(dirname, source) 16 | for f in files: 17 | dirs.append((os.path.join(basedir, dirname), 18 | [os.path.join(source, dirname, f)])) 19 | 20 | dirs.append(('/usr/share/applications', ['pkg/linux/horus.desktop'])) 21 | 22 | return dirs 23 | 24 | 25 | setup( 26 | name='Horus', 27 | version=__version__, 28 | author='Jesús Arroyo Torrens', 29 | author_email='jesus.arroyo@bq.com', 30 | description='Horus is a full software solution for 3D scanning', 31 | 32 | license='GPLv2', 33 | keywords="horus ciclop scanning 3d", 34 | url='https://github.com/bqlabs/horus', 35 | 36 | packages=find_packages('src'), 37 | package_dir={'': 'src'}, 38 | 39 | scripts=['horus'], 40 | data_files=package_data_dirs('res'), 41 | ) 42 | -------------------------------------------------------------------------------- /setup_mac.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | import os 4 | import sys 5 | from setuptools import setup 6 | 7 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) 8 | from horus import __version__ 9 | 10 | APP = ['horus'] 11 | DATA_FILES = ['res'] 12 | 13 | PLIST = { 14 | u'CFBundleName': u'Horus', 15 | u'CFBundleShortVersionString': __version__, 16 | u'CFBundleVersion': __version__, 17 | u'CFBundleIdentifier': u'com.bq.Horus-' + __version__, 18 | u'LSMinimumSystemVersion': u'10.8', 19 | u'LSApplicationCategoryType': u'public.app-category.graphics-design' 20 | } 21 | 22 | OPTIONS = { 23 | 'argv_emulation': False, 24 | 'iconfile': 'res/horus.icns', 25 | 'plist': PLIST 26 | } 27 | 28 | setup(name='Horus', 29 | version=__version__, 30 | author='Jesús Arroyo Torrens', 31 | author_email='jesus.arroyo@bq.com', 32 | description='Horus is a full software solution for 3D scanning', 33 | license='GPLv2', 34 | keywords="horus ciclop scanning 3d", 35 | url='https://www.diwo.bq.com/tag/ciclop', 36 | app=APP, 37 | data_files=DATA_FILES, 38 | options={'py2app': OPTIONS}, 39 | setup_requires=['py2app']) 40 | -------------------------------------------------------------------------------- /src/horus/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | __version__ = '0.2rc1' 9 | __datetime__ = '' 10 | __commit__ = '' 11 | 12 | 13 | def Singleton(class_): 14 | class class_w(class_): 15 | _instance = None 16 | 17 | def __new__(class_, *args, **kwargs): 18 | if class_w._instance is None: 19 | class_w._instance = super(class_w, class_).__new__(class_, *args, **kwargs) 20 | class_w._instance.__initialized = False 21 | return class_w._instance 22 | 23 | def __init__(class_, *args, **kwargs): 24 | if class_w._instance.__initialized: 25 | return 26 | super(class_w, class_).__init__(*args, **kwargs) 27 | class_w._instance.__initialized = True 28 | 29 | class_w.__name__ = class_.__name__ 30 | return class_w 31 | -------------------------------------------------------------------------------- /src/horus/engine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/engine/__init__.py -------------------------------------------------------------------------------- /src/horus/engine/algorithms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/engine/algorithms/__init__.py -------------------------------------------------------------------------------- /src/horus/engine/algorithms/image_detection.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import cv2 9 | import numpy as np 10 | 11 | from horus import Singleton 12 | from horus.engine.calibration.pattern import Pattern 13 | from horus.engine.calibration.calibration_data import CalibrationData 14 | 15 | 16 | @Singleton 17 | class ImageDetection(object): 18 | 19 | def __init__(self): 20 | self.pattern = Pattern() 21 | self.calibration_data = CalibrationData() 22 | 23 | self._criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) 24 | 25 | def detect_pattern(self, image): 26 | corners = self._detect_chessboard(image) 27 | if corners is not None: 28 | image = self.draw_pattern(image, corners) 29 | return image 30 | 31 | def draw_pattern(self, image, corners): 32 | image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) 33 | cv2.drawChessboardCorners( 34 | image, (self.pattern.columns, self.pattern.rows), corners, True) 35 | image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) 36 | return image 37 | 38 | def detect_corners(self, image): 39 | corners = self._detect_chessboard(image) 40 | return corners 41 | 42 | def detect_pose(self, image): 43 | corners = self._detect_chessboard(image) 44 | if corners is not None: 45 | ret, rvecs, tvecs = cv2.solvePnP( 46 | self.pattern.object_points, corners, 47 | self.calibration_data.camera_matrix, self.calibration_data.distortion_vector) 48 | if ret: 49 | return (cv2.Rodrigues(rvecs)[0], tvecs, corners) 50 | 51 | def detect_pattern_plane(self, pose): 52 | if pose is not None: 53 | R = pose[0] 54 | t = pose[1].T[0] 55 | c = pose[2] 56 | n = R.T[2] 57 | d = np.dot(n, t) 58 | return (d, n, c) 59 | 60 | def pattern_mask(self, image, corners): 61 | if image is not None: 62 | h, w, d = image.shape 63 | if corners is not None: 64 | corners = corners.astype(np.int) 65 | p1 = corners[0][0] 66 | p2 = corners[self.pattern.columns - 1][0] 67 | p3 = corners[self.pattern.columns * (self.pattern.rows - 1)][0] 68 | p4 = corners[self.pattern.columns * self.pattern.rows - 1][0] 69 | mask = np.zeros((h, w), np.uint8) 70 | points = np.array([p1, p2, p4, p3]) 71 | cv2.fillConvexPoly(mask, points, 255) 72 | image = cv2.bitwise_and(image, image, mask=mask) 73 | return image 74 | 75 | def _detect_chessboard(self, image): 76 | if image is not None: 77 | if self.pattern.rows > 2 and self.pattern.columns > 2: 78 | gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) 79 | ret, corners = cv2.findChessboardCorners( 80 | gray, (self.pattern.columns, self.pattern.rows), flags=cv2.CALIB_CB_FAST_CHECK) 81 | if ret: 82 | cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), self._criteria) 83 | return corners 84 | -------------------------------------------------------------------------------- /src/horus/engine/algorithms/point_cloud_generation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | 9 | import numpy as np 10 | 11 | from horus import Singleton 12 | from horus.engine.calibration.calibration_data import CalibrationData 13 | 14 | 15 | @Singleton 16 | class PointCloudGeneration(object): 17 | 18 | def __init__(self): 19 | self.calibration_data = CalibrationData() 20 | 21 | def compute_point_cloud(self, theta, points_2d, index): 22 | # Load calibration values 23 | R = np.matrix(self.calibration_data.platform_rotation) 24 | t = np.matrix(self.calibration_data.platform_translation).T 25 | # Compute platform transformation 26 | Xwo = self.compute_platform_point_cloud(points_2d, R, t, index) 27 | # Rotate to world coordinates 28 | c, s = np.cos(-theta), np.sin(-theta) 29 | Rz = np.matrix([[c, -s, 0], [s, c, 0], [0, 0, 1]]) 30 | Xw = Rz * Xwo 31 | # Return point cloud 32 | if Xw.size > 0: 33 | return np.array(Xw) 34 | else: 35 | return None 36 | 37 | def compute_platform_point_cloud(self, points_2d, R, t, index): 38 | # Load calibration values 39 | n = self.calibration_data.laser_planes[index].normal 40 | d = self.calibration_data.laser_planes[index].distance 41 | # Camera system 42 | Xc = self.compute_camera_point_cloud(points_2d, d, n) 43 | # Compute platform transformation 44 | return R.T * Xc - R.T * t 45 | 46 | def compute_camera_point_cloud(self, points_2d, d, n): 47 | # Load calibration values 48 | fx = self.calibration_data.camera_matrix[0][0] 49 | fy = self.calibration_data.camera_matrix[1][1] 50 | cx = self.calibration_data.camera_matrix[0][2] 51 | cy = self.calibration_data.camera_matrix[1][2] 52 | # Compute projection point 53 | u, v = points_2d 54 | x = np.concatenate(((u - cx) / fx, (v - cy) / fy, np.ones(len(u)))).reshape(3, len(u)) 55 | # Compute laser intersection 56 | return d / np.dot(n, x) * x 57 | -------------------------------------------------------------------------------- /src/horus/engine/calibration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/engine/calibration/__init__.py -------------------------------------------------------------------------------- /src/horus/engine/calibration/calibration.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import platform 9 | import threading 10 | 11 | from horus.engine.driver.driver import Driver 12 | 13 | from horus.engine.calibration.pattern import Pattern 14 | from horus.engine.calibration.calibration_data import CalibrationData 15 | 16 | from horus.engine.algorithms.image_capture import ImageCapture 17 | from horus.engine.algorithms.image_detection import ImageDetection 18 | from horus.engine.algorithms.laser_segmentation import LaserSegmentation 19 | from horus.engine.algorithms.point_cloud_generation import PointCloudGeneration 20 | 21 | system = platform.system() 22 | 23 | """ 24 | Calibrations: 25 | 26 | - Autocheck Algorithm 27 | - Camera Intrinsics Calibration 28 | - Laser Triangulation Calibration 29 | - Platform Extrinsics Calibration 30 | """ 31 | 32 | 33 | class CalibrationCancel(Exception): 34 | 35 | def __init__(self): 36 | Exception.__init__(self, "CalibrationCancel") 37 | 38 | 39 | class Calibration(object): 40 | 41 | """Generic class for threading calibration""" 42 | 43 | def __init__(self): 44 | self.driver = Driver() 45 | self.pattern = Pattern() 46 | self.calibration_data = CalibrationData() 47 | self.image_capture = ImageCapture() 48 | self.image_detection = ImageDetection() 49 | self.laser_segmentation = LaserSegmentation() 50 | self.point_cloud_generation = PointCloudGeneration() 51 | 52 | # TODO: Callbacks to Observer pattern 53 | self._before_callback = None 54 | self._progress_callback = None 55 | self._after_callback = None 56 | self._is_calibrating = False 57 | 58 | def set_callbacks(self, before, progress, after): 59 | self._before_callback = before 60 | self._progress_callback = progress 61 | self._after_callback = after 62 | 63 | def start(self): 64 | if not self._is_calibrating: 65 | if self._before_callback is not None: 66 | self._before_callback() 67 | 68 | if self._progress_callback is not None: 69 | self._progress_callback(0) 70 | 71 | self._is_calibrating = True 72 | threading.Thread(target=self._start).start() 73 | 74 | def _start(self): 75 | pass 76 | 77 | def cancel(self): 78 | self._is_calibrating = False 79 | -------------------------------------------------------------------------------- /src/horus/engine/calibration/calibration_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | 9 | import md5 10 | import cv2 11 | import numpy as np 12 | 13 | from horus import Singleton 14 | 15 | 16 | class LaserPlane(object): 17 | 18 | def __init__(self): 19 | self.normal = None 20 | self.distance = None 21 | 22 | 23 | @Singleton 24 | class CalibrationData(object): 25 | 26 | def __init__(self): 27 | self.width = 0 28 | self.height = 0 29 | 30 | self._camera_matrix = None 31 | self._distortion_vector = None 32 | self._roi = None 33 | self._dist_camera_matrix = None 34 | self._weight_matrix = None 35 | 36 | self._md5_hash = None 37 | 38 | self.laser_planes = [LaserPlane(), LaserPlane()] 39 | self.platform_rotation = None 40 | self.platform_translation = None 41 | 42 | def set_resolution(self, width, height): 43 | if self.width != width or self.height != height: 44 | self.width = width 45 | self.height = height 46 | self._compute_weight_matrix() 47 | 48 | @property 49 | def camera_matrix(self): 50 | return self._camera_matrix 51 | 52 | @camera_matrix.setter 53 | def camera_matrix(self, value): 54 | self._camera_matrix = value 55 | self._compute_dist_camera_matrix() 56 | 57 | @property 58 | def distortion_vector(self): 59 | return self._distortion_vector 60 | 61 | @distortion_vector.setter 62 | def distortion_vector(self, value): 63 | self._distortion_vector = value 64 | self._compute_dist_camera_matrix() 65 | 66 | @property 67 | def roi(self): 68 | return self._roi 69 | 70 | @property 71 | def dist_camera_matrix(self): 72 | return self._dist_camera_matrix 73 | 74 | @property 75 | def weight_matrix(self): 76 | return self._weight_matrix 77 | 78 | def _compute_dist_camera_matrix(self): 79 | if self._camera_matrix is not None and self._distortion_vector is not None: 80 | self._dist_camera_matrix, self._roi = cv2.getOptimalNewCameraMatrix( 81 | self._camera_matrix, self._distortion_vector, 82 | (int(self.width), int(self.height)), alpha=1) 83 | self._md5_hash = md5.new() 84 | self._md5_hash.update(self._camera_matrix) 85 | self._md5_hash.update(self._distortion_vector) 86 | self._md5_hash = self._md5_hash.hexdigest() 87 | 88 | def _compute_weight_matrix(self): 89 | self._weight_matrix = np.array((np.matrix(np.linspace(0, self.width - 1, self.width)).T * 90 | np.matrix(np.ones(self.height))).T) 91 | 92 | def check_calibration(self): 93 | if self.camera_matrix is None or self.distortion_vector is None: 94 | return False 95 | for plane in self.laser_planes: 96 | if plane.distance is None or plane.normal is None: 97 | return False 98 | if plane.distance == 0.0 or self._is_zero(plane.normal): 99 | return False 100 | if self.platform_rotation is None or self.platform_translation is None: 101 | return False 102 | if self._is_zero(self.platform_rotation) or self._is_zero(self.platform_translation): 103 | return False 104 | return True 105 | 106 | def _is_zero(self, array): 107 | return np.all(array == 0.0) 108 | 109 | def md5_hash(self): 110 | return self._md5_hash 111 | -------------------------------------------------------------------------------- /src/horus/engine/calibration/camera_intrinsics.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import cv2 9 | import numpy as np 10 | 11 | from horus import Singleton 12 | from horus.engine.calibration.calibration import Calibration 13 | 14 | 15 | class CameraIntrinsicsError(Exception): 16 | 17 | def __init__(self): 18 | Exception.__init__(self, "CameraIntrinsicsError") 19 | 20 | 21 | @Singleton 22 | class CameraIntrinsics(Calibration): 23 | 24 | """Camera calibration algorithms, based on [Zhang2000] and [BouguetMCT]: 25 | 26 | - Camera matrix 27 | - Distortion vector 28 | """ 29 | 30 | def __init__(self): 31 | Calibration.__init__(self) 32 | self.shape = None 33 | self.camera_matrix = None 34 | self.distortion_vector = None 35 | self.image_points = [] 36 | self.object_points = [] 37 | 38 | def _start(self): 39 | ret, error, cmat, dvec, rvecs, tvecs = self.calibrate() 40 | 41 | if ret: 42 | self.camera_matrix = cmat 43 | self.distortion_vector = dvec 44 | response = (True, (error, cmat, dvec, rvecs, tvecs)) 45 | else: 46 | response = (False, CameraIntrinsicsError) 47 | 48 | self._is_calibrating = False 49 | 50 | if self._after_callback is not None: 51 | self._after_callback(response) 52 | 53 | def capture(self): 54 | if self.driver.is_connected: 55 | image = self.image_capture.capture_pattern() 56 | self.shape = image[:, :, 0].shape 57 | corners = self.image_detection.detect_corners(image) 58 | if corners is not None: 59 | if len(self.object_points) < 15: 60 | self.image_points.append(corners) 61 | self.object_points.append(self.pattern.object_points) 62 | return image 63 | 64 | def calibrate(self): 65 | error = 0 66 | ret, cmat, dvec, rvecs, tvecs = cv2.calibrateCamera( 67 | self.object_points, self.image_points, self.shape) 68 | 69 | if ret: 70 | # Compute calibration error 71 | for i in xrange(len(self.object_points)): 72 | imgpoints2, _ = cv2.projectPoints( 73 | self.object_points[i], rvecs[i], tvecs[i], cmat, dvec) 74 | error += cv2.norm(self.image_points[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2) 75 | error /= len(self.object_points) 76 | 77 | return ret, error, np.round(cmat, 3), np.round(dvec.ravel(), 3), rvecs, tvecs 78 | 79 | def reset(self): 80 | self.image_points = [] 81 | self.object_points = [] 82 | 83 | def accept(self): 84 | self.calibration_data.camera_matrix = self.camera_matrix 85 | self.calibration_data.distortion_vector = self.distortion_vector 86 | 87 | def cancel(self): 88 | pass 89 | -------------------------------------------------------------------------------- /src/horus/engine/calibration/moving_calibration.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import time 9 | from horus.engine.calibration.calibration import Calibration 10 | 11 | 12 | class MovingCalibration(Calibration): 13 | 14 | """Moving calibration: 15 | 16 | - Move motor sequence 17 | - Call _capture at each position 18 | - Call _calibrate at the end 19 | """ 20 | 21 | def __init__(self): 22 | Calibration.__init__(self) 23 | self.step = 3 24 | 25 | def _initialize(self): 26 | raise NotImplementedError 27 | 28 | def _capture(self, angle): 29 | raise NotImplementedError 30 | 31 | def _calibrate(self): 32 | raise NotImplementedError 33 | 34 | def _start(self): 35 | if self.driver.is_connected: 36 | angle = 0.0 37 | 38 | self._initialize() 39 | 40 | # Setup scanner 41 | self.driver.board.lasers_off() 42 | self.driver.board.motor_enable() 43 | self.driver.board.motor_reset_origin() 44 | self.driver.board.motor_speed(200) 45 | self.driver.board.motor_acceleration(200) 46 | 47 | # Move to starting position 48 | self.driver.board.motor_move(-90) 49 | 50 | if self._progress_callback is not None: 51 | self._progress_callback(0) 52 | 53 | while self._is_calibrating and abs(angle) < 180: 54 | 55 | if self._progress_callback is not None: 56 | self._progress_callback(100 * abs(angle) / 180.) 57 | 58 | self._capture(angle) 59 | 60 | angle += self.step 61 | self.driver.board.motor_move(self.step) 62 | time.sleep(0.5) 63 | 64 | # Move to origin 65 | self.driver.board.motor_move(90 - angle) 66 | 67 | self.driver.board.lasers_off() 68 | self.driver.board.motor_disable() 69 | 70 | # Compute calibration 71 | response = self._calibrate() 72 | 73 | if self._after_callback is not None: 74 | self._after_callback(response) 75 | -------------------------------------------------------------------------------- /src/horus/engine/calibration/pattern.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import numpy as np 9 | 10 | from horus import Singleton 11 | 12 | 13 | @Singleton 14 | class Pattern(object): 15 | 16 | def __init__(self): 17 | self._rows = 0 18 | self._columns = 0 19 | self._square_width = 0 20 | self.origin_distance = 0 21 | 22 | @property 23 | def rows(self): 24 | return self._rows 25 | 26 | @rows.setter 27 | def rows(self, value): 28 | value = self.to_int(value) 29 | if self._rows != value: 30 | self._rows = value 31 | self._generate_object_points() 32 | 33 | def set_rows(self, value): 34 | self.rows = value 35 | 36 | @property 37 | def columns(self): 38 | return self._columns 39 | 40 | @columns.setter 41 | def columns(self, value): 42 | value = self.to_int(value) 43 | if self._columns != value: 44 | self._columns = value 45 | self._generate_object_points() 46 | 47 | def set_columns(self, value): 48 | self.columns = value 49 | 50 | @property 51 | def square_width(self): 52 | return self._square_width 53 | 54 | @square_width.setter 55 | def square_width(self, value): 56 | value = self.to_float(value) 57 | if self._square_width != value: 58 | self._square_width = value 59 | self._generate_object_points() 60 | 61 | def set_square_width(self, value): 62 | self.square_width = value 63 | 64 | def _generate_object_points(self): 65 | objp = np.zeros((self.rows * self.columns, 3), np.float32) 66 | objp[:, :2] = np.mgrid[0:self.columns, 0:self.rows].T.reshape(-1, 2) 67 | objp = np.multiply(objp, self.square_width) 68 | self.object_points = objp 69 | 70 | def set_origin_distance(self, value): 71 | self.origin_distance = self.to_float(value) 72 | 73 | def to_int(self, value): 74 | try: 75 | value = int(value) 76 | if value > 0: 77 | return value 78 | else: 79 | return 0 80 | except: 81 | return 0 82 | 83 | def to_float(self, value): 84 | try: 85 | value = float(value) 86 | if value > 0.0: 87 | return value 88 | else: 89 | return 0.0 90 | except: 91 | return 0.0 92 | -------------------------------------------------------------------------------- /src/horus/engine/driver/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/engine/driver/__init__.py -------------------------------------------------------------------------------- /src/horus/engine/driver/driver.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import threading 9 | 10 | from horus import Singleton 11 | from horus.engine.driver.board import Board 12 | from horus.engine.driver.camera import Camera 13 | 14 | 15 | @Singleton 16 | class Driver(object): 17 | 18 | """Driver class. For managing scanner hw""" 19 | 20 | def __init__(self): 21 | self.board = Board(self) 22 | self.camera = Camera(self) 23 | self.is_connected = False 24 | self.unplugged = False 25 | 26 | # TODO: Callbacks to Observer pattern 27 | self._before_callback = None 28 | self._after_callback = None 29 | 30 | def connect(self): 31 | self.__init__() 32 | if self._before_callback is not None: 33 | self._before_callback() 34 | threading.Thread(target=self._connect).start() 35 | 36 | def _connect(self): 37 | exception = None 38 | self.is_connected = False 39 | try: 40 | self.camera.connect() 41 | self.board.connect() 42 | except Exception as e: 43 | exception = e 44 | else: 45 | self.is_connected = True 46 | finally: 47 | if exception is None: 48 | self.unplugged = False 49 | response = (True, self.is_connected) 50 | else: 51 | response = (False, exception) 52 | self.disconnect() 53 | if self._after_callback is not None: 54 | self._after_callback(response) 55 | 56 | def disconnect(self): 57 | self.is_connected = False 58 | self.camera.disconnect() 59 | self.board.disconnect() 60 | 61 | def set_callbacks(self, before, after): 62 | self._before_callback = before 63 | self._after_callback = after 64 | -------------------------------------------------------------------------------- /src/horus/engine/driver/uvc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/engine/driver/uvc/__init__.py -------------------------------------------------------------------------------- /src/horus/engine/driver/uvc/mac/cf_string.py: -------------------------------------------------------------------------------- 1 | ''' 2 | (*)~---------------------------------------------------------------------------------- 3 | Pupil - eye tracking platform 4 | Copyright (C) 2012-2014 Pupil Labs 5 | 6 | Distributed under the terms of the CC BY-NC-SA License. 7 | License details are in the file license.txt, distributed as part of this software. 8 | ----------------------------------------------------------------------------------~(*) 9 | ''' 10 | 11 | # Python string to/from CFString conversion helper functions: 12 | 13 | from ctypes import * 14 | from ctypes import util 15 | 16 | cf = cdll.LoadLibrary(util.find_library('CoreFoundation')) 17 | 18 | # Setup return types for functions that return pointers. 19 | # (Otherwise ctypes returns 32-bit int which breaks on 64-bit systems.) 20 | # Note that you must also wrap the return value with c_void_p before 21 | # you use it as an argument to another function, otherwise ctypes will 22 | # automatically convert it back to a 32-bit int again. 23 | cf.CFDictionaryCreateMutable.restype = c_void_p 24 | cf.CFStringCreateWithCString.restype = c_void_p 25 | cf.CFAttributedStringCreate.restype = c_void_p 26 | cf.CFDataCreate.restype = c_void_p 27 | cf.CFNumberCreate.restype = c_void_p 28 | 29 | # Core Foundation constants 30 | kCFStringEncodingUTF8 = 0x08000100 31 | kCFStringEncodingMacRoman = 0 32 | kCFStringEncodingWindowsLatin1 = 0x0500 33 | kCFStringEncodingISOLatin1 = 0x0201 34 | kCFStringEncodingNextStepLatin = 0x0B01 35 | kCFStringEncodingASCII = 0x0600 36 | kCFStringEncodingUnicode = 0x0100 37 | kCFStringEncodingUTF8 = 0x08000100 38 | kCFStringEncodingNonLossyASCII = 0x0BFF 39 | kCFStringEncodingUTF16 = 0x0100 40 | kCFStringEncodingUTF16BE = 0x10000100 41 | kCFStringEncodingUTF16LE = 0x14000100 42 | kCFStringEncodingUTF32 = 0x0c000100 43 | kCFStringEncodingUTF32BE = 0x18000100 44 | kCFStringEncodingUTF32LE = 0x1c000100 45 | kCFNumberSInt32Type = 3 46 | 47 | 48 | def CFSTR(text): 49 | return c_void_p(cf.CFStringCreateWithCString(None, text.encode('utf8'), kCFStringEncodingASCII)) 50 | 51 | def cfstring_to_string(cfstring): 52 | length = cf.CFStringGetLength(cfstring) 53 | size = cf.CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingASCII) 54 | buffer = c_buffer(size + 1) 55 | result = cf.CFStringGetCString(cfstring, buffer, len(buffer), kCFStringEncodingASCII) 56 | if result: 57 | return buffer.value 58 | 59 | def cfstring_to_string_release(cfstring): 60 | length = cf.CFStringGetLength(cfstring) 61 | size = cf.CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingASCII) 62 | buffer = c_buffer(size + 1) 63 | result = cf.CFStringGetCString(cfstring, buffer, len(buffer), kCFStringEncodingASCII) 64 | cf.CFRelease(cfstring) 65 | if result: 66 | return buffer.value 67 | 68 | def release(cfstring): 69 | cf.CFRelease(cfstring) 70 | 71 | 72 | if __name__ == '__main__': 73 | cf_pointer = CFSTR("THIS is a Test") 74 | print cfstring_to_string(cf_pointer) 75 | -------------------------------------------------------------------------------- /src/horus/engine/driver/uvc/mac/libuvcc-wrappers.c: -------------------------------------------------------------------------------- 1 | 2 | /* these are just wrappers.. not much to see here. */ 3 | 4 | #include "libuvcc.h" 5 | 6 | int uvccRWScanningMode(uvccCam *cam, uint8_t request, uint8_t *value) { 7 | return uvccSendRequest(cam, request, UVCC_REQ_SCANNING_MODE, value); 8 | } 9 | int uvccRWAutoExposureMode(uvccCam *cam, uint8_t request, int8_t *value) { 10 | return uvccSendRequest(cam, request, UVCC_REQ_EXPOSURE_AUTOMODE, value); 11 | } 12 | int uvccRWAutoExposurePrio(uvccCam *cam, uint8_t request, uint8_t *value) { 13 | return uvccSendRequest(cam, request, UVCC_REQ_EXPOSURE_AUTOPRIO, value); 14 | } 15 | int uvccRWExposure(uvccCam *cam, uint8_t request, uint32_t *value) { 16 | return uvccSendRequest(cam, request, UVCC_REQ_EXPOSURE_ABS, value); 17 | } 18 | int uvccRWExposureRelative(uvccCam *cam, uint8_t request, int8_t *value) { 19 | return uvccSendRequest(cam, request, UVCC_REQ_EXPOSURE_REL, value); 20 | } 21 | /* TODO: relative exposure */ 22 | int uvccRWAutoFocus(uvccCam *cam, uint8_t request, uint8_t *value) { 23 | return uvccSendRequest(cam, request, UVCC_REQ_FOCUS_AUTO, value); 24 | } 25 | int uvccRWFocus(uvccCam *cam, uint8_t request, uint16_t *value) { 26 | return uvccSendRequest(cam, request, UVCC_REQ_FOCUS_ABS, value); 27 | } 28 | /* TODO: relative focus */ 29 | int uvccRWIris(uvccCam *cam, uint8_t request, int16_t *value) { 30 | return uvccSendRequest(cam, request, UVCC_REQ_IRIS_ABS, value); 31 | } 32 | /* TODO: relative iris */ 33 | int uvccRWBacklightCompensation(uvccCam *cam, uint8_t request, uint16_t *value) { 34 | return uvccSendRequest(cam, request, UVCC_REQ_BACKLIGHT_COMPENSATION_ABS, value); 35 | } 36 | int uvccRWBrightness(uvccCam *cam, uint8_t request, int16_t *value) { 37 | return uvccSendRequest(cam, request, UVCC_REQ_BRIGHTNESS_ABS, value); 38 | } 39 | int uvccRWContrast(uvccCam *cam, uint8_t request, uint16_t *value) { 40 | return uvccSendRequest(cam, request, UVCC_REQ_CONTRAST_ABS, value); 41 | } 42 | int uvccRWGain(uvccCam *cam, uint8_t request, uint16_t *value) { 43 | return uvccSendRequest(cam, request, UVCC_REQ_GAIN_ABS, value); 44 | } 45 | int uvccRWPowerLineFrequency(uvccCam *cam, uint8_t request, uint8_t *value) { 46 | return uvccSendRequest(cam, request, UVCC_REQ_POWER_LINE_FREQ, value); 47 | } 48 | int uvccRWAutoHue(uvccCam *cam, uint8_t request, uint8_t *value) { 49 | return uvccSendRequest(cam, request, UVCC_REQ_HUE_AUTO, value); 50 | } 51 | int uvccRWHue(uvccCam *cam, uint8_t request, int16_t *value) { 52 | return uvccSendRequest(cam, request, UVCC_REQ_HUE_ABS, value); 53 | } 54 | int uvccRWSaturation(uvccCam *cam, uint8_t request, uint16_t *value) { 55 | return uvccSendRequest(cam, request, UVCC_REQ_SATURATION_ABS, value); 56 | } 57 | int uvccRWSharpness(uvccCam *cam, uint8_t request, uint16_t *value) { 58 | return uvccSendRequest(cam, request, UVCC_REQ_SHARPNESS_ABS, value); 59 | } 60 | int uvccRWGamma(uvccCam *cam, uint8_t request, uint16_t *value) { 61 | return uvccSendRequest(cam, request, UVCC_REQ_GAMMA_ABS, value); 62 | } 63 | int uvccRWAutoWhiteBalanceTemp(uvccCam *cam, uint8_t request, uint8_t *value) { 64 | return uvccSendRequest(cam, request, UVCC_REQ_WB_TEMP_AUTO, value); 65 | } 66 | int uvccRWWhiteBalanceTemp(uvccCam *cam, uint8_t request, uint16_t *value) { 67 | return uvccSendRequest(cam, request, UVCC_REQ_WB_TEMP_ABS, value); 68 | } 69 | int uvccRWAutoWhiteBalanceComponent(uvccCam *cam, uint8_t request, uint8_t *value) { 70 | return uvccSendRequest(cam, request, UVCC_REQ_WB_COMPONENT_AUTO, value); 71 | } 72 | int uvccRWWhiteBalanceComponent(uvccCam *cam, uint8_t request, uint16_t *blue, uint16_t *red) { 73 | int32_t value = ((*red) << 16) | (*blue); 74 | int ret = uvccSendRequest(cam, request, UVCC_REQ_WB_COMPONENT_ABS, (void *)&value); 75 | *red = value >> 16; 76 | *blue = *((uint16_t *)&value); 77 | return ret; 78 | } 79 | -------------------------------------------------------------------------------- /src/horus/engine/driver/uvc/mac/makefile: -------------------------------------------------------------------------------- 1 | INC= -I/opt/local/include -I/usr/local/include 2 | 3 | CFLAGS= -shared -fPIC -Wall 4 | 5 | LIBS = -framework IOKit -framework CoreFoundation -L/usr/local/lib 6 | OBJ = libuvcc.c 7 | 8 | make_all: uvcc.so 9 | 10 | uvcc.so: $(OBJ) 11 | -clang $(CFLAGS) -o $@ $^ $(INC) $(LIBS) # $^ = dependencies , $@=Target 12 | #mv *.so c/ 13 | # uvcc.o: $(OBJ) 14 | # -clang $(CFLAGS) -c $^ $(INC) $(LIBS) 15 | 16 | # uvcc.so: uvcc-wrappers.c uvcc.o 17 | # -clang $(CFLAGS) -o $@ $^ $(INC) $(LIBS) # $^ = dependencies , $@=Target 18 | # #mv *.so c/ 19 | 20 | .PHONY: clean 21 | 22 | clean: 23 | rm *.so -------------------------------------------------------------------------------- /src/horus/engine/scan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/engine/scan/__init__.py -------------------------------------------------------------------------------- /src/horus/engine/scan/current_video.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import cv2 9 | import numpy as np 10 | 11 | from horus import Singleton 12 | 13 | 14 | @Singleton 15 | class CurrentVideo(object): 16 | 17 | def __init__(self): 18 | self.mode = 'Texture' 19 | 20 | self.images = {} 21 | self.images['Texture'] = None 22 | self.images['Laser'] = None 23 | self.images['Gray'] = None 24 | self.images['Line'] = None 25 | 26 | def set_texture(self, image): 27 | self.images['Texture'] = image 28 | 29 | def set_laser(self, images): 30 | image = self._combine_images(images) 31 | self.images['Laser'] = image 32 | 33 | def set_gray(self, images): 34 | image = self._combine_images(images) 35 | image = cv2.merge((image, image, image)) 36 | self.images['Gray'] = image 37 | 38 | def set_line(self, points, image): 39 | images = [None, None] 40 | if image is not None: 41 | for i in xrange(2): 42 | if points[i]: 43 | images[i] = self._compute_line_image(points[i], image) 44 | image = self._combine_images(images) 45 | image = cv2.merge((image, image, image)) 46 | self.images['Line'] = image 47 | 48 | def _combine_images(self, images): 49 | if images[0] is not None and images[1] is not None: 50 | return np.maximum(images[0], images[1]) 51 | 52 | if images[0] is not None: 53 | return images[0] 54 | 55 | if images[1] is not None: 56 | return images[1] 57 | 58 | def _compute_line_image(self, points, image): 59 | if points is not None: 60 | u, v = points 61 | image = np.zeros_like(image) 62 | image[v.astype(int), np.around(u).astype(int)] = 255 63 | return image 64 | 65 | def capture(self): 66 | return self.images[self.mode] 67 | -------------------------------------------------------------------------------- /src/horus/engine/scan/scan.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import threading 9 | 10 | from horus.engine.driver.driver import Driver 11 | from horus.engine.algorithms.image_capture import ImageCapture 12 | from horus.engine.algorithms.image_detection import ImageDetection 13 | from horus.engine.algorithms.laser_segmentation import LaserSegmentation 14 | from horus.engine.algorithms.point_cloud_generation import PointCloudGeneration 15 | from horus.engine.algorithms.point_cloud_roi import PointCloudROI 16 | 17 | 18 | class Scan(object): 19 | 20 | """Generic class for threading scanning""" 21 | 22 | def __init__(self): 23 | self.driver = Driver() 24 | self.image_capture = ImageCapture() 25 | self.image_detection = ImageDetection() 26 | self.laser_segmentation = LaserSegmentation() 27 | self.point_cloud_generation = PointCloudGeneration() 28 | self.point_cloud_roi = PointCloudROI() 29 | self.is_scanning = False 30 | 31 | # TODO: Callbacks to Observer pattern 32 | self._before_callback = None 33 | self._progress_callback = None 34 | self._after_callback = None 35 | self._progress = 0 36 | self._range = 0 37 | self._inactive = False 38 | 39 | def set_callbacks(self, before, progress, after): 40 | self._before_callback = before 41 | self._progress_callback = progress 42 | self._after_callback = after 43 | 44 | def start(self): 45 | if not self.is_scanning: 46 | if self._before_callback is not None: 47 | self._before_callback() 48 | 49 | if self._progress_callback is not None: 50 | self._progress_callback(0) 51 | 52 | self._initialize() 53 | 54 | self.is_scanning = True 55 | self._inactive = False 56 | 57 | threading.Thread(target=self._capture).start() 58 | threading.Thread(target=self._process).start() 59 | 60 | def stop(self): 61 | self._inactive = False 62 | self.is_scanning = False 63 | 64 | def pause(self): 65 | self._inactive = True 66 | 67 | def resume(self): 68 | self._inactive = False 69 | 70 | def _initialize(self): 71 | pass 72 | 73 | def _capture(self): 74 | pass 75 | 76 | def _process(self): 77 | pass 78 | -------------------------------------------------------------------------------- /src/horus/engine/scan/scan_capture.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | 9 | class ScanCapture(object): 10 | 11 | def __init__(self): 12 | self.theta = 0 13 | self.texture = None 14 | self.lasers = [None, None] 15 | -------------------------------------------------------------------------------- /src/horus/gui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/gui/__init__.py -------------------------------------------------------------------------------- /src/horus/gui/app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import wx._core 9 | import logging.config 10 | import datetime 11 | 12 | from horus.gui.main import MainWindow 13 | from horus.gui.splash import SplashScreen 14 | from horus.gui.welcome import WelcomeDialog 15 | 16 | from horus.util import profile, resources, version, system as sys 17 | from horus.gui.util.version_window import VersionWindow 18 | 19 | 20 | class HorusApp(wx.App): 21 | 22 | def __init__(self): 23 | super(HorusApp, self).__init__(redirect=False) 24 | 25 | self.splash = None 26 | 27 | if sys.is_darwin(): 28 | self.after_splash_callback() 29 | else: 30 | self.splash = SplashScreen(self.after_splash_callback) 31 | 32 | def after_splash_callback(self): 33 | # Load settings 34 | profile.load_settings() 35 | 36 | # Load logger 37 | self.clear_log_if_required() 38 | logging.config.fileConfig(resources.get_path_for_logger('logger.conf')) 39 | 40 | # Load language 41 | resources.setup_localization(profile.settings['language']) 42 | 43 | # Load version 44 | version.download_lastest_data() 45 | 46 | # Create main window 47 | self.main_window = MainWindow() 48 | 49 | # Check for updates 50 | if profile.settings['check_for_updates'] and version.check_for_updates(): 51 | v = VersionWindow(self.main_window) 52 | if v.download: 53 | if self.splash is not None: 54 | self.splash.Show(False) 55 | self.splash = None 56 | self.main_window.Close(True) 57 | return 58 | 59 | # Hide Splash 60 | if self.splash is not None: 61 | self.splash.Show(False) 62 | self.splash = None 63 | 64 | # Show main window 65 | self.SetTopWindow(self.main_window) 66 | self.main_window.Show() 67 | 68 | if profile.settings['show_welcome']: 69 | # Create welcome window 70 | WelcomeDialog(self.main_window) 71 | 72 | set_full_screen_capable(self.main_window) 73 | 74 | if sys.is_darwin(): 75 | wx.CallAfter(self.stupid_mac_os_workaround) 76 | 77 | def __del__(self): 78 | # Save profile and preferences 79 | profile.settings.save_settings() 80 | 81 | def clear_log_if_required(self): 82 | date_format = '%Y-%m-%d %H:%M:%S' 83 | current_log_date = datetime.datetime.now() 84 | last_log_date = profile.settings['last_clear_log_date'] 85 | try: 86 | last_log_date = datetime.datetime.strptime(last_log_date, date_format) 87 | # Remove log if have elapsed 7 days after last log clear 88 | if (current_log_date - last_log_date).days >= 7: 89 | with open('horus.log', 'w'): 90 | pass 91 | except: 92 | pass 93 | self._update_log_date() 94 | 95 | def _update_log_date(self): 96 | date_format = '%Y-%m-%d %H:%M:%S' 97 | current_log_date = datetime.datetime.now() 98 | profile.settings['last_clear_log_date'] = str(current_log_date.strftime(date_format)) 99 | 100 | def MacReopenApp(self): 101 | self.GetTopWindow().Raise() 102 | 103 | def stupid_mac_os_workaround(self): 104 | """ 105 | On MacOS for some magical reason opening new frames does not work 106 | until you opened a new modal dialog and closed it. If we do this from 107 | software, then, as if by magic, the bug which prevents opening extra frames is gone. 108 | """ 109 | dlg = wx.Dialog(None, size=(1, 1)) 110 | wx.PostEvent(dlg, wx.CommandEvent(wx.EVT_CLOSE.typeId)) 111 | dlg.ShowModal() 112 | dlg.Destroy() 113 | 114 | if sys.is_darwin(): # Mac magic. Dragons live here. This sets full screen options. 115 | try: 116 | import ctypes 117 | import objc 118 | _objc = ctypes.PyDLL(objc._objc.__file__) 119 | 120 | # PyObject *PyObjCObject_New(id objc_object, int flags, int retain) 121 | _objc.PyObjCObject_New.restype = ctypes.py_object 122 | _objc.PyObjCObject_New.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int] 123 | 124 | def set_full_screen_capable(frame): 125 | frameobj = _objc.PyObjCObject_New(frame.GetHandle(), 0, 1) 126 | 127 | NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7 128 | window = frameobj.window() 129 | newBehavior = window.collectionBehavior() | NSWindowCollectionBehaviorFullScreenPrimary 130 | window.setCollectionBehavior_(newBehavior) 131 | except: 132 | def set_full_screen_capable(frame): 133 | pass 134 | 135 | else: 136 | def set_full_screen_capable(frame): 137 | pass 138 | -------------------------------------------------------------------------------- /src/horus/gui/engine.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | from horus.engine.driver.driver import Driver 9 | from horus.engine.scan.ciclop_scan import CiclopScan 10 | from horus.engine.scan.current_video import CurrentVideo 11 | from horus.engine.calibration.pattern import Pattern 12 | from horus.engine.calibration.calibration_data import CalibrationData 13 | from horus.engine.calibration.camera_intrinsics import CameraIntrinsics 14 | from horus.engine.calibration.autocheck import Autocheck 15 | from horus.engine.calibration.laser_triangulation import LaserTriangulation 16 | from horus.engine.calibration.platform_extrinsics import PlatformExtrinsics 17 | from horus.engine.calibration.combo_calibration import ComboCalibration 18 | from horus.engine.algorithms.image_capture import ImageCapture 19 | from horus.engine.algorithms.image_detection import ImageDetection 20 | from horus.engine.algorithms.laser_segmentation import LaserSegmentation 21 | from horus.engine.algorithms.point_cloud_generation import PointCloudGeneration 22 | from horus.engine.algorithms.point_cloud_roi import PointCloudROI 23 | 24 | 25 | # Instances of engine modules 26 | 27 | driver = Driver() 28 | ciclop_scan = CiclopScan() 29 | current_video = CurrentVideo() 30 | pattern = Pattern() 31 | calibration_data = CalibrationData() 32 | camera_intrinsics = CameraIntrinsics() 33 | scanner_autocheck = Autocheck() 34 | laser_triangulation = LaserTriangulation() 35 | platform_extrinsics = PlatformExtrinsics() 36 | combo_calibration = ComboCalibration() 37 | image_capture = ImageCapture() 38 | image_detection = ImageDetection() 39 | laser_segmentation = LaserSegmentation() 40 | point_cloud_generation = PointCloudGeneration() 41 | point_cloud_roi = PointCloudROI() 42 | -------------------------------------------------------------------------------- /src/horus/gui/splash.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.\ 6 | Copyright (C) 2013 David Braam from Cura Project' 7 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 8 | 9 | import time 10 | import wx._core 11 | 12 | from horus.util.resources import get_path_for_image 13 | 14 | 15 | class SplashScreen(wx.SplashScreen): 16 | 17 | def __init__(self, callback): 18 | self.callback = callback 19 | 20 | bitmap = wx.Image(get_path_for_image("splash.png"), wx.BITMAP_TYPE_PNG).ConvertToBitmap() 21 | super(SplashScreen, self).__init__(bitmap, wx.SPLASH_CENTRE_ON_SCREEN, 0, None) 22 | # TODO: fix in wx.SplashScreen class 23 | time.sleep(0.03) 24 | wx.CallAfter(self.do_callback) 25 | 26 | def do_callback(self): 27 | self.callback() 28 | if self: 29 | self.Destroy() 30 | -------------------------------------------------------------------------------- /src/horus/gui/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/gui/util/__init__.py -------------------------------------------------------------------------------- /src/horus/gui/util/image_view.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import wx._core 9 | 10 | from horus.util import resources 11 | 12 | 13 | class ImageView(wx.Panel): 14 | 15 | def __init__(self, parent, resize=True, quality=wx.IMAGE_QUALITY_NORMAL, 16 | size=(-1, -1), black=False, style=wx.NO_BORDER): 17 | wx.Panel.__init__(self, parent, size=size, style=style) 18 | 19 | self.x_offset = 0 20 | self.y_offset = 0 21 | self.quality = quality 22 | 23 | self.default_image = wx.Image(resources.get_path_for_image("nusb.png")) 24 | self.image = self.default_image 25 | self.bitmap = wx.BitmapFromImage(self.default_image) 26 | 27 | self.black = black 28 | self.frame = None 29 | self.current_size = self.GetSizeTuple() 30 | self.SetDoubleBuffered(True) 31 | 32 | self.Bind(wx.EVT_SHOW, self.on_show) 33 | self.Bind(wx.EVT_PAINT, self.on_paint) 34 | if resize: 35 | self.Bind(wx.EVT_SIZE, self.on_resize) 36 | 37 | self.hide = False 38 | 39 | def on_show(self, event): 40 | if event.GetShow(): 41 | self.GetParent().Layout() 42 | self.Layout() 43 | 44 | def on_paint(self, event): 45 | if not self.hide: 46 | if self.black: 47 | dc = wx.BufferedPaintDC(self) 48 | dc.SetBackground(wx.BLACK_BRUSH) 49 | dc.Clear() 50 | else: 51 | dc = wx.PaintDC(self) 52 | dc.DrawBitmap(self.bitmap, self.x_offset, self.y_offset) 53 | 54 | def on_resize(self, size): 55 | new_size = size.GetSize() 56 | if self.current_size != new_size: 57 | self.current_size = new_size 58 | self.refresh_bitmap() 59 | 60 | def set_image(self, image): 61 | if image is not None: 62 | if self.hide: 63 | self.hide = False 64 | self.image = image 65 | self.refresh_bitmap() 66 | 67 | def set_default_image(self): 68 | self.set_image(self.default_image) 69 | 70 | def set_frame(self, frame): 71 | self.frame = frame 72 | if frame is not None: 73 | height, width = frame.shape[:2] 74 | self.set_image(wx.ImageFromBuffer(width, height, frame)) 75 | 76 | def refresh_bitmap(self): 77 | (w, h, self.x_offset, self.y_offset) = self.get_best_size() 78 | if w > 0 and h > 0: 79 | self.bitmap = wx.BitmapFromImage(self.image.Scale(w, h, self.quality)) 80 | self.Refresh() 81 | 82 | def get_best_size(self): 83 | (wwidth, wheight) = self.current_size 84 | (width, height) = self.image.GetSize() 85 | 86 | if height > 0 and wheight > 0: 87 | if float(width) / height > float(wwidth) / wheight: 88 | nwidth = wwidth 89 | nheight = float(wwidth * height) / width 90 | x_offset = 0 91 | y_offset = (wheight - nheight) / 2.0 92 | else: 93 | nwidth = float(wheight * width) / height 94 | nheight = wheight 95 | x_offset = (wwidth - nwidth) / 2.0 96 | y_offset = 0 97 | return (nwidth, nheight, x_offset, y_offset) 98 | else: 99 | return (0, 0, 0, 0) 100 | -------------------------------------------------------------------------------- /src/horus/gui/util/pattern_distance_window.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Irene Sanz Nieto ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import wx._core 9 | from horus.util import profile, resources 10 | 11 | from horus.gui.engine import pattern 12 | 13 | 14 | class PatternDistanceWindow(wx.Dialog): 15 | 16 | def __init__(self, parent): 17 | super(PatternDistanceWindow, self).__init__( 18 | parent, title=_("Pattern distance"), 19 | size=(420, -1), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER) 20 | 21 | self.value = float(profile.settings['pattern_origin_distance']) 22 | 23 | # Elements 24 | self.description = wx.StaticText(self, label=_( 25 | "The pattern distance value must be higher than 0. " 26 | "Please change it in the textbox below.")) 27 | self.description.Wrap(400) 28 | tooltip = _( 29 | "Minimum distance between the origin of the pattern (bottom-left corner) " 30 | "and the pattern's base surface (mm)") 31 | self.image = wx.Image( 32 | resources.get_path_for_image('pattern-distance.jpg'), wx.BITMAP_TYPE_ANY) 33 | self.image = wx.StaticBitmap(self, wx.ID_ANY, wx.BitmapFromImage(self.image)) 34 | self.image.SetToolTip(wx.ToolTip(tooltip)) 35 | self.label = wx.StaticText(self, label=_("Pattern distance (mm)")) 36 | self.label.SetToolTip(wx.ToolTip(tooltip)) 37 | self.text_box = wx.TextCtrl( 38 | self, value=str(profile.settings['pattern_origin_distance'])) 39 | self.ok_button = wx.Button(self, label=_("Accept")) 40 | self.cancel_button = wx.Button(self, label=_("Cancel")) 41 | 42 | # Events 43 | self.text_box.Bind(wx.EVT_TEXT, self.on_text_box_changed) 44 | self.cancel_button.Bind(wx.EVT_BUTTON, self.on_close) 45 | self.ok_button.Bind(wx.EVT_BUTTON, self.on_ok) 46 | self.Bind(wx.EVT_CLOSE, self.on_close) 47 | 48 | # Layout 49 | vbox = wx.BoxSizer(wx.VERTICAL) 50 | vbox.Add(self.description, 0, wx.ALL | wx.CENTER, 10) 51 | vbox.Add(self.image, 0, wx.ALL | wx.CENTER, 10) 52 | hbox = wx.BoxSizer(wx.HORIZONTAL) 53 | hbox.Add(self.label, 0, wx.ALL, 7) 54 | hbox.Add(self.text_box, 0, wx.ALL, 3) 55 | hbox.Add(self.ok_button, 0, wx.ALL, 3) 56 | hbox.Add(self.cancel_button, 0, wx.ALL, 3) 57 | vbox.Add(hbox, 0, wx.ALL | wx.CENTER, 10) 58 | self.SetSizer(vbox) 59 | self.CenterOnScreen() 60 | self.Fit() 61 | 62 | self.ShowModal() 63 | 64 | def on_text_box_changed(self, event): 65 | try: 66 | value = float(self.text_box.GetValue()) 67 | if value >= 0: 68 | self.value = value 69 | except: 70 | pass 71 | 72 | def set_pattern_distance(self, distance): 73 | profile.settings['pattern_origin_distance'] = distance 74 | pattern.origin_distance = distance 75 | 76 | def on_ok(self, event): 77 | self.set_pattern_distance(self.value) 78 | self.EndModal(wx.ID_OK) 79 | self.Destroy() 80 | 81 | def on_close(self, event): 82 | self.EndModal(wx.ID_OK) 83 | self.Destroy() 84 | -------------------------------------------------------------------------------- /src/horus/gui/util/version_window.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import wx._core 9 | 10 | from horus.util import version 11 | 12 | 13 | class VersionWindow(wx.Dialog): 14 | 15 | def __init__(self, parent): 16 | super(VersionWindow, self).__init__( 17 | parent, title=_('New version available!'), 18 | size=(420, -1), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER) 19 | 20 | # Elements 21 | self.description = wx.StaticText( 22 | self, label=_('A new version of Horus is available, would you like to download it?')) 23 | self.download_button = wx.Button(self, label=_('Download')) 24 | self.cancel_button = wx.Button(self, label=_('Cancel')) 25 | 26 | self.download = False 27 | 28 | # Events 29 | self.download_button.Bind(wx.EVT_BUTTON, self.on_download_button) 30 | self.cancel_button.Bind(wx.EVT_BUTTON, self.on_cancel_button) 31 | self.Bind(wx.EVT_CLOSE, self.on_cancel_button) 32 | 33 | # Layout 34 | vbox = wx.BoxSizer(wx.VERTICAL) 35 | vbox.Add(self.description, 0, wx.ALL | wx.CENTER, 10) 36 | hbox = wx.BoxSizer(wx.HORIZONTAL) 37 | hbox.Add(self.cancel_button, 0, wx.ALL, 3) 38 | hbox.Add(self.download_button, 0, wx.ALL, 3) 39 | vbox.Add(hbox, 0, wx.ALL | wx.CENTER, 10) 40 | self.SetSizer(vbox) 41 | self.Center() 42 | self.Fit() 43 | 44 | self.ShowModal() 45 | 46 | def on_download_button(self, event): 47 | self.download = True 48 | version.download_latest_version() 49 | self.EndModal(wx.ID_OK) 50 | self.Destroy() 51 | 52 | def on_cancel_button(self, event): 53 | self.EndModal(wx.ID_OK) 54 | self.Destroy() 55 | -------------------------------------------------------------------------------- /src/horus/gui/util/video_view.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import wx._core 9 | 10 | from threading import Timer 11 | from horus.gui.util.image_view import ImageView 12 | 13 | 14 | class VideoView(ImageView): 15 | 16 | def __init__(self, parent, callback=None, size=(-1, -1), wxtimer=True): 17 | ImageView.__init__(self, parent, size=size, black=True) 18 | 19 | self.callback = callback 20 | 21 | self.wxtimer = wxtimer 22 | self.playing = False 23 | 24 | if self.wxtimer: 25 | self.timer = wx.Timer(self) 26 | self.Bind(wx.EVT_TIMER, self.on_timer, self.timer) 27 | 28 | def on_timer(self, event): 29 | try: 30 | if self.wxtimer: 31 | self.timer.Stop() 32 | else: 33 | self.timer.cancel() 34 | if self.playing: 35 | if self.callback is not None: 36 | frame = self.callback() 37 | if frame is not None: 38 | if self.playing: 39 | if self.wxtimer: 40 | self.set_frame(frame) 41 | else: 42 | wx.CallAfter(self.set_frame, frame) 43 | self._start() 44 | except: 45 | pass 46 | 47 | def set_callback(self, callback): 48 | self.callback = callback 49 | 50 | def play(self, flush=True): 51 | if not self.playing: 52 | self.playing = True 53 | if self.callback is not None: 54 | # Flush video 55 | if flush: 56 | self.callback() 57 | self.callback() 58 | frame = self.callback() 59 | self.set_frame(frame) 60 | self._start() 61 | 62 | def _start(self): 63 | if self.wxtimer: 64 | self.timer.Start(milliseconds=100) 65 | else: 66 | self.timer = Timer(0.1, self.on_timer, (None,)) 67 | self.timer.start() 68 | 69 | def stop(self): 70 | if self.playing: 71 | self.playing = False 72 | if self.wxtimer: 73 | self.timer.Stop() 74 | else: 75 | self.timer.cancel() 76 | self.timer.join() 77 | 78 | def reset(self): 79 | self.hide = True 80 | self.set_default_image() 81 | -------------------------------------------------------------------------------- /src/horus/gui/wizard/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/gui/wizard/__init__.py -------------------------------------------------------------------------------- /src/horus/gui/workbench/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/gui/workbench/__init__.py -------------------------------------------------------------------------------- /src/horus/gui/workbench/adjustment/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/gui/workbench/adjustment/__init__.py -------------------------------------------------------------------------------- /src/horus/gui/workbench/adjustment/current_video.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import cv2 9 | import time 10 | 11 | from horus import Singleton 12 | from horus.gui.engine import image_capture, image_detection, laser_segmentation 13 | 14 | 15 | @Singleton 16 | class CurrentVideo(object): 17 | 18 | def __init__(self): 19 | self.mode = 'Texture' 20 | self.updating = False 21 | self.latest_image = None 22 | self.capturing = False 23 | 24 | def get_frame(self): 25 | if not self.updating: 26 | self.latest_image = self.capture() 27 | return self.latest_image 28 | 29 | def capture(self): 30 | self.capturing = True 31 | image = None 32 | 33 | if self.mode == 'Texture': 34 | image = image_capture.capture_texture() 35 | 36 | if self.mode == 'Pattern': 37 | image = image_capture.capture_pattern() 38 | image = image_detection.detect_pattern(image) 39 | 40 | if self.mode == 'Laser': 41 | image = image_capture.capture_all_lasers() 42 | 43 | if self.mode == 'Gray': 44 | images = image_capture.capture_lasers() 45 | for i in xrange(2): 46 | images[i] = laser_segmentation.compute_line_segmentation(images[i]) 47 | if images[0] is not None and images[1] is not None: 48 | image = images[0] + images[1] 49 | image = cv2.merge((image, image, image)) 50 | else: 51 | image = None 52 | 53 | self.capturing = False 54 | return image 55 | 56 | def sync(self): 57 | # Wait until latest capture is completed 58 | while self.capturing: 59 | time.sleep(0.05) 60 | 61 | def flush(self): 62 | if self.mode == 'Texture': 63 | for i in range(2): 64 | image_capture.flush_texture() 65 | elif self.mode == 'Pattern': 66 | for i in range(2): 67 | image_capture.flush_pattern() 68 | else: 69 | for i in range(1): 70 | image_capture.flush_laser() 71 | -------------------------------------------------------------------------------- /src/horus/gui/workbench/adjustment/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | from horus.util import profile 9 | 10 | from horus.gui.engine import driver, pattern, calibration_data, image_capture 11 | from horus.gui.util.video_view import VideoView 12 | from horus.gui.workbench.workbench import Workbench 13 | from horus.gui.workbench.adjustment.current_video import CurrentVideo 14 | from horus.gui.workbench.adjustment.panels import ScanCapturePanel, ScanSegmentationPanel, \ 15 | CalibrationCapturePanel, CalibrationSegmentationPanel 16 | 17 | 18 | class AdjustmentWorkbench(Workbench): 19 | 20 | def __init__(self, parent): 21 | Workbench.__init__(self, parent, name=_('Adjustment workbench')) 22 | 23 | self.current_video = CurrentVideo() 24 | 25 | def add_panels(self): 26 | self.add_panel('scan_capture', ScanCapturePanel) 27 | self.add_panel('scan_segmentation', ScanSegmentationPanel) 28 | self.add_panel('calibration_capture', CalibrationCapturePanel) 29 | self.add_panel('calibration_segmentation', CalibrationSegmentationPanel) 30 | 31 | def add_pages(self): 32 | self.add_page('video_view', VideoView(self, self._video_frame, wxtimer=False)) 33 | self.panels_collection.expandable_panels[ 34 | profile.settings['current_panel_adjustment']].on_title_clicked(None) 35 | 36 | def _video_frame(self): 37 | return self.current_video.get_frame() 38 | 39 | def on_open(self): 40 | current_video_mode = profile.settings['current_video_mode_adjustment'] 41 | self.pages_collection['video_view'].play( 42 | flush=not (current_video_mode == 'Laser' or current_video_mode == 'Gray')) 43 | 44 | def on_close(self): 45 | try: 46 | self.pages_collection['video_view'].stop() 47 | except: 48 | pass 49 | 50 | def reset(self): 51 | self.pages_collection['video_view'].reset() 52 | 53 | def setup_engine(self): 54 | driver.camera.set_frame_rate(int(profile.settings['frame_rate'])) 55 | driver.camera.set_resolution( 56 | profile.settings['camera_width'], profile.settings['camera_height']) 57 | driver.camera.set_rotate(profile.settings['camera_rotate']) 58 | driver.camera.set_hflip(profile.settings['camera_hflip']) 59 | driver.camera.set_vflip(profile.settings['camera_vflip']) 60 | driver.camera.set_luminosity(profile.settings['luminosity']) 61 | self.current_video.mode = profile.settings['current_video_mode_adjustment'] 62 | pattern.rows = profile.settings['pattern_rows'] 63 | pattern.columns = profile.settings['pattern_columns'] 64 | pattern.square_width = profile.settings['pattern_square_width'] 65 | pattern.distance = profile.settings['pattern_origin_distance'] 66 | image_capture.set_use_distortion(profile.settings['use_distortion']) 67 | width, height = driver.camera.get_resolution() 68 | calibration_data.set_resolution(width, height) 69 | calibration_data.camera_matrix = profile.settings['camera_matrix'] 70 | calibration_data.distortion_vector = profile.settings['distortion_vector'] 71 | self.panels_collection.expandable_panels[ 72 | profile.settings['current_panel_adjustment']].on_title_clicked(None) 73 | -------------------------------------------------------------------------------- /src/horus/gui/workbench/calibration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/gui/workbench/calibration/__init__.py -------------------------------------------------------------------------------- /src/horus/gui/workbench/calibration/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/gui/workbench/calibration/pages/__init__.py -------------------------------------------------------------------------------- /src/horus/gui/workbench/calibration/pages/capture_page.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import wx._core 9 | 10 | from horus.util import resources 11 | 12 | from horus.gui.engine import image_capture, image_detection, camera_intrinsics 13 | from horus.gui.workbench.calibration.pages.page import Page 14 | from horus.gui.util.image_view import ImageView 15 | from horus.gui.util.video_view import VideoView 16 | 17 | 18 | class CapturePage(Page): 19 | 20 | def __init__(self, parent, start_callback=None): 21 | Page.__init__(self, parent, 22 | title=_("Camera intrinsics (advanced)"), 23 | desc=_("Default values are recommended. To perform the calibration, " 24 | "click over the video panel and press " 25 | "space bar to perform the captures."), 26 | left=_("Reset"), 27 | right=_("Start"), 28 | button_left_callback=self.initialize, 29 | button_right_callback=start_callback, 30 | view_progress=True) 31 | 32 | self.right_button.Hide() 33 | 34 | # Elements 35 | self.video_view = VideoView(self.panel, self.get_image) 36 | self.rows, self.columns = 3, 5 37 | self.panel_grid = [] 38 | self.current_grid = 0 39 | self.image_grid_panel = wx.Panel(self.panel) 40 | self.grid_sizer = wx.GridSizer(self.rows, self.columns, 3, 3) 41 | for panel in xrange(self.rows * self.columns): 42 | self.panel_grid.append(ImageView(self.image_grid_panel)) 43 | self.panel_grid[panel].Bind(wx.EVT_KEY_DOWN, self.on_key_press) 44 | self.grid_sizer.Add(self.panel_grid[panel], 0, wx.ALL | wx.EXPAND) 45 | self.image_grid_panel.SetSizer(self.grid_sizer) 46 | 47 | # Layout 48 | self.panel_box.Add(self.video_view, 2, wx.ALL | wx.EXPAND, 2) 49 | self.panel_box.Add(self.image_grid_panel, 3, wx.ALL | wx.EXPAND, 3) 50 | self.Layout() 51 | 52 | # Events 53 | self.Bind(wx.EVT_KEY_DOWN, self.on_key_press) 54 | self.video_view.Bind(wx.EVT_KEY_DOWN, self.on_key_press) 55 | self.image_grid_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) 56 | 57 | def initialize(self): 58 | self.desc_text.SetLabel( 59 | _("Default values are recommended. To perform the calibration, " 60 | "click over the video panel and press " 61 | "space bar to perform the captures.")) 62 | self.current_grid = 0 63 | self.gauge.SetValue(0) 64 | camera_intrinsics.reset() 65 | for panel in xrange(self.rows * self.columns): 66 | self.panel_grid[panel].SetBackgroundColour((221, 221, 221)) 67 | self.panel_grid[panel].set_image(wx.Image(resources.get_path_for_image("void.png"))) 68 | 69 | def play(self): 70 | self.gauge.SetValue(0) 71 | self.video_view.play() 72 | self.image_grid_panel.SetFocus() 73 | self.GetParent().Layout() 74 | self.Layout() 75 | 76 | def stop(self): 77 | self.initialize() 78 | self.video_view.stop() 79 | 80 | def reset(self): 81 | self.video_view.reset() 82 | 83 | def get_image(self): 84 | image = image_capture.capture_pattern() 85 | chessboard = image_detection.detect_pattern(image) 86 | return chessboard 87 | 88 | def on_key_press(self, event): 89 | if event.GetKeyCode() == 32: # spacebar 90 | self.video_view.stop() 91 | image = camera_intrinsics.capture() 92 | if image is not None: 93 | self.add_frame_to_grid(image) 94 | if self.current_grid <= self.rows * self.columns: 95 | self.gauge.SetValue(self.current_grid * 100.0 / self.rows / self.columns) 96 | self.video_view.play() 97 | 98 | def add_frame_to_grid(self, image): 99 | if self.current_grid < (self.columns * self.rows): 100 | self.panel_grid[self.current_grid].set_frame(image) 101 | self.current_grid += 1 102 | if self.current_grid is (self.columns * self.rows): 103 | self.desc_text.SetLabel(_("Press space bar to continue")) 104 | if self.button_right_callback is not None: 105 | self.button_right_callback() 106 | -------------------------------------------------------------------------------- /src/horus/gui/workbench/calibration/pages/page.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import wx._core 9 | 10 | 11 | class Page(wx.Panel): 12 | 13 | def __init__(self, parent, title="Title", desc="", left="Left", right="Right", 14 | button_left_callback=None, button_right_callback=None, view_progress=False): 15 | wx.Panel.__init__(self, parent) # , style=wx.RAISED_BORDER) 16 | 17 | self.button_left_callback = button_left_callback 18 | self.button_right_callback = button_right_callback 19 | 20 | # Elements 21 | self.panel = wx.Panel(self) 22 | button_panel = wx.Panel(self) 23 | title_text = wx.StaticText(self, label=title) 24 | title_font = title_text.GetFont() 25 | title_font.SetWeight(wx.BOLD) 26 | title_text.SetFont(title_font) 27 | if desc != "": 28 | self.desc_text = wx.StaticText(self, label=desc) 29 | self.gauge = wx.Gauge(self, range=100, size=(-1, 30)) 30 | self.left_button = wx.Button(button_panel, -1, left) 31 | self.right_button = wx.Button(button_panel, -1, right) 32 | if not view_progress: 33 | self.gauge.Hide() 34 | 35 | # Layout 36 | vbox = wx.BoxSizer(wx.VERTICAL) 37 | hbox = wx.BoxSizer(wx.HORIZONTAL) 38 | self.panel_box = wx.BoxSizer(wx.HORIZONTAL) 39 | vbox.Add(title_text, 0, wx.ALL ^ wx.BOTTOM | wx.EXPAND, 12) 40 | if desc != "": 41 | vbox.Add(self.desc_text, 0, wx.ALL | wx.EXPAND, 14) 42 | vbox.Add(self.panel, 1, wx.ALL | wx.EXPAND, 8) 43 | vbox.Add(self.gauge, 0, wx.ALL | wx.EXPAND, 10) 44 | self.panel.SetSizer(self.panel_box) 45 | vbox.Add(button_panel, 0, wx.ALL | wx.EXPAND, 1) 46 | hbox.Add(self.left_button, 0, wx.ALL | wx.EXPAND | 47 | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT, 8) 48 | hbox.Add((0, 0), 1, wx.EXPAND) 49 | hbox.Add(self.right_button, 0, wx.ALL | wx.EXPAND | 50 | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 8) 51 | button_panel.SetSizer(hbox) 52 | self.SetSizer(vbox) 53 | self.Layout() 54 | 55 | # Events 56 | self.left_button.Bind(wx.EVT_BUTTON, self._on_left_button_pressed) 57 | self.right_button.Bind(wx.EVT_BUTTON, self._on_right_button_pressed) 58 | 59 | def _on_left_button_pressed(self, event): 60 | if self.button_left_callback is not None: 61 | self.button_left_callback() 62 | 63 | def _on_right_button_pressed(self, event): 64 | if self.button_right_callback is not None: 65 | self.button_right_callback() 66 | -------------------------------------------------------------------------------- /src/horus/gui/workbench/calibration/pages/scanner_autocheck.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import wx._core 9 | 10 | from horus.gui.engine import scanner_autocheck, image_capture 11 | from horus.engine.calibration.autocheck import PatternNotDetected, WrongMotorDirection, \ 12 | LaserNotDetected 13 | 14 | from horus.gui.workbench.calibration.pages.video_page import VideoPage 15 | 16 | 17 | class ScannerAutocheckPages(wx.Panel): 18 | 19 | def __init__(self, parent, start_callback=None, exit_callback=None): 20 | wx.Panel.__init__(self, parent) # , style=wx.RAISED_BORDER) 21 | 22 | self.start_callback = start_callback 23 | self.exit_callback = exit_callback 24 | 25 | # Elements 26 | self.video_page = VideoPage(self, title=_('Scanner autocheck'), 27 | start_callback=self.on_start, 28 | cancel_callback=self.on_cancel) 29 | 30 | # Layout 31 | hbox = wx.BoxSizer(wx.HORIZONTAL) 32 | hbox.Add(self.video_page, 1, wx.ALL | wx.EXPAND, 0) 33 | self.SetSizer(hbox) 34 | 35 | self._initialize() 36 | self.Layout() 37 | 38 | def _initialize(self): 39 | self.video_page.initialize() 40 | 41 | def play(self): 42 | self.video_page.play() 43 | 44 | def stop(self): 45 | self.video_page.stop() 46 | 47 | def reset(self): 48 | self.video_page.reset() 49 | 50 | def before_calibration(self): 51 | if self.start_callback is not None: 52 | self.start_callback() 53 | self.video_page.right_button.Disable() 54 | if not hasattr(self, 'wait_cursor'): 55 | self.wait_cursor = wx.BusyCursor() 56 | 57 | def progress_calibration(self, progress): 58 | self.video_page.gauge.SetValue(progress) 59 | 60 | def after_calibration(self, response): 61 | ret, result = response 62 | 63 | # Flush video 64 | image_capture.capture_pattern() 65 | image_capture.capture_pattern() 66 | 67 | if ret: 68 | dlg = wx.MessageDialog( 69 | self, _("Scanner configured correctly"), 70 | _("Success"), wx.OK | wx.ICON_INFORMATION) 71 | dlg.ShowModal() 72 | dlg.Destroy() 73 | else: 74 | if isinstance(result, PatternNotDetected): 75 | dlg = wx.MessageDialog( 76 | self, _("Please, put the pattern on the platform. " 77 | "Also you can set up the calibration's capture " 78 | "settings in the \"Adjustment workbench\" " 79 | "until the pattern is detected correctly"), 80 | _(result), wx.OK | wx.ICON_ERROR) 81 | dlg.ShowModal() 82 | dlg.Destroy() 83 | elif isinstance(result, WrongMotorDirection): 84 | dlg = wx.MessageDialog( 85 | self, _( 86 | "Please, select \"Invert the motor direction\" in the preferences"), 87 | _(result), wx.OK | wx.ICON_ERROR) 88 | dlg.ShowModal() 89 | dlg.Destroy() 90 | self.GetParent().GetParent().launch_preferences(basic=True) 91 | elif isinstance(result, LaserNotDetected): 92 | dlg = wx.MessageDialog( 93 | self, _("Please, check the lasers connection. " 94 | "Also you can set up the calibration's capture and " 95 | "segmentation settings in the \"Adjustment workbench\" " 96 | "until the lasers are detected correctly"), 97 | _(result), wx.OK | wx.ICON_ERROR) 98 | dlg.ShowModal() 99 | dlg.Destroy() 100 | self._initialize() 101 | self.video_page.right_button.Enable() 102 | if hasattr(self, 'wait_cursor'): 103 | del self.wait_cursor 104 | if self.exit_callback is not None: 105 | self.exit_callback() 106 | 107 | def on_start(self): 108 | scanner_autocheck.set_callbacks(lambda: wx.CallAfter(self.before_calibration), 109 | lambda p: wx.CallAfter(self.progress_calibration, p), 110 | lambda r: wx.CallAfter(self.after_calibration, r)) 111 | scanner_autocheck.start() 112 | 113 | def on_cancel(self): 114 | self._initialize() 115 | self.video_page.right_button.Enable() 116 | scanner_autocheck.cancel() 117 | if hasattr(self, 'wait_cursor'): 118 | del self.wait_cursor 119 | if self.exit_callback is not None: 120 | self.exit_callback() 121 | -------------------------------------------------------------------------------- /src/horus/gui/workbench/calibration/pages/video_page.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import wx._core 9 | 10 | from horus.util import resources 11 | 12 | from horus.gui.engine import image_capture, image_detection, scanner_autocheck, laser_triangulation, \ 13 | platform_extrinsics 14 | from horus.gui.workbench.calibration.pages.page import Page 15 | from horus.gui.util.image_view import ImageView 16 | from horus.gui.util.video_view import VideoView 17 | 18 | 19 | class VideoPage(Page): 20 | 21 | def __init__(self, parent, title='Video page', start_callback=None, cancel_callback=None): 22 | Page.__init__(self, parent, 23 | title=title, 24 | desc=_("Put the pattern on the platform as shown in the " 25 | "picture and press \"Start\""), 26 | left=_("Cancel"), 27 | right=_("Start"), 28 | button_left_callback=cancel_callback, 29 | button_right_callback=start_callback, 30 | view_progress=True) 31 | 32 | # Elements 33 | image_view = ImageView(self.panel, quality=wx.IMAGE_QUALITY_HIGH) 34 | image_view.set_image(wx.Image(resources.get_path_for_image("pattern-position.png"))) 35 | self.video_view = VideoView(self.panel, self.get_image) 36 | 37 | # Layout 38 | self.panel_box.Add(image_view, 3, wx.ALL | wx.EXPAND, 3) 39 | self.panel_box.Add(self.video_view, 2, wx.ALL | wx.EXPAND, 3) 40 | self.Layout() 41 | 42 | def initialize(self): 43 | self.gauge.SetValue(0) 44 | self.right_button.Enable() 45 | 46 | def play(self): 47 | self.video_view.play() 48 | self.GetParent().Layout() 49 | self.Layout() 50 | 51 | def stop(self): 52 | self.initialize() 53 | self.video_view.stop() 54 | 55 | def reset(self): 56 | self.video_view.reset() 57 | 58 | def get_image(self): 59 | if scanner_autocheck.image is not None: 60 | image = scanner_autocheck.image 61 | elif laser_triangulation.image is not None: 62 | image = laser_triangulation.image 63 | elif platform_extrinsics.image is not None: 64 | image = platform_extrinsics.image 65 | else: 66 | image = image_capture.capture_pattern() 67 | image = image_detection.detect_pattern(image) 68 | return image 69 | -------------------------------------------------------------------------------- /src/horus/gui/workbench/control/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/gui/workbench/control/__init__.py -------------------------------------------------------------------------------- /src/horus/gui/workbench/control/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | from horus.util import profile 9 | 10 | from horus.gui.engine import driver, calibration_data, image_capture 11 | from horus.gui.util.video_view import VideoView 12 | from horus.gui.workbench.workbench import Workbench 13 | from horus.gui.workbench.control.panels import CameraControl, LaserControl, \ 14 | MotorControl, GcodeControl 15 | 16 | 17 | class ControlWorkbench(Workbench): 18 | 19 | def __init__(self, parent): 20 | Workbench.__init__(self, parent, name=_('Control workbench')) 21 | 22 | def add_panels(self): 23 | self.add_panel('camera_control', CameraControl) 24 | self.add_panel('laser_control', LaserControl) 25 | # self.add_panel('ldr_value', LDRControl) 26 | self.add_panel('motor_control', MotorControl) 27 | self.add_panel('gcode_control', GcodeControl) 28 | 29 | def add_pages(self): 30 | self.add_page('video_view', VideoView(self, self._video_frame)) 31 | self.panels_collection.expandable_panels[ 32 | profile.settings['current_panel_control']].on_title_clicked(None) 33 | 34 | def _video_frame(self): 35 | return image_capture.capture_image() 36 | 37 | def on_open(self): 38 | self.pages_collection['video_view'].play() 39 | 40 | def on_close(self): 41 | try: 42 | driver.board.lasers_off() 43 | self.pages_collection['video_view'].stop() 44 | laser_control = self.panels_collection.expandable_panels['laser_control'] 45 | laser_control.get_control('left_button').control.SetValue(False) 46 | laser_control.get_control('right_button').control.SetValue(False) 47 | except: 48 | pass 49 | 50 | def reset(self): 51 | self.pages_collection['video_view'].reset() 52 | 53 | def setup_engine(self): 54 | driver.camera.set_frame_rate(int(profile.settings['frame_rate'])) 55 | driver.camera.set_resolution( 56 | profile.settings['camera_width'], profile.settings['camera_height']) 57 | driver.camera.set_rotate(profile.settings['camera_rotate']) 58 | driver.camera.set_hflip(profile.settings['camera_hflip']) 59 | driver.camera.set_vflip(profile.settings['camera_vflip']) 60 | driver.camera.set_luminosity(profile.settings['luminosity']) 61 | image_capture.set_mode_texture() 62 | image_capture.texture_mode.set_brightness(profile.settings['brightness_control']) 63 | image_capture.texture_mode.set_contrast(profile.settings['contrast_control']) 64 | image_capture.texture_mode.set_saturation(profile.settings['saturation_control']) 65 | image_capture.texture_mode.set_exposure(profile.settings['exposure_control']) 66 | image_capture.set_use_distortion(profile.settings['use_distortion']) 67 | width, height = driver.camera.get_resolution() 68 | calibration_data.set_resolution(width, height) 69 | calibration_data.camera_matrix = profile.settings['camera_matrix'] 70 | calibration_data.distortion_vector = profile.settings['distortion_vector'] 71 | driver.board.motor_speed(profile.settings['motor_speed_control']) 72 | driver.board.motor_acceleration(profile.settings['motor_acceleration_control']) 73 | -------------------------------------------------------------------------------- /src/horus/gui/workbench/scanning/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/gui/workbench/scanning/__init__.py -------------------------------------------------------------------------------- /src/horus/gui/workbench/scanning/view_page.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import wx._core 9 | 10 | from horus.util import profile 11 | 12 | from horus.gui.engine import driver, current_video 13 | 14 | from horus.gui.util.video_view import VideoView 15 | from horus.gui.util.scene_view import SceneView 16 | 17 | 18 | class ViewPage(wx.SplitterWindow): 19 | 20 | def __init__(self, parent, get_image): 21 | wx.SplitterWindow.__init__(self, parent, style=wx.SP_3D) # | wx.SP_LIVE_UPDATE) 22 | 23 | self.get_image = get_image 24 | 25 | self.video_view = VideoView(self, get_image) 26 | 27 | self.scene_panel = wx.Panel(self) 28 | self.scene_view = SceneView(self.scene_panel) 29 | self.gauge = wx.Gauge(self.scene_panel, size=(-1, 30)) 30 | self.gauge.Hide() 31 | 32 | # Layout 33 | vbox = wx.BoxSizer(wx.VERTICAL) 34 | vbox.Add(self.scene_view, 1, wx.ALL | wx.EXPAND, 0) 35 | vbox.Add(self.gauge, 0, wx.ALL | wx.EXPAND, 0) 36 | self.scene_panel.SetSizer(vbox) 37 | 38 | self.SplitVertically(self.video_view, self.scene_panel) 39 | self.SetMinimumPaneSize(200) 40 | 41 | # Video view selector 42 | _choices = [] 43 | choices = profile.settings.get_possible_values('video_scanning') 44 | for i in choices: 45 | _choices.append(_(i)) 46 | self.video_views_dict = dict(zip(_choices, choices)) 47 | self.combo_video_views = wx.ComboBox(self.video_view, 48 | value=_(u'Texture'), 49 | choices=_choices, style=wx.CB_READONLY, 50 | size=(100, -1), pos=(0, -1)) 51 | self.combo_video_views.Hide() 52 | self.combo_video_views.Bind(wx.EVT_COMBOBOX, self.on_combo_box_video_views_select) 53 | 54 | # Events 55 | self.video_view.Bind(wx.EVT_SHOW, self.on_show) 56 | 57 | def on_show(self, event): 58 | if event.GetShow(): 59 | if driver.is_connected and profile.settings['workbench'] == 'scanning': 60 | self.video_view.play() 61 | else: 62 | try: 63 | self.video_view.stop() 64 | except: 65 | pass 66 | 67 | def on_combo_box_video_views_select(self, event): 68 | value = self.video_views_dict[self.combo_video_views.GetValue()] 69 | current_video.mode = value 70 | profile.settings['video_scanning'] = value 71 | -------------------------------------------------------------------------------- /src/horus/gui/workbench/workbench.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import wx._core 9 | import wx.lib.scrolledpanel 10 | from collections import OrderedDict 11 | 12 | from horus.gui.engine import driver 13 | from horus.gui.util.custom_panels import ExpandableCollection 14 | 15 | 16 | class Workbench(wx.Panel): 17 | 18 | def __init__(self, parent, name='Workbench'): 19 | wx.Panel.__init__(self, parent) 20 | self.name = name 21 | 22 | # Elements 23 | self.scroll_panel = wx.lib.scrolledpanel.ScrolledPanel(self, size=(-1, -1)) 24 | self.scroll_panel.SetupScrolling(scroll_x=False, scrollIntoView=False) 25 | self.scroll_panel.SetAutoLayout(1) 26 | self.panels_collection = ExpandableCollection(self.scroll_panel) 27 | self.pages_collection = OrderedDict() 28 | 29 | # Layout 30 | self.hbox = wx.BoxSizer(wx.HORIZONTAL) 31 | self.hbox.Add(self.scroll_panel, 0, wx.ALL ^ wx.RIGHT | wx.EXPAND, 1) 32 | self.SetSizer(self.hbox) 33 | 34 | self.add_panels() # Add panels to collection 35 | self.panels_collection.init_panels_layout() 36 | 37 | vsbox = wx.BoxSizer(wx.VERTICAL) 38 | vsbox.Add(self.panels_collection, 1, wx.ALL | wx.EXPAND, 0) 39 | self.scroll_panel.SetSizer(vsbox) 40 | vsbox.Fit(self.scroll_panel) 41 | panel_size = self.scroll_panel.GetSize()[0] + wx.SystemSettings_GetMetric(wx.SYS_VSCROLL_X) 42 | self.scroll_panel.SetMinSize((panel_size, -1)) 43 | self.scroll_panel.Disable() 44 | 45 | self.add_pages() 46 | self.Layout() 47 | 48 | # Events 49 | self.Bind(wx.EVT_SHOW, self.on_show) 50 | 51 | def add_panels(self): 52 | raise NotImplementedError 53 | 54 | def add_pages(self): 55 | raise NotImplementedError 56 | 57 | def setup_engine(self): 58 | raise NotImplementedError 59 | 60 | def on_open(self): 61 | raise NotImplementedError 62 | 63 | def on_close(self): 64 | raise NotImplementedError 65 | 66 | def reset(self): 67 | raise NotImplementedError 68 | 69 | def add_panel(self, name, panel, on_selected_callback=None): 70 | self.panels_collection.add_panel(name, panel, on_selected_callback) 71 | 72 | def add_page(self, name, page): 73 | self.pages_collection[name] = page 74 | self.hbox.Add(page, 1, wx.ALL | wx.EXPAND, 2) 75 | 76 | def enable_content(self): 77 | self.scroll_panel.Enable() 78 | self.panels_collection.enable_content() 79 | 80 | def disable_content(self): 81 | self.panels_collection.disable_content() 82 | self.scroll_panel.Disable() 83 | 84 | def update_controls(self): 85 | self.panels_collection.update_from_profile() 86 | if driver.is_connected: 87 | self.setup_engine() 88 | 89 | def on_connect(self): 90 | if driver.is_connected: 91 | self.setup_engine() 92 | for _, p in self.pages_collection.iteritems(): 93 | p.Enable() 94 | self.on_open() 95 | 96 | def on_disconnect(self): 97 | for _, p in self.pages_collection.iteritems(): 98 | p.Disable() 99 | self.on_close() 100 | self.disable_content() 101 | self.reset() 102 | 103 | def on_show(self, event): 104 | if event.GetShow(): 105 | if driver.is_connected: 106 | self.setup_engine() 107 | self.on_open() 108 | else: 109 | self.on_close() 110 | -------------------------------------------------------------------------------- /src/horus/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/util/__init__.py -------------------------------------------------------------------------------- /src/horus/util/avr_helpers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' 6 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 7 | 8 | import os 9 | import resources 10 | from subprocess import Popen, PIPE, STDOUT 11 | 12 | import logging 13 | logger = logging.getLogger(__name__) 14 | 15 | from horus.util import system as sys 16 | 17 | 18 | class AvrError(Exception): 19 | pass 20 | 21 | 22 | class AvrDude(object): 23 | 24 | def __init__(self, protocol="arduino", microcontroller="atmega328p", 25 | baud_rate="19200", port=None): 26 | self.protocol = protocol 27 | self.microcontroller = microcontroller 28 | self.baud_rate = baud_rate 29 | 30 | if sys.is_windows(): 31 | self.avrdude = os.path.abspath(resources.get_path_for_tools("avrdude.exe")) 32 | elif sys.is_darwin(): 33 | self.avrdude = os.path.abspath(resources.get_path_for_tools("avrdude")) 34 | else: 35 | try: 36 | Popen(["avrdude"], stdout=PIPE, stderr=STDOUT) 37 | self.avrdude = "avrdude" 38 | except: 39 | self.avrdude = None 40 | 41 | if self.avrdude is None: 42 | raise AvrError('avrdude not installed') 43 | 44 | self.avrconf = os.path.abspath(resources.get_path_for_tools("avrdude.conf")) 45 | self.port = port 46 | 47 | def _run_command(self, flags=[], callback=None): 48 | config = dict(avrdude=self.avrdude, avrconf=self.avrconf) 49 | cmd = ['%(avrdude)s'] + flags 50 | cmd = [v % config for v in cmd] 51 | logger.info(' ' + ' '.join(cmd)) 52 | p = Popen(cmd, stdout=PIPE, stderr=STDOUT, shell=sys.is_windows()) 53 | out = '' 54 | while True: 55 | char = p.stdout.read(1) 56 | if not char: 57 | break 58 | out += char 59 | if char == '#': 60 | if callback is not None: 61 | callback() 62 | return out 63 | 64 | def flash(self, hex_path=None, clear_eeprom=False, callback=None): 65 | if hex_path is None: 66 | hex_path = resources.get_path_for_firmware("horus-fw.hex") 67 | if clear_eeprom: 68 | hex_path = resources.get_path_for_firmware("eeprom_clear.hex") 69 | flags = ['-C', '%(avrconf)s', '-c', self.protocol, '-p', self.microcontroller, 70 | '-P', '%s' % self.port, '-b', str(self.baud_rate), '-D', '-U', 71 | 'flash:w:%s' % os.path.basename(hex_path)] 72 | try: 73 | cwd = os.getcwd() 74 | os.chdir(os.path.dirname(os.path.abspath(hex_path))) 75 | out = self._run_command(flags, callback) 76 | logger.info(' Upload completed') 77 | finally: 78 | os.chdir(cwd) 79 | return out 80 | -------------------------------------------------------------------------------- /src/horus/util/mesh_loader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.\ 6 | Copyright (C) 2013 David Braam from Cura Project' 7 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 8 | 9 | import os 10 | 11 | from horus.util.mesh_loaders import ply 12 | from horus.util.mesh_loaders import stl 13 | 14 | import logging 15 | logger = logging.getLogger(__name__) 16 | 17 | 18 | def load_supported_extensions(): 19 | """ return a list of supported file extensions for loading. """ 20 | return ['.ply', '.stl'] 21 | 22 | 23 | def save_supported_extensions(): 24 | """ return a list of supported file extensions for saving. """ 25 | return ['.ply'] 26 | 27 | 28 | def load_mesh(filename): 29 | """ 30 | loadMesh loads one model from a file. 31 | """ 32 | ext = os.path.splitext(filename)[1].lower() 33 | if ext == '.ply': 34 | return ply.load_scene(filename) 35 | if ext == '.stl': 36 | return stl.load_scene(filename) 37 | logger.error('Error: Unknown model extension: %s' % (ext)) 38 | return None 39 | 40 | 41 | def save_mesh(filename, _object): 42 | """ 43 | Save a object into the file given by the filename. 44 | Use the filename extension to find out the file format. 45 | """ 46 | ext = os.path.splitext(filename)[1].lower() 47 | if ext == '.ply': 48 | ply.save_scene(filename, _object) 49 | return 50 | logger.error('Error: Unknown model extension: %s' % (ext)) 51 | -------------------------------------------------------------------------------- /src/horus/util/mesh_loaders/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/src/horus/util/mesh_loaders/__init__.py -------------------------------------------------------------------------------- /src/horus/util/mesh_loaders/stl.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of the Horus Project 3 | 4 | __author__ = 'Jesús Arroyo Torrens ' 5 | __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.\ 6 | Copyright (C) 2013 David Braam from Cura Project' 7 | __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' 8 | 9 | """ 10 | STL file mesh loader. 11 | STL is the most common file format used for 3D printing right now. 12 | STLs come in 2 flavors. 13 | Binary, which is easy and quick to read. 14 | Ascii, which is harder to read, as can come with windows, mac and unix style newlines. 15 | The ascii reader has been designed so it has great compatibility 16 | with all kinds of formats or slightly broken exports from tools. 17 | 18 | This module also contains a function to save objects as an STL file. 19 | 20 | http://en.wikipedia.org/wiki/STL_(file_format) 21 | """ 22 | 23 | import os 24 | import struct 25 | import numpy as np 26 | 27 | from horus.util import model 28 | 29 | 30 | def _load_ascii(mesh, stream): 31 | cnt = 0 32 | for lines in stream: 33 | for line in lines.split('\r'): 34 | if 'vertex' in line: 35 | cnt += 1 36 | mesh._prepare_face_count(int(cnt) / 3) 37 | stream.seek(5, os.SEEK_SET) 38 | cnt = 0 39 | data = [None, None, None] 40 | for lines in stream: 41 | for line in lines.split('\r'): 42 | if 'vertex' in line: 43 | data[cnt] = line.split()[1:] 44 | cnt += 1 45 | if cnt == 3: 46 | mesh._add_face(float(data[0][0]), float(data[0][1]), float(data[0][2]), 47 | float(data[1][0]), float(data[1][1]), float(data[1][2]), 48 | float(data[2][0]), float(data[2][1]), float(data[2][2])) 49 | cnt = 0 50 | 51 | 52 | def _load_binary(mesh, stream): 53 | # Skip the header 54 | stream.read(80 - 5) 55 | count = struct.unpack('= current_version.number and \ 61 | latest_version.prenumber >= current_version.prenumber and \ 62 | current_datetime is not '' and \ 63 | latest_datetime > current_datetime 64 | 65 | 66 | def _get_executable_url(version): 67 | url = None 68 | if sys.is_linux(): 69 | import platform 70 | url = 'https://launchpad.net/~bqlabs/+archive/ubuntu/horus/+files/' 71 | url += 'horus_' 72 | url += version + '-' 73 | url += platform.linux_distribution()[2] + '1_' 74 | if platform.architecture()[0] == '64bit': 75 | url += 'amd64.deb' 76 | elif platform.architecture()[0] == '32bit': 77 | url += 'i386.deb' 78 | del platform 79 | elif sys.is_windows(): 80 | url = URL_DOWNLOAD 81 | url += latest_version 82 | url += '/Horus_' 83 | url += version + '.exe' 84 | elif sys.is_darwin(): 85 | url = URL_DOWNLOAD 86 | url += latest_version 87 | url += '/Horus_' 88 | url += version + '.dmg' 89 | return url 90 | 91 | 92 | def download_latest_version(): 93 | url = _get_executable_url(latest_version) 94 | if url is not None: 95 | webbrowser.open(url) 96 | -------------------------------------------------------------------------------- /test/engine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/horus/420612d5bd64dcff08f8859e14c7b25bcc226c58/test/engine/__init__.py -------------------------------------------------------------------------------- /test/engine/test_board.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from horus.engine.driver.board import Board 3 | 4 | 5 | class BoardTest(unittest.TestCase): 6 | 7 | def setUp(self): 8 | self.board = Board() 9 | 10 | def test_serial_name(self): 11 | self.assertEqual(self.board.serial_name, '/dev/ttyUSB0') 12 | 13 | def test_baud_rate(self): 14 | self.assertEqual(self.board.baud_rate, 115200) 15 | --------------------------------------------------------------------------------