diff --git a/doc/readthedocs/locale/en/LC_MESSAGES/source.po b/doc/readthedocs/locale/en/LC_MESSAGES/source.po index e772d76c..ab74e2de 100644 --- a/doc/readthedocs/locale/en/LC_MESSAGES/source.po +++ b/doc/readthedocs/locale/en/LC_MESSAGES/source.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Horus 0.2rc1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-06-03 10:22+0200\n" +"POT-Creation-Date: 2016-06-10 13:25+0200\n" "PO-Revision-Date: 2016-05-26 14:59+0100\n" "Last-Translator: \n" "Language-Team: \n" @@ -628,23 +628,87 @@ msgid "ZUM BT-328" msgstr "" #: ../../source/scanner-components/board.rst:16 -msgid "ZUM SCAN Shield" +msgid "Arduino UNO" msgstr "" -#: ../../source/scanner-components/board.rst:23 -msgid "Horus Firmware" +#: ../../source/scanner-components/board.rst:22 +msgid "Supported shields" msgstr "" #: ../../source/scanner-components/board.rst:25 +msgid "ZUM SCAN" +msgstr "" + +#: ../../source/scanner-components/board.rst:32 +msgid "Horus Firmware" +msgstr "" + +#: ../../source/scanner-components/board.rst:34 msgid "" -"The firmware is an adaptation of Grbl. Source code is here: " -"https://github.com/bqlabs/horus-fw." +"The firmware is an adaptation of Grbl. Releases are hosted here: " +"https://github.com/bqlabs/horus-fw/releases." msgstr "" -#: ../../source/scanner-components/board.rst:27 +#: ../../source/scanner-components/board.rst:36 msgid "It can be uploaded in **Preferences > Upload firmware**." msgstr "" +#: ../../source/scanner-components/board.rst:39 +#: ../../source/scanner-components/camera.rst:146 +#: ../../source/scanner-components/lasers.rst:69 +#: ../../source/scanner-components/motor.rst:66 +#: ../../source/scanner-components/pattern.rst:60 +msgid "Troubleshooting" +msgstr "" + +#: ../../source/scanner-components/board.rst:42 +msgid "Board not detected" +msgstr "" + +#: ../../source/scanner-components/board.rst:44 +msgid "" +"Sometimes **Arduino UNO** board is not detected correctly, specially if " +"you are using **Windows 10** or **strange clones**. First of all, follow " +"this steps to ensure that your board and your operating system are " +"working fine together:" +msgstr "" + +#: ../../source/scanner-components/board.rst:46 +msgid "" +"Follow this guide to install the drivers and the Arduino IDE: " +"https://www.arduino.cc/en/Guide/Windows." +msgstr "" + +#: ../../source/scanner-components/board.rst:47 +msgid "" +"Upload the `blink` example. If it doesn't work go to: " +"https://forum.arduino.cc/ to find a solution." +msgstr "" + +#: ../../source/scanner-components/board.rst:49 +msgid "" +"Then if the led is blinking is time to upload the Horus firmware. You " +"have two options:" +msgstr "" + +#: ../../source/scanner-components/board.rst:51 +msgid "" +"In Horus GUI, go to *Preferences*. Select your board and press *Upload " +"firmware*." +msgstr "" + +#: ../../source/scanner-components/board.rst:52 +msgid "" +"In Arduino IDE, download the source code (https://github.com/bqlabs" +"/horus-fw) and upload the file horus-fw.ino." +msgstr "" + +#: ../../source/scanner-components/board.rst:56 +msgid "" +"If you have any problem related to the board or the shield please put it " +"here: https://github.com/bqlabs/horus/issues to update this manual." +msgstr "" + #: ../../source/scanner-components/camera.rst:4 #: ../../source/workbenches/control.rst:11 msgid "Camera" @@ -999,13 +1063,6 @@ msgid "" "`reasons`_. In Windows and Mac, standard 2.4.9 version is used." msgstr "" -#: ../../source/scanner-components/camera.rst:146 -#: ../../source/scanner-components/lasers.rst:69 -#: ../../source/scanner-components/motor.rst:66 -#: ../../source/scanner-components/pattern.rst:60 -msgid "Troubleshooting" -msgstr "" - #: ../../source/scanner-components/camera.rst:149 msgid "Focus image" msgstr "" @@ -1490,7 +1547,7 @@ msgid "These settings are applied during the scanning process." msgstr "" #: ../../source/workbenches/adjustment.rst:14 -#: ../../source/workbenches/adjustment.rst:44 +#: ../../source/workbenches/adjustment.rst:47 msgid "Capture" msgstr "" @@ -1517,7 +1574,7 @@ msgid "" msgstr "" #: ../../source/workbenches/adjustment.rst:27 -#: ../../source/workbenches/adjustment.rst:57 +#: ../../source/workbenches/adjustment.rst:60 msgid "Segmentation" msgstr "" @@ -1528,52 +1585,58 @@ msgid "" msgstr "" #: ../../source/workbenches/adjustment.rst:31 -#: ../../source/workbenches/adjustment.rst:61 +msgid "" +"**Draw line**: show the computed lines in red from the segmented laser " +"image." +msgstr "" + +#: ../../source/workbenches/adjustment.rst:32 +#: ../../source/workbenches/adjustment.rst:64 msgid "" "**Threshold**: remove all pixels which intensity is less that the " "threshold value." msgstr "" -#: ../../source/workbenches/adjustment.rst:32 -#: ../../source/workbenches/adjustment.rst:62 +#: ../../source/workbenches/adjustment.rst:33 +#: ../../source/workbenches/adjustment.rst:65 msgid "**Blur**: blur with Normalized box filter. Kernel size: 2 * value + 1." msgstr "" -#: ../../source/workbenches/adjustment.rst:33 -#: ../../source/workbenches/adjustment.rst:63 +#: ../../source/workbenches/adjustment.rst:34 +#: ../../source/workbenches/adjustment.rst:66 msgid "" "**Window**: filter pixels out of 2 * window value around the intensity " "peak." msgstr "" -#: ../../source/workbenches/adjustment.rst:34 +#: ../../source/workbenches/adjustment.rst:35 msgid "" "**Refinement**: apply None or SGF algorithms for line smooth. SGF " "produces continous surfaces." msgstr "" -#: ../../source/workbenches/adjustment.rst:39 +#: ../../source/workbenches/adjustment.rst:42 msgid "Calibration adjustments" msgstr "" -#: ../../source/workbenches/adjustment.rst:41 +#: ../../source/workbenches/adjustment.rst:44 msgid "These settings are applied during the calibration processes." msgstr "" -#: ../../source/workbenches/adjustment.rst:46 +#: ../../source/workbenches/adjustment.rst:49 msgid "" "In this section you can adjust the parameters of the capture during the " "calibration process. These parameters must be adjusted with the " "calibration pattern." msgstr "" -#: ../../source/workbenches/adjustment.rst:48 +#: ../../source/workbenches/adjustment.rst:51 msgid "" "The *Pattern mode* contains the settings used to capture the pattern. " "These are: brightness, constrast, saturation and exposure." msgstr "" -#: ../../source/workbenches/adjustment.rst:52 +#: ../../source/workbenches/adjustment.rst:55 msgid "" "The *Laser mode* contains the settings used to capture and detect the " "laser over the pattern. These are: brightness, constrast, saturation, " @@ -1581,13 +1644,13 @@ msgid "" "the laser detection by consuming twice as long." msgstr "" -#: ../../source/workbenches/adjustment.rst:59 +#: ../../source/workbenches/adjustment.rst:62 msgid "" "In this section you can adjust the parameters for the laser stripe " "segmentation during the calibration processes." msgstr "" -#: ../../source/workbenches/adjustment.rst:64 +#: ../../source/workbenches/adjustment.rst:67 msgid "" "**Refinement**: apply None, SGF, RANSAC algorithms for line smooth. SGF " "produces continous surfaces. RANSAC produces flat surfaces." @@ -1974,12 +2037,12 @@ msgstr "" #~ msgid "To enable the advanced mode go to *Menu > View > Advanced mode*." #~ msgstr "" +#~ msgid "ZUM SCAN Shield" +#~ msgstr "" + #~ msgid "" -#~ "**Step**: is the angle increased in " -#~ "each scan iteration. The smaller step," -#~ " the greater radial resolution, and " -#~ "also more scanning time. The default " -#~ "value is 0.45º, that is 800 steps" -#~ " per revolution." +#~ "In Arduino IDE, download the source " +#~ "code (https://github.com/bqlabs/horus-fw) and " +#~ "upload horus-fw.ino." #~ msgstr "" diff --git a/doc/readthedocs/locale/es/LC_MESSAGES/index.mo b/doc/readthedocs/locale/es/LC_MESSAGES/index.mo index dbe12f5a..818a7f87 100644 Binary files a/doc/readthedocs/locale/es/LC_MESSAGES/index.mo and b/doc/readthedocs/locale/es/LC_MESSAGES/index.mo differ diff --git a/doc/readthedocs/locale/es/LC_MESSAGES/source.mo b/doc/readthedocs/locale/es/LC_MESSAGES/source.mo index 90185c10..55531b1e 100644 Binary files a/doc/readthedocs/locale/es/LC_MESSAGES/source.mo and b/doc/readthedocs/locale/es/LC_MESSAGES/source.mo differ diff --git a/doc/readthedocs/locale/es/LC_MESSAGES/source.po b/doc/readthedocs/locale/es/LC_MESSAGES/source.po index 56ec6974..f9912a96 100644 --- a/doc/readthedocs/locale/es/LC_MESSAGES/source.po +++ b/doc/readthedocs/locale/es/LC_MESSAGES/source.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Horus 0.2rc1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-06-03 10:23+0200\n" -"PO-Revision-Date: 2016-06-03 10:42+0100\n" +"POT-Creation-Date: 2016-06-10 13:25+0200\n" +"PO-Revision-Date: 2016-06-10 13:27+0100\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -735,25 +735,106 @@ msgid "ZUM BT-328" msgstr "ZUM BT-328" #: ../../source/scanner-components/board.rst:16 -msgid "ZUM SCAN Shield" -msgstr "ZUM SCAN Shield" +msgid "Arduino UNO" +msgstr "Arduino UNO" -#: ../../source/scanner-components/board.rst:23 +#: ../../source/scanner-components/board.rst:22 +msgid "Supported shields" +msgstr "Shields soportadas" + +#: ../../source/scanner-components/board.rst:25 +msgid "ZUM SCAN" +msgstr "ZUM SCAN" + +#: ../../source/scanner-components/board.rst:32 msgid "Horus Firmware" msgstr "Horus Firmware" -#: ../../source/scanner-components/board.rst:25 +#: ../../source/scanner-components/board.rst:34 msgid "" -"The firmware is an adaptation of Grbl. Source code is here: https://github." -"com/bqlabs/horus-fw." +"The firmware is an adaptation of Grbl. Releases are hosted here: https://" +"github.com/bqlabs/horus-fw/releases." msgstr "" -"El firmware es una adaptación de Grbl. El código fuente está aquí: https://" -"github.com/bqlabs/horus-fw." +"El firmware es una adaptación de Grbl. Las versiones liberadas están " +"alojadas aquí: https://github.com/bqlabs/horus-fw." -#: ../../source/scanner-components/board.rst:27 +#: ../../source/scanner-components/board.rst:36 msgid "It can be uploaded in **Preferences > Upload firmware**." msgstr "Puede ser cargado en **Preferencias > Cargar firmware**." +#: ../../source/scanner-components/board.rst:39 +#: ../../source/scanner-components/camera.rst:146 +#: ../../source/scanner-components/lasers.rst:69 +#: ../../source/scanner-components/motor.rst:66 +#: ../../source/scanner-components/pattern.rst:60 +msgid "Troubleshooting" +msgstr "Resolución de problemas" + +#: ../../source/scanner-components/board.rst:42 +msgid "Board not detected" +msgstr "Placa no detectada" + +#: ../../source/scanner-components/board.rst:44 +msgid "" +"Sometimes **Arduino UNO** board is not detected correctly, specially if you " +"are using **Windows 10** or **strange clones**. First of all, follow this " +"steps to ensure that your board and your operating system are working fine " +"together:" +msgstr "" +"En ocasiones no se detecta correctamente la placa **Arduino UNO**, " +"especialmente si se está utilizando **Windows 10** o **clones extraños**. " +"Para empezar, sigue los siguientes pasos para asegurar que tu placa y tu " +"sistema operativo se comportan bien juntos." + +#: ../../source/scanner-components/board.rst:46 +msgid "" +"Follow this guide to install the drivers and the Arduino IDE: https://www." +"arduino.cc/en/Guide/Windows." +msgstr "" +"Sigue esta guía para instalar los drivers y el IDE de Arduino: https://www." +"arduino.cc/en/Guide/Windows." + +#: ../../source/scanner-components/board.rst:47 +msgid "" +"Upload the `blink` example. If it doesn't work go to: https://forum.arduino." +"cc/ to find a solution." +msgstr "" +"Carga el ejemplo `blink`. Si no funciona ve a: https://forum.arduino.cc/ " +"para buscar una solución." + +#: ../../source/scanner-components/board.rst:49 +msgid "" +"Then if the led is blinking is time to upload the Horus firmware. You have " +"two options:" +msgstr "" +"Entonces, si el led está parpadeande es el momento de cargar el firmware de " +"Horus. Tienes dos opciones:" + +#: ../../source/scanner-components/board.rst:51 +msgid "" +"In Horus GUI, go to *Preferences*. Select your board and press *Upload " +"firmware*." +msgstr "" +"En Horus GUI, ve a *Preferencias*. Selecciona tu placa y pulsa *Cargar " +"firmware*." + +#: ../../source/scanner-components/board.rst:52 +msgid "" +"In Arduino IDE, download the source code (https://github.com/bqlabs/horus-" +"fw) and upload the file horus-fw.ino." +msgstr "" +"En el IDE de Arduino, descarga el código fuente (https://github.com/bqlabs/" +"horus-fw) y carga el fichero horus-fw.ino." + +#: ../../source/scanner-components/board.rst:56 +msgid "" +"If you have any problem related to the board or the shield please put it " +"here: https://github.com/bqlabs/horus/issues to update this manual." +msgstr "" +"Si tienes cualquier problema relacionado con la placa o la shield, por favor " +"ponlo aquí: https://github.com/bqlabs/horus/issues para actualizar este " +"manual." + #: ../../source/scanner-components/camera.rst:4 #: ../../source/workbenches/control.rst:11 msgid "Camera" @@ -1119,13 +1200,6 @@ msgstr "" "siguientes `razones`_. En Windows y Mac, se utiliza la versión estándar " "2.4.9." -#: ../../source/scanner-components/camera.rst:146 -#: ../../source/scanner-components/lasers.rst:69 -#: ../../source/scanner-components/motor.rst:66 -#: ../../source/scanner-components/pattern.rst:60 -msgid "Troubleshooting" -msgstr "Resolución de problemas" - #: ../../source/scanner-components/camera.rst:149 msgid "Focus image" msgstr "Enfocar imagen" @@ -1654,7 +1728,7 @@ msgid "These settings are applied during the scanning process." msgstr "Estos ajustes se aplican durante el proceso de escaneado." #: ../../source/workbenches/adjustment.rst:14 -#: ../../source/workbenches/adjustment.rst:44 +#: ../../source/workbenches/adjustment.rst:47 msgid "Capture" msgstr "Captura" @@ -1692,7 +1766,7 @@ msgstr "" "doble de tiempo." #: ../../source/workbenches/adjustment.rst:27 -#: ../../source/workbenches/adjustment.rst:57 +#: ../../source/workbenches/adjustment.rst:60 msgid "Segmentation" msgstr "Segmentación" @@ -1705,7 +1779,14 @@ msgstr "" "del láser durante el proceso de escaneo." #: ../../source/workbenches/adjustment.rst:31 -#: ../../source/workbenches/adjustment.rst:61 +msgid "" +"**Draw line**: show the computed lines in red from the segmented laser image." +msgstr "" +"**Mostrar línea**: muestra las líneas en rojo obtenidas a partir de la " +"imagen del láser segmentada." + +#: ../../source/workbenches/adjustment.rst:32 +#: ../../source/workbenches/adjustment.rst:64 msgid "" "**Threshold**: remove all pixels which intensity is less that the threshold " "value." @@ -1713,22 +1794,22 @@ msgstr "" "**Umbral**: elimina todos los píxeles cuya intensidad sea inferior al valor " "límite." -#: ../../source/workbenches/adjustment.rst:32 -#: ../../source/workbenches/adjustment.rst:62 +#: ../../source/workbenches/adjustment.rst:33 +#: ../../source/workbenches/adjustment.rst:65 msgid "**Blur**: blur with Normalized box filter. Kernel size: 2 * value + 1." msgstr "" "**Desenfoque**: desenfoque con el filtro Normalized box. Tamaño del kernel: " "2 * valor + 1." -#: ../../source/workbenches/adjustment.rst:33 -#: ../../source/workbenches/adjustment.rst:63 +#: ../../source/workbenches/adjustment.rst:34 +#: ../../source/workbenches/adjustment.rst:66 msgid "" "**Window**: filter pixels out of 2 * window value around the intensity peak." msgstr "" "**Ventana**: filtra píxeles fuera del valor * 2 alrededor de la intensidad " "máxima." -#: ../../source/workbenches/adjustment.rst:34 +#: ../../source/workbenches/adjustment.rst:35 msgid "" "**Refinement**: apply None or SGF algorithms for line smooth. SGF produces " "continous surfaces." @@ -1736,15 +1817,15 @@ msgstr "" "**Refinamiento**: aplica el algoritmo SGF para suavizar la línea. SGF " "produce superficies continuas." -#: ../../source/workbenches/adjustment.rst:39 +#: ../../source/workbenches/adjustment.rst:42 msgid "Calibration adjustments" msgstr "Ajustes de la calibración" -#: ../../source/workbenches/adjustment.rst:41 +#: ../../source/workbenches/adjustment.rst:44 msgid "These settings are applied during the calibration processes." msgstr "Estos ajustes se aplican durante el proceso de calibración." -#: ../../source/workbenches/adjustment.rst:46 +#: ../../source/workbenches/adjustment.rst:49 msgid "" "In this section you can adjust the parameters of the capture during the " "calibration process. These parameters must be adjusted with the calibration " @@ -1754,7 +1835,7 @@ msgstr "" "durante el proceso de calibración. Estos parámetros de deben ajustar con el " "patrón de calibración." -#: ../../source/workbenches/adjustment.rst:48 +#: ../../source/workbenches/adjustment.rst:51 msgid "" "The *Pattern mode* contains the settings used to capture the pattern. These " "are: brightness, constrast, saturation and exposure." @@ -1762,7 +1843,7 @@ msgstr "" "El *Modo patrón* contiene los parámetros que utilizará el escáner para " "detectar el patrón. Éstos son: brillo, contraste, saturación y exposición." -#: ../../source/workbenches/adjustment.rst:52 +#: ../../source/workbenches/adjustment.rst:55 msgid "" "The *Laser mode* contains the settings used to capture and detect the laser " "over the pattern. These are: brightness, constrast, saturation, exposure and " @@ -1774,7 +1855,7 @@ msgstr "" "saturación, exposición y eliminación del fondo. La opción *Eliminar fondo* " "facilita la detección de los láseres a costa de consumir el doble de tiempo." -#: ../../source/workbenches/adjustment.rst:59 +#: ../../source/workbenches/adjustment.rst:62 msgid "" "In this section you can adjust the parameters for the laser stripe " "segmentation during the calibration processes." @@ -1782,7 +1863,7 @@ msgstr "" "En esta sección se ajustan los parámetros para la segmentación de la curva " "del láser durante el proceso de calibración." -#: ../../source/workbenches/adjustment.rst:64 +#: ../../source/workbenches/adjustment.rst:67 msgid "" "**Refinement**: apply None, SGF, RANSAC algorithms for line smooth. SGF " "produces continous surfaces. RANSAC produces flat surfaces." @@ -2236,3 +2317,6 @@ msgstr "" #~ msgid "Start here" #~ msgstr "Empieza aquí" + +#~ msgid "ZUM SCAN Shield" +#~ msgstr "ZUM SCAN Shield" diff --git a/doc/readthedocs/source/_static/scanner-components/arduino-uno.jpg b/doc/readthedocs/source/_static/scanner-components/arduino-uno.jpg new file mode 100644 index 00000000..37112e28 Binary files /dev/null and b/doc/readthedocs/source/_static/scanner-components/arduino-uno.jpg differ diff --git a/doc/readthedocs/source/_static/workbenches/adjustment-calibration-capture-laser.png b/doc/readthedocs/source/_static/workbenches/adjustment-calibration-capture-laser.png index 06da0cdc..ae598d42 100644 Binary files a/doc/readthedocs/source/_static/workbenches/adjustment-calibration-capture-laser.png and b/doc/readthedocs/source/_static/workbenches/adjustment-calibration-capture-laser.png differ diff --git a/doc/readthedocs/source/_static/workbenches/adjustment-calibration-capture-pattern.png b/doc/readthedocs/source/_static/workbenches/adjustment-calibration-capture-pattern.png index b23bafe6..3a6375be 100644 Binary files a/doc/readthedocs/source/_static/workbenches/adjustment-calibration-capture-pattern.png and b/doc/readthedocs/source/_static/workbenches/adjustment-calibration-capture-pattern.png differ diff --git a/doc/readthedocs/source/_static/workbenches/adjustment-calibration-segmentation.png b/doc/readthedocs/source/_static/workbenches/adjustment-calibration-segmentation.png index 9281c26c..f5550f13 100644 Binary files a/doc/readthedocs/source/_static/workbenches/adjustment-calibration-segmentation.png and b/doc/readthedocs/source/_static/workbenches/adjustment-calibration-segmentation.png differ diff --git a/doc/readthedocs/source/_static/workbenches/adjustment-scan-capture-laser.png b/doc/readthedocs/source/_static/workbenches/adjustment-scan-capture-laser.png index 576531b3..cebc82d7 100644 Binary files a/doc/readthedocs/source/_static/workbenches/adjustment-scan-capture-laser.png and b/doc/readthedocs/source/_static/workbenches/adjustment-scan-capture-laser.png differ diff --git a/doc/readthedocs/source/_static/workbenches/adjustment-scan-capture-texture.png b/doc/readthedocs/source/_static/workbenches/adjustment-scan-capture-texture.png index 2feb2104..98b88080 100644 Binary files a/doc/readthedocs/source/_static/workbenches/adjustment-scan-capture-texture.png and b/doc/readthedocs/source/_static/workbenches/adjustment-scan-capture-texture.png differ diff --git a/doc/readthedocs/source/_static/workbenches/adjustment-scan-segmentation-line.png b/doc/readthedocs/source/_static/workbenches/adjustment-scan-segmentation-line.png new file mode 100644 index 00000000..23d86019 Binary files /dev/null and b/doc/readthedocs/source/_static/workbenches/adjustment-scan-segmentation-line.png differ diff --git a/doc/readthedocs/source/_static/workbenches/adjustment-scan-segmentation.png b/doc/readthedocs/source/_static/workbenches/adjustment-scan-segmentation.png index 4c115bed..561a1055 100644 Binary files a/doc/readthedocs/source/_static/workbenches/adjustment-scan-segmentation.png and b/doc/readthedocs/source/_static/workbenches/adjustment-scan-segmentation.png differ diff --git a/doc/readthedocs/source/_static/workbenches/calibration-rotating-platform.png b/doc/readthedocs/source/_static/workbenches/calibration-rotating-platform.png new file mode 100644 index 00000000..d43801b5 Binary files /dev/null and b/doc/readthedocs/source/_static/workbenches/calibration-rotating-platform.png differ diff --git a/doc/readthedocs/source/scanner-components/board.rst b/doc/readthedocs/source/scanner-components/board.rst index 53dad3ed..b20b8058 100644 --- a/doc/readthedocs/source/scanner-components/board.rst +++ b/doc/readthedocs/source/scanner-components/board.rst @@ -12,8 +12,17 @@ ZUM BT-328 .. image:: ../_static/scanner-components/zum.jpg :width: 250 px -ZUM SCAN Shield -``````````````` +Arduino UNO +``````````` + +.. image:: ../_static/scanner-components/arduino-uno.jpg + :width: 250 px + +Supported shields +----------------- + +ZUM SCAN +```````` .. image:: ../_static/scanner-components/zum-scan.jpg :width: 250 px @@ -22,9 +31,26 @@ ZUM SCAN Shield Horus Firmware -------------- -The firmware is an adaptation of Grbl. Source code is here: https://github.com/bqlabs/horus-fw. +The firmware is an adaptation of Grbl. Releases are hosted here: https://github.com/bqlabs/horus-fw/releases. It can be uploaded in **Preferences > Upload firmware**. -.. Troubleshooting -.. --------------- +Troubleshooting +--------------- + +Board not detected +`````````````````` + + Sometimes **Arduino UNO** board is not detected correctly, specially if you are using **Windows 10** or **strange clones**. First of all, follow this steps to ensure that your board and your operating system are working fine together: + + 1. Follow this guide to install the drivers and the Arduino IDE: https://www.arduino.cc/en/Guide/Windows. + 2. Upload the `blink` example. If it doesn't work go to: https://forum.arduino.cc/ to find a solution. + + Then if the led is blinking is time to upload the Horus firmware. You have two options: + + - In Horus GUI, go to *Preferences*. Select your board and press *Upload firmware*. + - In Arduino IDE, download the source code (https://github.com/bqlabs/horus-fw) and upload the file horus-fw.ino. + + .. hint:: + + If you have any problem related to the board or the shield please put it here: https://github.com/bqlabs/horus/issues to update this manual. diff --git a/doc/readthedocs/source/workbenches/adjustment.rst b/doc/readthedocs/source/workbenches/adjustment.rst index 44d4d0a0..7bc3edef 100644 --- a/doc/readthedocs/source/workbenches/adjustment.rst +++ b/doc/readthedocs/source/workbenches/adjustment.rst @@ -28,6 +28,7 @@ Segmentation In this section you can adjust the parameters for the laser stripe segmentation during the scanning process. +* **Draw line**: show the computed lines in red from the segmented laser image. * **Threshold**: remove all pixels which intensity is less that the threshold value. * **Blur**: blur with Normalized box filter. Kernel size: 2 * value + 1. * **Window**: filter pixels out of 2 * window value around the intensity peak. @@ -35,6 +36,8 @@ In this section you can adjust the parameters for the laser stripe segmentation .. image:: ../_static/workbenches/adjustment-scan-segmentation.png +.. image:: ../_static/workbenches/adjustment-scan-segmentation-line.png + Calibration adjustments ----------------------- diff --git a/src/horus/engine/algorithms/image_capture.py b/src/horus/engine/algorithms/image_capture.py index c5187435..0edd4183 100644 --- a/src/horus/engine/algorithms/image_capture.py +++ b/src/horus/engine/algorithms/image_capture.py @@ -107,17 +107,17 @@ def set_mode_laser(self): def set_mode_pattern(self): self.set_mode(self.pattern_mode) - def flush_texture(self): + def flush_texture(self, value=0): self.set_mode_texture() - self.capture_image(flush=0) + self.capture_image(flush=value) - def flush_laser(self): + def flush_laser(self, value=0): self.set_mode_laser() - self.capture_image(flush=0) + self.capture_image(flush=value) - def flush_pattern(self): + def flush_pattern(self, value=0): self.set_mode_pattern() - self.capture_image(flush=0) + self.capture_image(flush=value) def capture_texture(self): self.set_mode(self.texture_mode) diff --git a/src/horus/engine/algorithms/image_detection.py b/src/horus/engine/algorithms/image_detection.py index 32d34133..4406e4cd 100644 --- a/src/horus/engine/algorithms/image_detection.py +++ b/src/horus/engine/algorithms/image_detection.py @@ -19,6 +19,7 @@ class ImageDetection(object): def __init__(self): self.pattern = Pattern() self.calibration_data = CalibrationData() + self.chessboard_mask = None self._criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) @@ -70,6 +71,8 @@ def pattern_mask(self, image, corners): points = np.array([p1, p2, p4, p3]) cv2.fillConvexPoly(mask, points, 255) image = cv2.bitwise_and(image, image, mask=mask) + if self.chessboard_mask is not None: + image = cv2.bitwise_and(image, image, mask=self.chessboard_mask) return image def _detect_chessboard(self, image): @@ -79,5 +82,9 @@ def _detect_chessboard(self, image): ret, corners = cv2.findChessboardCorners( gray, (self.pattern.columns, self.pattern.rows), flags=cv2.CALIB_CB_FAST_CHECK) if ret: + self.chessboard_mask = cv2.threshold( + gray, gray.max() / 2, 255, cv2.THRESH_BINARY)[1] cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), self._criteria) return corners + else: + self.chessboard_mask = None diff --git a/src/horus/engine/algorithms/laser_segmentation.py b/src/horus/engine/algorithms/laser_segmentation.py index 82bc2f8b..c366537a 100644 --- a/src/horus/engine/algorithms/laser_segmentation.py +++ b/src/horus/engine/algorithms/laser_segmentation.py @@ -64,10 +64,12 @@ def compute_2d_points(self, image): u = (self.calibration_data.weight_matrix * image).sum(axis=1)[v] / s[v] if self.refinement_method == 'SGF': # Segmented gaussian filter - u, v = self._sgf(u, v, s) + u = self._sgf(u, s) elif self.refinement_method == 'RANSAC': # Random sample consensus - u, v = self._ransac(u, v) + u = self._ransac(u, v) + # Saturate u + u = np.clip(u, 0, self.calibration_data.width - 1) return (u, v), image def compute_hough_lines(self, image): @@ -81,24 +83,20 @@ def compute_hough_lines(self, image): # u2 = u1 - height * np.tan(theta) return lines - def compute_line_segmentation(self, image, roi_mask=False): + def compute_line_segmentation(self, image): if image is not None: # Apply ROI mask - if roi_mask: - image = self.point_cloud_roi.mask_image(image) - # Obtain red channel + image = self.point_cloud_roi.mask_image(image) image = self._obtain_red_channel(image) - if image is not None: - # Threshold image - image = self._threshold_image(image) - # Window mask - image = self._window_mask(image) + image = self._threshold_image(image) + image = self._window_mask(image) return image def _obtain_red_channel(self, image): ret = None if self.red_channel == 'R (RGB)': ret = cv2.split(image)[0] + # ret = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) elif self.red_channel == 'Cr (YCrCb)': ret = cv2.split(cv2.cvtColor(image, cv2.COLOR_RGB2YCR_CB))[1] elif self.red_channel == 'U (YUV)': @@ -107,29 +105,31 @@ def _obtain_red_channel(self, image): def _threshold_image(self, image): if self.threshold_enable: - image = cv2.threshold( - image, self.threshold_value, 255, cv2.THRESH_TOZERO)[1] - if self.blur_enable: - image = cv2.blur(image, (self.blur_value, self.blur_value)) - image = cv2.threshold( - image, self.threshold_value, 255, cv2.THRESH_TOZERO)[1] + if image is not None: + image = cv2.threshold( + image, self.threshold_value, 255, cv2.THRESH_TOZERO)[1] + if self.blur_enable: + image = cv2.blur(image, (self.blur_value, self.blur_value)) + image = cv2.threshold( + image, self.threshold_value, 255, cv2.THRESH_TOZERO)[1] return image def _window_mask(self, image): if self.window_enable: - peak = image.argmax(axis=1) - _min = peak - self.window_value - _max = peak + self.window_value + 1 - mask = np.zeros_like(image) - for i in xrange(self.calibration_data.height): - mask[i, _min[i]:_max[i]] = 255 - # Apply mask - image = cv2.bitwise_and(image, mask) + if image is not None: + peak = image.argmax(axis=1) + _min = peak - self.window_value + _max = peak + self.window_value + 1 + mask = np.zeros_like(image) + for i in xrange(self.calibration_data.height): + mask[i, _min[i]:_max[i]] = 255 + # Apply mask + image = cv2.bitwise_and(image, mask) return image # Segmented gaussian filter - def _sgf(self, u, v, s): + def _sgf(self, u, s): if len(u) > 1: i = 0 sigma = 2.0 @@ -142,18 +142,19 @@ def _sgf(self, u, v, s): fseg = scipy.ndimage.gaussian_filter(u[i:i + j], sigma=sigma) f = np.concatenate((f, fseg)) i += j - return f, v + return f else: - return u, v + return u # RANSAC implementation: https://github.com/ahojnnes/numpy-snippets/blob/master/ransac.py def _ransac(self, u, v): if len(u) > 1: data = np.vstack((v.ravel(), u.ravel())).T - dr, thetar = self.ransac(data, self.LinearLeastSquares2D(), 2, 2) + dr, thetar = self.ransac(data, self.LinearLeastSquares2D(), 2, 1) + # v = np.array(range(min(v), max(v))) u = (dr - v * math.sin(thetar)) / math.cos(thetar) - return u, v + return u class LinearLeastSquares2D(object): ''' diff --git a/src/horus/engine/algorithms/point_cloud_generation.py b/src/horus/engine/algorithms/point_cloud_generation.py index 3b28d798..a38d891e 100644 --- a/src/horus/engine/algorithms/point_cloud_generation.py +++ b/src/horus/engine/algorithms/point_cloud_generation.py @@ -7,6 +7,7 @@ import numpy as np +import cv2 from horus import Singleton from horus.engine.calibration.calibration_data import CalibrationData @@ -19,6 +20,9 @@ def __init__(self): self.calibration_data = CalibrationData() def compute_point_cloud(self, theta, points_2d, index): + if points_2d[0].size == 0 or points_2d[1].size == 0: + return None + # Load calibration values R = np.matrix(self.calibration_data.platform_rotation) t = np.matrix(self.calibration_data.platform_translation).T @@ -29,10 +33,7 @@ def compute_point_cloud(self, theta, points_2d, index): Rz = np.matrix([[c, -s, 0], [s, c, 0], [0, 0, 1]]) Xw = Rz * Xwo # Return point cloud - if Xw.size > 0: - return np.array(Xw) - else: - return None + return np.array(Xw) def compute_platform_point_cloud(self, points_2d, R, t, index): # Load calibration values @@ -40,17 +41,29 @@ def compute_platform_point_cloud(self, points_2d, R, t, index): d = self.calibration_data.laser_planes[index].distance # Camera system Xc = self.compute_camera_point_cloud(points_2d, d, n) - # Compute platform transformation + # Transformation to platform system return R.T * Xc - R.T * t def compute_camera_point_cloud(self, points_2d, d, n): - # Load calibration values - fx = self.calibration_data.camera_matrix[0][0] - fy = self.calibration_data.camera_matrix[1][1] - cx = self.calibration_data.camera_matrix[0][2] - cy = self.calibration_data.camera_matrix[1][2] + if points_2d[0].size == 0 or points_2d[1].size == 0: + return np.array([]).reshape(3, 0) + # Compute projection point u, v = points_2d - x = np.concatenate(((u - cx) / fx, (v - cy) / fy, np.ones(len(u)))).reshape(3, len(u)) - # Compute laser intersection - return d / np.dot(n, x) * x + points_for_undistort = np.array([np.concatenate((u, v)).reshape(2, len(u)).T]) + + # use opencv's undistortPoints, which incorporates the distortion coefficients + points_undistorted = cv2.undistortPoints(points_for_undistort, self.calibration_data.camera_matrix, self.calibration_data.distortion_vector) + + u, v = np.hsplit(points_undistorted[0], points_undistorted[0].shape[1]) + + # make homogenous coordinates + x = np.concatenate((u.T[0], v.T[0], np.ones(len(u)))).reshape(3, len(u)) + # normalize to get unit direction vectors + cam_point_direction = x / np.linalg.norm(x, axis=0) + + # Compute laser intersection: + # dlc = dot(laser_normal, cam_point_direction) = projection of camera ray on laser-plane normal + # d / dlc = distance from cam center to 3D point + # cam_point_direction * d / dlc = 3D point + return d / np.dot(n, cam_point_direction) * cam_point_direction diff --git a/src/horus/engine/algorithms/point_cloud_roi.py b/src/horus/engine/algorithms/point_cloud_roi.py index 09b8d50a..c8423cd9 100644 --- a/src/horus/engine/algorithms/point_cloud_roi.py +++ b/src/horus/engine/algorithms/point_cloud_roi.py @@ -88,7 +88,7 @@ def mask_point_cloud(self, point_cloud, texture): def draw_cross(self, image): if self._center_v != 0 and self._center_u != 0 and self._show_center: - thickness = 3 + thickness = 2 v_max, u_max, _ = image.shape cv2.line(image, (0, self._center_v), (u_max, self._center_v), (200, 0, 0), thickness) cv2.line(image, (self._center_u, 0), (self._center_u, v_max), (200, 0, 0), thickness) diff --git a/src/horus/engine/calibration/autocheck.py b/src/horus/engine/calibration/autocheck.py index 81bf6767..2257de89 100644 --- a/src/horus/engine/calibration/autocheck.py +++ b/src/horus/engine/calibration/autocheck.py @@ -5,7 +5,6 @@ __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' -import time import numpy as np from horus import Singleton @@ -47,6 +46,7 @@ class Autocheck(Calibration): def __init__(self): self.image = None + self.current_angle = 0 Calibration.__init__(self) def _start(self): @@ -54,6 +54,7 @@ def _start(self): ret = False response = None self.image = None + self.current_angle = 0 self._is_calibrating = True self.image_capture.stream = False @@ -71,6 +72,8 @@ def _start(self): ret = True except Exception as exception: response = exception + if isinstance(exception, CalibrationCancel): + self._cancel() finally: self._is_calibrating = False self.image_capture.stream = True @@ -82,6 +85,12 @@ def _start(self): self._after_callback((ret, response)) self.image = None + def _cancel(self): + # Return to origin + if self.current_angle > 180: + self.current_angle = self.current_angle - 360 + self.driver.board.motor_move(-self.current_angle) + def check_pattern_and_motor(self): scan_step = 30 patterns_detected = {} @@ -92,6 +101,7 @@ def check_pattern_and_motor(self): # Capture data for i in xrange(0, 360, scan_step): + self.current_angle = i if not self._is_calibrating: raise CalibrationCancel() image = self.image_capture.capture_pattern() diff --git a/src/horus/engine/calibration/camera_intrinsics.py b/src/horus/engine/calibration/camera_intrinsics.py index 8af51861..a52bd708 100644 --- a/src/horus/engine/calibration/camera_intrinsics.py +++ b/src/horus/engine/calibration/camera_intrinsics.py @@ -64,7 +64,7 @@ def capture(self): def calibrate(self): error = 0 ret, cmat, dvec, rvecs, tvecs = cv2.calibrateCamera( - self.object_points, self.image_points, self.shape) + self.object_points, self.image_points, self.shape, None, None) if ret: # Compute calibration error diff --git a/src/horus/engine/calibration/combo_calibration.py b/src/horus/engine/calibration/combo_calibration.py index 0ffc1245..7a71d850 100644 --- a/src/horus/engine/calibration/combo_calibration.py +++ b/src/horus/engine/calibration/combo_calibration.py @@ -5,6 +5,7 @@ __copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.' __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' +import math import numpy as np from horus import Singleton @@ -48,22 +49,25 @@ def _capture(self, angle): if plane is not None: distance, normal, corners = plane - # Laser triangulation - if (angle > 65 and angle < 115): - self.image_capture.flush_laser() - self.image_capture.flush_laser() + # Angle between the pattern and the camera + alpha = np.rad2deg(math.acos(normal[2])) * math.copysign(1, normal[0]) + if abs(alpha) < 30: + self.image_capture.flush_laser(14) for i in xrange(2): - image = self.image_capture.capture_laser(i) - image = self.image_detection.pattern_mask(image, corners) - self.image = image - points_2d, _ = self.laser_segmentation.compute_2d_points(image) - point_3d = self.point_cloud_generation.compute_camera_point_cloud( - points_2d, distance, normal) - if self._point_cloud[i] is None: - self._point_cloud[i] = point_3d.T - else: - self._point_cloud[i] = np.concatenate( - (self._point_cloud[i], point_3d.T)) + if (i == 0 and alpha < 10) or (i == 1 and alpha > -10): + image = self.image_capture.capture_laser(i) + image = self.image_detection.pattern_mask(image, corners) + self.image = image + points_2d, image = self.laser_segmentation.compute_2d_points(image) + point_3d = self.point_cloud_generation.compute_camera_point_cloud( + points_2d, distance, normal) + if self._point_cloud[i] is None: + self._point_cloud[i] = point_3d.T + else: + self._point_cloud[i] = np.concatenate( + (self._point_cloud[i], point_3d.T)) + else: + self.image = image # Platform extrinsics origin = corners[self.pattern.columns * (self.pattern.rows - 1)][0] diff --git a/src/horus/engine/calibration/laser_triangulation.py b/src/horus/engine/calibration/laser_triangulation.py index 44acee60..90753f6e 100644 --- a/src/horus/engine/calibration/laser_triangulation.py +++ b/src/horus/engine/calibration/laser_triangulation.py @@ -6,6 +6,7 @@ __license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html' import struct +import math import numpy as np from horus import Singleton @@ -45,25 +46,28 @@ def _initialize(self): def _capture(self, angle): image = self.image_capture.capture_pattern() - if (angle > 65 and angle < 115): - pose = self.image_detection.detect_pose(image) - plane = self.image_detection.detect_pattern_plane(pose) - self.image_capture.flush_laser() - self.image_capture.flush_laser() - if plane is not None: - distance, normal, corners = plane + pose = self.image_detection.detect_pose(image) + plane = self.image_detection.detect_pattern_plane(pose) + if plane is not None: + distance, normal, corners = plane + + # Angle between the pattern and the camera + alpha = np.rad2deg(math.acos(normal[2])) * math.copysign(1, normal[0]) + if abs(alpha) < 30: + self.image_capture.flush_laser(14) for i in xrange(2): - image = self.image_capture.capture_laser(i) - image = self.image_detection.pattern_mask(image, corners) - self.image = image - points_2d, image = self.laser_segmentation.compute_2d_points(image) - point_3d = self.point_cloud_generation.compute_camera_point_cloud( - points_2d, distance, normal) - if self._point_cloud[i] is None: - self._point_cloud[i] = point_3d.T - else: - self._point_cloud[i] = np.concatenate( - (self._point_cloud[i], point_3d.T)) + if (i == 0 and alpha < 10) or (i == 1 and alpha > -10): + image = self.image_capture.capture_laser(i) + image = self.image_detection.pattern_mask(image, corners) + self.image = image + points_2d, image = self.laser_segmentation.compute_2d_points(image) + point_3d = self.point_cloud_generation.compute_camera_point_cloud( + points_2d, distance, normal) + if self._point_cloud[i] is None: + self._point_cloud[i] = point_3d.T + else: + self._point_cloud[i] = np.concatenate( + (self._point_cloud[i], point_3d.T)) else: self.image = image else: diff --git a/src/horus/engine/calibration/moving_calibration.py b/src/horus/engine/calibration/moving_calibration.py index 8402f99b..14bd379b 100644 --- a/src/horus/engine/calibration/moving_calibration.py +++ b/src/horus/engine/calibration/moving_calibration.py @@ -20,7 +20,9 @@ class MovingCalibration(Calibration): def __init__(self): Calibration.__init__(self) - self.step = 3 + self.motor_step = 0 + self.motor_speed = 0 + self.motor_acceleration = 0 def _initialize(self): raise NotImplementedError @@ -41,8 +43,8 @@ def _start(self): self.driver.board.lasers_off() self.driver.board.motor_enable() self.driver.board.motor_reset_origin() - self.driver.board.motor_speed(200) - self.driver.board.motor_acceleration(200) + self.driver.board.motor_speed(self.motor_speed) + self.driver.board.motor_acceleration(self.motor_acceleration) # Move to starting position self.driver.board.motor_move(-90) @@ -57,8 +59,8 @@ def _start(self): self._capture(angle) - angle += self.step - self.driver.board.motor_move(self.step) + angle += self.motor_step + self.driver.board.motor_move(self.motor_step) time.sleep(0.5) # Move to origin diff --git a/src/horus/engine/driver/camera.py b/src/horus/engine/driver/camera.py index 02b6cd5c..27f8e825 100644 --- a/src/horus/engine/driver/camera.py +++ b/src/horus/engine/driver/camera.py @@ -20,6 +20,33 @@ import uvc from uvc.mac import * +def check_opencv_major_version(v): + if cv2.__version__.split(".")[0] == v: + return True + +# Get the correct cv2 constant for OpenCV v2 or v3, depending on what the +# user has installed. +def cv_const(c): + if check_opencv_major_version("2"): + return { + "CAP_PROP_BRIGHTNESS": cv2.cv.CV_CAP_PROP_BRIGHTNESS, + "CAP_PROP_CONTRAST": cv2.cv.CV_CAP_PROP_CONTRAST, + "CAP_PROP_SATURATION": cv2.cv.CV_CAP_PROP_SATURATION, + "CAP_PROP_EXPOSURE": cv2.cv.CV_CAP_PROP_EXPOSURE, + "CAP_PROP_FPS": cv2.cv.CV_CAP_PROP_FPS, + "CAP_PROP_FRAME_WIDTH": cv2.cv.CV_CAP_PROP_FRAME_WIDTH, + "CAP_PROP_FRAME_HEIGHT": cv2.cv.CV_CAP_PROP_FRAME_HEIGHT + }[c] + if check_opencv_major_version("3"): + return { + "CAP_PROP_BRIGHTNESS": cv2.CAP_PROP_BRIGHTNESS, + "CAP_PROP_CONTRAST": cv2.CAP_PROP_CONTRAST, + "CAP_PROP_SATURATION": cv2.CAP_PROP_SATURATION, + "CAP_PROP_EXPOSURE": cv2.CAP_PROP_EXPOSURE, + "CAP_PROP_FPS": cv2.CAP_PROP_FPS, + "CAP_PROP_FRAME_WIDTH": cv2.CAP_PROP_FRAME_WIDTH, + "CAP_PROP_FRAME_HEIGHT": cv2.CAP_PROP_FRAME_HEIGHT + }[c] class WrongCamera(Exception): @@ -242,7 +269,7 @@ def set_brightness(self, value): ctl.set_val(self._line(value, 0, self._max_brightness, ctl.min, ctl.max)) else: value = int(value) / self._max_brightness - ret = self._capture.set(cv2.cv.CV_CAP_PROP_BRIGHTNESS, value) + ret = self._capture.set(cv_const("CAP_PROP_BRIGHTNESS"), value) if system == 'Linux' and ret: raise InputOutputError() self._updating = False @@ -257,7 +284,7 @@ def set_contrast(self, value): ctl.set_val(self._line(value, 0, self._max_contrast, ctl.min, ctl.max)) else: value = int(value) / self._max_contrast - ret = self._capture.set(cv2.cv.CV_CAP_PROP_CONTRAST, value) + ret = self._capture.set(cv_const("CAP_PROP_CONTRAST"), value) if system == 'Linux' and ret: raise InputOutputError() self._updating = False @@ -272,7 +299,7 @@ def set_saturation(self, value): ctl.set_val(self._line(value, 0, self._max_saturation, ctl.min, ctl.max)) else: value = int(value) / self._max_saturation - ret = self._capture.set(cv2.cv.CV_CAP_PROP_SATURATION, value) + ret = self._capture.set(cv_const("CAP_PROP_SATURATION"), value) if system == 'Linux' and ret: raise InputOutputError() self._updating = False @@ -291,10 +318,10 @@ def set_exposure(self, value, force=False): ctl.set_val(value) elif system == 'Windows': value = int(round(-math.log(value) / math.log(2))) - self._capture.set(cv2.cv.CV_CAP_PROP_EXPOSURE, value) + self._capture.set(cv_const("CAP_PROP_EXPOSURE"), value) else: value = int(value) / self._max_exposure - ret = self._capture.set(cv2.cv.CV_CAP_PROP_EXPOSURE, value) + ret = self._capture.set(cv_const("CAP_PROP_EXPOSURE"), value) if system == 'Linux' and ret: raise InputOutputError() self._updating = False @@ -313,7 +340,7 @@ def set_frame_rate(self, value): if self._frame_rate != value: self._frame_rate = value self._updating = True - self._capture.set(cv2.cv.CV_CAP_PROP_FPS, value) + self._capture.set(cv_const("CAP_PROP_FPS"), value) self._updating = False def set_resolution(self, width, height): @@ -326,14 +353,14 @@ def set_resolution(self, width, height): self._updating = False def _set_width(self, value): - self._capture.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH, value) + self._capture.set(cv_const("CAP_PROP_FRAME_WIDTH"), value) def _set_height(self, value): - self._capture.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT, value) + self._capture.set(cv_const("CAP_PROP_FRAME_HEIGHT"), value) def _update_resolution(self): - self._width = int(self._capture.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH)) - self._height = int(self._capture.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT)) + self._width = int(self._capture.get(cv_const("CAP_PROP_FRAME_WIDTH"))) + self._height = int(self._capture.get(cv_const("CAP_PROP_FRAME_HEIGHT"))) def get_brightness(self): if self._is_connected: @@ -341,7 +368,7 @@ def get_brightness(self): ctl = self.controls['UVCC_REQ_BRIGHTNESS_ABS'] value = ctl.get_val() else: - value = self._capture.get(cv2.cv.CV_CAP_PROP_BRIGHTNESS) + value = self._capture.get(cv_const("CAP_PROP_BRIGHTNESS")) value *= self._max_brightness return value @@ -352,10 +379,10 @@ def get_exposure(self): value = ctl.get_val() value /= self._rel_exposure elif system == 'Windows': - value = self._capture.get(cv2.cv.CV_CAP_PROP_EXPOSURE) + value = self._capture.get(cv_const("CAP_PROP_EXPOSURE")) value = 2 ** -value else: - value = self._capture.get(cv2.cv.CV_CAP_PROP_EXPOSURE) + value = self._capture.get(cv_const("CAP_PROP_EXPOSURE")) value *= self._max_exposure return value diff --git a/src/horus/engine/scan/current_video.py b/src/horus/engine/scan/current_video.py index cae2a8d8..08b50596 100644 --- a/src/horus/engine/scan/current_video.py +++ b/src/horus/engine/scan/current_video.py @@ -59,7 +59,9 @@ def _compute_line_image(self, points, image): if points is not None: u, v = points image = np.zeros_like(image) + image[v.astype(int), np.around(u).astype(int) - 1] = 255 image[v.astype(int), np.around(u).astype(int)] = 255 + image[v.astype(int), np.around(u).astype(int) + 1] = 255 return image def capture(self): diff --git a/src/horus/gui/util/preferences.py b/src/horus/gui/util/preferences.py index d5798fc8..f9a33b4d 100644 --- a/src/horus/gui/util/preferences.py +++ b/src/horus/gui/util/preferences.py @@ -206,7 +206,7 @@ def load_firmware(self, hex_baud_rate, clear_eeprom): time.sleep(3) # Clear EEPROM self.count = -50 out = avr_dude.flash(hex_path=self.hex_path, callback=self.increment_progress) - if 'not in sync' in out or 'Invalid' in out: + if 'not in sync' in out or 'Invalid' in out or 'is not responding' in out: wx.CallAfter(self.wrong_board_message) wx.CallAfter(self.after_load_firmware) except Exception as e: diff --git a/src/horus/gui/util/video_view.py b/src/horus/gui/util/video_view.py index e55d7204..b4c98307 100644 --- a/src/horus/gui/util/video_view.py +++ b/src/horus/gui/util/video_view.py @@ -41,8 +41,8 @@ def on_timer(self, event): else: wx.CallAfter(self.set_frame, frame) self._start() - except: - pass + except Exception as e: + print(e.message) def set_callback(self, callback): self.callback = callback diff --git a/src/horus/gui/welcome.py b/src/horus/gui/welcome.py index 8cbbbd12..36726f8d 100644 --- a/src/horus/gui/welcome.py +++ b/src/horus/gui/welcome.py @@ -65,7 +65,7 @@ def __init__(self, parent): logo.set_image(wx.Image(resources.get_path_for_image("logo.png"))) title_text = wx.StaticText(self, label=_("3D scanning for everyone")) title_font = title_text.GetFont() - title_font.SetPointSize(14) + title_font.SetPointSize(13) title_text.SetFont(title_font) separator = wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL) @@ -114,7 +114,6 @@ def on_wizard(self, event): parent = self.GetParent().GetParent() parent.Hide() Wizard(parent.parent) - parent.Close() def on_scan(self, event): profile.settings['workbench'] = 'scanning' diff --git a/src/horus/gui/wizard/connection_page.py b/src/horus/gui/wizard/connection_page.py index 7781dae0..07219da3 100644 --- a/src/horus/gui/wizard/connection_page.py +++ b/src/horus/gui/wizard/connection_page.py @@ -99,6 +99,14 @@ def on_connect_button_clicked(self, event): driver.disconnect() self.update_status(driver.is_connected) else: + # If no camera id is selected + video_list = driver.camera.get_video_list() + current_video_id = profile.settings['camera_id'] + if len(video_list) > 0: + if current_video_id not in video_list: + profile.settings['camera_id'] = unicode(video_list[0]) + driver.camera.camera_id = int(profile.settings['camera_id'][-1:]) + driver.set_callbacks( lambda: wx.CallAfter(self.before_connect), lambda r: wx.CallAfter(self.after_connect, r)) @@ -157,7 +165,11 @@ def after_connect(self, response): dlg.ShowModal() dlg.Destroy() self.update_status(False) + wrong_camera_id = profile.settings['camera_id'] self.GetParent().parent.launch_preferences(basic=True) + # Do not save camera id if it is wrong + if profile.settings['camera_id'] == wrong_camera_id: + profile.settings['camera_id'] = '' elif isinstance(result, CameraNotConnected): dlg = wx.MessageDialog( self, _("Please plug your camera in and try to connect again"), diff --git a/src/horus/gui/wizard/main.py b/src/horus/gui/wizard/main.py index 35d332f2..a5278a15 100644 --- a/src/horus/gui/wizard/main.py +++ b/src/horus/gui/wizard/main.py @@ -20,7 +20,7 @@ class Wizard(wx.Dialog): def __init__(self, parent): if sys.is_windows(): - size = (760, 540) + size = (760, 560) else: size = (760, 520) super(Wizard, self).__init__(parent, title="", size=size) diff --git a/src/horus/gui/workbench/adjustment/current_video.py b/src/horus/gui/workbench/adjustment/current_video.py index 7447508a..648b316b 100644 --- a/src/horus/gui/workbench/adjustment/current_video.py +++ b/src/horus/gui/workbench/adjustment/current_video.py @@ -7,6 +7,7 @@ import cv2 import time +import numpy as np from horus import Singleton from horus.gui.engine import image_capture, image_detection, laser_segmentation @@ -20,6 +21,11 @@ def __init__(self): self.updating = False self.latest_image = None self.capturing = False + self.calibration = False + self.draw_line = True + + def set_draw_line(self, value): + self.draw_line = value def get_frame(self): if not self.updating: @@ -38,21 +44,49 @@ def capture(self): image = image_detection.detect_pattern(image) if self.mode == 'Laser': - image = image_capture.capture_all_lasers() + if self.calibration: + image = image_capture.capture_pattern() + corners = image_detection.detect_corners(image) + image_capture.flush_laser(14) + image = image_capture.capture_all_lasers() + image = image_detection.pattern_mask(image, corners) + else: + image = image_capture.capture_all_lasers() if self.mode == 'Gray': - images = image_capture.capture_lasers() - for i in xrange(2): - images[i] = laser_segmentation.compute_line_segmentation(images[i]) + if self.calibration: + image = image_capture.capture_pattern() + corners = image_detection.detect_corners(image) + image_capture.flush_laser(14) + images = image_capture.capture_lasers() + for i in xrange(2): + images[i] = image_detection.pattern_mask(images[i], corners) + images[i] = laser_segmentation.compute_line_segmentation(images[i]) + images[i] = cv2.cvtColor(images[i], cv2.COLOR_GRAY2RGB) + else: + images = image_capture.capture_lasers() + for i in xrange(2): + images[i] = laser_segmentation.compute_line_segmentation(images[i]) + (u, v), _ = laser_segmentation.compute_2d_points(images[i]) + images[i] = cv2.cvtColor(images[i], cv2.COLOR_GRAY2RGB) + if self.draw_line: + self._draw_line(images[i], u, v) + if images[0] is not None and images[1] is not None: - image = images[0] + images[1] - image = cv2.merge((image, image, image)) + image = np.maximum(images[0], images[1]) else: image = None self.capturing = False return image + def _draw_line(self, image, u, v): + v = v.astype(int) + u = np.around(u).astype(int) + image[v, u - 1] = (255, 0, 0) + image[v, u] = (255, 0, 0) + image[v, u + 1] = (255, 0, 0) + def sync(self): # Wait until latest capture is completed while self.capturing: diff --git a/src/horus/gui/workbench/adjustment/panels.py b/src/horus/gui/workbench/adjustment/panels.py index b406b68d..1b3b6ba7 100644 --- a/src/horus/gui/workbench/adjustment/panels.py +++ b/src/horus/gui/workbench/adjustment/panels.py @@ -86,6 +86,7 @@ def on_selected(self): current_video.updating = True current_video.sync() # Update mode settings + current_video.calibration = False current_video.mode = profile.settings['capture_mode_scanning'] texture_mode = image_capture.texture_mode texture_mode.set_brightness(profile.settings['brightness_texture_scanning']) @@ -139,7 +140,7 @@ def _set_mode_layout(self, mode): self.content.SetSizerAndFit(self.content.vbox) if sys.is_windows(): self.parent.Refresh() - self.parent.Layout() + self.parent.Layout() self.Layout() @@ -150,6 +151,7 @@ def __init__(self, parent, on_selected_callback): def add_controls(self): # self.add_control('red_channel_scanning', ComboBox) + self.add_control('draw_line_scanning', CheckBox) self.add_control( 'threshold_value_scanning', Slider, _("Remove all pixels which intensity is less that the threshold value")) @@ -172,6 +174,7 @@ def add_controls(self): def update_callbacks(self): # self.update_callback('red_channel_scanning', laser_segmentation.set_red_channel) + self.update_callback('draw_line_scanning', current_video.set_draw_line) self.update_callback('threshold_value_scanning', laser_segmentation.set_threshold_value) self.update_callback('threshold_enable_scanning', laser_segmentation.set_threshold_enable) self.update_callback('blur_value_scanning', laser_segmentation.set_blur_value) @@ -184,6 +187,7 @@ def on_selected(self): current_video.updating = True current_video.sync() # Update mode settings + current_video.calibration = False current_video.mode = 'Gray' laser_mode = image_capture.laser_mode laser_mode.set_brightness(profile.settings['brightness_laser_scanning']) @@ -191,6 +195,7 @@ def on_selected(self): laser_mode.set_saturation(profile.settings['saturation_laser_scanning']) laser_mode.set_exposure(profile.settings['exposure_laser_scanning']) image_capture.set_remove_background(profile.settings['remove_background_scanning']) + current_video.set_draw_line(profile.settings['draw_line_scanning']) laser_segmentation.set_red_channel(profile.settings['red_channel_scanning']) laser_segmentation.set_threshold_value(profile.settings['threshold_value_scanning']) laser_segmentation.set_threshold_enable(profile.settings['threshold_enable_scanning']) @@ -276,6 +281,7 @@ def on_selected(self): current_video.updating = True current_video.sync() # Update mode settings + current_video.calibration = True current_video.mode = profile.settings['capture_mode_calibration'] profile.settings['current_video_mode_adjustment'] = current_video.mode profile.settings['current_panel_adjustment'] = 'calibration_capture' @@ -329,7 +335,7 @@ def _set_mode_layout(self, mode): self.content.SetSizerAndFit(self.content.vbox) if sys.is_windows(): self.parent.Refresh() - self.parent.Layout() + self.parent.Layout() self.Layout() @@ -375,6 +381,7 @@ def on_selected(self): current_video.updating = True current_video.sync() # Update mode settings + current_video.calibration = True current_video.mode = 'Gray' profile.settings['current_video_mode_adjustment'] = current_video.mode profile.settings['current_panel_adjustment'] = 'calibration_segmentation' diff --git a/src/horus/gui/workbench/calibration/main.py b/src/horus/gui/workbench/calibration/main.py index ff8309c4..534a2195 100644 --- a/src/horus/gui/workbench/calibration/main.py +++ b/src/horus/gui/workbench/calibration/main.py @@ -8,11 +8,11 @@ from horus.util import profile from horus.gui.engine import driver, pattern, calibration_data, image_capture, image_detection, \ - laser_segmentation + laser_segmentation, laser_triangulation, platform_extrinsics, combo_calibration from horus.gui.util.video_view import VideoView from horus.gui.workbench.workbench import Workbench from horus.gui.workbench.calibration.panels import PatternSettings, CameraIntrinsics, \ - ScannerAutocheck, LaserTriangulation, PlatformExtrinsics, VideoSettings + ScannerAutocheck, RotatingPlatform, LaserTriangulation, PlatformExtrinsics, VideoSettings from horus.gui.workbench.calibration.pages.camera_intrinsics import CameraIntrinsicsPages from horus.gui.workbench.calibration.pages.scanner_autocheck import ScannerAutocheckPages @@ -27,17 +27,26 @@ def __init__(self, parent): def add_panels(self): self.add_panel( - 'pattern_settings', PatternSettings, self.on_pattern_settings_selected) + 'pattern_settings', PatternSettings, + self.on_pattern_settings_selected) self.add_panel( - 'scanner_autocheck', ScannerAutocheck, self.on_scanner_autocheck_selected) + 'scanner_autocheck', ScannerAutocheck, + self.on_scanner_autocheck_selected) self.add_panel( - 'laser_triangulation', LaserTriangulation, self.on_laser_triangulation_selected) + 'rotating_platform_settings', RotatingPlatform, + self.on_rotating_platform_settings_selected) self.add_panel( - 'platform_extrinsics', PlatformExtrinsics, self.on_platform_extrinsics_selected) + 'laser_triangulation', LaserTriangulation, + self.on_laser_triangulation_selected) self.add_panel( - 'video_settings', VideoSettings, self.on_video_settings_selected) + 'platform_extrinsics', PlatformExtrinsics, + self.on_platform_extrinsics_selected) self.add_panel( - 'camera_intrinsics', CameraIntrinsics, self.on_camera_intrinsics_selected) + 'video_settings', VideoSettings, + self.on_video_settings_selected) + self.add_panel( + 'camera_intrinsics', CameraIntrinsics, + self.on_camera_intrinsics_selected) def add_pages(self): self.add_page('video_view', VideoView(self, self.get_image)) @@ -139,11 +148,24 @@ def setup_engine(self): calibration_data.set_resolution(width, height) calibration_data.camera_matrix = profile.settings['camera_matrix'] calibration_data.distortion_vector = profile.settings['distortion_vector'] + laser_triangulation.motor_step = profile.settings['motor_step_calibration'] + laser_triangulation.motor_speed = profile.settings['motor_speed_calibration'] + laser_triangulation.motor_acceleration = profile.settings['motor_acceleration_calibration'] + platform_extrinsics.motor_step = profile.settings['motor_step_calibration'] + platform_extrinsics.motor_speed = profile.settings['motor_speed_calibration'] + platform_extrinsics.motor_acceleration = profile.settings['motor_acceleration_calibration'] + combo_calibration.motor_step = profile.settings['motor_step_calibration'] + combo_calibration.motor_speed = profile.settings['motor_speed_calibration'] + combo_calibration.motor_acceleration = profile.settings['motor_acceleration_calibration'] def on_pattern_settings_selected(self): profile.settings['current_panel_calibration'] = 'pattern_settings' self._on_panel_selected(self.pages_collection['video_view']) + def on_rotating_platform_settings_selected(self): + profile.settings['current_panel_calibration'] = 'rotating_platform_settings' + self._on_panel_selected(self.pages_collection['video_view']) + def on_video_settings_selected(self): profile.settings['current_panel_calibration'] = 'video_settings' self._on_panel_selected(self.pages_collection['video_view']) diff --git a/src/horus/gui/workbench/calibration/pages/laser_triangulation.py b/src/horus/gui/workbench/calibration/pages/laser_triangulation.py index d8b10593..80db3414 100644 --- a/src/horus/gui/workbench/calibration/pages/laser_triangulation.py +++ b/src/horus/gui/workbench/calibration/pages/laser_triangulation.py @@ -144,9 +144,11 @@ def process_calibration(self, response): nR = result[1][1] stdR = result[1][2] self.result = (dL, nL, dR, nR) + np.set_printoptions(formatter={'float': '{:g}'.format}) text = ' L: {0} {1} R: {2} {3}'.format( round(dL, 3), np.round(nL, 3), round(dR, 3), np.round(nR, 3)) + np.set_printoptions() self.desc_text.SetLabel(text) self.plot_panel.clear() self.plot_panel.add((dL, nL, stdL, dR, nR, stdR)) diff --git a/src/horus/gui/workbench/calibration/pages/platform_extrinsics.py b/src/horus/gui/workbench/calibration/pages/platform_extrinsics.py index 360b6a7e..5e97a140 100644 --- a/src/horus/gui/workbench/calibration/pages/platform_extrinsics.py +++ b/src/horus/gui/workbench/calibration/pages/platform_extrinsics.py @@ -142,8 +142,10 @@ def process_calibration(self, response): R = result[0] t = result[1] self.result = (R, t) + np.set_printoptions(formatter={'float': '{:g}'.format}) text = ' R: {0} t: {1}'.format( np.round(R, 2), np.round(t, 4)).replace('\n', '') + np.set_printoptions() self.desc_text.SetLabel(text) self.plot_panel.clear() self.plot_panel.add(result) diff --git a/src/horus/gui/workbench/calibration/pages/scanner_autocheck.py b/src/horus/gui/workbench/calibration/pages/scanner_autocheck.py index 543640fb..9d20d366 100644 --- a/src/horus/gui/workbench/calibration/pages/scanner_autocheck.py +++ b/src/horus/gui/workbench/calibration/pages/scanner_autocheck.py @@ -111,9 +111,9 @@ def on_start(self): scanner_autocheck.start() def on_cancel(self): + scanner_autocheck.cancel() self._initialize() self.video_page.right_button.Enable() - scanner_autocheck.cancel() if hasattr(self, 'wait_cursor'): del self.wait_cursor if self.exit_callback is not None: diff --git a/src/horus/gui/workbench/calibration/panels.py b/src/horus/gui/workbench/calibration/panels.py index 24751a17..c0005f26 100644 --- a/src/horus/gui/workbench/calibration/panels.py +++ b/src/horus/gui/workbench/calibration/panels.py @@ -7,7 +7,8 @@ import wx._core -from horus.gui.engine import driver, pattern, calibration_data +from horus.gui.engine import driver, pattern, calibration_data, laser_triangulation, \ + platform_extrinsics from horus.util import system as sys from horus.gui.util.custom_panels import ExpandablePanel, Slider, CheckBox, \ FloatTextBox, FloatTextBoxArray, FloatLabel, FloatLabelArray, Button, \ @@ -59,6 +60,39 @@ def __init__(self, parent, on_selected_callback): has_undo=False, has_restore=False) +class RotatingPlatform(ExpandablePanel): + + def __init__(self, parent, on_selected_callback): + ExpandablePanel.__init__( + self, parent, _("Rotating platform"), + selected_callback=on_selected_callback, has_undo=False) + + def add_controls(self): + self.add_control('motor_step_calibration', FloatTextBox, + _("Step for laser and platform calibration")) + self.add_control('motor_speed_calibration', FloatTextBox, + _("Speed for laser and platform calibration")) + self.add_control('motor_acceleration_calibration', FloatTextBox, + _("Acceleration for laser and platform calibration")) + + def update_callbacks(self): + self.update_callback('motor_step_calibration', self._set_step) + self.update_callback('motor_speed_calibration', self._set_speed) + self.update_callback('motor_acceleration_calibration', self._set_acceleration) + + def _set_step(self, value): + laser_triangulation.motor_step = value + platform_extrinsics.motor_step = value + + def _set_speed(self, value): + laser_triangulation.motor_speed = value + platform_extrinsics.motor_speed = value + + def _set_acceleration(self, value): + laser_triangulation.motor_acceleration = value + platform_extrinsics.motor_acceleration = value + + class LaserTriangulation(ExpandablePanel): def __init__(self, parent, on_selected_callback): @@ -79,8 +113,8 @@ def __init__(self, parent, on_selected_callback): selected_callback=on_selected_callback, has_undo=False) def add_controls(self): - self.add_control('rotation_matrix', FloatLabelArray) - self.add_control('translation_vector', FloatLabelArray) + self.add_control('rotation_matrix', FloatTextBoxArray) + self.add_control('translation_vector', FloatTextBoxArray) class VideoSettings(ExpandablePanel): diff --git a/src/horus/gui/workbench/toolbar.py b/src/horus/gui/workbench/toolbar.py index 98ab84f0..12568959 100644 --- a/src/horus/gui/workbench/toolbar.py +++ b/src/horus/gui/workbench/toolbar.py @@ -7,7 +7,7 @@ import wx._core -from horus.util import resources, system +from horus.util import resources, system, profile from horus.gui.engine import driver from horus.engine.driver.board import WrongFirmware, BoardNotConnected, OldFirmware @@ -64,6 +64,14 @@ def __init__( self.Bind(wx.EVT_TOOL, self.on_disconnect_tool_clicked, self.disconnect_tool) def on_connect_tool_clicked(self, event): + # If no camera id is selected + video_list = driver.camera.get_video_list() + current_video_id = profile.settings['camera_id'] + if len(video_list) > 0: + if current_video_id not in video_list: + profile.settings['camera_id'] = unicode(video_list[0]) + driver.camera.camera_id = int(profile.settings['camera_id'][-1:]) + driver.set_callbacks(lambda: wx.CallAfter(self.before_connect), lambda r: wx.CallAfter(self.after_connect, r)) driver.connect() @@ -107,7 +115,11 @@ def after_connect(self, response): _("You probably have selected the wrong camera.\n" "Please select another Camera ID")) self.update_status(False) + wrong_camera_id = profile.settings['camera_id'] self.GetParent().launch_preferences(basic=True) + # Do not save camera id if it is wrong + if profile.settings['camera_id'] == wrong_camera_id: + profile.settings['camera_id'] = '' elif isinstance(result, CameraNotConnected): self._show_message(_(result), wx.ICON_ERROR, _("Please plug your camera in and try to connect again")) diff --git a/src/horus/util/avr_helpers.py b/src/horus/util/avr_helpers.py index e32691c1..908db1b9 100644 --- a/src/horus/util/avr_helpers.py +++ b/src/horus/util/avr_helpers.py @@ -56,9 +56,14 @@ def _run_command(self, flags=[], callback=None): if not char: break out += char + if 'not in sync' in out or \ + 'Invalid' in out or \ + 'is not responding' in out: + break if char == '#': if callback is not None: callback() + p.kill() return out def flash(self, hex_path=None, clear_eeprom=False, callback=None): @@ -74,6 +79,8 @@ def flash(self, hex_path=None, clear_eeprom=False, callback=None): os.chdir(os.path.dirname(os.path.abspath(hex_path))) out = self._run_command(flags, callback) logger.info(' Upload completed') + except Exception as e: + print(e) finally: os.chdir(cwd) return out diff --git a/src/horus/util/profile.py b/src/horus/util/profile.py index 6e308f15..d300e762 100644 --- a/src/horus/util/profile.py +++ b/src/horus/util/profile.py @@ -87,7 +87,7 @@ def cast_and_set(self, key, value): elif setting_type == np.ndarray: value = np.asarray(value) except: - raise ValueError("Unable to cast setting %s to type %s" % (key, setting_type)) + logger.error("Unable to cast setting %s to type %s" % (key, setting_type)) else: self.get_setting(key).value = value @@ -105,6 +105,13 @@ def _load_json_dict(self, json_dict, categories): continue if categories is None or category in categories: for key in json_dict[category]: + if key == 'machine_model_path': + # before putting this key in the settings dict, make sure the + # file actually exists (else an exception is thrown) + file_path_from_settings = json_dict[category][key]['value'] + if not os.path.exists(file_path_from_settings): + print "can't find file {0}".format(file_path_from_settings) + continue if key in self._settings_dict: self._convert_to_type(key, json_dict[category][key]) self.get_setting(key)._load_json_dict(json_dict[category][key]) @@ -246,6 +253,8 @@ def _initialize_settings(self): Setting('remove_background_scanning', _('Remove background'), 'profile_settings', bool, True)) + self._add_setting( + Setting('draw_line_scanning', _('Draw line'), 'profile_settings', bool, True)) self._add_setting( Setting('red_channel_scanning', _('Red channel'), 'profile_settings', unicode, u'R (RGB)', @@ -320,10 +329,10 @@ def _initialize_settings(self): 'profile_settings', bool, True)) self._add_setting( Setting('threshold_value_calibration', _('Threshold'), 'profile_settings', - int, 30, min_value=0, max_value=255)) + int, 50, min_value=0, max_value=255)) self._add_setting( Setting('blur_enable_calibration', _('Enable blur'), - 'profile_settings', bool, True)) + 'profile_settings', bool, False)) self._add_setting( Setting('blur_value_calibration', _('Blur'), 'profile_settings', int, 2, min_value=0, max_value=10)) @@ -397,25 +406,25 @@ def _initialize_settings(self): unicode, u'Texture', possible_values=(u'Texture', u'Laser', u'Gray', u'Line'))) self._add_setting( - Setting('save_image_button', _('Save image'), 'profile_settings', unicode, u'')) + Setting('save_image_button', _('Save image'), 'no_settings', unicode, u'')) self._add_setting( - Setting('left_button', _('Left'), 'profile_settings', unicode, u'')) + Setting('left_button', _('Left'), 'no_settings', unicode, u'')) self._add_setting( - Setting('right_button', _('Right'), 'profile_settings', unicode, u'')) + Setting('right_button', _('Right'), 'no_settings', unicode, u'')) self._add_setting( - Setting('move_button', _('Move'), 'profile_settings', unicode, u'')) + Setting('move_button', _('Move'), 'no_settings', unicode, u'')) self._add_setting( - Setting('enable_button', _('Enable'), 'profile_settings', unicode, u'')) + Setting('enable_button', _('Enable'), 'no_settings', unicode, u'')) self._add_setting( - Setting('reset_origin_button', _('Reset origin'), 'profile_settings', unicode, u'')) + Setting('reset_origin_button', _('Reset origin'), 'no_settings', unicode, u'')) self._add_setting( - Setting('gcode_gui', _('Send'), 'profile_settings', unicode, u'')) + Setting('gcode_gui', _('Send'), 'no_settings', unicode, u'')) self._add_setting( - Setting('ldr_value', _('Send'), 'profile_settings', unicode, u'')) + Setting('ldr_value', _('Send'), 'no_settings', unicode, u'')) self._add_setting( - Setting('autocheck_button', _('Perform autocheck'), 'profile_settings', unicode, u'')) + Setting('autocheck_button', _('Perform autocheck'), 'no_settings', unicode, u'')) self._add_setting( - Setting('set_resolution_button', _('Set resolution'), 'profile_settings', unicode, u'')) + Setting('set_resolution_button', _('Set resolution'), 'no_settings', unicode, u'')) # -- Calibration Settings @@ -432,6 +441,16 @@ def _initialize_settings(self): Setting('pattern_origin_distance', _('Origin distance (mm)'), 'calibration_settings', float, 0.0, min_value=0.0)) + self._add_setting( + Setting('motor_step_calibration', _(u'Step (º)'), 'calibration_settings', + float, 4.5)) + self._add_setting( + Setting('motor_speed_calibration', _(u'Speed (º/s)'), 'calibration_settings', + float, 200.0, min_value=1.0, max_value=1000.0)) + self._add_setting( + Setting('motor_acceleration_calibration', _(u'Acceleration (º/s²)'), + 'calibration_settings', float, 200.0, min_value=1.0, max_value=1000.0)) + self._add_setting( Setting('adjust_laser', _('Adjust laser'), 'calibration_settings', bool, True)) @@ -495,8 +514,9 @@ def _initialize_settings(self): Setting('current_panel_calibration', u'pattern_settings', 'profile_settings', unicode, u'pattern_settings', possible_values=(u'pattern_settings', u'camera_intrinsics', - u'scanner_autocheck', u'laser_triangulation', - u'platform_extrinsics', u'video_settings'))) + u'scanner_autocheck', u'rotating_platform_settings', + u'laser_triangulation', u'platform_extrinsics', + u'video_settings'))) # -- Machine Settings @@ -558,21 +578,21 @@ def _initialize_settings(self): np.ndarray, np.ndarray(shape=(3,), dtype=int, buffer=np.array([3, 2, 3])))) self._add_setting( Setting('flush_stream_linux', 'Flush stream Linux', 'preferences', - np.ndarray, np.ndarray(shape=(3,), dtype=int, buffer=np.array([0, 2, 0])))) + np.ndarray, np.ndarray(shape=(3,), dtype=int, buffer=np.array([0, 3, 3])))) # - Darwin self._add_setting( Setting('flush_darwin', 'Flush Darwin', 'preferences', np.ndarray, np.ndarray(shape=(3,), dtype=int, buffer=np.array([4, 3, 4])))) self._add_setting( Setting('flush_stream_darwin', 'Flush stream Darwin', 'preferences', - np.ndarray, np.ndarray(shape=(3,), dtype=int, buffer=np.array([0, 2, 0])))) + np.ndarray, np.ndarray(shape=(3,), dtype=int, buffer=np.array([0, 3, 3])))) # - Windows self._add_setting( Setting('flush_windows', 'Flush Windows', 'preferences', np.ndarray, np.ndarray(shape=(3,), dtype=int, buffer=np.array([4, 3, 4])))) self._add_setting( Setting('flush_stream_windows', 'Flush stream Windows', 'preferences', - np.ndarray, np.ndarray(shape=(3,), dtype=int, buffer=np.array([0, 2, 0])))) + np.ndarray, np.ndarray(shape=(3,), dtype=int, buffer=np.array([0, 3, 3])))) self._add_setting( Setting('point_size', 'Point size', 'preferences', int, 2, min_value=1, max_value=4)) @@ -654,7 +674,7 @@ def value(self, value): return self._check_type(value) value = self._check_range(value) - self._check_possible_values(value) + value = self._check_possible_values(value) self.__value = value @property @@ -665,7 +685,7 @@ def default(self): def default(self, value): self._check_type(value) value = self._check_range(value) - self._check_possible_values(value) + value = self._check_possible_values(value) self.__default = value @property @@ -690,20 +710,16 @@ def max_value(self, value): def _check_type(self, value): if not isinstance(value, self._type): - raise TypeError("Error when setting %s.\n%s (%s) is not of type %s. " - "Please remove current profile at ~/.horus" % - (self._id, value, type(value), self._type)) + logger.error("Error when setting %s.\n%s (%s) is not of type %s. " + "Please remove current profile at ~/.horus" % + (self._id, value, type(value), self._type)) def _check_range(self, value): if self.min_value is not None and value < self.min_value: - # raise ValueError('Error when setting %s.\n%s is below min value %s.' % - # (self._id, value, self.min_value)) logger.warning('Warning: For setting %s, %s is below min value %s.' % (self._id, value, self.min_value)) return self.min_value if self.max_value is not None and value > self.max_value: - # raise ValueError('Error when setting %s.\n%s is above max value %s.' % - # (self._id, value, self.max_value)) logger.warning('Warning: For setting %s.\n%s is above max value %s.' % (self._id, value, self.max_value)) return self.max_value @@ -711,8 +727,11 @@ def _check_range(self, value): def _check_possible_values(self, value): if self._possible_values is not None and value not in self._possible_values: - raise ValueError('Error when setting %s.\n%s is not within the possible values %s.' % ( + logger.error('Error when setting %s.\n%s is not within the possible values %s.' % ( self._id, value, self._possible_values)) + if len(self._possible_values) > 0: + return self._possible_values[0] + return value def _load_json_dict(self, json_dict): # Only load configurable fields (__value, __min_value, __max_value) diff --git a/src/horus/util/version.py b/src/horus/util/version.py index 0538bae2..3019de3a 100644 --- a/src/horus/util/version.py +++ b/src/horus/util/version.py @@ -27,6 +27,9 @@ def __init__(self, version): self.number = version self.prenumber = 'z' + def __str__(self): + return str(self.number) + str(self.prenumber) + current_version = Version(__version__) current_datetime = __datetime__ @@ -67,9 +70,9 @@ def _get_executable_url(version): url = None if sys.is_linux(): import platform - url = 'https://launchpad.net/~bqlabs/+archive/ubuntu/horus/+files/' + url = 'https://launchpad.net/~bqlabs/+archive/ubuntu/horus-dev/+files/' url += 'horus_' - url += version + '-' + url += str(version) + '-' url += platform.linux_distribution()[2] + '1_' if platform.architecture()[0] == '64bit': url += 'amd64.deb' @@ -78,12 +81,12 @@ def _get_executable_url(version): del platform elif sys.is_windows(): url = URL_DOWNLOAD - url += latest_version + url += str(version) url += '/Horus_' url += version + '.exe' elif sys.is_darwin(): url = URL_DOWNLOAD - url += latest_version + url += str(version) url += '/Horus_' url += version + '.dmg' return url