Merge branch 'dev' into orb_slam2

Conflicts:
	configure.sh
	opendm/config.py
	opendm/types.py
	scripts/odm_app.py

Former-commit-id: efb2520241
pull/1161/head
Pau Gargallo 2016-12-13 11:18:22 +01:00
commit f5f3c628bb
36 zmienionych plików z 1802 dodań i 777 usunięć

Wyświetl plik

@ -3,4 +3,12 @@ tests/test_data
SuperBuild/build
SuperBuild/download
SuperBuild/install
SuperBuild/src
SuperBuild/src
build
opensfm
pmvs
odm_orthophoto
odm_texturing
odm_meshing
odm_georeferencing
images_resize

78
CONTRIBUTING.md 100644
Wyświetl plik

@ -0,0 +1,78 @@
# Contributing to OpenDroneMap
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
### Code of Conduct
This project adheres to the Contributor Covenant [code of conduct](code_of_conduct.md).
By participating, you are expected to uphold this code.
Please report unacceptable behavior to the [Project Maintainer](mailto:svm@clevelandmetroparks.com).
## How can I contribute?
### Reporting bugs
Bugs are tracked as Github issues. Please create an issue in the repository and tag it with the Bug tag.
Explain the problem and include additional details to help maintainers reproduce the problem:
* **Use a clear and descriptive title** for the issue to identify the problem.
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you run ODM (Docker, Vagrant, etc), e.g. which command exactly you used in the terminal. When listing steps, **don't just say what you did, but explain how you did it**.
* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
* **Explain which behavior you expected to see instead and why.**
* **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem. If you use the keyboard while following the steps, **record the GIF with the [Keybinding Resolver](https://github.com/atom/keybinding-resolver) shown**. You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux.
* **If the problem is related to performance**, please post your machine's specs (host and guest machine).
* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below.
Include details about your configuration and environment:
* **Which version of ODM are you using?** A stable release? a clone of master or dev?
* **What's the name and version of the OS you're using**?
* **Are you running ODM in a virtual machine?** If so, which VM software are you using and which operating systems and versions are used for the host and the guest?
#### Template For Submitting Bug Reports
[Short description of problem here]
**Reproduction Steps:**
1. [First Step]
2. [Second Step]
3. [Other Steps...]
**Expected behavior:**
[Describe expected behavior here]
**Observed behavior:**
[Describe observed behavior here]
**Screenshots and GIFs**
![Screenshots and GIFs which follow reproduction steps to demonstrate the problem](url)
**ODM version:** [Enter Atom version here]
**OS and version:** [Enter OS name and version here]
**Additional information:**
* Problem started happening recently, didn't happen in an older version of ODM: [Yes/No]
* Problem can be reliably reproduced, doesn't happen randomly: [Yes/No]
* Problem happens with all datasets and projects, not only some datasets or projects: [Yes/No]
### Pull Requests
* ***Pull Requests should be made to the `dev` branch unless it is a critical bug.***
* Include screenshots and animated GIFs in your pull request whenever possible.
* Follow the [PEP8 Python Style Guide](https://www.python.org/dev/peps/pep-0008/).
* End files with a newline.
* Avoid platform-dependent code:
* Use `require('fs-plus').getHomeDirectory()` to get the home directory.
* Use `path.join()` to concatenate filenames.
* Use `os.tmpdir()` rather than `/tmp` when you need to reference the
temporary directory.
* Using a plain `return` when returning explicitly at the end of a function.
* Not `return null`, `return undefined`, `null`, or `undefined`

Wyświetl plik

@ -1,80 +1,32 @@
FROM ubuntu:14.04
MAINTAINER Danilo Bargen <mail@dbrgn.ch>
# Env variables
ENV DEBIAN_FRONTEND noninteractive
# Install dependencies
RUN apt-get update \
&& sudo apt-get remove libdc1394-22-dev \
&& apt-get install -y --install-recommends \
build-essential \
cmake \
git \
python-pip \
libgdal-dev \
libgeotiff-dev \
pkg-config \
libgtk2.0-dev \
libavcodec-dev \
libavformat-dev \
libswscale-dev \
python-dev \
python-numpy \
libtbb2 \
libtbb-dev \
libjpeg-dev \
libpng-dev \
libtiff-dev \
libjasper-dev \
libflann-dev \
libproj-dev \
libxext-dev \
liblapack-dev \
libeigen3-dev \
libvtk5-dev \
python-networkx \
libgoogle-glog-dev \
libsuitesparse-dev \
libboost-filesystem-dev \
libboost-iostreams-dev \
libboost-regex-dev \
libboost-python-dev \
libboost-date-time-dev \
libboost-thread-dev \
python-empy \
python-nose \
python-pyside \
python-pyexiv2 \
python-scipy \
jhead \
liblas-bin \
&& apt-get autoremove \
&& apt-get clean
# Add users
RUN useradd -m -U odm
#Pull in previously built packages image with lots of libraries.
FROM packages
# Prepare directories
RUN mkdir /code
WORKDIR /code
# Add repository files
ADD . /code/
# Copy repository files
COPY ccd_defs_check.py /code/ccd_defs_check.py
COPY CMakeLists.txt /code/CMakeLists.txt
COPY configure.sh /code/configure.sh
COPY /.git/ /code/.git/
COPY .gitignore /code/.gitignore
COPY .gitmodules /code/.gitmodules
COPY /modules/ /code/modules/
COPY /opendm/ /code/opendm/
COPY /patched_files/ /code/patched_files/
COPY run.py /code/run.py
COPY /scripts/ /code/scripts/
COPY /SuperBuild/cmake/ /code/SuperBuild/cmake/
COPY /SuperBuild/CMakeLists.txt /code/SuperBuild/CMakeLists.txt
COPY /tests/ /code/tests/
# Update submodules
RUN git submodule init && git submodule update
# Build OpenDroneMap
RUN bash ./configure.sh && \
mkdir build && cd build && cmake .. && make && cd .. && \
chown -R odm:odm /code
USER odm
ENV PYTHONPATH=${PYTHONPATH}:/code/SuperBuild/install/lib/python2.7/dist-packages:/code/SuperBuild/src/opensfm \
LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/code/SuperBuild/install/lib
#Compile code in SuperBuild and root directories
RUN cd SuperBuild && mkdir build && cd build && cmake .. && make -j$(nproc) \
&& cd ../.. && mkdir build && cd build && cmake .. && make -j$(nproc)
# Entry point
VOLUME ["/images"]
# WORKDIR /images
ENTRYPOINT ["python", "/code/run.py", "--project-path", "/images"]
ENTRYPOINT ["python", "/code/run.py", "--project-path", "/code/"]

139
README.md
Wyświetl plik

@ -1,13 +1,12 @@
# OpenDroneMap
![](https://opendronemap.github.io/OpenDroneMap/img/odm_image.png)
![](https://raw.githubusercontent.com/OpenDroneMap/OpenDroneMap/master/img/odm_image.png)
What is it?
===========
## What is it?
OpenDroneMap is an open source toolkit for processing aerial drone imagery. Typical drones use simple point-and-shoot cameras, so the images from drones, while from a different perspective, are similar to any pictures taken from point-and-shoot cameras, i.e. non-metric imagery. OpenDroneMap turns those simple images into three dimensional geographic data that can be used in combination with other geographic datasets.
![](https://opendronemap.github.io/OpenDroneMap/img/tol_ptcloud.png)
![](https://raw.githubusercontent.com/OpenDroneMap/OpenDroneMap/master/img/tol_ptcloud.png)
In a word, OpenDroneMap is a toolchain for processing raw civilian UAS imagery to other useful products. What kind of products?
@ -19,67 +18,58 @@ In a word, OpenDroneMap is a toolchain for processing raw civilian UAS imagery t
6. Digital Elevation Models
7. etc.
So far, it does Point Clouds, Digital Surface Models, Textured Digital Surface Models, and Orthorectified Imagery.
So far, it does Point Clouds, Digital Surface Models, Textured Digital Surface Models, and Orthorectified Imagery. Open Drone Map now includes state-of-the-art 3D reconstruction work by Michael Waechter, Nils Moehrle, and Michael Goesele. See their publication at http://www.gcc.tu-darmstadt.de/media/gcc/papers/Waechter-2014-LTB.pdf.
Users' mailing list: http://lists.osgeo.org/cgi-bin/mailman/listinfo/opendronemap-users
Developer's mailing list: http://lists.osgeo.org/cgi-bin/mailman/listinfo/opendronemap-dev
## QUICKSTART
Overview video: https://www.youtube.com/watch?v=0UctfoeNB_Y
Requires Ubuntu 14.04 or later, see https://github.com/OpenDroneMap/odm_vagrant for running on Windows in a VM
Developers
=================
*Support for Ubuntu 12.04 is currently BROKEN with the addition of OpenSfM and Ceres-Solver. It is likely to remain broken unless a champion is found to fix it.*
Help improve our software!
**[Download the latest release here](https://github.com/OpenDroneMap/OpenDroneMap/releases)**
[![Join the chat at https://gitter.im/OpenDroneMap/OpenDroneMap](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/OpenDroneMap/OpenDroneMap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Current version: 0.2 (beta)
1. Try to keep commits clean and simple
2. Submit a pull request with detailed changes and test results
### Installation
Before installing you need to set your environment variables. Open the ~/.bashrc file on your machine and add the following 3 lines at the end. The file can be opened with ```gedit ~/.bashrc``` if you are using an Ubuntu desktop environment. Be sure to replace the "/your/path/" with the correct path to the location where you extracted OpenDroneMap:
Steps to get OpenDroneMap running:
==================================
export PYTHONPATH=$PYTHONPATH:/your/path/OpenDroneMap/SuperBuild/install/lib/python2.7/dist-packages
export PYTHONPATH=$PYTHONPATH:/your/path/OpenDroneMap/SuperBuild/src/opensfm
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/your/path/OpenDroneMap/SuperBuild/install/lib
(Requires Ubuntu 14.04 or later, see https://github.com/OpenDroneMap/odm_vagrant for running on Windows in a VM)
Support for Ubuntu 12.04 is currently BROKEN with the addition of OpenSfM and Ceres-Solver. We are working hard to get it working again in the future.
#### Building OpenDroneMap using git
cd path/to/odm/dir
git clone https://github.com/OpenDroneMap/OpenDroneMap.git .
export PYTHONPATH=$PYTHONPATH:`pwd`/SuperBuild/install/lib/python2.7/dist-packages:`pwd`/SuperBuild/src/opensfm
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`/SuperBuild/install/lib
Now, enter the OpenDroneMap directory and compile all of the code by executing a single configuration script:
bash configure.sh
mkdir build && cd build && cmake .. && make && cd ..
For Ubuntu 15.10 users, this will help you get running:
For Ubuntu 15.10 users, this will help you get running:
sudo apt-get install python-xmltodict
sudo ln -s /usr/lib/x86_64-linux-gnu/libproj.so.9 /usr/lib/libproj.so
#### Running OpenDroneMap
### Run OpenDroneMap
First you need a set of images, which may or may not be georeferenced. There are two ways OpenDroneMap can understand geographic coordinates. First, the images can be geotagged in their EXIF data. This is the default. Alternatively, you can create a GCP file, [a process detailed here](https://github.com/OpenDroneMap/OpenDroneMap/wiki/2.-Running-OpenDroneMap#running-odm-with-ground-control)
First you need a set of images, taken from a drone or otherwise.
Create a project folder and places your images in an "images" directory:
|-- /path/to/project/
|-- images/
|-- img-1234.jpg
|-- ...
Example data can be cloned from https://github.com/OpenDroneMap/odm_data
Then run:
python run.py --project-path /path/to/project
There are many options for tuning your project. See the [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki/3.-Run-Time-Parameters) or run `python run.py -h`
There are many options for tuning your project. See the [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki/Run-Time-Parameters) or run `python run.py -h`
When the process finishes, the results will be organized as follows
### View Results
When the process finishes, the results will be organized as follows:
|-- images/
|-- img-1234.jpg
@ -88,11 +78,11 @@ When the process finishes, the results will be organized as follows
|-- img-1234.jpg
|-- ...
|-- opensfm/
|-- not much useful in here
|-- see mapillary/opensfm repository for more info
|-- pmvs/
|-- recon0/
|-- models/
|-- option-0000.ply # Dense point cloud
|-- option-0000.ply # Dense point cloud (not georeferenced)
|-- odm_meshing/
|-- odm_mesh.ply # A 3D mesh
|-- odm_meshing_log.txt # Output of the meshing task. May point out errors.
@ -112,44 +102,79 @@ When the process finishes, the results will be organized as follows
|-- odm_orthophoto_log.txt # Log file
|-- gdal_translate_log.txt # Log for georeferencing the png file
##### Viewing your results
Any file ending in .obj or .ply can be opened and viewed in [MeshLab](http://meshlab.sourceforge.net/) or similar software. That includes `pmvs/recon0/models/option-000.ply`, `odm_meshing/odm_mesh.ply`, `odm_texturing/odm_textured_model[_geo].obj`, or `odm_georeferencing/odm_georeferenced_model.ply`. Below is an example textured mesh:
![](https://opendronemap.github.io/OpenDroneMap/img/tol_text.png)
![](https://raw.githubusercontent.com/alexhagiopol/OpenDroneMap/feature-better-docker/toledo_dataset_example_mesh.jpg)
You can also view the orthophoto GeoTIFF in QGIS or other mapping software:
![](https://raw.githubusercontent.com/OpenDroneMap/OpenDroneMap/dev/img/bellus_map.png)
![](https://raw.githubusercontent.com/OpenDroneMap/OpenDroneMap/master/img/bellus_map.png)
#### Using Docker
## Build and Run Using Docker
You can build and run OpenDroneMap in a Docker container:
(Instructions below apply to Ubuntu 14.04, but the Docker image workflow
has equivalent procedures for Mac OS X and Windows. See [docs.docker.com](docs.docker.com))
export IMAGES=/absolute/path/to/your/project
docker build -t opendronemap:latest .
docker run -v $IMAGES:/images opendronemap:latest
OpenDroneMap is Dockerized, meaning you can use containerization to build and run it without tampering with the configuration of libraries and packages already
installed on your machine. Docker software is free to install and use in this context. If you don't have it installed,
see the [Docker Ubuntu installation tutorial](https://docs.docker.com/engine/installation/linux/ubuntulinux/) and follow the
instructions up until "Create a Docker group" inclusive. Once Docker is installed, an OpenDroneMap Docker image can be created
like so:
Replace /absolute/path/to/your/images with an absolute path to the directory containing your project (where the images are)
To pass in custom parameters to the `run.py` script, simply pass it as arguments to the `docker run` command.
git clone https://github.com/OpenDroneMap/OpenDroneMap.git
cd OpenDroneMap
docker build -t packages -f packages.Dockerfile .
docker build -t odm_image .
docker run -it --user root\
-v $(pwd)/images:/code/images\
-v $(pwd)/odm_orthophoto:/code/odm_orthophoto\
-v $(pwd)/odm_texturing:/code/odm_texturing\
--rm odm_image
---
Using this method, the containerized ODM will process the images in the OpenDroneMap/images directory and output results
to the OpenDroneMap/odm_orthophoto and OpenDroneMap/odm_texturing directories as described in the **Viewing Results** section.
If you want to view other results outside the Docker image simply add which directories you're interested in to the run command in the same pattern
established above. For example, if you're interested in the dense cloud results generated by PMVS and in the orthophoto,
simply use the following `docker run` command after building the image:
docker run -it --user root\
-v $(pwd)/images:/code/images\
-v $(pwd)/pmvs:/code/pmvs\
-v $(pwd)/odm_orthophoto:/code/odm_orthophoto\
--rm odm_image
To pass in custom parameters to the run.py script, simply pass it as arguments to the `docker run` command.
## User Interface
A web interface and API to OpenDroneMap is currently under active development in the [WebODM](https://github.com/OpenDroneMap/WebODM) repository.
## Examples
Here are some other videos, which may be outdated:
- https://www.youtube.com/watch?v=7ZTufQkODLs (2015-01-30)
- https://www.youtube.com/watch?v=m0i4GQdfl8A (2015-03-15)
Now that texturing is in the code base, you can access the full textured meshes using MeshLab. Open MeshLab, choose `File:Import Mesh` and choose your textured mesh from a location similar to the following: `reconstruction-with-image-size-1200-results\odm_texturing\odm_textured_model.obj`
Now that texturing is in the code base, you can access the full textured meshes using MeshLab.
Open MeshLab, choose `File:Import Mesh` and choose your textured mesh from a location similar to the following:
`reconstruction-with-image-size-1200-results\odm_texturing\odm_textured_model.obj`. Long term, the aim is for
the toolchain to also be able to optionally push to a variety of online data repositories, pushing hi-resolution
aerials to [OpenAerialMap](https://openaerialmap.org/), point clouds to [OpenTopography](http://opentopography.org/),
and pushing digital elevation models to an emerging global repository (yet to be named...). That leaves only
digital surface model meshes and UV textured meshes with no global repository home.
---
## Documentation:
Long term, the aim is for the toolchain to also be able to optionally push to a variety of online data repositories, pushing hi-resolution aerials to [OpenAerialMap](http://opentopography.org/), point clouds to [OpenTopography](http://opentopography.org/), and pushing digital elevation models to an emerging global repository (yet to be named...). That leaves only digital surface model meshes and UV textured meshes with no global repository home.
---
For documentation, please take a look at our [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki).Check here first if you are heaving problems. If you still need help, look through the issue queue or create one. There's also a general help chat [here](https://gitter.im/OpenDroneMap/generalhelp).
## Developers
Help improve our software!
[![Join the chat at https://gitter.im/OpenDroneMap/OpenDroneMap](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/OpenDroneMap/OpenDroneMap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
1. Try to keep commits clean and simple
2. Submit a pull request with detailed changes and test results
Documentation:
==============
For documentation, please take a look at our [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki).

Wyświetl plik

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.1)
project(ODM-SuperBuild)
@ -103,7 +103,9 @@ set(custom_libs OpenGV
CMVS
Catkin
Ecto
PDAL)
PDAL
MvsTexturing
)
# Dependencies of the SLAM module
if(ODM_BUILD_SLAM)

Wyświetl plik

@ -0,0 +1,29 @@
set(_proj_name mvstexturing)
set(_SB_BINARY_DIR "${SB_BINARY_DIR}/${_proj_name}")
ExternalProject_Add(${_proj_name}
DEPENDS
PREFIX ${_SB_BINARY_DIR}
TMP_DIR ${_SB_BINARY_DIR}/tmp
STAMP_DIR ${_SB_BINARY_DIR}/stamp
#--Download step--------------
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}
URL https://github.com/nmoehrle/mvs-texturing/archive/dab68acaa693275c183c254a958130ee6d29c3e4.zip
URL_MD5 0b0466f5d1046699594ce7fc77bdad02
#--Update/Patch step----------
UPDATE_COMMAND ""
#--Configure step-------------
SOURCE_DIR ${SB_SOURCE_DIR}/${_proj_name}
CMAKE_ARGS
-DRESEARCH=OFF
-DCMAKE_BUILD_TYPE:STRING=Release
-DCMAKE_INSTALL_PREFIX:PATH=${SB_INSTALL_DIR}
#--Build step-----------------
BINARY_DIR ${_SB_BINARY_DIR}
#--Install step---------------
INSTALL_DIR ${SB_INSTALL_DIR}
#--Output logging-------------
LOG_DOWNLOAD OFF
LOG_CONFIGURE OFF
LOG_BUILD OFF
)

Wyświetl plik

@ -8,15 +8,17 @@ ExternalProject_Add(${_proj_name}
STAMP_DIR ${_SB_BINARY_DIR}/stamp
#--Download step--------------
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}
URL https://github.com/mapillary/OpenSfM/archive/a4b07056aec1184692c1432fbdd1074710aec32b.zip
URL_MD5 42B2B1994C3309BBF4525C8CC1F6F741
URL https://github.com/mapillary/OpenSfM/archive/odm-4.zip
URL_MD5 C7444B8595AAE33C6E191D8E8518BD5E
#--Update/Patch step----------
UPDATE_COMMAND ""
#--Configure step-------------
SOURCE_DIR ${SB_SOURCE_DIR}/${_proj_name}
CONFIGURE_COMMAND cmake <SOURCE_DIR>/${_proj_name}/src
-DCERES_ROOT_DIR=${SB_INSTALL_DIR}
-DCERES_ROOT_DIR=${SB_INSTALL_DIR}
-DOpenCV_DIR=${SB_INSTALL_DIR}/share/OpenCV
-DOPENSFM_BUILD_TESTS=off
#--Build step-----------------
BINARY_DIR ${_SB_BINARY_DIR}
#--Install step---------------

0
ccd_defs_check.py 100755 → 100644
Wyświetl plik

Wyświetl plik

@ -1,15 +1,74 @@
# Contributor Code of Conduct
# Contributor Covenant Code of Conduct
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
## Our Pledge
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
## Our Standards
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
Examples of behavior that contributes to creating a positive environment
include:
This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
Examples of unacceptable behavior by participants include:
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

111
configure.sh 100644 → 100755
Wyświetl plik

@ -1,43 +1,32 @@
#!/bin/bash
# Check OS
if [ ! $(command -v apt-get) ]; then
echo -e "\e[1;31mERROR: Not a Debian-based linux system.
Impossible to install OpenCV with this script\e[0;39m"
exit 1
fi
## Set up library paths
RUNPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
export PYTHONPATH=$RUNPATH/SuperBuild/install/lib/python2.7/dist-packages:$RUNPATH/SuperBuild/src/opensfm:$PYTHONPATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$RUNPATH/SuperBuild/install/lib
## Before installing
echo -e "\e[1;34mUpdating the system\e[0;39m"
echo "Updating the system"
sudo apt-get update
END_CMD1=$?
# sudo apt-get upgrade -y
# END_CMD2=$?
if [ $END_CMD1 -ne 0 ]
then
echo -e "\e[1;31mERROR: \e[39mWhen Updating the system\e[0m"
exit 1
fi
## Install Required Requisites
echo -e "\e[1;34mInstalling Required Requisites\e[0;39m"
sudo apt-get install build-essential \
cmake \
echo "Installing Required Requisites"
sudo apt-get install -y -qq build-essential \
git \
cmake \
python-pip \
libgdal-dev \
gdal-bin \
libgeotiff-dev \
pkg-config -y -qq
if [ $? -ne 0 ]
then
echo -e "\e[1;31mERROR: \e[39mWhen Installing Required Requisites\e[0m"
exit 1
fi
pkg-config
## Installing Optional Requisites
echo -e "\e[1;34mInstalling OpenCV Dependencies\e[0;39m"
sudo apt-get install libgtk2.0-dev \
echo "Getting CMake 3.1 for MVS-Texturing"
sudo apt-get install -y software-properties-common python-software-properties
sudo add-apt-repository -y ppa:george-edison55/cmake-3.x
sudo apt-get update -y
sudo apt-get install -y --only-upgrade cmake
echo "Installing OpenCV Dependencies"
sudo apt-get install -y -qq libgtk2.0-dev \
libavcodec-dev \
libavformat-dev \
libswscale-dev \
@ -54,20 +43,14 @@ sudo apt-get install libgtk2.0-dev \
libxext-dev \
liblapack-dev \
libeigen3-dev \
libvtk5-dev -y -qq
if [ $? -ne 0 ]
then
echo -e "\e[1;31mERROR: \e[39mError when Installing Dependencies Requisites\e[0m"
exit 1
fi
libvtk5-dev
## Remove libdc1394-22-dev due to python opencv issue
echo -e "\e[1;34mRemoving libdc1394-22-dev\e[0;39m"
echo "Removing libdc1394-22-dev due to python opencv issue"
sudo apt-get remove libdc1394-22-dev
## Installing OpenSfM Requisites
echo -e "\e[1;34mInstalling OpenSfM Dependencies\e[0;39m"
sudo apt-get install python-networkx \
echo "Installing OpenSfM Dependencies"
sudo apt-get install -y -qq python-networkx \
libgoogle-glog-dev \
libsuitesparse-dev \
libboost-filesystem-dev \
@ -75,54 +58,34 @@ sudo apt-get install python-networkx \
libboost-regex-dev \
libboost-python-dev \
libboost-date-time-dev \
libboost-thread-dev -y -qq
libboost-thread-dev \
python-pyproj
sudo pip install -U PyYAML \
exifread \
gpxpy \
xmltodict
if [ $? -ne 0 ]
then
echo -e "\e[1;31mERROR: \e[39mError when Installing OpenSfM Dependencies\e[0m"
exit 1
fi
## Installing Ecto Requisites
echo -e "\e[1;34mInstalling Ecto Dependencies\e[0;39m"
echo "Installing Ecto Dependencies"
sudo pip install -U catkin-pkg
sudo apt-get install python-empy \
sudo apt-get install -y -qq python-empy \
python-nose \
python-pyside -y -qq
if [ $? -ne 0 ]
then
echo -e "\e[1;31mERROR: \e[39mError when Installing Ecto Dependencies\e[0m"
exit 1
fi
python-pyside
## Installing OpenDroneMap Requisites
echo -e "\e[1;34mInstalling OpenDroneMap Dependencies\e[0;39m"
sudo apt-get install python-pyexiv2 \
echo "Installing OpenDroneMap Dependencies"
sudo apt-get install -y -qq python-pyexiv2 \
python-scipy \
jhead \
liblas-bin -y -qq
if [ $? -ne 0 ]
then
echo -e "\e[1;31mERROR: \e[39mError when Installing OpenDroneMap Dependencies\e[0m"
exit 1
fi
liblas-bin
## Get sys vars
NUM_CORES=`grep -c processor /proc/cpuinfo`
## Add SuperBuild path to the library path
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`/SuperBuild/install/lib
## Add SuperBuild path to the python path
export PYTHONPATH=$PYTHONPATH:`pwd`/SuperBuild/install/lib/python2.7/dist-packages:`pwd`/SuperBuild/src/opensfm
## Compile SuperBuild
echo "Compiling SuperBuild"
cd SuperBuild
mkdir -p build && cd build
cmake .. && make -j${NUM_CORES}
cmake .. && make -j$(nproc)
echo -e "\e[1;34mScript finished\e[0;39m"
echo "Compiling build"
cd ../..
mkdir -p build && cd build
cmake .. && make -j$(nproc)
echo "Configuration Finished"

0
hooks/pre-commit 100755 → 100644
Wyświetl plik

Wyświetl plik

@ -24,3 +24,4 @@ Licensing for portions of OpenDroneMap are as follows:
* vtk5 - BSD - http://www.vtk.org/VTK/project/license.html
* libext - https://github.com/OpenDroneMap/OpenDroneMap/blob/gh-pages/licenses/libext_copyright.txt
* libx11 - https://github.com/OpenDroneMap/OpenDroneMap/blob/gh-pages/licenses/libx11_copyright.txt
* MVS Texturing - BSD - https://github.com/nmoehrle/mvs-texturing/blob/master/LICENSE.txt

Wyświetl plik

@ -210,7 +210,7 @@ void UtmExtractor::extractUtm()
// Open output file
std::ofstream outputCoordStream(outputCoordFileName_.c_str());
if (!outputCoordStream.good()) {
throw UtmExtractorException("Failed to openg " + outputCoordFileName_ + " for writing.");
throw UtmExtractorException("Failed to open " + outputCoordFileName_ + " for writing.");
}
outputCoordStream.precision(10);

Wyświetl plik

@ -1,203 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by Qt Creator 2.4.1, 2015-02-03T10:36:15. -->
<qtcreator>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QString" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QString" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">System</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap"/>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.DefaultCMakeTarget</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="CMakeProjectManager.CMakeBuildConfiguration.BuildDirectory">/home/spotscale/odm/OpenDroneMap-texturing_orthophoto_spotscale_additions/odm_georef-build</value>
<value type="QString" key="CMakeProjectManager.CMakeBuildConfiguration.ToolChain">ProjectExplorer.ToolChain.Gcc:/usr/bin/g++.x86-linux-generic-elf-32bit./usr/bin/gdb</value>
<value type="QString" key="ProjectExplorer.BuildCOnfiguration.ToolChain">ProjectExplorer.ToolChain.Gcc:/usr/bin/g++.x86-linux-generic-elf-32bit./usr/bin/gdb</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.AdditionalArguments"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="CMakeProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.AdditionalArguments">clean</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="CMakeProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">all</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">No deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Project.UseGlobal">true</value>
<value type="bool" key="Analyzer.Project.UseGlobal">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="CMakeProjectManager.BaseEnvironmentBase">2</value>
<value type="QString" key="CMakeProjectManager.CMakeRunConfiguation.Title">odm_georef</value>
<value type="QString" key="CMakeProjectManager.CMakeRunConfiguration.Arguments">-verbose -bundleFile ../../../shared_folder/seneca/reconstruction-with-image-size-1200/pmvs/bundle.rd.out -inputFile ../../../shared_folder/seneca/reconstruction-with-image-size-1200-results/odm_texturing/odm_textured_model.obj -imagesListPath ../../../shared_folder/seneca/reconstruction-with-image-size-1200/pmvs/list.rd.txt -gcpFile ../../../shared_folder/seneca_georef_input/gcp_list.txt -imagesPath ../../../shared_folder/seneca/ -bundleResizedTo 1200</value>
<value type="bool" key="CMakeProjectManager.CMakeRunConfiguration.UseTerminal">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.CMakeRunConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="CMakeProjectManager.CMakeRunConfiguration.UserWorkingDirectory"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">odm_georef</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.</value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.EnvironmentId</variable>
<value type="QString">{785a73be-b55f-490c-9d46-e1451c235840}</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">10</value>
</data>
</qtcreator>

Wyświetl plik

@ -6,9 +6,6 @@
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
// Modified PCL
#include "modifiedPclFunctions.hpp"
// This
#include "Georef.hpp"
@ -753,7 +750,7 @@ void Georef::performGeoreferencingWithGCP()
log_ << "Reading mesh file " << inputObjFilename_ <<"\n";
log_ << '\n';
pcl::TextureMesh mesh;
if (pcl::io::loadOBJFile(inputObjFilename_, mesh) == -1)
if (loadObjFile(inputObjFilename_, mesh) == -1)
{
throw GeorefException("Error when reading model from:\n" + inputObjFilename_ + "\n");
}
@ -1077,7 +1074,7 @@ void Georef::createGeoreferencedModelFromExifData()
log_ << '\n';
log_ << "Reading mesh file...\n";
pcl::TextureMesh mesh;
pcl::io::loadOBJFile(inputObjFilename_, mesh);
loadObjFile(inputObjFilename_, mesh);
log_ << ".. mesh file read.\n";
// Contains the vertices of the mesh.
@ -1249,3 +1246,364 @@ void Georef::printGeorefSystem()
}
bool Georef::loadObjFile(std::string inputFile, pcl::TextureMesh &mesh)
{
int data_type;
unsigned int data_idx;
int file_version;
int offset = 0;
Eigen::Vector4f origin;
Eigen::Quaternionf orientation;
if (!readHeader(inputFile, mesh.cloud, origin, orientation, file_version, data_type, data_idx, offset))
{
throw GeorefException("Problem reading header in modelfile!\n");
}
std::ifstream fs;
fs.open (inputFile.c_str (), std::ios::binary);
if (!fs.is_open () || fs.fail ())
{
//PCL_ERROR ("[pcl::OBJReader::readHeader] Could not open file '%s'! Error : %s\n", file_name.c_str (), strerror(errno));
fs.close ();
log_<<"Could not read mesh from file ";
log_ << inputFile.c_str();
log_ <<"\n";
throw GeorefException("Problem reading mesh from file!\n");
}
// Seek at the given offset
fs.seekg (data_idx, std::ios::beg);
// Get normal_x field indices
int normal_x_field = -1;
for (std::size_t i = 0; i < mesh.cloud.fields.size (); ++i)
{
if (mesh.cloud.fields[i].name == "normal_x")
{
normal_x_field = i;
break;
}
}
std::size_t v_idx = 0;
std::size_t vn_idx = 0;
std::size_t vt_idx = 0;
std::size_t f_idx = 0;
std::string line;
std::vector<std::string> st;
std::vector<Eigen::Vector2f> coordinates;
std::vector<Eigen::Vector2f> allTexCoords;
std::map<int, int> f2vt;
try
{
while (!fs.eof ())
{
getline (fs, line);
// Ignore empty lines
if (line == "")
continue;
// Tokenize the line
std::stringstream sstream (line);
sstream.imbue (std::locale::classic ());
line = sstream.str ();
boost::trim (line);
boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on);
// Ignore comments
if (st[0] == "#")
continue;
// Vertex
if (st[0] == "v")
{
try
{
for (int i = 1, f = 0; i < 4; ++i, ++f)
{
float value = boost::lexical_cast<float> (st[i]);
memcpy (&mesh.cloud.data[v_idx * mesh.cloud.point_step + mesh.cloud.fields[f].offset], &value, sizeof (float));
}
++v_idx;
}
catch (const boost::bad_lexical_cast &e)
{
log_<<"Unable to convert %s to vertex coordinates!\n";
throw GeorefException("Unable to convert %s to vertex coordinates!");
}
continue;
}
// Vertex normal
if (st[0] == "vn")
{
try
{
for (int i = 1, f = normal_x_field; i < 4; ++i, ++f)
{
float value = boost::lexical_cast<float> (st[i]);
memcpy (&mesh.cloud.data[vn_idx * mesh.cloud.point_step + mesh.cloud.fields[f].offset],
&value,
sizeof (float));
}
++vn_idx;
}
catch (const boost::bad_lexical_cast &e)
{
log_<<"Unable to convert %s to vertex normal!\n";
throw GeorefException("Unable to convert %s to vertex normal!");
}
continue;
}
// Texture coordinates
if (st[0] == "vt")
{
try
{
Eigen::Vector3f c (0, 0, 0);
for (std::size_t i = 1; i < st.size (); ++i)
c[i-1] = boost::lexical_cast<float> (st[i]);
if (c[2] == 0)
coordinates.push_back (Eigen::Vector2f (c[0], c[1]));
else
coordinates.push_back (Eigen::Vector2f (c[0]/c[2], c[1]/c[2]));
++vt_idx;
}
catch (const boost::bad_lexical_cast &e)
{
log_<<"Unable to convert %s to vertex texture coordinates!\n";
throw GeorefException("Unable to convert %s to vertex texture coordinates!");
}
continue;
}
// Material
if (st[0] == "usemtl")
{
mesh.tex_polygons.push_back (std::vector<pcl::Vertices> ());
mesh.tex_materials.push_back (pcl::TexMaterial ());
for (std::size_t i = 0; i < companions_.size (); ++i)
{
std::vector<pcl::TexMaterial>::const_iterator mat_it = companions_[i].getMaterial (st[1]);
if (mat_it != companions_[i].materials_.end ())
{
mesh.tex_materials.back () = *mat_it;
break;
}
}
// We didn't find the appropriate material so we create it here with name only.
if (mesh.tex_materials.back ().tex_name == "")
mesh.tex_materials.back ().tex_name = st[1];
mesh.tex_coordinates.push_back (coordinates);
coordinates.clear ();
continue;
}
// Face
if (st[0] == "f")
{
//We only care for vertices indices
pcl::Vertices face_v; face_v.vertices.resize (st.size () - 1);
for (std::size_t i = 1; i < st.size (); ++i)
{
int v;
sscanf (st[i].c_str (), "%d", &v);
v = (v < 0) ? v_idx + v : v - 1;
face_v.vertices[i-1] = v;
int v2, vt, vn;
sscanf (st[i].c_str (), "%d/%d/%d", &v2, &vt, &vn);
f2vt[3*(f_idx) + i-1] = vt-1;
}
mesh.tex_polygons.back ().push_back (face_v);
++f_idx;
continue;
}
}
}
catch (const char *exception)
{
fs.close ();
log_<<"Unable to read file!\n";
throw GeorefException("Unable to read file!");
}
if (vt_idx != v_idx)
{
std::vector<Eigen::Vector2f> texcoordinates = std::vector<Eigen::Vector2f>(0);
for (size_t faceIndex = 0; faceIndex < f_idx; ++faceIndex)
{
for(size_t i = 0; i < 3; ++i)
{
Eigen::Vector2f vt = mesh.tex_coordinates[0][f2vt[3*faceIndex+i]];
texcoordinates.push_back(vt);
}
}
mesh.tex_coordinates.clear();
mesh.tex_coordinates.push_back(texcoordinates);
}
fs.close();
return (0);
}
bool Georef::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud,
Eigen::Vector4f &origin, Eigen::Quaternionf &orientation,
int &file_version, int &data_type, unsigned int &data_idx,
const int offset)
{
origin = Eigen::Vector4f::Zero ();
orientation = Eigen::Quaternionf::Identity ();
file_version = 0;
cloud.width = cloud.height = cloud.point_step = cloud.row_step = 0;
cloud.data.clear ();
data_type = 0;
data_idx = offset;
std::ifstream fs;
std::string line;
if (file_name == "" || !boost::filesystem::exists (file_name))
{
return false;
}
// Open file in binary mode to avoid problem of
// std::getline() corrupting the result of ifstream::tellg()
fs.open (file_name.c_str (), std::ios::binary);
if (!fs.is_open () || fs.fail ())
{
fs.close ();
return false;
}
// Seek at the given offset
fs.seekg (offset, std::ios::beg);
// Read the header and fill it in with wonderful values
bool vertex_normal_found = false;
bool vertex_texture_found = false;
// Material library, skip for now!
// bool material_found = false;
std::vector<std::string> material_files;
std::size_t nr_point = 0;
std::vector<std::string> st;
try
{
while (!fs.eof ())
{
getline (fs, line);
// Ignore empty lines
if (line == "")
continue;
// Tokenize the line
std::stringstream sstream (line);
sstream.imbue (std::locale::classic ());
line = sstream.str ();
boost::trim (line);
boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on);
// Ignore comments
if (st.at (0) == "#")
continue;
// Vertex
if (st.at (0) == "v")
{
++nr_point;
continue;
}
// Vertex texture
if ((st.at (0) == "vt") && !vertex_texture_found)
{
vertex_texture_found = true;
continue;
}
// Vertex normal
if ((st.at (0) == "vn") && !vertex_normal_found)
{
vertex_normal_found = true;
continue;
}
// Material library, skip for now!
if (st.at (0) == "mtllib")
{
material_files.push_back (st.at (1));
continue;
}
}
}
catch (const char *exception)
{
fs.close ();
return false;
}
if (!nr_point)
{
fs.close ();
return false;
}
int field_offset = 0;
for (int i = 0; i < 3; ++i, field_offset += 4)
{
cloud.fields.push_back (pcl::PCLPointField ());
cloud.fields[i].offset = field_offset;
cloud.fields[i].datatype = pcl::PCLPointField::FLOAT32;
cloud.fields[i].count = 1;
}
cloud.fields[0].name = "x";
cloud.fields[1].name = "y";
cloud.fields[2].name = "z";
if (vertex_normal_found)
{
std::string normals_names[3] = { "normal_x", "normal_y", "normal_z" };
for (int i = 0; i < 3; ++i, field_offset += 4)
{
cloud.fields.push_back (pcl::PCLPointField ());
pcl::PCLPointField& last = cloud.fields.back ();
last.name = normals_names[i];
last.offset = field_offset;
last.datatype = pcl::PCLPointField::FLOAT32;
last.count = 1;
}
}
if (material_files.size () > 0)
{
for (std::size_t i = 0; i < material_files.size (); ++i)
{
pcl::MTLReader companion;
if (companion.read (file_name, material_files[i]))
{
log_<<"Problem reading material file.";
}
companions_.push_back (companion);
}
}
cloud.point_step = field_offset;
cloud.width = nr_point;
cloud.height = 1;
cloud.row_step = cloud.point_step * cloud.width;
cloud.is_dense = true;
cloud.data.resize (cloud.point_step * nr_point);
fs.close ();
return true;
}

Wyświetl plik

@ -8,6 +8,8 @@
// PCL
#include <pcl/common/eigen.h>
#include <pcl/common/common.h>
// Modified PCL
#include "modifiedPclFunctions.hpp"
// Logger
#include "Logger.hpp"
@ -204,6 +206,24 @@ private:
**/
void printGeorefSystem();
/*!
* \brief Loads a model from an .obj file (replacement for the pcl obj loader).
*
* \param inputFile Path to the .obj file.
* \param mesh The model.
* \return True if model was loaded successfully.
*/
bool loadObjFile(std::string inputFile, pcl::TextureMesh &mesh);
/*!
* \brief Function is compied straight from the function in the pcl::io module.
*/
bool readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud,
Eigen::Vector4f &origin, Eigen::Quaternionf &orientation,
int &file_version, int &data_type, unsigned int &data_idx,
const int offset);
Logger log_; /**< Logging object. */
std::string logFile_; /**< The path to the output log file. */
@ -230,6 +250,10 @@ private:
std::vector<std::string> imageList_; /**< A vector containing the names of the corresponding cameras. **/
GeorefSystem georefSystem_; /**< Contains the georeference system. **/
bool multiMaterial_; /**< True if the mesh has multiple materials. **/
std::vector<pcl::MTLReader> companions_; /**< Materials (used by loadOBJFile). **/
};
/*!

Wyświetl plik

@ -8,6 +8,7 @@
#include <pcl/point_types.h>
#include <pcl/surface/texture_mapping.h>
#include <pcl/io/ply_io.h>
#include <pcl/io/obj_io.h>
int saveOBJFile(const std::string &file_name, const pcl::TextureMesh &tex_mesh, unsigned precision);

Wyświetl plik

@ -1,203 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by Qt Creator 2.4.1, 2015-02-04T09:12:31. -->
<qtcreator>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QString" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QString" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">System</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap"/>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.DefaultCMakeTarget</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="CMakeProjectManager.CMakeBuildConfiguration.BuildDirectory">/home/spotscale/odm/OpenDroneMap-texturing_orthophoto_spotscale_additions/odm_orthophoto-build</value>
<value type="QString" key="CMakeProjectManager.CMakeBuildConfiguration.ToolChain">ProjectExplorer.ToolChain.Gcc:/usr/bin/g++.x86-linux-generic-elf-32bit./usr/bin/gdb</value>
<value type="QString" key="ProjectExplorer.BuildCOnfiguration.ToolChain">ProjectExplorer.ToolChain.Gcc:/usr/bin/g++.x86-linux-generic-elf-32bit./usr/bin/gdb</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.AdditionalArguments"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="CMakeProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.AdditionalArguments">clean</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="CMakeProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">all</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">No deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Project.UseGlobal">true</value>
<value type="bool" key="Analyzer.Project.UseGlobal">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="CMakeProjectManager.BaseEnvironmentBase">2</value>
<value type="QString" key="CMakeProjectManager.CMakeRunConfiguation.Title">odm_orthophoto</value>
<value type="QString" key="CMakeProjectManager.CMakeRunConfiguration.Arguments"></value>
<value type="bool" key="CMakeProjectManager.CMakeRunConfiguration.UseTerminal">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.CMakeRunConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="CMakeProjectManager.CMakeRunConfiguration.UserWorkingDirectory"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">odm_orthophoto</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.</value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.EnvironmentId</variable>
<value type="QString">{785a73be-b55f-490c-9d46-e1451c235840}</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">10</value>
</data>
</qtcreator>

Wyświetl plik

@ -266,7 +266,7 @@ void OdmOrthoPhoto::printHelp()
log_ << "Call the program with flag \"-help\", or without parameters to print this message, or check any generated log file.\n";
log_ << "Call the program with flag \"-verbose\", to print log messages in the standard output stream as well as in the log file.\n\n";
log_ << "Parameters are specified as: \"-<argument name> <argument>\", (without <>), and the following parameters are configureable:n";
log_ << "Parameters are specified as: \"-<argument name> <argument>\", (without <>), and the following parameters are configureable:\n";
log_ << "\"-inputFile <path>\" (mandatory)\n";
log_ << "\"Input obj file that must contain a textured mesh.\n\n";
@ -319,20 +319,22 @@ void OdmOrthoPhoto::createOrthoPhoto()
}
log_ << "Reading mesh file...\n";
// The textureds mesh.
// The textured mesh.
pcl::TextureMesh mesh;
pcl::io::loadOBJFile(inputFile_, mesh);
loadObjFile(inputFile_, mesh);
log_ << ".. mesh file read.\n\n";
// Does the model have more than one material?
multiMaterial_ = 1 < mesh.tex_materials.size();
bool splitModel = false;
if(multiMaterial_)
{
// Need to check relationship between texture coordinates and faces.
if(!isModelOk(mesh))
{
throw OdmOrthoPhotoException("Could not generate ortho photo: The given mesh has multiple textures, but the number of texture coordinates is NOT equal to 3 times the number of faces.");
splitModel = true;
}
}
@ -357,7 +359,7 @@ void OdmOrthoPhoto::createOrthoPhoto()
float yDiff = yMax - yMin;
log_ << "Ortho photo area : " << xDiff*yDiff << "m2\n";
// The resolution neccesary to fit the area with the given resolution.
// The resolution necessary to fit the area with the given resolution.
int rowRes = static_cast<int>(std::ceil(resolution_*yDiff));
int colRes = static_cast<int>(std::ceil(resolution_*xDiff));
log_ << "Ortho photo resolution, width x height : " << colRes << "x" << rowRes << '\n';
@ -386,6 +388,57 @@ void OdmOrthoPhoto::createOrthoPhoto()
pcl::PointCloud<pcl::PointXYZ>::Ptr meshCloud (new pcl::PointCloud<pcl::PointXYZ>);
pcl::fromPCLPointCloud2 (mesh.cloud, *meshCloud);
// Split model and make copies of vertices and texture coordinates for all faces
if (splitModel)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr meshCloudSplit (new pcl::PointCloud<pcl::PointXYZ>);
std::vector<Eigen::Vector2f> textureCoordinates = std::vector<Eigen::Vector2f>(0);
size_t vertexIndexCount = 0;
for(size_t t = 0; t < mesh.tex_polygons.size(); ++t)
{
for(size_t faceIndex = 0; faceIndex < mesh.tex_polygons[t].size(); ++faceIndex)
{
pcl::Vertices polygon = mesh.tex_polygons[t][faceIndex];
// The index to the vertices of the polygon.
size_t v1i = polygon.vertices[0];
size_t v2i = polygon.vertices[1];
size_t v3i = polygon.vertices[2];
// The polygon's points.
pcl::PointXYZ v1 = meshCloud->points[v1i];
pcl::PointXYZ v2 = meshCloud->points[v2i];
pcl::PointXYZ v3 = meshCloud->points[v3i];
Eigen::Vector2f vt1 = mesh.tex_coordinates[0][3*faceIndex];
Eigen::Vector2f vt2 = mesh.tex_coordinates[0][3*faceIndex + 1];
Eigen::Vector2f vt3 = mesh.tex_coordinates[0][3*faceIndex + 2];
meshCloudSplit->points.push_back(v1);
textureCoordinates.push_back(vt1);
mesh.tex_polygons[t][faceIndex].vertices[0] = vertexIndexCount;
++vertexIndexCount;
meshCloudSplit->points.push_back(v2);
textureCoordinates.push_back(vt2);
mesh.tex_polygons[t][faceIndex].vertices[1] = vertexIndexCount;
++vertexIndexCount;
meshCloudSplit->points.push_back(v3);
textureCoordinates.push_back(vt3);
mesh.tex_polygons[t][faceIndex].vertices[2] = vertexIndexCount;
++vertexIndexCount;
}
}
mesh.tex_coordinates.clear();
mesh.tex_coordinates.push_back(textureCoordinates);
meshCloud = meshCloudSplit;
}
// Creates a transformation which aligns the area for the ortho photo.
Eigen::Transform<float, 3, Eigen::Affine> transform = getROITransform(xMin, -yMax);
@ -395,12 +448,13 @@ void OdmOrthoPhoto::createOrthoPhoto()
pcl::transformPointCloud(*meshCloud, *meshCloud, transform);
log_ << ".. mesh translated and scaled.\n\n";
// Flatten texture coordiantes.
// Flatten texture coordinates.
std::vector<Eigen::Vector2f> uvs;
for(size_t t = 0; t < mesh.tex_coordinates.size(); ++t)
{
uvs.insert(uvs.end(), mesh.tex_coordinates[t].begin(), mesh.tex_coordinates[t].end());
}
//cv::namedWindow("dsfs");
// The current material texture
cv::Mat texture;
@ -416,7 +470,7 @@ void OdmOrthoPhoto::createOrthoPhoto()
// The material of the current submesh.
pcl::TexMaterial material = mesh.tex_materials[t];
texture = cv::imread(material.tex_file);
// Check for missing files.
if(texture.empty())
{
@ -591,7 +645,7 @@ void OdmOrthoPhoto::adjustBoundsForEntireModel(const pcl::TextureMesh &mesh)
Eigen::Transform<float, 3, Eigen::Affine> OdmOrthoPhoto::getROITransform(float xMin, float yMin) const
{
// The tranform used to move the chosen area into the ortho photo.
// The transform used to move the chosen area into the ortho photo.
Eigen::Transform<float, 3, Eigen::Affine> transform;
transform(0, 0) = resolution_; // x Scaling.
@ -653,12 +707,13 @@ void OdmOrthoPhoto::drawTexturedTriangle(const cv::Mat &texture, const pcl::Vert
v2x = v2.x; v2y = v2.y; v2z = v2.z;
v3x = v3.x; v3y = v3.y; v3z = v3.z;
// Get texture coorinates. (Special cases for PCL when using multiple materials vs one material)
// Get texture coordinates. (Special cases for PCL when using multiple materials vs one material)
if(multiMaterial_)
{
v1u = uvs[3*faceIndex][0]; v1v = uvs[3*faceIndex][1];
v2u = uvs[3*faceIndex+1][0]; v2v = uvs[3*faceIndex+1][1];
v3u = uvs[3*faceIndex+2][0]; v3v = uvs[3*faceIndex+2][1];
}
else
{
@ -671,22 +726,22 @@ void OdmOrthoPhoto::drawTexturedTriangle(const cv::Mat &texture, const pcl::Vert
int xMin = static_cast<int>(std::min(std::min(v1x, v2x), v3x));
if(xMin > photo_.cols)
{
return; // Completly outside to the right.
return; // Completely outside to the right.
}
int xMax = static_cast<int>(std::max(std::max(v1x, v2x), v3x));
if(xMax < 0)
{
return; // Completly outside to the left.
return; // Completely outside to the left.
}
int yMin = static_cast<int>(std::min(std::min(v1y, v2y), v3y));
if(yMin > photo_.rows)
{
return; // Completly outside to the top.
return; // Completely outside to the top.
}
int yMax = static_cast<int>(std::max(std::max(v1y, v2y), v3y));
if(yMax < 0)
{
return; // Completly outside to the bottom.
return; // Completely outside to the bottom.
}
// Top point row and column positions
@ -785,7 +840,7 @@ void OdmOrthoPhoto::drawTexturedTriangle(const cv::Mat &texture, const pcl::Vert
// The last pixel row for the top part of the triangle.
int rqEnd = std::min(static_cast<int>(std::floor(midR+0.5f)), photo_.rows);
// Travers along row from top to middle.
// Traverse along row from top to middle.
for(int rq = rqStart; rq < rqEnd; ++rq)
{
// Set the current column positions.
@ -800,8 +855,8 @@ void OdmOrthoPhoto::drawTexturedTriangle(const cv::Mat &texture, const pcl::Vert
for(int cq = cqStart; cq < cqEnd; ++cq)
{
// Get barycentric coordinates for the current point.
getBarycentricCoordiantes(v1, v2, v3, static_cast<float>(cq)+0.5f, static_cast<float>(rq)+0.5f, l1, l2, l3);
getBarycentricCoordinates(v1, v2, v3, static_cast<float>(cq)+0.5f, static_cast<float>(rq)+0.5f, l1, l2, l3);
if(0.f > l1 || 0.f > l2 || 0.f > l3)
{
//continue;
@ -843,7 +898,7 @@ void OdmOrthoPhoto::drawTexturedTriangle(const cv::Mat &texture, const pcl::Vert
// The last pixel row for the bottom part of the triangle.
int rqEnd = std::min(static_cast<int>(std::floor(botR+0.5f)), photo_.rows);
// Travers along row from middle to bottom.
// Traverse along row from middle to bottom.
for(int rq = rqStart; rq < rqEnd; ++rq)
{
// Set the current column positions.
@ -858,8 +913,8 @@ void OdmOrthoPhoto::drawTexturedTriangle(const cv::Mat &texture, const pcl::Vert
for(int cq = cqStart; cq < cqEnd; ++cq)
{
// Get barycentric coordinates for the current point.
getBarycentricCoordiantes(v1, v2, v3, static_cast<float>(cq)+0.5f, static_cast<float>(rq)+0.5f, l1, l2, l3);
getBarycentricCoordinates(v1, v2, v3, static_cast<float>(cq)+0.5f, static_cast<float>(rq)+0.5f, l1, l2, l3);
if(0.f > l1 || 0.f > l2 || 0.f > l3)
{
//continue;
@ -941,7 +996,7 @@ void OdmOrthoPhoto::renderPixel(int row, int col, float s, float t, const cv::Ma
photo_.at<cv::Vec4b>(row,col) = cv::Vec4b(static_cast<unsigned char>(b), static_cast<unsigned char>(g), static_cast<unsigned char>(r), 255);
}
void OdmOrthoPhoto::getBarycentricCoordiantes(pcl::PointXYZ v1, pcl::PointXYZ v2, pcl::PointXYZ v3, float x, float y, float &l1, float &l2, float &l3) const
void OdmOrthoPhoto::getBarycentricCoordinates(pcl::PointXYZ v1, pcl::PointXYZ v2, pcl::PointXYZ v3, float x, float y, float &l1, float &l2, float &l3) const
{
// Diff along y.
float y2y3 = v2.y-v3.y;
@ -994,3 +1049,365 @@ bool OdmOrthoPhoto::isModelOk(const pcl::TextureMesh &mesh)
return 3*nFaces == nTextureCoordinates;
}
bool OdmOrthoPhoto::loadObjFile(std::string inputFile, pcl::TextureMesh &mesh)
{
int data_type;
unsigned int data_idx;
int file_version;
int offset = 0;
Eigen::Vector4f origin;
Eigen::Quaternionf orientation;
if (!readHeader(inputFile, mesh.cloud, origin, orientation, file_version, data_type, data_idx, offset))
{
throw OdmOrthoPhotoException("Problem reading header in modelfile!\n");
}
std::ifstream fs;
fs.open (inputFile.c_str (), std::ios::binary);
if (!fs.is_open () || fs.fail ())
{
//PCL_ERROR ("[pcl::OBJReader::readHeader] Could not open file '%s'! Error : %s\n", file_name.c_str (), strerror(errno));
fs.close ();
log_<<"Could not read mesh from file ";
log_ << inputFile.c_str();
log_ <<"\n";
throw OdmOrthoPhotoException("Problem reading mesh from file!\n");
}
// Seek at the given offset
fs.seekg (data_idx, std::ios::beg);
// Get normal_x field indices
int normal_x_field = -1;
for (std::size_t i = 0; i < mesh.cloud.fields.size (); ++i)
{
if (mesh.cloud.fields[i].name == "normal_x")
{
normal_x_field = i;
break;
}
}
std::size_t v_idx = 0;
std::size_t vn_idx = 0;
std::size_t vt_idx = 0;
std::size_t f_idx = 0;
std::string line;
std::vector<std::string> st;
std::vector<Eigen::Vector2f> coordinates;
std::vector<Eigen::Vector2f> allTexCoords;
std::map<int, int> f2vt;
try
{
while (!fs.eof ())
{
getline (fs, line);
// Ignore empty lines
if (line == "")
continue;
// Tokenize the line
std::stringstream sstream (line);
sstream.imbue (std::locale::classic ());
line = sstream.str ();
boost::trim (line);
boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on);
// Ignore comments
if (st[0] == "#")
continue;
// Vertex
if (st[0] == "v")
{
try
{
for (int i = 1, f = 0; i < 4; ++i, ++f)
{
float value = boost::lexical_cast<float> (st[i]);
memcpy (&mesh.cloud.data[v_idx * mesh.cloud.point_step + mesh.cloud.fields[f].offset], &value, sizeof (float));
}
++v_idx;
}
catch (const boost::bad_lexical_cast &e)
{
log_<<"Unable to convert %s to vertex coordinates!\n";
throw OdmOrthoPhotoException("Unable to convert %s to vertex coordinates!");
}
continue;
}
// Vertex normal
if (st[0] == "vn")
{
try
{
for (int i = 1, f = normal_x_field; i < 4; ++i, ++f)
{
float value = boost::lexical_cast<float> (st[i]);
memcpy (&mesh.cloud.data[vn_idx * mesh.cloud.point_step + mesh.cloud.fields[f].offset],
&value,
sizeof (float));
}
++vn_idx;
}
catch (const boost::bad_lexical_cast &e)
{
log_<<"Unable to convert %s to vertex normal!\n";
throw OdmOrthoPhotoException("Unable to convert %s to vertex normal!");
}
continue;
}
// Texture coordinates
if (st[0] == "vt")
{
try
{
Eigen::Vector3f c (0, 0, 0);
for (std::size_t i = 1; i < st.size (); ++i)
c[i-1] = boost::lexical_cast<float> (st[i]);
if (c[2] == 0)
coordinates.push_back (Eigen::Vector2f (c[0], c[1]));
else
coordinates.push_back (Eigen::Vector2f (c[0]/c[2], c[1]/c[2]));
++vt_idx;
}
catch (const boost::bad_lexical_cast &e)
{
log_<<"Unable to convert %s to vertex texture coordinates!\n";
throw OdmOrthoPhotoException("Unable to convert %s to vertex texture coordinates!");
}
continue;
}
// Material
if (st[0] == "usemtl")
{
mesh.tex_polygons.push_back (std::vector<pcl::Vertices> ());
mesh.tex_materials.push_back (pcl::TexMaterial ());
for (std::size_t i = 0; i < companions_.size (); ++i)
{
std::vector<pcl::TexMaterial>::const_iterator mat_it = companions_[i].getMaterial (st[1]);
if (mat_it != companions_[i].materials_.end ())
{
mesh.tex_materials.back () = *mat_it;
break;
}
}
// We didn't find the appropriate material so we create it here with name only.
if (mesh.tex_materials.back ().tex_name == "")
mesh.tex_materials.back ().tex_name = st[1];
mesh.tex_coordinates.push_back (coordinates);
coordinates.clear ();
continue;
}
// Face
if (st[0] == "f")
{
//We only care for vertices indices
pcl::Vertices face_v; face_v.vertices.resize (st.size () - 1);
for (std::size_t i = 1; i < st.size (); ++i)
{
int v;
sscanf (st[i].c_str (), "%d", &v);
v = (v < 0) ? v_idx + v : v - 1;
face_v.vertices[i-1] = v;
int v2, vt, vn;
sscanf (st[i].c_str (), "%d/%d/%d", &v2, &vt, &vn);
f2vt[3*(f_idx) + i-1] = vt-1;
}
mesh.tex_polygons.back ().push_back (face_v);
++f_idx;
continue;
}
}
}
catch (const char *exception)
{
fs.close ();
log_<<"Unable to read file!\n";
throw OdmOrthoPhotoException("Unable to read file!");
}
if (vt_idx != v_idx)
{
std::vector<Eigen::Vector2f> texcoordinates = std::vector<Eigen::Vector2f>(0);
for (size_t faceIndex = 0; faceIndex < f_idx; ++faceIndex)
{
for(size_t i = 0; i < 3; ++i)
{
Eigen::Vector2f vt = mesh.tex_coordinates[0][f2vt[3*faceIndex+i]];
texcoordinates.push_back(vt);
}
}
mesh.tex_coordinates.clear();
mesh.tex_coordinates.push_back(texcoordinates);
}
fs.close();
return (0);
}
bool OdmOrthoPhoto::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud,
Eigen::Vector4f &origin, Eigen::Quaternionf &orientation,
int &file_version, int &data_type, unsigned int &data_idx,
const int offset)
{
origin = Eigen::Vector4f::Zero ();
orientation = Eigen::Quaternionf::Identity ();
file_version = 0;
cloud.width = cloud.height = cloud.point_step = cloud.row_step = 0;
cloud.data.clear ();
data_type = 0;
data_idx = offset;
std::ifstream fs;
std::string line;
if (file_name == "" || !boost::filesystem::exists (file_name))
{
return false;
}
// Open file in binary mode to avoid problem of
// std::getline() corrupting the result of ifstream::tellg()
fs.open (file_name.c_str (), std::ios::binary);
if (!fs.is_open () || fs.fail ())
{
fs.close ();
return false;
}
// Seek at the given offset
fs.seekg (offset, std::ios::beg);
// Read the header and fill it in with wonderful values
bool vertex_normal_found = false;
bool vertex_texture_found = false;
// Material library, skip for now!
// bool material_found = false;
std::vector<std::string> material_files;
std::size_t nr_point = 0;
std::vector<std::string> st;
try
{
while (!fs.eof ())
{
getline (fs, line);
// Ignore empty lines
if (line == "")
continue;
// Tokenize the line
std::stringstream sstream (line);
sstream.imbue (std::locale::classic ());
line = sstream.str ();
boost::trim (line);
boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on);
// Ignore comments
if (st.at (0) == "#")
continue;
// Vertex
if (st.at (0) == "v")
{
++nr_point;
continue;
}
// Vertex texture
if ((st.at (0) == "vt") && !vertex_texture_found)
{
vertex_texture_found = true;
continue;
}
// Vertex normal
if ((st.at (0) == "vn") && !vertex_normal_found)
{
vertex_normal_found = true;
continue;
}
// Material library, skip for now!
if (st.at (0) == "mtllib")
{
material_files.push_back (st.at (1));
continue;
}
}
}
catch (const char *exception)
{
fs.close ();
return false;
}
if (!nr_point)
{
fs.close ();
return false;
}
int field_offset = 0;
for (int i = 0; i < 3; ++i, field_offset += 4)
{
cloud.fields.push_back (pcl::PCLPointField ());
cloud.fields[i].offset = field_offset;
cloud.fields[i].datatype = pcl::PCLPointField::FLOAT32;
cloud.fields[i].count = 1;
}
cloud.fields[0].name = "x";
cloud.fields[1].name = "y";
cloud.fields[2].name = "z";
if (vertex_normal_found)
{
std::string normals_names[3] = { "normal_x", "normal_y", "normal_z" };
for (int i = 0; i < 3; ++i, field_offset += 4)
{
cloud.fields.push_back (pcl::PCLPointField ());
pcl::PCLPointField& last = cloud.fields.back ();
last.name = normals_names[i];
last.offset = field_offset;
last.datatype = pcl::PCLPointField::FLOAT32;
last.count = 1;
}
}
if (material_files.size () > 0)
{
for (std::size_t i = 0; i < material_files.size (); ++i)
{
pcl::MTLReader companion;
if (companion.read (file_name, material_files[i]))
{
log_<<"Problem reading material file.";
}
companions_.push_back (companion);
}
}
cloud.point_step = field_offset;
cloud.width = nr_point;
cloud.height = 1;
cloud.row_step = cloud.point_step * cloud.width;
cloud.is_dense = true;
cloud.data.resize (cloud.point_step * nr_point);
fs.close ();
return true;
}

Wyświetl plik

@ -24,8 +24,8 @@
#include "Logger.hpp"
/*!
* \brief The WorldPoint struct encapsules world coordiantes used for the orhto photo boundary.
* Points are separated into integersand fractional parts for high numerical stability.
* \brief The WorldPoint struct encapsules world coordinates used for the ortho photo boundary.
* Points are separated into integers and fractional parts for high numerical stability.
*/
struct WorldPoint
{
@ -54,7 +54,7 @@ struct WorldPoint
};
/*!
* \brief The OdmOrthoPhoto class is used to create an orthograpic photo over a given area.
* \brief The OdmOrthoPhoto class is used to create an orthographic photo over a given area.
* The class reads an oriented textured mesh from an OBJ-file.
* The class uses file read from pcl.
* The class uses image read and write from opencv.
@ -125,7 +125,7 @@ private:
* \param texture The texture of the polygon.
* \param polygon The polygon as athree indices relative meshCloud.
* \param meshCloud Contains all vertices.
* \param uvs Contains the texture coordiantes for the active material.
* \param uvs Contains the texture coordinates for the active material.
* \param faceIndex The index of the face.
*/
void drawTexturedTriangle(const cv::Mat &texture, const pcl::Vertices &polygon, const pcl::PointCloud<pcl::PointXYZ>::Ptr &meshCloud, const std::vector<Eigen::Vector2f> &uvs, size_t faceIndex);
@ -140,9 +140,9 @@ private:
* \param texture The texture from which to get the color.
**/
void renderPixel(int row, int col, float u, float v, const cv::Mat &texture);
/*!
* \brief Calcualtes the barycentric coordinates of a point in a triangle.
* \brief Calculates the barycentric coordinates of a point in a triangle.
*
* \param v1 The first triangle vertex.
* \param v2 The second triangle vertex.
@ -153,8 +153,8 @@ private:
* \param l2 The second vertex weight.
* \param l3 The third vertex weight.
*/
void getBarycentricCoordiantes(pcl::PointXYZ v1, pcl::PointXYZ v2, pcl::PointXYZ v3, float x, float y, float &l1, float &l2, float &l3) const;
void getBarycentricCoordinates(pcl::PointXYZ v1, pcl::PointXYZ v2, pcl::PointXYZ v3, float x, float y, float &l1, float &l2, float &l3) const;
/*!
* \brief Check if a given polygon is a sliver polygon.
*
@ -172,6 +172,23 @@ private:
*/
bool isModelOk(const pcl::TextureMesh &mesh);
/*!
* \brief Loads a model from an .obj file (replacement for the pcl obj loader).
*
* \param inputFile Path to the .obj file.
* \param mesh The model.
* \return True if model was loaded successfully.
*/
bool loadObjFile(std::string inputFile, pcl::TextureMesh &mesh);
/*!
* \brief Function is compied straight from the function in the pcl::io module.
*/
bool readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud,
Eigen::Vector4f &origin, Eigen::Quaternionf &orientation,
int &file_version, int &data_type, unsigned int &data_idx,
const int offset);
Logger log_; /**< Logging object. */
std::string inputFile_; /**< Path to the textured mesh as an obj-file. */
@ -184,10 +201,10 @@ private:
bool boundaryDefined_; /**< True if the user has defined a boundary. */
WorldPoint worldPoint1_; /**< The first boundary point for the ortho photo, in world coordiantes. */
WorldPoint worldPoint2_; /**< The second boundary point for the ortho photo, in world coordiantes. */
WorldPoint worldPoint3_; /**< The third boundary point for the ortho photo, in world coordiantes. */
WorldPoint worldPoint4_; /**< The fourth boundary point for the ortho photo, in world coordiantes. */
WorldPoint worldPoint1_; /**< The first boundary point for the ortho photo, in world coordinates. */
WorldPoint worldPoint2_; /**< The second boundary point for the ortho photo, in world coordinates. */
WorldPoint worldPoint3_; /**< The third boundary point for the ortho photo, in world coordinates. */
WorldPoint worldPoint4_; /**< The fourth boundary point for the ortho photo, in world coordinates. */
Eigen::Vector2f boundaryPoint1_; /**< The first boundary point for the ortho photo, in local coordinates. */
Eigen::Vector2f boundaryPoint2_; /**< The second boundary point for the ortho photo, in local coordinates. */
@ -198,6 +215,8 @@ private:
cv::Mat depth_; /**< The depth of the ortho photo as an OpenCV matrix, CV_32F. */
bool multiMaterial_; /**< True if the mesh has multiple materials. **/
std::vector<pcl::MTLReader> companions_; /**< Materials (used by loadOBJFile). **/
};
/*!

Wyświetl plik

@ -3,7 +3,7 @@ import context
# parse arguments
processopts = ['resize', 'opensfm', 'slam', 'cmvs', 'pmvs',
'odm_meshing', 'odm_texturing', 'odm_georeferencing',
'odm_meshing', 'mvs_texturing', 'odm_georeferencing',
'odm_orthophoto']
@ -89,7 +89,7 @@ def config():
type=float,
help=('Ignore matched keypoints if the two images share '
'less than <float> percent of keypoints. Default:'
' $(default)s'))
' %(default)s'))
parser.add_argument('--matcher-ratio',
metavar='<float>',
@ -118,6 +118,12 @@ def config():
'images based on GPS exif data. Set to 0 to skip '
'pre-matching. Default: %(default)s')
parser.add_argument('--use-opensfm-pointcloud',
action='store_true',
default=False,
help='Use OpenSfM to compute the point cloud instead '
'of PMVS')
parser.add_argument('--cmvs-maxImages',
metavar='<integer>',
default=500,
@ -207,6 +213,46 @@ def config():
'times slightly but helps reduce memory usage. '
'Default: %(default)s'))
parser.add_argument('--mvs_texturing-dataTerm',
metavar='<string>',
default='gmi',
help=('Data term: [area, gmi]. Default: %(default)s'))
parser.add_argument('--mvs_texturing-outlierRemovalType',
metavar='<string>',
default='none',
help=('Type of photometric outlier removal method: '
'[none, gauss_damping, gauss_clamping]. Default: '
'%(default)s'))
parser.add_argument('--mvs_texturing-skipGeometricVisibilityTest',
action='store_true',
default=False,
help=('Skip geometric visibility test. Default: %(default)s'))
parser.add_argument('--mvs_texturing-skipGlobalSeamLeveling',
action='store_true',
default=False,
help=('Skip geometric visibility test. Default: %(default)s'))
parser.add_argument('--mvs_texturing-skipLocalSeamLeveling',
action='store_true',
default=False,
help=('Skip local seam blending. Default: %(default)s'))
parser.add_argument('--mvs_texturing-skipHoleFilling',
action='store_true',
default=False,
help=('Skip filling of holes in the mesh. Default: %(default)s'))
parser.add_argument('--mvs_texturing-keepUnseenFaces',
action='store_true',
default=False,
help=('Keep faces in the mesh that are not seen in any camera. '
'Default: %(default)s'))
# Old odm_texturing arguments
parser.add_argument('--odm_texturing-textureResolution',
metavar='<positive integer>',
default=4096,
@ -221,6 +267,8 @@ def config():
help=('The resolution to rescale the images performing '
'the texturing. Default: %(default)s'))
# End of old odm_texturing arguments
parser.add_argument('--odm_georeferencing-gcpFile',
metavar='<path string>',
default='gcp_list.txt',

Wyświetl plik

@ -26,6 +26,9 @@ cmvs_path = os.path.join(superbuild_path, "install/bin/cmvs")
cmvs_opts_path = os.path.join(superbuild_path, "install/bin/genOption")
pmvs2_path = os.path.join(superbuild_path, "install/bin/pmvs2")
# define mvstex path
mvstex_path = os.path.join(superbuild_path, "install/bin/texrecon")
# define txt2las path
txt2las_path = os.path.join(superbuild_path, 'src/las-tools/bin')
pdal_path = os.path.join(superbuild_path, 'build/pdal/bin')

Wyświetl plik

@ -13,7 +13,8 @@ tasks_dict = {'1': 'resize',
'3': 'cmvs',
'4': 'pmvs',
'5': 'odm_meshing',
'6': 'odm_texturing',
# '6': 'odm_texturing',
'6': 'mvs_texturing',
'7': 'odm_georeferencing',
'8': 'odm_orthophoto',
'9': 'zip_results'}
@ -68,7 +69,7 @@ class ODMTaskManager(object):
command = None
inputs = {}
elif task_name == 'odm_texturing':
elif task_name == 'mvs_texturing':
# setup this task
command = None
inputs = {}

Wyświetl plik

@ -25,9 +25,9 @@ class ODM_Photo:
self.focal_length = None
self.focal_length_px = None
# other attributes
self.camera_make = None
self.camera_model = None
self.make_model = None
self.camera_make = ''
self.camera_model = ''
self.make_model = ''
# parse values from metadata
self.parse_pyexiv2_values(self.path_file, force_focal, force_ccd)
# compute focal length into pixels
@ -69,8 +69,11 @@ class ODM_Photo:
self.focal_length = float(val)
except (pyexiv2.ExifValueError, ValueError) as e:
pass
except NotImplementedError as e:
pass
self.make_model = sensor_string(self.camera_make, self.camera_model)
if self.camera_make and self.camera_model:
self.make_model = sensor_string(self.camera_make, self.camera_model)
# needed to do that since sometimes metadata contains wrong data
img = cv2.imread(_path_file)
@ -357,6 +360,7 @@ class ODM_Tree(object):
self.opensfm_bundle_list = io.join_paths(self.opensfm, 'list_r000.out')
self.opensfm_image_list = io.join_paths(self.opensfm, 'image_list.txt')
self.opensfm_reconstruction = io.join_paths(self.opensfm, 'reconstruction.json')
self.opensfm_model = io.join_paths(self.opensfm, 'depthmaps/merged.ply')
# pmvs
self.pmvs_rec_path = io.join_paths(self.pmvs, 'recon0')
@ -369,13 +373,14 @@ class ODM_Tree(object):
self.odm_mesh = io.join_paths(self.odm_meshing, 'odm_mesh.ply')
self.odm_meshing_log = io.join_paths(self.odm_meshing, 'odm_meshing_log.txt')
# odm_texturing
# texturing
self.odm_texturing_undistorted_image_path = io.join_paths(
self.odm_texturing, 'undistorted')
self.odm_textured_model_obj = io.join_paths(
self.odm_texturing, 'odm_textured_model.obj')
self.odm_textured_model_mtl = io.join_paths(
self.odm_texturing, 'odm_textured_model.mtl')
# Log is only used by old odm_texturing
self.odm_texuring_log = io.join_paths(
self.odm_texturing, 'odm_texturing_log.txt')
@ -406,6 +411,6 @@ class ODM_Tree(object):
# odm_orthophoto
self.odm_orthophoto_file = io.join_paths(self.odm_orthophoto, 'odm_orthophoto.png')
self.odm_orthophoto_tif = io.join_paths(self.odm_orthophoto, 'odm_orthophoto.tif')
self.odm_orthophoto_corners = io.join_paths(self.odm_orthophoto, 'odm_orthphoto_corners.txt')
self.odm_orthophoto_corners = io.join_paths(self.odm_orthophoto, 'odm_orthophoto_corners.txt')
self.odm_orthophoto_log = io.join_paths(self.odm_orthophoto, 'odm_orthophoto_log.txt')
self.odm_orthophoto_tif_log = io.join_paths(self.odm_orthophoto, 'gdal_translate_log.txt')

Wyświetl plik

@ -0,0 +1,91 @@
FROM ubuntu:14.04
MAINTAINER Alex Hagiopol <alex.hagiopol@icloud.com>
# Env variables
ENV DEBIAN_FRONTEND noninteractive
#Install dependencies
#Required Requisites
RUN apt-get update \
&& apt-get install -y -qq \
build-essential \
cmake \
git \
python-pip \
libgdal-dev \
gdal-bin \
libgeotiff-dev \
pkg-config
#CMake 3.1 for MVS-Texturing
RUN apt-get install -y software-properties-common python-software-properties
RUN add-apt-repository -y ppa:george-edison55/cmake-3.x
RUN apt-get update -y
RUN apt-get install -y --only-upgrade cmake
#Installing OpenCV Dependencies
RUN apt-get install -y -qq libgtk2.0-dev \
libavcodec-dev \
libavformat-dev \
libswscale-dev \
python-dev \
python-numpy \
libtbb2 \
libtbb-dev \
libjpeg-dev \
libpng-dev \
libtiff-dev \
libjasper-dev \
libflann-dev \
libproj-dev \
libxext-dev \
liblapack-dev \
libeigen3-dev \
libvtk5-dev
#Removing libdc1394-22-dev due to python opencv issue
RUN apt-get remove libdc1394-22-dev
#Installing OpenSfM Dependencies
RUN apt-get install -y -qq python-networkx \
libgoogle-glog-dev \
libsuitesparse-dev \
libboost-filesystem-dev \
libboost-iostreams-dev \
libboost-regex-dev \
libboost-python-dev \
libboost-date-time-dev \
libboost-thread-dev \
python-pyproj
RUN pip install -U PyYAML \
exifread \
gpxpy \
xmltodict \
catkin-pkg
#Installing Ecto Dependencies
RUN apt-get install -y -qq python-empy \
python-nose \
python-pyside
#"Installing OpenDroneMap Dependencies"
RUN apt-get install -y python-pyexiv2 \
python-scipy \
jhead \
liblas-bin -y -qq
RUN apt-get install -y python-empy \
python-nose \
python-pyside \
python-pyexiv2 \
python-scipy \
jhead \
liblas-bin \
python-matplotlib \
libatlas-base-dev \
libatlas3gf-base
ENV PYTHONPATH="$PYTHONPATH:/code/SuperBuild/install/lib/python2.7/dist-packages"
ENV PYTHONPATH="$PYTHONPATH:/code/SuperBuild/src/opensfm"
ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/code/SuperBuild/install/lib"

12
run.py
Wyświetl plik

@ -6,6 +6,7 @@ from opendm import system
import sys
import ecto
import os
from scripts.odm_app import ODMApp
@ -26,6 +27,17 @@ if __name__ == '__main__':
if args.project_path is None:
usage()
#If user asks to rerun everything, delete all of the existing progress directories.
if args.rerun_all:
os.system("rm -rf "
+ args.project_path + "images_resize/ "
+ args.project_path + "odm_georeferencing/ "
+ args.project_path + "odm_meshing/ "
+ args.project_path + "odm_orthophoto/ "
+ args.project_path + "odm_texturing/ "
+ args.project_path + "opensfm/ "
+ args.project_path + "pmvs/")
# create an instance of my App BlackBox
# internally configure all tasks
app = ODMApp(args=args)

Wyświetl plik

@ -1,23 +1,31 @@
import os
import ecto
from functools import partial
from multiprocessing import Pool
from opendm import context
from opendm import io
from opendm import types
from opendm import log
def make_odm_photo(force_focal, force_ccd, path_file):
return types.ODM_Photo(path_file,
force_focal,
force_ccd)
class ODMLoadDatasetCell(ecto.Cell):
def declare_params(self, params):
params.declare("force_focal", 'Override the focal length information for the '
'images', None)
params.declare("force_ccd", 'Override the ccd widht information for the '
params.declare("force_ccd", 'Override the ccd width information for the '
'images', None)
def declare_io(self, params, inputs, outputs):
inputs.declare("tree", "Struct with paths", [])
outputs.declare("photos", "list of ODMPhoto's", [])
outputs.declare("photos", "list of ODMPhotos", [])
def process(self, inputs, outputs):
# check if the extension is sopported
@ -49,13 +57,11 @@ class ODMLoadDatasetCell(ecto.Cell):
if files:
# create ODMPhoto list
photos = []
for f in files:
path_file = io.join_paths(images_dir, f)
photo = types.ODM_Photo(path_file,
self.params.force_focal,
self.params.force_ccd)
photos.append(photo)
path_files = [io.join_paths(images_dir, f) for f in files]
photos = Pool().map(
partial(make_odm_photo, self.params.force_focal, self.params.force_ccd),
path_files
)
log.ODM_INFO('Found %s usable images' % len(photos))
else:

115
scripts/mvstex.py 100644
Wyświetl plik

@ -0,0 +1,115 @@
import ecto
from opendm import log
from opendm import io
from opendm import system
from opendm import context
import pmvs2nvmcams
class ODMMvsTexCell(ecto.Cell):
def declare_params(self, params):
params.declare("data_term", 'Data term: [area, gmi] default: gmi', "gmi")
params.declare("outlier_rem_type", 'Type of photometric outlier removal method: [none, gauss_damping, gauss_clamping]. default: none', "none")
params.declare("skip_vis_test", 'Skip geometric visibility test based on ray intersection.', False)
params.declare("skip_glob_seam_leveling", 'Skip global seam leveling.', False)
params.declare("skip_loc_seam_leveling", 'Skip local seam leveling (Poisson editing).', False)
params.declare("skip_hole_fill", 'Skip hole filling.', False)
params.declare("keep_unseen_faces", 'Keep unseen faces.', False)
def declare_io(self, params, inputs, outputs):
inputs.declare("tree", "Struct with paths", [])
inputs.declare("args", "The application arguments.", {})
inputs.declare("reconstruction", "Clusters output. list of ODMReconstructions", [])
outputs.declare("reconstruction", "Clusters output. list of ODMReconstructions", [])
def process(self, inputs, outputs):
# Benchmarking
start_time = system.now_raw()
log.ODM_INFO('Running MVS Texturing Cell')
# get inputs
args = self.inputs.args
tree = self.inputs.tree
# define paths and create working directories
system.mkdir_p(tree.odm_texturing)
# check if we rerun cell or not
rerun_cell = (args.rerun is not None and
args.rerun == 'mvs_texturing') or \
(args.rerun_all) or \
(args.rerun_from is not None and
'mvs_texturing' in args.rerun_from)
if not io.file_exists(tree.odm_textured_model_obj) or rerun_cell:
log.ODM_DEBUG('Writing MVS Textured file in: %s'
% tree.odm_textured_model_obj)
# Format arguments to fit Mvs-Texturing app
skipGeometricVisibilityTest = ""
skipGlobalSeamLeveling = ""
skipLocalSeamLeveling = ""
skipHoleFilling = ""
keepUnseenFaces = ""
if (self.params.skip_vis_test):
skipGeometricVisibilityTest = "--skip_geometric_visibility_test"
if (self.params.skip_glob_seam_leveling):
skipGlobalSeamLeveling = "--skip_global_seam_leveling"
if (self.params.skip_loc_seam_leveling):
skipLocalSeamLeveling = "--skip_local_seam_leveling"
if (self.params.skip_hole_fill):
skipHoleFilling = "--skip_hole_filling"
if (self.params.keep_unseen_faces):
keepUnseenFaces = "--keep_unseen_faces"
# mvstex definitions
kwargs = {
'bin': context.mvstex_path,
'out_dir': io.join_paths(tree.odm_texturing, "odm_textured_model"),
'pmvs_folder': tree.pmvs_rec_path,
'nvm_file': io.join_paths(tree.pmvs_rec_path, "nvmCams.nvm"),
'model': tree.odm_mesh,
'dataTerm': self.params.data_term,
'outlierRemovalType': self.params.outlier_rem_type,
'skipGeometricVisibilityTest': skipGeometricVisibilityTest,
'skipGlobalSeamLeveling': skipGlobalSeamLeveling,
'skipLocalSeamLeveling': skipLocalSeamLeveling,
'skipHoleFilling': skipHoleFilling,
'keepUnseenFaces': keepUnseenFaces
}
if args.use_opensfm_pointcloud:
kwargs['nvm_file'] = io.join_paths(tree.opensfm,
"reconstruction.nvm")
else:
log.ODM_DEBUG('Generating .nvm file from pmvs output: %s'
% '{nvm_file}'.format(**kwargs))
# Create .nvm camera file.
pmvs2nvmcams.run('{pmvs_folder}'.format(**kwargs),
'{nvm_file}'.format(**kwargs))
# run texturing binary
system.run('{bin} {nvm_file} {model} {out_dir} '
'-d {dataTerm} -o {outlierRemovalType} '
'{skipGeometricVisibilityTest} '
'{skipGlobalSeamLeveling} '
'{skipLocalSeamLeveling} '
'{skipHoleFilling} '
'{keepUnseenFaces}'.format(**kwargs))
else:
log.ODM_WARNING('Found a valid ODM Texture file in: %s'
% tree.odm_textured_model_obj)
if args.time:
system.benchmark(start_time, tree.benchmarking, 'Texturing')
log.ODM_INFO('Running ODM Texturing Cell - Finished')
return ecto.OK if args.end_with != 'odm_texturing' else ecto.QUIT

Wyświetl plik

@ -14,7 +14,8 @@ from odm_slam import ODMSlamCell
from pmvs import ODMPmvsCell
from cmvs import ODMCmvsCell
from odm_meshing import ODMeshingCell
from odm_texturing import ODMTexturingCell
#from odm_texturing import ODMTexturingCell
from mvstex import ODMMvsTexCell
from odm_georeferencing import ODMGeoreferencingCell
from odm_orthophoto import ODMOrthoPhotoCell
@ -59,9 +60,16 @@ class ODMApp(ecto.BlackBox):
oct_tree=p.args.odm_meshing_octreeDepth,
samples=p.args.odm_meshing_samplesPerNode,
solver=p.args.odm_meshing_solverDivide),
'texturing': ODMTexturingCell(resize=p.args.resize_to,
resolution=p.args.odm_texturing_textureResolution,
size=p.args.odm_texturing_textureWithSize),
'texturing': ODMMvsTexCell(data_term=p.args.mvs_texturing_dataTerm,
outlier_rem_type=p.args.mvs_texturing_outlierRemovalType,
skip_vis_test=p.args.mvs_texturing_skipGeometricVisibilityTest,
skip_glob_seam_leveling=p.args.mvs_texturing_skipGlobalSeamLeveling,
skip_loc_seam_leveling=p.args.mvs_texturing_skipLocalSeamLeveling,
skip_hole_fill=p.args.mvs_texturing_skipHoleFilling,
keep_unseen_faces=p.args.mvs_texturing_keepUnseenFaces),
# Old odm_texturing
# 'texturing': ODMTexturingCell(resize=p.args['resize_to'],
# resolution=p.args['odm_texturing_textureResolution'],
'georeferencing': ODMGeoreferencingCell(img_size=p.args.resize_to,
gcp_file=p.args.odm_georeferencing_gcpFile,
use_gcp=p.args.odm_georeferencing_useGcp),
@ -83,7 +91,8 @@ class ODMApp(ecto.BlackBox):
b.write('ODM Benchmarking file created %s\nNumber of Cores: %s\n\n' % (system.now(), context.num_cores))
def connections(self, _p):
run_slam = _p.args.video
if _p.args.video:
return self.slam_connections(_p)
# define initial task
# TODO: What is this?
@ -91,36 +100,72 @@ class ODMApp(ecto.BlackBox):
# initial_task_id = config.processopts.index(initial_task)
# define the connections like you would for the plasm
connections = []
if run_slam:
# run slam cell
connections += [self.tree[:] >> self.slam['tree'],
self.args[:] >> self.slam['args']]
# load the dataset
connections = [self.tree[:] >> self.dataset['tree']]
# run cmvs
connections += [self.tree[:] >> self.cmvs['tree'],
self.args[:] >> self.cmvs['args'],
self.slam['reconstruction'] >> self.cmvs['reconstruction']]
# run resize cell
connections += [self.tree[:] >> self.resize['tree'],
self.args[:] >> self.resize['args'],
self.dataset['photos'] >> self.resize['photos']]
# run opensfm with images from load dataset
connections += [self.tree[:] >> self.opensfm['tree'],
self.args[:] >> self.opensfm['args'],
self.resize['photos'] >> self.opensfm['photos']]
if _p.args.use_opensfm_pointcloud:
# create odm mesh from opensfm point cloud
connections += [self.tree[:] >> self.meshing['tree'],
self.args[:] >> self.meshing['args'],
self.opensfm['reconstruction'] >> self.meshing['reconstruction']]
else:
# load the dataset
connections += [self.tree[:] >> self.dataset['tree']]
# run resize cell
connections += [self.tree[:] >> self.resize['tree'],
self.args[:] >> self.resize['args'],
self.dataset['photos'] >> self.resize['photos']]
# run opensfm with images from load dataset
connections += [self.tree[:] >> self.opensfm['tree'],
self.args[:] >> self.opensfm['args'],
self.resize['photos'] >> self.opensfm['photos']]
# run cmvs
connections += [self.tree[:] >> self.cmvs['tree'],
self.args[:] >> self.cmvs['args'],
self.opensfm['reconstruction'] >> self.cmvs['reconstruction']]
# run pmvs
connections += [self.tree[:] >> self.pmvs['tree'],
self.args[:] >> self.pmvs['args'],
self.cmvs['reconstruction'] >> self.pmvs['reconstruction']]
# create odm mesh from pmvs point cloud
connections += [self.tree[:] >> self.meshing['tree'],
self.args[:] >> self.meshing['args'],
self.pmvs['reconstruction'] >> self.meshing['reconstruction']]
# create odm texture
connections += [self.tree[:] >> self.texturing['tree'],
self.args[:] >> self.texturing['args'],
self.meshing['reconstruction'] >> self.texturing['reconstruction']]
# create odm georeference
connections += [self.tree[:] >> self.georeferencing['tree'],
self.args[:] >> self.georeferencing['args'],
self.dataset['photos'] >> self.georeferencing['photos'],
self.texturing['reconstruction'] >> self.georeferencing['reconstruction']]
# create odm orthophoto
connections += [self.tree[:] >> self.orthophoto['tree'],
self.args[:] >> self.orthophoto['args'],
self.georeferencing['reconstruction'] >> self.orthophoto['reconstruction']]
return connections
def slam_connections(self, _p):
"""Get connections used when running from video instead of images."""
connections = []
# run slam cell
connections += [self.tree[:] >> self.slam['tree'],
self.args[:] >> self.slam['args']]
# run cmvs
connections += [self.tree[:] >> self.cmvs['tree'],
self.args[:] >> self.cmvs['args'],
self.slam['reconstruction'] >> self.cmvs['reconstruction']]
# run pmvs
connections += [self.tree[:] >> self.pmvs['tree'],
self.args[:] >> self.pmvs['args'],
@ -136,16 +181,4 @@ class ODMApp(ecto.BlackBox):
self.args[:] >> self.texturing['args'],
self.meshing['reconstruction'] >> self.texturing['reconstruction']]
if not run_slam:
# create odm georeference
connections += [self.tree[:] >> self.georeferencing['tree'],
self.args[:] >> self.georeferencing['args'],
self.dataset['photos'] >> self.georeferencing['photos'],
self.texturing['reconstruction'] >> self.georeferencing['reconstruction']]
# create odm orthophoto
connections += [self.tree[:] >> self.orthophoto['tree'],
self.args[:] >> self.orthophoto['args'],
self.georeferencing['reconstruction'] >> self.orthophoto['reconstruction']]
return connections

Wyświetl plik

@ -50,7 +50,7 @@ class ODMGeoreferencingCell(ecto.Cell):
# odm_georeference definitions
kwargs = {
'bin': context.odm_modules_path,
'imgs': tree.dataset_raw,
'imgs': tree.dataset_resize,
'imgs_list': tree.opensfm_bundle_list,
'coords': tree.odm_georeferencing_coords,
'log': tree.odm_georeferencing_utm_log
@ -82,10 +82,9 @@ class ODMGeoreferencingCell(ecto.Cell):
kwargs = {
'bin': context.odm_modules_path,
'bundle': tree.opensfm_bundle,
'imgs': tree.dataset_raw,
'imgs': tree.dataset_resize,
'imgs_list': tree.opensfm_bundle_list,
'model': tree.odm_textured_model_obj,
'pc': tree.pmvs_model,
'log': tree.odm_georeferencing_log,
'coords': tree.odm_georeferencing_coords,
'pc_geo': tree.odm_georeferencing_model_ply_geo,
@ -95,6 +94,10 @@ class ODMGeoreferencingCell(ecto.Cell):
'gcp': gcpfile,
}
if args.use_opensfm_pointcloud:
kwargs['pc'] = tree.opensfm_model
else:
kwargs['pc'] = tree.pmvs_model
if self.params.use_gcp and \
io.file_exists(gcpfile):

Wyświetl plik

@ -52,7 +52,6 @@ class ODMeshingCell(ecto.Cell):
kwargs = {
'bin': context.odm_modules_path,
'infile': tree.pmvs_model,
'outfile': tree.odm_mesh,
'log': tree.odm_meshing_log,
'max_vertex': self.params.max_vertex,
@ -60,6 +59,10 @@ class ODMeshingCell(ecto.Cell):
'samples': self.params.samples,
'solver': self.params.solver
}
if args.use_opensfm_pointcloud:
kwargs['infile'] = tree.opensfm_model
else:
kwargs['infile'] = tree.pmvs_model
# run meshing binary
system.run('{bin}/odm_meshing -inputFile {infile} '

Wyświetl plik

@ -89,6 +89,12 @@ class ODMOrthoPhotoCell(ecto.Cell):
}
system.run('gdal_translate -a_ullr {ulx} {uly} {lrx} {lry} '
'-co TILED=yes '
'-co COMPRESS=DEFLATE '
'-co PREDICTOR=2 '
'-co BLOCKXSIZE=512 '
'-co BLOCKYSIZE=512 '
'-co NUM_THREADS=ALL_CPUS '
'-a_srs \"EPSG:{epsg}\" {png} {tiff} > {log}'.format(**kwargs))
geotiffcreated = True
if not geotiffcreated:

Wyświetl plik

@ -48,9 +48,13 @@ class ODMOpenSfMCell(ecto.Cell):
(args.rerun_from is not None and
'opensfm' in args.rerun_from)
# check if reconstruction was done before
if args.use_opensfm_pointcloud:
output_file = tree.opensfm_model
else:
output_file = tree.opensfm_reconstruction
if not io.file_exists(tree.opensfm_reconstruction) or rerun_cell:
# check if reconstruction was done before
if not io.file_exists(output_file) or rerun_cell:
# create file list
list_path = io.join_paths(tree.opensfm, 'image_list.txt')
with open(list_path, 'w') as fout:
@ -77,12 +81,18 @@ class ODMOpenSfMCell(ecto.Cell):
# run OpenSfM reconstruction
system.run('PYTHONPATH=%s %s/bin/run_all %s' %
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
if args.use_opensfm_pointcloud:
system.run('PYTHONPATH=%s %s/bin/opensfm export_visualsfm %s' %
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
system.run('PYTHONPATH=%s %s/bin/opensfm undistort %s' %
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
system.run('PYTHONPATH=%s %s/bin/opensfm compute_depthmaps %s' %
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
else:
log.ODM_WARNING('Found a valid OpenSfM file in: %s' %
tree.opensfm_reconstruction)
# check if reconstruction was exported to bundler before
if not io.file_exists(tree.opensfm_bundle_list) or rerun_cell:
# convert back to bundler's format
system.run('PYTHONPATH=%s %s/bin/export_bundler %s' %
@ -91,17 +101,17 @@ class ODMOpenSfMCell(ecto.Cell):
log.ODM_WARNING('Found a valid Bundler file in: %s' %
tree.opensfm_reconstruction)
# check if reconstruction was exported to pmvs before
if not args.use_opensfm_pointcloud:
# check if reconstruction was exported to pmvs before
if not io.file_exists(tree.pmvs_visdat) or rerun_cell:
# run PMVS converter
system.run('PYTHONPATH=%s %s/bin/export_pmvs %s --output %s' %
(context.pyopencv_path, context.opensfm_path, tree.opensfm, tree.pmvs))
else:
log.ODM_WARNING('Found a valid CMVS file in: %s' % tree.pmvs_visdat)
if not io.file_exists(tree.pmvs_visdat) or rerun_cell:
# run PMVS converter
system.run('PYTHONPATH=%s %s/bin/export_pmvs %s --output %s' %
(context.pyopencv_path, context.opensfm_path, tree.opensfm, tree.pmvs))
else:
log.ODM_WARNING('Found a valid CMVS file in: %s' % tree.pmvs_visdat)
if args.time:
system.benchmark(start_time, tree.benchmarking, 'OpenSfM')
if args.time:
system.benchmark(start_time, tree.benchmarking, 'OpenSfM')
log.ODM_INFO('Running ODM OpenSfM Cell - Finished')
return ecto.OK if args.end_with != 'opensfm' else ecto.QUIT

Wyświetl plik

@ -0,0 +1,144 @@
import os
import numpy as np
from opendm import log
# Go from QR-factorizatoin to corresponding RQ-factorization.
def rq(A):
Q,R = np.linalg.qr(np.flipud(A).T)
R = np.flipud(R.T)
Q = Q.T
return R[:,::-1],Q[::-1,:]
# Create a unit quaternion from rotation matrix.
def rot2quat(R):
# Float epsilon (use square root to be well with the stable region).
eps = np.sqrt(np.finfo(float).eps)
# If the determinant is not 1, it's not a rotation matrix
if np.abs(np.linalg.det(R) - 1.0) > eps:
log.ODM_ERROR('Matrix passed to rot2quat was not a rotation matrix, det != 1.0')
tr = np.trace(R)
quat = np.zeros((1,4))
# Is trace big enough be computationally stable?
if tr > eps:
S = 0.5 / np.sqrt(tr + 1.0)
quat[0,0] = 0.25 / S
quat[0,1] = (R[2,1] - R[1,2]) * S
quat[0,2] = (R[0,2] - R[2,0]) * S
quat[0,3] = (R[1,0] - R[0,1]) * S
else: # It's not, use the largest diagonal.
if R[0,0] > R[1,1] and R[0,0] > R[2,2]:
S = np.sqrt(1.0 + R[0,0] - R[1,1] - R[2,2]) * 2.0
quat[0,0] = (R[2,1] - R[1,2]) / S
quat[0,1] = 0.25 * S
quat[0,2] = (R[0,1] + R[1,0]) / S
quat[0,3] = (R[0,2] + R[2,0]) / S
elif R[1,1] > R[2,2]:
S = np.sqrt(1.0 - R[0,0] + R[1,1] - R[2,2]) * 2.0
quat[0,0] = (R[0,2] - R[2,0]) / S
quat[0,1] = (R[0,1] + R[1,0]) / S
quat[0,2] = 0.25 * S
quat[0,3] = (R[1,2] + R[2,1]) / S
else:
S = np.sqrt(1.0 - R[0,0] - R[1,1] + R[2,2]) * 2.0
quat[0,0] = (R[1,0] - R[0,1]) / S
quat[0,1] = (R[0,2] + R[2,0]) / S
quat[0,2] = (R[1,2] + R[2,1]) / S
quat[0,3] = 0.25 * S
return quat
# Decompose a projection matrix into parts
# (Intrinsic projection, Rotation, Camera position)
def decomposeProjection(projectionMatrix):
# Check input:
if projectionMatrix.shape != (3,4):
log.ODM_ERROR('Unable to decompose projection matrix, shape != (3,4)')
RQ = rq(projectionMatrix[:,:3])
# Fix sign, since we know K is upper triangular and has a positive diagonal.
signMat = np.diag(np.diag(np.sign(RQ[0])))
K = signMat*RQ[0]
R = signMat*RQ[1]
# Calculate camera position from translation vector.
t = np.linalg.inv(-1.0*projectionMatrix[:,:3])*projectionMatrix[:,3]
return K, R, t
# Parses pvms contour file.
def parseContourFile(filePath):
with open(filePath, 'r') as contourFile:
if (contourFile.readline().strip() != "CONTOUR"):
return np.array([])
else:
pMatData = np.loadtxt(contourFile, float, '#', None, None, 0)
if pMatData.shape == (3,4):
return pMatData
return np.array([])
# Creates a .nvm camera file in the pmvs folder.
def run(pmvsFolder, outputFile):
projectionFolder = pmvsFolder + "/txt"
imageFolder = pmvsFolder + "/visualize"
pMatrices = []
imageFileNames = []
# for all files in the visualize folder:
for imageFileName in os.listdir(imageFolder):
fileNameNoExt = os.path.splitext(imageFileName)[0]
# look for corresponding projection matrix txt file
projectionFilePath = os.path.join(projectionFolder, fileNameNoExt)
projectionFilePath += ".txt"
if os.path.isfile(projectionFilePath):
pMatData = parseContourFile(projectionFilePath)
if pMatData.size == 0:
log.ODM_WARNING('Unable to parse contour file, skipping: %s'
% projectionFilePath)
else:
pMatrices.append(np.matrix(pMatData))
imageFileNames.append(imageFileName)
# Decompose projection matrices
focals = []
rotations = []
translations = []
for projection in pMatrices:
KRt = decomposeProjection(projection)
focals.append(KRt[0][0,0])
rotations.append(rot2quat(KRt[1]))
translations.append(KRt[2])
# Create .nvm file
with open (outputFile, 'w') as nvmFile:
nvmFile.write("NVM_V3\n\n")
nvmFile.write('%d' % len(rotations) + "\n")
for idx, imageFileName in enumerate(imageFileNames):
nvmFile.write(os.path.join("visualize", imageFileName))
nvmFile.write(" " + '%f' % focals[idx])
nvmFile.write(" " + '%f' % rotations[idx][0,0] +
" " + '%f' % rotations[idx][0,1] +
" " + '%f' % rotations[idx][0,2] +
" " + '%f' % rotations[idx][0,3])
nvmFile.write(" " + '%f' % translations[idx][0] +
" " + '%f' % translations[idx][1] +
" " + '%f' % translations[idx][2])
nvmFile.write(" 0 0\n")
nvmFile.write("0\n\n")
nvmFile.write("0\n\n")
nvmFile.write("0")

Wyświetl plik

@ -2,12 +2,61 @@ import ecto
import cv2
import pyexiv2
from functools import partial
from multiprocessing import Pool
from opendm import log
from opendm import system
from opendm import io
from opendm import types
def resize(src_dir, target_dir, resize_to, rerun_cell, photo):
# define image paths
path_file = photo.path_file
new_path_file = io.join_paths(target_dir, photo.filename)
# set raw image path in case we want to rerun cell
if io.file_exists(new_path_file) and rerun_cell:
path_file = io.join_paths(src_dir, photo.filename)
if not io.file_exists(new_path_file) or rerun_cell:
# open and resize image with opencv
img = cv2.imread(path_file)
# compute new size
max_side = max(img.shape[0], img.shape[1])
if max_side <= resize_to:
log.ODM_WARNING('Resize Parameter is greater than the largest side of the image')
ratio = float(resize_to) / float(max_side)
img_r = cv2.resize(img, None, fx=ratio, fy=ratio)
# write image with opencv
cv2.imwrite(new_path_file, img_r)
# read metadata with pyexiv2
old_meta = pyexiv2.ImageMetadata(path_file)
new_meta = pyexiv2.ImageMetadata(new_path_file)
old_meta.read()
new_meta.read()
# copy metadata
old_meta.copy(new_meta)
# update metadata size
new_meta['Exif.Photo.PixelXDimension'] = img_r.shape[0]
new_meta['Exif.Photo.PixelYDimension'] = img_r.shape[1]
new_meta.write()
# update photos array with new values
photo.path_file = new_path_file
photo.width = img_r.shape[0]
photo.height = img_r.shape[1]
photo.update_focal()
# log message
log.ODM_DEBUG('Resized %s | dimensions: %s' %
(photo.filename, img_r.shape))
else:
# log message
log.ODM_WARNING('Already resized %s | dimensions: %s x %s' %
(photo.filename, photo.width, photo.height))
return photo
class ODMResizeCell(ecto.Cell):
def declare_params(self, params):
params.declare("resize_to", "resizes images by the largest side", 2400)
@ -51,49 +100,14 @@ class ODMResizeCell(ecto.Cell):
'resize' in args.rerun_from)
# loop over photos
for photo in photos:
# define image paths
path_file = photo.path_file
new_path_file = io.join_paths(tree.dataset_resize, photo.filename)
# set raw image path in case we want to rerun cell
if io.file_exists(new_path_file) and rerun_cell:
path_file = io.join_paths(tree.dataset_raw, photo.filename)
if not io.file_exists(new_path_file) or rerun_cell:
# open and resize image with opencv
img = cv2.imread(path_file)
# compute new size
max_side = max(img.shape[0], img.shape[1])
if max_side <= self.params.resize_to:
log.ODM_WARNING('Resize Parameter is greater than the largest side of the image')
ratio = float(self.params.resize_to) / float(max_side)
img_r = cv2.resize(img, None, fx=ratio, fy=ratio)
# write image with opencv
cv2.imwrite(new_path_file, img_r)
# read metadata with pyexiv2
old_meta = pyexiv2.ImageMetadata(path_file)
new_meta = pyexiv2.ImageMetadata(new_path_file)
old_meta.read()
new_meta.read()
# copy metadata
old_meta.copy(new_meta)
# update metadata size
new_meta['Exif.Photo.PixelXDimension'] = img_r.shape[0]
new_meta['Exif.Photo.PixelYDimension'] = img_r.shape[1]
new_meta.write()
# update photos array with new values
photo.path_file = new_path_file
photo.width = img_r.shape[0]
photo.height = img_r.shape[1]
photo.update_focal()
# log message
log.ODM_DEBUG('Resized %s | dimensions: %s' %
(photo.filename, img_r.shape))
else:
# log message
log.ODM_WARNING('Already resized %s | dimensions: %s x %s' %
(photo.filename, photo.width, photo.height))
photos = Pool().map(
partial(resize,
tree.dataset_raw,
tree.dataset_resize,
self.params.resize_to,
rerun_cell),
photos
)
log.ODM_INFO('Resized %s images' % len(photos))
@ -105,4 +119,3 @@ class ODMResizeCell(ecto.Cell):
log.ODM_INFO('Running ODM Resize Cell - Finished')
return ecto.OK if args.end_with != 'resize' else ecto.QUIT

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 193 KiB