├── .gitignore ├── README.md ├── debian ├── changelog ├── compat ├── control ├── copyright ├── icons │ ├── 48x48 │ │ └── ppt_icon.png │ └── 64x64 │ │ └── ppt_icon.png ├── ppt-gui.desktop ├── ppt-gui.install ├── ppt-gui.links ├── rules └── source │ └── format ├── gui ├── __init__.py ├── assets │ └── icons │ │ ├── info_icon.png │ │ └── python_icon.png └── main.py ├── ppt-gui.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # gedit 2 | *~ 3 | #vim 4 | *.swp 5 | # Python 6 | *.pyc 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WHAT IS PYTHON PHOTOGRAMMETRY TOOLBOX GUI? 2 | ========================================== 3 | 4 | PPT-GUI is a very simple GUI for Python Photogrammetry Toolbox (formerly 5 | known as osm-bundler, http://code.google.com/p/osm-bundler/). 6 | This GUI was created by Alessandro and Luca Bezzi and is powered by Python 7 | and PyQT4. 8 | 9 | LICENCE 10 | ======== 11 | 12 | It is a free software released under the GNU General Public License, 13 | version 2. 14 | http://www.gnu.org/licenses/gpl-2.0.html 15 | 16 | CONTACTS 17 | ======== 18 | 19 | alessandro.bezzi~at~arc-team.com 20 | luca.bezzi~at~arc-team.com 21 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | ppt-gui (0.1+nmu1) theodoric; urgency=low 2 | * Non-maintainer upload 3 | * Initial theodoric release 4 | * Last update before complete refactoring 5 | * Converted to 3.0 (native) source package 6 | * (hopefully) Better organisation of code 7 | * Tighter integration with ppt 8 | * Added a small distutils file (setup.py) 9 | 10 | -- Romain Janvier Tue, 22 Jan 2013 12:05:31 +0100 11 | 12 | ppt-gui (0.1-0archeos5) caesar; urgency=low 13 | 14 | * Added check-camera-database function 15 | 16 | -- Alessendro Bezzi Tue, 22 Jan 2013 12:04:31 +0100 17 | 18 | ppt-gui (0.1-0archeos4) caesar; urgency=low 19 | 20 | * Fixed Icon: entry in desktop file 21 | 22 | -- Alessendro Bezzi Tue, 22 Jan 2013 12:04:31 +0100 23 | 24 | ppt-gui (0.1-0archeos3) caesar; urgency=low 25 | 26 | * Changed /usr/share/application/ to /usr/share/applications/ 27 | 28 | -- Alessendro Bezzi Tue, 22 Jan 2013 12:04:31 +0100 29 | 30 | ppt-gui (0.1-0archeos2) caesar; urgency=low 31 | 32 | * Added changelog 33 | 34 | -- Alessendro Bezzi Tue, 22 Jan 2013 12:04:31 +0100 35 | 36 | ppt-gui (0.1-0archeos1) caesar; urgency=low 37 | 38 | * Initial release 39 | 40 | -- Alessendro Bezzi Tue, 22 Jan 2013 12:04:31 +0100 41 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 7 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: ppt-gui 2 | Maintainer: Alessendro Bezzi 3 | uploaders: Luca Bezzi , Romain Janvier 4 | Section: contrib/python 5 | Priority: optional 6 | Standards-Version: 3.9.4 7 | Build-Depends: python, debhelper (>= 9.0.0) 8 | X-Python-Version: >= 2.7 9 | Homepage: http://www.archeos.eu 10 | Vcs-Git: git://github.com/archeos/archeos-ppt-gui.git 11 | Vcs-Browser: https://github.com/archeos/archeos-ppt-gui/ 12 | 13 | 14 | Package: ppt-gui 15 | Architecture: all 16 | Depends: ${misc:Depends}, ${python:Depends}, ppt (>= 0.1), python-qt4 (>=4.9.3) 17 | Description: Simple GUI on top of Python Photogrammetry Toolbox 18 | This GUI helps you to run Bundler, CMVS, PMVS through Python Photogrammetry 19 | Toolbox. It's based on Python 2 and PyQt4. 20 | . 21 | PLEASE NOTE that this package depends on PPT which use a free implementation 22 | of the SIFT algorithm (VLFeat). This algorithm is patented and must not 23 | be used for commercial purpose. 24 | 25 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: PPT-GUI 3 | Source: https://github.com/archeos/archeos-ppt-gui/ 4 | 5 | Files: * 6 | Copyright: 2011-2012, Alessendro Bezzi 7 | 2011-2012, Luca Bezzi 8 | License: GPL-2.0+ 9 | 10 | Files: debian/* 11 | Copyright: 2013 Romain Janvier 12 | 2011-2012 Alessandro Bezzi 13 | License: GPL-2.0+ 14 | 15 | License: GPL-2.0+ 16 | This package is free software; you can redistribute it and/or modify 17 | it under the terms of the GNU General Public License as published by 18 | the Free Software Foundation; either version 2 of the License, or 19 | (at your option) any later version. 20 | . 21 | This package is distributed in the hope that it will be useful, 22 | but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | GNU General Public License for more details. 25 | . 26 | You should have received a copy of the GNU General Public License 27 | along with this program. If not, see 28 | . 29 | On Debian systems, the complete text of the GNU General 30 | Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". 31 | -------------------------------------------------------------------------------- /debian/icons/48x48/ppt_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archeos/archeos-ppt-gui/e65be865d3b7dd13be8f9571e9745630f237a3fd/debian/icons/48x48/ppt_icon.png -------------------------------------------------------------------------------- /debian/icons/64x64/ppt_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archeos/archeos-ppt-gui/e65be865d3b7dd13be8f9571e9745630f237a3fd/debian/icons/64x64/ppt_icon.png -------------------------------------------------------------------------------- /debian/ppt-gui.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Categories=Photogrammetry; 3 | Exec=ppt-gui 4 | GenericName=Python Photogrammetry Toolbox 5 | Hidden=false 6 | Icon=ppt_icon 7 | Name=Python Photogrammetry Toolbox 8 | NoDisplay=false 9 | StartupNotify=true 10 | Terminal=false 11 | TryExec=ppt-gui 12 | Type=Application 13 | Version=0.1 14 | Name[en_GB]=ppt-gui 15 | -------------------------------------------------------------------------------- /debian/ppt-gui.install: -------------------------------------------------------------------------------- 1 | debian/ppt-gui.desktop /usr/share/applications 2 | debian/icons/64x64/ppt_icon.png /usr/share/icons/hicolor/64x64/apps 3 | debian/icons/48x48/ppt_icon.png /usr/share/icons/hicolor/48x48/apps 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /debian/ppt-gui.links: -------------------------------------------------------------------------------- 1 | /usr/share/ppt-gui/ppt-gui.py /usr/bin/ppt-gui 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | # This file was automatically generated by stdeb 0.6.0+git at 4 | # Tue, 22 Jan 2013 12:04:31 +0100 5 | 6 | %: 7 | dh $@ --with python2 8 | 9 | override_dh_auto_install: 10 | python setup.py install --root=debian/ppt-gui --install-layout=deb --install-lib=/usr/share/ppt-gui --install-scripts=/usr/share/ppt-gui --install-data=/usr/share 11 | 12 | override_dh_auto_build: 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /gui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archeos/archeos-ppt-gui/e65be865d3b7dd13be8f9571e9745630f237a3fd/gui/__init__.py -------------------------------------------------------------------------------- /gui/assets/icons/info_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archeos/archeos-ppt-gui/e65be865d3b7dd13be8f9571e9745630f237a3fd/gui/assets/icons/info_icon.png -------------------------------------------------------------------------------- /gui/assets/icons/python_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archeos/archeos-ppt-gui/e65be865d3b7dd13be8f9571e9745630f237a3fd/gui/assets/icons/python_icon.png -------------------------------------------------------------------------------- /gui/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | from PyQt4.QtGui import * 5 | from PyQt4 import QtCore 6 | from PyQt4.QtCore import QProcess 7 | 8 | """ 9 | - Processes are defined in the Global scope because I noticed 10 | 11 | - I also use an interleave stdout and stderr (MergedChannels). 12 | we need to do that because CMVS write some output in stderr (?). 13 | So, a proper error handling in a separate channel is impossible. 14 | """ 15 | procB = QProcess() 16 | procB.setProcessChannelMode(QProcess.MergedChannels) 17 | procC = QProcess() 18 | procC.setProcessChannelMode(QProcess.MergedChannels) 19 | procP = QProcess() 20 | procP.setProcessChannelMode(QProcess.MergedChannels) 21 | procCam = QProcess() 22 | procCam.setProcessChannelMode(QProcess.MergedChannels) 23 | 24 | 25 | class PPTGUI(QWidget): 26 | """The main Widget. 27 | TODO: PEP8 28 | TODO: Translations 29 | TODO: One Console, One process 30 | """ 31 | 32 | def __init__(self): 33 | # Icons 34 | self.py_icon = QIcon( 35 | os.path.join( 36 | os.path.dirname(os.path.abspath(__file__)), 37 | 'assets/icons/python_icon.png')) 38 | self.help_icon = QIcon( 39 | os.path.join( 40 | os.path.dirname(os.path.abspath(__file__)), 41 | 'assets/icons/info_icon.png')) 42 | 43 | super(PPTGUI, self).__init__() 44 | ############################################################ 45 | self.setWindowTitle('Python Photogrammetry Toolbox GUI v. 0.1') 46 | self.setGeometry(300, 300, 900, 580) 47 | self.setWindowIcon(self.py_icon) 48 | ############################################################# 49 | self.tabWidget = QTabWidget(self) 50 | self.tabWidget.setGeometry(QtCore.QRect(0, 0, 900, 580)) 51 | self.tabWidget.setObjectName("tabWidget") 52 | self.tab = QWidget() 53 | self.tab.setObjectName("tab") 54 | self.tabWidget.addTab(self.tab, "") 55 | self.tab_3 = QWidget() 56 | self.tab_3.setObjectName("tab_3") 57 | self.tabWidget.addTab(self.tab_3, "") 58 | self.tab_2 = QWidget() 59 | self.tab_2.setObjectName("tab_2") 60 | self.tabWidget.addTab(self.tab_2, "") 61 | self.tab_4 = QWidget() 62 | self.tab_4.setObjectName("tab_4") 63 | self.tabWidget.addTab(self.tab_4, "") 64 | self.initUI() 65 | # tabWidgets 66 | self.tabWidget.setTabText( 67 | self.tabWidget.indexOf(self.tab), 68 | QApplication.translate( 69 | "MainWindow", 70 | "1. Run Bundler", 71 | None, 72 | QApplication.UnicodeUTF8)) 73 | self.tabWidget.setTabText( 74 | self.tabWidget.indexOf(self.tab_3), 75 | QApplication.translate( 76 | "MainWindow", 77 | "2. Run CMVS/PMVS", 78 | None, 79 | QApplication.UnicodeUTF8)) 80 | self.tabWidget.setTabText( 81 | self.tabWidget.indexOf(self.tab_2), 82 | QApplication.translate( 83 | "MainWindow", 84 | "or run PMVS without CMVS", 85 | None, 86 | QApplication.UnicodeUTF8)) 87 | self.tabWidget.setTabText( 88 | self.tabWidget.indexOf(self.tab_4), 89 | QApplication.translate("MainWindow", 90 | "Check Camera Database", 91 | None, 92 | QApplication.UnicodeUTF8)) 93 | 94 | def initUI(self): 95 | """ Init GUI""" 96 | # BUNDLER 97 | # button 1 for pictures directory 98 | self.button1 = QPushButton('Select Photos Path', self.tab) 99 | self.button1.setFocusPolicy(QtCore.Qt.NoFocus) 100 | self.button1.move(20, 30) 101 | self.connect(self.button1, 102 | QtCore.SIGNAL('clicked()'), 103 | self.showDialog1) 104 | self.setFocus() 105 | 106 | # directory path label 107 | self.label9 = QLabel('path:', self.tab) 108 | self.label9.move(190, 34) 109 | self.text4 = QLineEdit(self.tab) 110 | self.text4.move(235, 30) 111 | self.text4.resize(550, 27) 112 | 113 | # help button select directory 114 | self.help_button1 = QPushButton("", self.tab) 115 | self.help_button1.setIcon(self.help_icon) 116 | self.help_button1.setFocusPolicy(QtCore.Qt.NoFocus) 117 | self.help_button1.move(800, 26) 118 | self.connect( 119 | self.help_button1, 120 | QtCore.SIGNAL('clicked()'), 121 | self.on_help1_clicked) 122 | self.setFocus() 123 | 124 | # features extractor combo 125 | self.label16 = QLabel('Select Feature Extractor:', self.tab) 126 | self.label16.move(20, 84) 127 | self.text15 = QLineEdit("siftvlfeat", self.tab) 128 | self.text15.setReadOnly(True) 129 | self.combo = QComboBox(self.tab) 130 | self.combo.addItem("siftvlfeat") 131 | self.combo.addItem("siftlowe") 132 | self.combo.move(200, 80) 133 | self.text15.move(360, 100) 134 | self.text15.resize(0, 0) 135 | self.connect( 136 | self.combo, 137 | QtCore.SIGNAL('activated(QString)'), 138 | self.onActivated) 139 | 140 | # help button features extractor 141 | self.help_button2 = QPushButton("", self.tab) 142 | self.help_button2.setIcon(self.help_icon) 143 | self.help_button2.setFocusPolicy(QtCore.Qt.NoFocus) 144 | self.help_button2.move(300, 76) 145 | self.connect(self.help_button2, 146 | QtCore.SIGNAL('clicked()'), 147 | self.on_help2_clicked) 148 | self.setFocus() 149 | 150 | # image width 151 | self.cb1 = QCheckBox('Set desired Photos Width:', self.tab) 152 | self.cb1.setFocusPolicy(QtCore.Qt.NoFocus) 153 | self.cb1.move(380, 80) 154 | self.cb1.toggle() 155 | self.connect( 156 | self.cb1, 157 | QtCore.SIGNAL('stateChanged(int)'), 158 | self.changesize1) 159 | self.text13 = QLineEdit('1200', self.tab) 160 | self.text13.move(600, 78) 161 | self.text13.resize(70, 27) 162 | 163 | # help button width 164 | self.help_button3 = QPushButton("", self.tab) 165 | self.help_button3.setIcon(self.help_icon) 166 | self.help_button3.setFocusPolicy(QtCore.Qt.NoFocus) 167 | self.help_button3.move(720, 76) 168 | self.connect(self.help_button3, 169 | QtCore.SIGNAL('clicked()'), 170 | self.on_help3_clicked) 171 | self.setFocus() 172 | 173 | # image resize 174 | self.cb2 = QCheckBox( 175 | 'Scale Photos with a Scaling Factor', 176 | self.tab) 177 | self.cb2.setFocusPolicy(QtCore.Qt.NoFocus) 178 | self.cb2.move(380, 130) 179 | self.cb2.toggle() 180 | self.cb2.setChecked(False) 181 | self.connect( 182 | self.cb2, 183 | QtCore.SIGNAL('stateChanged(int)'), 184 | self.changesize2) 185 | self.text11 = QLineEdit("1", self.tab) 186 | self.text11.setReadOnly(True) 187 | self.combo2 = QComboBox(self.tab) 188 | self.combo2.hide() 189 | self.combo2.addItem("1") 190 | self.combo2.addItem("0.75") 191 | self.combo2.addItem("0.5") 192 | self.combo2.addItem("0.25") 193 | self.combo2.move(650, 130) 194 | self.text11.move(390, 100) 195 | self.text11.resize(0, 0) 196 | self.connect( 197 | self.combo2, 198 | QtCore.SIGNAL('activated(QString)'), 199 | self.onActivated2) 200 | 201 | # help button resize 202 | self.help_button4 = QPushButton("", self.tab) 203 | self.help_button4.setIcon(self.help_icon) 204 | self.help_button4.setFocusPolicy(QtCore.Qt.NoFocus) 205 | self.help_button4.move(720, 126) 206 | self.connect( 207 | self.help_button4, 208 | QtCore.SIGNAL('clicked()'), 209 | self.on_help4_clicked) 210 | self.setFocus() 211 | 212 | # button 4 for start bundler 213 | self.button4 = QPushButton('Run', self.tab) 214 | self.button4.setIcon(self.py_icon) 215 | self.button4.move(20, 180) 216 | self.connect( 217 | self.button4, 218 | QtCore.SIGNAL('clicked()'), 219 | self.startbundler) 220 | self.text2 = QLineEdit(self.tab) 221 | self.text2.move(120, 184) 222 | self.text2.setReadOnly(True) 223 | self.text2.resize(760, 27) 224 | self.connect( 225 | self.text4, 226 | QtCore.SIGNAL('textChanged(QString)'), 227 | self.onChangedpathbundler) 228 | self.connect( 229 | self.text15, 230 | QtCore.SIGNAL('textChanged(QString)'), 231 | self.onChangedextractor) 232 | self.connect(self.text13, 233 | QtCore.SIGNAL('textChanged(QString)'), 234 | self.onChangedwidth) 235 | self.connect(self.text11, 236 | QtCore.SIGNAL('textChanged(QString)'), 237 | self.onChangedsize) 238 | 239 | # output 240 | self.line1 = QFrame(self.tab) 241 | self.line1.setGeometry(QtCore.QRect(10, 220, 880, 20)) 242 | self.line1.setFrameShape(QFrame.HLine) 243 | self.line1.setFrameShadow(QFrame.Sunken) 244 | self.line1.setObjectName("line1") 245 | self.label20 = QLabel('Output Bundler:', self.tab) 246 | self.label20.move(20, 240) 247 | self.output1 = QTextBrowser(self.tab) 248 | self.output1.move(20, 264) 249 | self.output1.resize(850, 270) 250 | self.output1.setAcceptRichText(True) 251 | self.output1.setAutoFormatting(QTextEdit.AutoBulletList) 252 | self.scroll1 = self.output1.verticalScrollBar() 253 | 254 | 255 | # CMVS/PMVS 256 | # button 2 for Bundler output directory 257 | self.button2 = QPushButton( 258 | 'Select Bundler Output Path', 259 | self.tab_3) 260 | self.button2.setFocusPolicy(QtCore.Qt.NoFocus) 261 | self.button2.move(20, 30) 262 | self.connect( 263 | self.button2, 264 | QtCore.SIGNAL('clicked()'), 265 | self.showDialog2) 266 | self.setFocus() 267 | 268 | # directory output path label 269 | self.label10 = QLabel('path:', self.tab_3) 270 | self.label10.move(240, 34) 271 | self.text3 = QLineEdit(self.tab_3) 272 | self.text3.move(285, 30) 273 | self.text3.resize(500, 27) 274 | 275 | # help button 5 select bundler output directory 276 | self.help_button5 = QPushButton("", self.tab_3) 277 | self.help_button5.setIcon(self.help_icon) 278 | self.help_button5.setFocusPolicy(QtCore.Qt.NoFocus) 279 | self.help_button5.move(800, 26) 280 | self.connect( 281 | self.help_button5, 282 | QtCore.SIGNAL('clicked()'), 283 | self.on_help5_clicked) 284 | self.setFocus() 285 | 286 | # number images for cluster 287 | self.label11 = QLabel( 288 | 'Number of Photos in each Cluster:', 289 | self.tab_3) 290 | self.label11.move(240, 84) 291 | self.text5 = QLineEdit('10', self.tab_3) 292 | self.text5.move(490, 82) 293 | self.text5.resize(70, 27) 294 | 295 | # help button 6 select bundler output directory 296 | self.help_button6 = QPushButton("", self.tab_3) 297 | self.help_button6.setIcon(self.help_icon) 298 | self.help_button6.setFocusPolicy(QtCore.Qt.NoFocus) 299 | self.help_button6.move(580, 79) 300 | self.connect( 301 | self.help_button6, 302 | QtCore.SIGNAL('clicked()'), 303 | self.on_help6_clicked) 304 | self.setFocus() 305 | 306 | # button run CMVS 307 | self.button5 = QPushButton('Run', self.tab_3) 308 | self.button5.setIcon(self.py_icon) 309 | self.button5.move(20, 130) 310 | self.connect( 311 | self.button5, 312 | QtCore.SIGNAL('clicked()'), 313 | self.startcmvs) 314 | self.text6 = QLineEdit(self.tab_3) 315 | self.text6.move(120, 134) 316 | self.text6.setReadOnly(True) 317 | self.text6.resize(760, 27) 318 | self.connect( 319 | self.text3, 320 | QtCore.SIGNAL('textChanged(QString)'), 321 | self.onChangedpathcmvs) 322 | self.connect( 323 | self.text5, 324 | QtCore.SIGNAL('textChanged(QString)'), 325 | self.onChangedcluster) 326 | 327 | # output 328 | self.line3 = QFrame(self.tab_3) 329 | self.line3.setGeometry(QtCore.QRect(10, 220, 880, 20)) 330 | self.line3.setFrameShape(QFrame.HLine) 331 | self.line3.setFrameShadow(QFrame.Sunken) 332 | self.line3.setObjectName("line3") 333 | self.label21 = QLabel('Output CMVS/PMVS:', self.tab_3) 334 | self.label21.move(20, 240) 335 | self.output2 = QTextBrowser(self.tab_3) 336 | self.output2.move(20, 264) 337 | self.output2.resize(850, 270) 338 | self.output2.setAcceptRichText(True) 339 | self.output2.setAutoFormatting(QTextEdit.AutoBulletList) 340 | self.scroll2 = self.output2.verticalScrollBar() 341 | 342 | 343 | # run only PMVS 344 | self.cb3 = QCheckBox( 345 | 'Use directly PMVS2 (without CMVS):', 346 | self.tab_2) 347 | self.cb3.setFocusPolicy(QtCore.Qt.NoFocus) 348 | self.cb3.move(20, 30) 349 | self.cb3.toggle() 350 | self.cb3.setChecked(False) 351 | self.connect( 352 | self.cb3, 353 | QtCore.SIGNAL('stateChanged(int)'), 354 | self.openpmvs) 355 | 356 | # button 3 for output directory 357 | self.button3 = QPushButton( 358 | 'Select Bundler Output Path', 359 | self.tab_2) 360 | self.button3.setFocusPolicy(QtCore.Qt.NoFocus) 361 | self.button3.move(20, 80) 362 | self.button3.hide() 363 | self.connect( 364 | self.button3, 365 | QtCore.SIGNAL('clicked()'), 366 | self.showDialog3) 367 | self.setFocus() 368 | 369 | # directory output path label 370 | self.label14 = QLabel('path:', self.tab_2) 371 | self.label14.move(240, 84) 372 | self.label14.hide() 373 | 374 | self.text7 = QLineEdit(self.tab_2) 375 | self.text7.move(280, 80) 376 | self.text7.hide() 377 | self.text7.resize(500, 27) 378 | 379 | # help button 7 select bundler output directory 380 | self.help_button7 = QPushButton("", self.tab_2) 381 | self.help_button7.setIcon(self.help_icon) 382 | self.help_button7.setFocusPolicy(QtCore.Qt.NoFocus) 383 | self.help_button7.move(800, 76) 384 | self.help_button7.hide() 385 | self.connect( 386 | self.help_button7, 387 | QtCore.SIGNAL('clicked()'), 388 | self.on_help5_clicked) 389 | self.setFocus() 390 | 391 | # run PMVS 392 | self.button6 = QPushButton('Run', self.tab_2) 393 | self.button6.setIcon(self.py_icon) 394 | self.button6.move(20, 130) 395 | self.button6.hide() 396 | self.connect( 397 | self.button6, 398 | QtCore.SIGNAL('clicked()'), 399 | self.startpmvs) 400 | self.text8 = QLineEdit(self.tab_2) 401 | self.text8.move(120, 134) 402 | self.text8.setReadOnly(True) 403 | self.text8.hide() 404 | self.text8.resize(760, 27) 405 | self.connect( 406 | self.text7, 407 | QtCore.SIGNAL('textChanged(QString)'), 408 | self.onChangedpathpmvs) 409 | 410 | # output 411 | self.line2 = QFrame(self.tab_2) 412 | self.line2.setGeometry(QtCore.QRect(10, 220, 880, 20)) 413 | self.line2.setFrameShape(QFrame.HLine) 414 | self.line2.setFrameShadow(QFrame.Sunken) 415 | self.line2.setObjectName("line2") 416 | self.label22 = QLabel('Output PMVS:', self.tab_2) 417 | self.label22.move(20, 240) 418 | self.output3 = QTextBrowser(self.tab_2) 419 | self.output3.move(20, 264) 420 | self.output3.resize(850, 270) 421 | self.output3.setAcceptRichText(True) 422 | self.output3.setAutoFormatting(QTextEdit.AutoBulletList) 423 | self.scroll3 = self.output3.verticalScrollBar() 424 | 425 | 426 | # CAMERA DATABASE 427 | # button 1 for pictures directory 428 | self.button8 = QPushButton('Select Photos Path', self.tab_4) 429 | self.button8.setFocusPolicy(QtCore.Qt.NoFocus) 430 | self.button8.move(20, 30) 431 | self.connect( 432 | self.button8, 433 | QtCore.SIGNAL('clicked()'), 434 | self.showDialog4) 435 | self.setFocus() 436 | 437 | # directory path label 438 | self.label12 = QLabel('path:', self.tab_4) 439 | self.label12.move(190, 34) 440 | self.text9 = QLineEdit(self.tab_4) 441 | self.text9.move(235, 30) 442 | self.text9.resize(550, 27) 443 | 444 | # help button select directory 445 | self.help_button9 = QPushButton("", self.tab_4) 446 | self.help_button9.setIcon(self.help_icon) 447 | self.help_button9.setFocusPolicy(QtCore.Qt.NoFocus) 448 | self.help_button9.move(800, 26) 449 | self.connect( 450 | self.help_button9, 451 | QtCore.SIGNAL('clicked()'), 452 | self.on_help9_clicked) 453 | self.setFocus() 454 | 455 | # button run Camera Database 456 | self.button10 = QPushButton('Run', self.tab_4) 457 | self.button10.setIcon(self.py_icon) 458 | self.button10.move(20, 80) 459 | self.connect( 460 | self.button10, 461 | QtCore.SIGNAL('clicked()'), 462 | self.startcamdat) 463 | self.text10 = QLineEdit(self.tab_4) 464 | self.text10.move(120, 84) 465 | self.text10.setReadOnly(True) 466 | self.text10.resize(760, 27) 467 | self.connect(self.text9, 468 | QtCore.SIGNAL('textChanged(QString)'), 469 | self.onChangedpathcamdat) 470 | 471 | # output 472 | self.line4 = QFrame(self.tab_4) 473 | self.line4.setGeometry(QtCore.QRect(10, 220, 880, 20)) 474 | self.line4.setFrameShape(QFrame.HLine) 475 | self.line4.setFrameShadow(QFrame.Sunken) 476 | self.line4.setObjectName("line1") 477 | self.label23 = QLabel( 478 | 'Output Camera Database:', 479 | self.tab_4) 480 | self.label23.move(20, 240) 481 | self.output4 = QTextBrowser(self.tab_4) 482 | self.output4.move(20, 264) 483 | self.output4.resize(850, 270) 484 | self.output4.setAcceptRichText(True) 485 | self.output4.setAutoFormatting(QTextEdit.AutoBulletList) 486 | self.scroll4 = self.output4.verticalScrollBar() 487 | 488 | 489 | # select directory with photos 490 | def showDialog1(self): 491 | directoryname = QFileDialog.getExistingDirectory( 492 | self, 493 | 'Open directory with photos', 494 | '/home') 495 | self.text4.setText(directoryname) 496 | 497 | # combo vlfeat-sift 498 | def onActivated(self, text): 499 | self.text15.setText(text) 500 | 501 | # combo size-image 502 | def onActivated2(self, text): 503 | self.text11.setText(text) 504 | 505 | # width-size select 506 | def changesize1(self, value): 507 | if self.cb1.isChecked(): 508 | self.combo2.hide() 509 | self.text13.show() 510 | self.cb2.setChecked(False) 511 | self.text2.setText( 512 | "RunBundler --photos=" + 513 | self.text4.displayText() + 514 | " --featureExtractor=" + 515 | self.text15.displayText() + 516 | " --maxPhotoDimension=" + 517 | self.text13.displayText()) 518 | 519 | def changesize2(self, text): 520 | if self.cb2.isChecked(): 521 | self.combo2.show() 522 | self.text13.hide() 523 | self.cb1.setChecked(False) 524 | self.text2.setText( 525 | "RunBundler --photos=" + 526 | self.text4.displayText() + 527 | " --featureExtractor=" + 528 | self.text15.displayText() + 529 | " --photoScalingFactor=" + 530 | self.text11.displayText()) 531 | 532 | # help button 1 - select directory 533 | def on_help1_clicked(self): 534 | QMessageBox.information( 535 | self, 536 | "Help!", 537 | "Select the directory with original photos." + 538 | "Pictures have to be in JPG file format.", 539 | QMessageBox.Ok) 540 | 541 | # help button 2 - feature extractor 542 | def on_help2_clicked(self): 543 | QMessageBox.information( 544 | self, 545 | "Help!", 546 | "Select the feature extractor: VLFEAT or SIFT." + 547 | "\n\n - VLFEAT (http://www.vlfeat.org/) is released" + 548 | "under GPL v.2 license." + 549 | "\n\n - SIFT (http://www.cs.ubc.ca/~lowe/keypoints/) is" + 550 | "being made available for individual research use only." + 551 | "Any commercial use or any redistribution of this" + 552 | "software requires a license from the University of" + 553 | "British Columbia. Before use SIFT download and copy" + 554 | "the binary into the folder.", 555 | QMessageBox.Ok) 556 | 557 | # help button 3 - feature extractor 558 | def on_help3_clicked(self): 559 | QMessageBox.information( 560 | self, 561 | "Help!", 562 | "Copy of a photo will be scaled down if either width or" + 563 | "height exceeds the value insert in ." + 564 | "After scaling the maximum of width and height will be" + 565 | "equal to the value insert in ." + 566 | "\n\nDefault value is 1200: an image of 3008x2000 px" + 567 | "will be scale into a copy of 1200x798 px.", 568 | QMessageBox.Ok) 569 | 570 | # help button 4 - feature extractor 571 | def on_help4_clicked(self): 572 | QMessageBox.information(self, 573 | "Help!", 574 | "Scale all photos to the specified scaling factor:" + 575 | "\n\n 1 = original size \n\n 0.75 = 75% of" + 576 | "the original size \n\n 0.5 = half size" + 577 | "\n\n 0.25 = 25% of the original size.", 578 | QMessageBox.Ok) 579 | 580 | # connection path-command 581 | def onChangedpathbundler(self, text): 582 | if self.cb2.isChecked(): 583 | self.text2.setText( 584 | "RunBundler --photos=" + 585 | self.text4.displayText() + 586 | " --featureExtractor=" + 587 | self.text15.displayText() + 588 | "--photoScalingFactor=" + 589 | self.text11.displayText()) 590 | if self.cb1.isChecked(): 591 | self.text2.setText( 592 | "RunBundler --photos=" + 593 | self.text4.displayText() + 594 | " --featureExtractor=" + 595 | self.text15.displayText() + 596 | " --maxPhotoDimension=" + 597 | self.text13.displayText()) 598 | 599 | # connection extractor-command 600 | def onChangedextractor(self, text): 601 | if self.cb2.isChecked(): 602 | self.text2.setText( 603 | "RunBundler --photos=" + 604 | self.text4.displayText() + 605 | " --featureExtractor=" + 606 | self.text15.displayText() + 607 | " --photoScalingFactor=" + 608 | self.text11.displayText()) 609 | if self.cb1.isChecked(): 610 | self.text2.setText( 611 | "RunBundler --photos=" + 612 | self.text4.displayText() + 613 | " --featureExtractor=" + 614 | self.text15.displayText() + 615 | " --maxPhotoDimension=" 616 | + self.text13.displayText()) 617 | 618 | # connection width-command 619 | def onChangedwidth(self, text): 620 | self.cb2.setChecked(False) 621 | self.cb1.setChecked(True) 622 | self.text2.setText( 623 | "RunBundler --photos=" + 624 | self.text4.displayText() + 625 | " --featureExtractor=" + 626 | self.text15.displayText() + 627 | " --maxPhotoDimension=" + 628 | self.text13.displayText()) 629 | 630 | # connection size-command 631 | def onChangedsize(self, text): 632 | self.cb1.setChecked(False) 633 | self.cb2.setChecked(True) 634 | self.text2.setText( 635 | "RunBundler --photos=" + 636 | self.text4.displayText() + 637 | " --featureExtractor=" + 638 | self.text15.displayText() + 639 | " --photoScalingFactor=" + 640 | self.text11.displayText()) 641 | 642 | # select directory with photos 643 | def showDialog2(self): 644 | directoryname = QFileDialog.getExistingDirectory( 645 | self, 646 | 'Open directory with Bundler output files.', 647 | '/home') 648 | self.text3.setText(directoryname) 649 | 650 | # help button 5 - select directory 651 | def on_help5_clicked(self): 652 | QMessageBox.information( 653 | self, 654 | "Help!", 655 | "Select the Bundler output directory.", 656 | QMessageBox.Ok) 657 | 658 | # connection path-command 659 | def onChangedpathcmvs(self, text): 660 | self.text6.setText( 661 | "RunCMVS --bundlerOutputPath=" + 662 | self.text3.displayText() + 663 | " --ClusterToCompute=" + 664 | self.text5.displayText()) 665 | 666 | # connection number cluster 667 | def onChangedcluster(self, text): 668 | self.text6.setText( 669 | "RunCMVS --bundlerOutputPath=" + 670 | self.text3.displayText() + 671 | " --ClusterToCompute=" 672 | + self.text5.displayText()) 673 | 674 | # help button 6 - cluster 675 | def on_help6_clicked(self): 676 | QMessageBox.information( 677 | self, 678 | "Help!", 679 | "Select the max number of photos for each cluster that" + 680 | "CMVS should compute. Separated PLY output files will be" + 681 | "created. \n\n - Depends on the CPUs of your computer:" + 682 | "if infinite loop occur, stop the process and try a" + 683 | "different value. \n\n - Default value is 10: an image" + 684 | "set with 28 photos will be compute in 3 separated" + 685 | "clusters.", 686 | QMessageBox.Ok) 687 | 688 | # open pmvs 689 | def openpmvs(self, text): 690 | if self.cb3.isChecked(): 691 | self.button3.show() 692 | self.button6.show() 693 | self.text7.show() 694 | self.text8.show() 695 | self.label14.show() 696 | self.help_button7.show() 697 | else: 698 | self.label14.hide() 699 | self.button3.hide() 700 | self.button6.hide() 701 | self.text7.hide() 702 | self.text8.hide() 703 | self.help_button7.hide() 704 | 705 | # select directory with bundler output 706 | def showDialog3(self): 707 | directoryname = QFileDialog.getExistingDirectory( 708 | self, 709 | 'Open directory with Bundler output files', 710 | '/home') 711 | self.text7.setText(directoryname) 712 | 713 | # connection path-command 714 | def onChangedpathpmvs(self, text): 715 | self.text8.setText( 716 | "RunPMVS --bundlerOutputPath=" + 717 | self.text7.displayText()) 718 | 719 | #format text functions 720 | def format_out(self, array): 721 | """ Format standard output as Html """ 722 | return unicode(array).replace("\n", "
") 723 | 724 | # QPROCESS signals 725 | """ 726 | Multiple signals for the same thing... 727 | This is a violatation of the DRY principle / Only for test now 728 | Proper solution is to use heritage (QtTab... see docstring) 729 | 730 | on_start should toggle "Run" Button to "Cancel" and block access to 731 | others tabs? 732 | on_finish/on_error, invert toggle 733 | """ 734 | def on_bundler_start(self): 735 | self.output1.insertHtml("

Process has started...


") 736 | 737 | def on_cmvs_start(self): 738 | self.output2.insertHtml("

Process has started...


") 739 | 740 | def on_pmvs_start(self): 741 | self.output3.insertHtml("

Process has started...


") 742 | 743 | def on_cam_start(self): 744 | self.output4.insertHtml("

Process has started...


") 745 | 746 | def on_bundler_out(self): 747 | self.output1.insertHtml( 748 | self.format_out(procB.readAllStandardOutput())) 749 | self.scroll1.setValue(self.scroll1.maximum()) 750 | 751 | def on_cmvs_out(self): 752 | self.output2.insertHtml( 753 | self.format_out(procC.readAllStandardOutput())) 754 | self.scroll2.setValue(self.scroll2.maximum()) 755 | 756 | def on_pmvs_out(self): 757 | self.output3.insertHtml( 758 | self.format_out(procP.readAllStandardOutput())) 759 | self.scroll3.setValue(self.scroll3.maximum()) 760 | 761 | def on_cam_out(self): 762 | out = unicode(procCam.readAllStandardOutput()) 763 | if out.startswith("Type CCD width"): 764 | label = out.split(". Press")[0] 765 | ccd = list(QInputDialog.getDouble( 766 | self, 767 | "CCD width", 768 | label, 769 | 0, 0, 3000, 1)) 770 | arg = unicode(ccd[0]) + u"\n" if(ccd[1] and 771 | ccd[0] != 0.0) else u"\n" 772 | procCam.write(arg) 773 | else: 774 | self.output4.insertHtml(self.format_out(out)) 775 | self.scroll4.setValue(self.scroll4.maximum()) 776 | 777 | 778 | def on_bundler_finish(self, status): 779 | self.output1.insertHtml("

Process is finished!



") 780 | 781 | def on_cmvs_finish(self, status): 782 | self.output2.insertHtml("

Process is finished!



") 783 | 784 | def on_pmvs_finish(self, status): 785 | self.output3.insertHtml("

Process is finished!



") 786 | 787 | def on_cam_finish(self, status): 788 | self.output4.insertHtml("

Check is finished!



") 789 | 790 | def kill_bundler_process(self): 791 | procB.terminate() 792 | procB.kill() 793 | # Need to disconnect to avoid multiple 794 | # start/ finish msg 795 | procB.disconnect( 796 | procB, 797 | QtCore.SIGNAL("started()"), 798 | self.on_bundler_start) 799 | procB.waitForFinished() 800 | procB.disconnect( 801 | procB, 802 | QtCore.SIGNAL("finished(int)"), 803 | self.on_bundler_finish) 804 | 805 | def kill_cmvs_process(self): 806 | procC.terminate() 807 | procC.kill() 808 | # Need to disconnect to avoid multiple 809 | # start/ finish msg 810 | procC.disconnect( 811 | procC, 812 | QtCore.SIGNAL("started()"), 813 | self.on_cmvs_start) 814 | procC.waitForFinished() 815 | procC.disconnect( 816 | procC, 817 | QtCore.SIGNAL("finished(int)"), 818 | self.on_cmvs_finish) 819 | 820 | def kill_pmvs_process(self): 821 | procP.terminate() 822 | procP.kill() 823 | # Need to disconnect to avoid multiple 824 | # start/ finish msg 825 | procP.disconnect( 826 | procP, 827 | QtCore.SIGNAL("started()"), 828 | self.on_pmvs_start) 829 | procP.waitForFinished() 830 | procP.disconnect( 831 | procP, 832 | QtCore.SIGNAL("finished(int)"), 833 | self.on_pmvs_finish) 834 | 835 | def kill_cam_process(self): 836 | procCam.terminate() 837 | procCam.kill() 838 | # Need to disconnect to avoid multiple 839 | # start/ finish msg 840 | procCam.disconnect( 841 | procCam, 842 | QtCore.SIGNAL("started()"), 843 | self.on_cam_start) 844 | procCam.waitForFinished() 845 | procCam.disconnect( 846 | procCam, 847 | QtCore.SIGNAL("finished(int)"), 848 | self.on_cam_finish) 849 | 850 | # Start bundler 851 | def startbundler(self): 852 | if procB.state() == QProcess.Running: 853 | self.kill_bundler_process() 854 | command = self.text2.displayText() 855 | procB.connect( 856 | procB, 857 | QtCore.SIGNAL("started()"), 858 | self.on_bundler_start) 859 | procB.connect( 860 | procB, 861 | QtCore.SIGNAL("finished(int)"), 862 | self.on_bundler_finish) 863 | procB.connect( 864 | procB, 865 | QtCore.SIGNAL("readyReadStandardOutput()"), 866 | self.on_bundler_out) 867 | procB.start(command) 868 | procB.waitForStarted() 869 | 870 | # Start cmvs 871 | def startcmvs(self): 872 | if procC.state() == QProcess.Running: 873 | self.kill_cmvs_process() 874 | command = self.text6.displayText() 875 | procC.connect( 876 | procC, 877 | QtCore.SIGNAL("started()"), 878 | self.on_cmvs_start) 879 | procC.connect( 880 | procC, 881 | QtCore.SIGNAL("finished(int)"), 882 | self.on_cmvs_finish) 883 | procC.connect( 884 | procC, 885 | QtCore.SIGNAL("readyReadStandardOutput()"), 886 | self.on_cmvs_out) 887 | procC.start(command) 888 | procC.waitForStarted() 889 | 890 | # Start pmvs 891 | def startpmvs(self): 892 | if procP.state() == QProcess.Running: 893 | self.kill_pmvs_process() 894 | command = self.text8.displayText() 895 | procP.connect( 896 | procP, 897 | QtCore.SIGNAL("started()"), 898 | self.on_pmvs_start) 899 | procP.connect( 900 | procP, 901 | QtCore.SIGNAL("finished(int)"), 902 | self.on_pmvs_finish) 903 | procP.connect( 904 | procP, 905 | QtCore.SIGNAL("readyReadStandardOutput()"), 906 | self.on_pmvs_out) 907 | procP.start(command) 908 | procP.waitForStarted() 909 | 910 | # select directory with photos (Camera Database) 911 | def showDialog4(self): 912 | directoryname = QFileDialog.getExistingDirectory( 913 | self, 914 | 'Open directory with photos', 915 | '/home') 916 | self.text9.setText(directoryname) 917 | 918 | # help button 9 - select directory 919 | def on_help9_clicked(self): 920 | QMessageBox.information(self, 921 | "Help!", 922 | "Select the directory with original photos. Pictures" + 923 | "have to be in JPG file format.\n\n - Press the RUN" + 924 | "button to check if the camera is already inside the" + 925 | "database.\n\n - If the camera is not correctly" + 926 | "saved, please insert in the windows the CCD width in mm", 927 | QMessageBox.Ok) 928 | 929 | # connection path-command 930 | def onChangedpathcamdat(self, text): 931 | self.text10.setText( 932 | "RunBundler --photos=" + 933 | self.text9.displayText() + 934 | " --checkCameraDatabase") 935 | 936 | # start Camera Database 937 | def startcamdat(self): 938 | self.kill_cam_process() 939 | command = self.text10.displayText() 940 | procCam.connect( 941 | procCam, 942 | QtCore.SIGNAL("started()"), 943 | self.on_cam_start) 944 | procCam.connect( 945 | procCam, 946 | QtCore.SIGNAL("finished(int)"), 947 | self.on_cam_finish) 948 | procCam.connect( 949 | procCam, 950 | QtCore.SIGNAL("readyReadStandardOutput()"), 951 | self.on_cam_out) 952 | procCam.start(command) 953 | procCam.waitForStarted() 954 | -------------------------------------------------------------------------------- /ppt-gui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import signal 6 | 7 | from PyQt4 import QtGui 8 | 9 | from gui.main import PPTGUI 10 | 11 | 12 | def main(): 13 | """The Main Loop !""" 14 | app = QtGui.QApplication(sys.argv) 15 | ppt = PPTGUI() 16 | ppt.show() 17 | sys.exit(app.exec_()) 18 | 19 | if __name__ == '__main__': 20 | signal.signal(signal.SIGINT, signal.SIG_DFL) 21 | main() 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from distutils.core import setup 3 | 4 | setup(name='PPT-GUI', 5 | version='0.1', 6 | description='A simple GUI for Python Photogrammetry Toolbox', 7 | author='Alessendro Bezzi, Luca Bezzi', 8 | author_email='alessandro.bezzi@arcteam.com, luca.bezzi@arteam.com', 9 | url='http://www.archeos.eu', 10 | license='GPL2', 11 | packages = ['gui'], 12 | package_data = {'gui' : ['assets/icons/info_icon.png', 13 | 'assets/icons/python_icon.png' 14 | ], 15 | }, 16 | scripts=['ppt-gui.py'], 17 | data_files=[ 18 | ('doc/ppt-gui', ['README.md']) 19 | ], 20 | requires=['ppt (>= 0.1)','pyQt4 (>= 4.9.3)'], 21 | ) 22 | --------------------------------------------------------------------------------